uint64 count);
static void pgss_ExecutorFinish(QueryDesc *queryDesc);
static void pgss_ExecutorEnd(QueryDesc *queryDesc);
-static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
+static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag);
static uint32 pgss_hash_fn(const void *key, Size keysize);
* ProcessUtility hook
*/
static void
-pgss_ProcessUtility(Node *parsetree, const char *queryString,
+pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag)
{
+ Node *parsetree = pstmt->utilityStmt;
+
/*
* If it's an EXECUTE statement, we don't track it and don't increment the
* nesting level. This allows the cycles to be charged to the underlying
PG_TRY();
{
if (prev_ProcessUtility)
- prev_ProcessUtility(parsetree, queryString,
+ prev_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
else
- standard_ProcessUtility(parsetree, queryString,
+ standard_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
nested_level--;
else
{
if (prev_ProcessUtility)
- prev_ProcessUtility(parsetree, queryString,
+ prev_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
else
- standard_ProcessUtility(parsetree, queryString,
+ standard_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
}
* break whole of the things if nefarious user would use.
*/
static void
-sepgsql_utility_command(Node *parsetree,
+sepgsql_utility_command(PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
+ Node *parsetree = pstmt->utilityStmt;
sepgsql_context_info_t saved_context_info = sepgsql_context_info;
ListCell *cell;
}
if (next_ProcessUtility_hook)
- (*next_ProcessUtility_hook) (parsetree, queryString,
+ (*next_ProcessUtility_hook) (pstmt, queryString,
context, params,
dest, completionTag);
else
- standard_ProcessUtility(parsetree, queryString,
+ standard_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
}
querytree_list = NIL;
foreach(lc, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc);
+ RawStmt *parsetree = (RawStmt *) lfirst(lc);
List *querytree_sublist;
querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
/* non-export function prototypes */
-static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel, Node *raw_query,
- const Oid queryRelId, List *attnamelist,
+static CopyState BeginCopy(ParseState *pstate, bool is_from, Relation rel,
+ RawStmt *raw_query, Oid queryRelId, List *attnamelist,
List *options);
static void EndCopy(CopyState cstate);
static void ClosePipeToProgram(CopyState cstate);
-static CopyState BeginCopyTo(ParseState *pstate, Relation rel, Node *query,
- const Oid queryRelId, const char *filename, bool is_program,
+static CopyState BeginCopyTo(ParseState *pstate, Relation rel, RawStmt *query,
+ Oid queryRelId, const char *filename, bool is_program,
List *attnamelist, List *options);
static void EndCopyTo(CopyState cstate);
static uint64 DoCopyTo(CopyState cstate);
* Do not allow the copy if user doesn't have proper permission to access
* the table or the specifically requested columns.
*/
-Oid
-DoCopy(ParseState *pstate, const CopyStmt *stmt, uint64 *processed)
+void
+DoCopy(ParseState *pstate, const CopyStmt *stmt,
+ int stmt_location, int stmt_len,
+ uint64 *processed)
{
CopyState cstate;
bool is_from = stmt->is_from;
bool pipe = (stmt->filename == NULL);
Relation rel;
Oid relid;
- Node *query = NULL;
+ RawStmt *query = NULL;
List *range_table = NIL;
/* Disallow COPY to/from file or program except to superusers. */
select->targetList = targetList;
select->fromClause = list_make1(from);
- query = (Node *) select;
+ query = makeNode(RawStmt);
+ query->stmt = (Node *) select;
+ query->stmt_location = stmt_location;
+ query->stmt_len = stmt_len;
/*
* Close the relation for now, but keep the lock on it to prevent
{
Assert(stmt->query);
- query = stmt->query;
+ query = makeNode(RawStmt);
+ query->stmt = stmt->query;
+ query->stmt_location = stmt_location;
+ query->stmt_len = stmt_len;
+
relid = InvalidOid;
rel = NULL;
}
*/
if (rel != NULL)
heap_close(rel, (is_from ? NoLock : AccessShareLock));
-
- return relid;
}
/*
BeginCopy(ParseState *pstate,
bool is_from,
Relation rel,
- Node *raw_query,
- const Oid queryRelId,
+ RawStmt *raw_query,
+ Oid queryRelId,
List *attnamelist,
List *options)
{
* function and is executed repeatedly. (See also the same hack in
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
*/
- rewritten = pg_analyze_and_rewrite((Node *) copyObject(raw_query),
+ rewritten = pg_analyze_and_rewrite((RawStmt *) copyObject(raw_query),
pstate->p_sourcetext, NULL, 0);
/* check that we got back something we can work with */
static CopyState
BeginCopyTo(ParseState *pstate,
Relation rel,
- Node *query,
- const Oid queryRelId,
+ RawStmt *query,
+ Oid queryRelId,
const char *filename,
bool is_program,
List *attnamelist,
query = (Query *) linitial(rewritten);
Assert(query->commandType == CMD_SELECT);
- /* plan the query */
+ /* plan the query --- note we disallow parallelism */
plan = pg_plan_query(query, 0, params);
/*
#define X_CLOSE_IMMEDIATE 2
#define X_NOWHITESPACE 4
-static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
+static void ExplainOneQuery(Query *query, int cursorOptions,
+ IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
/* Explain every plan */
foreach(l, rewritten)
{
- ExplainOneQuery((Query *) lfirst(l), NULL, es,
+ ExplainOneQuery((Query *) lfirst(l),
+ CURSOR_OPT_PARALLEL_OK, NULL, es,
queryString, params);
/* Separate plans with an appropriate separator */
* "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
*/
static void
-ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
+ExplainOneQuery(Query *query, int cursorOptions,
+ IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params)
{
/* planner will not cope with utility statements */
/* if an advisor plugin is present, let it manage things */
if (ExplainOneQuery_hook)
- (*ExplainOneQuery_hook) (query, into, es, queryString, params);
+ (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
+ queryString, params);
else
{
PlannedStmt *plan;
INSTR_TIME_SET_CURRENT(planstart);
/* plan the query */
- plan = pg_plan_query(query, into ? 0 : CURSOR_OPT_PARALLEL_OK, params);
+ plan = pg_plan_query(query, cursorOptions, params);
INSTR_TIME_SET_CURRENT(planduration);
INSTR_TIME_SUBTRACT(planduration, planstart);
* We have to rewrite the contained SELECT and then pass it back to
* ExplainOneQuery. It's probably not really necessary to copy the
* contained parsetree another time, but let's be safe.
+ *
+ * Like ExecCreateTableAs, disallow parallelism in the plan.
*/
CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
List *rewritten;
Assert(IsA(ctas->query, Query));
rewritten = QueryRewrite((Query *) copyObject(ctas->query));
Assert(list_length(rewritten) == 1);
- ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es,
+ ExplainOneQuery((Query *) linitial(rewritten),
+ 0, ctas->into, es,
+ queryString, params);
+ }
+ else if (IsA(utilityStmt, DeclareCursorStmt))
+ {
+ /*
+ * Likewise for DECLARE CURSOR.
+ *
+ * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
+ * actually run the query. This is different from pre-8.3 behavior
+ * but seems more useful than not running the query. No cursor will
+ * be created, however.
+ */
+ DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
+ List *rewritten;
+
+ Assert(IsA(dcs->query, Query));
+ rewritten = QueryRewrite((Query *) copyObject(dcs->query));
+ Assert(list_length(rewritten) == 1);
+ ExplainOneQuery((Query *) linitial(rewritten),
+ dcs->options, NULL, es,
queryString, params);
}
else if (IsA(utilityStmt, ExecuteStmt))
* "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
* in which case executing the query should result in creating that table.
*
- * Since we ignore any DeclareCursorStmt that might be attached to the query,
- * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
- * query. This is different from pre-8.3 behavior but seems more useful than
- * not running the query. No cursor will be created, however.
- *
* This is exported because it's called back from prepare.c in the
* EXPLAIN EXECUTE case, and because an index advisor plugin would need
* to call it.
int eflags;
int instrument_option = 0;
+ Assert(plannedstmt->commandType != CMD_UTILITY);
+
if (es->analyze && es->timing)
instrument_option |= INSTRUMENT_TIMER;
else if (es->analyze)
*/
foreach(lc1, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc1);
+ RawStmt *parsetree = (RawStmt *) lfirst(lc1);
List *stmt_list;
ListCell *lc2;
foreach(lc2, stmt_list)
{
- Node *stmt = (Node *) lfirst(lc2);
-
- if (IsA(stmt, TransactionStmt))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("transaction control statements are not allowed within an extension script")));
+ PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
CommandCounterIncrement();
PushActiveSnapshot(GetTransactionSnapshot());
- if (IsA(stmt, PlannedStmt) &&
- ((PlannedStmt *) stmt)->utilityStmt == NULL)
+ if (stmt->utilityStmt == NULL)
{
QueryDesc *qdesc;
- qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+ qdesc = CreateQueryDesc(stmt,
sql,
GetActiveSnapshot(), NULL,
dest, NULL, 0);
}
else
{
+ if (IsA(stmt->utilityStmt, TransactionStmt))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("transaction control statements are not allowed within an extension script")));
+
ProcessUtility(stmt,
sql,
PROCESS_UTILITY_QUERY,
csstmt->authrole = NULL; /* will be created by current user */
csstmt->schemaElts = NIL;
csstmt->if_not_exists = false;
- CreateSchemaCommand(csstmt, NULL);
+ CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
+ -1, -1);
/*
* CreateSchemaCommand includes CommandCounterIncrement, so new
*/
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
/* Ensure creation schema is the one given in IMPORT statement */
cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
+ /* No planning needed, just make a wrapper PlannedStmt */
+ pstmt = makeNode(PlannedStmt);
+ pstmt->commandType = CMD_UTILITY;
+ pstmt->canSetTag = false;
+ pstmt->utilityStmt = (Node *) cstmt;
+ pstmt->stmt_location = rs->stmt_location;
+ pstmt->stmt_len = rs->stmt_len;
+
/* Execute statement */
- ProcessUtility((Node *) cstmt,
+ ProcessUtility(pstmt,
cmd,
PROCESS_UTILITY_SUBCOMMAND, NULL,
None_Receiver, NULL);
#include "commands/portalcmds.h"
#include "executor/executor.h"
#include "executor/tstoreReceiver.h"
+#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
/*
* PerformCursorOpen
* Execute SQL DECLARE CURSOR command.
- *
- * The query has already been through parse analysis, rewriting, and planning.
- * When it gets here, it looks like a SELECT PlannedStmt, except that the
- * utilityStmt field is set.
*/
void
-PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
+PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
const char *queryString, bool isTopLevel)
{
- DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
+ Query *query = (Query *) cstmt->query;
+ List *rewritten;
+ PlannedStmt *plan;
Portal portal;
MemoryContext oldContext;
- if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
- elog(ERROR, "PerformCursorOpen called for non-cursor query");
+ Assert(IsA(query, Query)); /* else parse analysis wasn't done */
/*
* Disallow empty-string cursor name (conflicts with protocol-level
if (!(cstmt->options & CURSOR_OPT_HOLD))
RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
+ /*
+ * Parse analysis was done already, but we still have to run the rule
+ * rewriter. We do not do AcquireRewriteLocks: we assume the query either
+ * came straight from the parser, or suitable locks were acquired by
+ * plancache.c.
+ *
+ * Because the rewriter and planner tend to scribble on the input, we make
+ * a preliminary copy of the source querytree. This prevents problems in
+ * the case that the DECLARE CURSOR is in a portal or plpgsql function and
+ * is executed repeatedly. (See also the same hack in EXPLAIN and
+ * PREPARE.) XXX FIXME someday.
+ */
+ rewritten = QueryRewrite((Query *) copyObject(query));
+
+ /* SELECT should never rewrite to more or less than one query */
+ if (list_length(rewritten) != 1)
+ elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
+
+ query = (Query *) linitial(rewritten);
+
+ if (query->commandType != CMD_SELECT)
+ elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
+
+ /* Plan the query, applying the specified options */
+ plan = pg_plan_query(query, cstmt->options, params);
+
/*
* Create a portal and copy the plan and queryString into its memory.
*/
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
- stmt = copyObject(stmt);
- stmt->utilityStmt = NULL; /* make it look like plain SELECT */
+ plan = copyObject(plan);
queryString = pstrdup(queryString);
NULL,
queryString,
"SELECT", /* cursor's query is always a SELECT */
- list_make1(stmt),
+ list_make1(plan),
NULL);
/*----------
portal->cursorOptions = cstmt->options;
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{
- if (stmt->rowMarks == NIL &&
- ExecSupportsBackwardScan(stmt->planTree))
+ if (plan->rowMarks == NIL &&
+ ExecSupportsBackwardScan(plan->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
else
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
* Implements the 'PREPARE' utility statement.
*/
void
-PrepareQuery(PrepareStmt *stmt, const char *queryString)
+PrepareQuery(PrepareStmt *stmt, const char *queryString,
+ int stmt_location, int stmt_len)
{
+ RawStmt *rawstmt;
CachedPlanSource *plansource;
Oid *argtypes = NULL;
int nargs;
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
errmsg("invalid statement name: must not be empty")));
+ /*
+ * Need to wrap the contained statement in a RawStmt node to pass it to
+ * parse analysis.
+ *
+ * Because parse analysis scribbles on the raw querytree, we must make a
+ * copy to ensure we don't modify the passed-in tree. FIXME someday.
+ */
+ rawstmt = makeNode(RawStmt);
+ rawstmt->stmt = (Node *) copyObject(stmt->query);
+ rawstmt->stmt_location = stmt_location;
+ rawstmt->stmt_len = stmt_len;
+
/*
* Create the CachedPlanSource before we do parse analysis, since it needs
* to see the unmodified raw parse tree.
*/
- plansource = CreateCachedPlan(stmt->query, queryString,
+ plansource = CreateCachedPlan(rawstmt, queryString,
CreateCommandTag(stmt->query));
/* Transform list of TypeNames to array of type OIDs */
* Analyze the statement using these parameter types (any parameters
* passed in from above us will not be visible to it), allowing
* information about unknown parameters to be deduced from context.
- *
- * Because parse analysis scribbles on the raw querytree, we must make a
- * copy to ensure we don't modify the passed-in tree. FIXME someday.
*/
- query = parse_analyze_varparams((Node *) copyObject(stmt->query),
- queryString,
+ query = parse_analyze_varparams(rawstmt, queryString,
&argtypes, &nargs);
/*
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
pstmt = (PlannedStmt *) linitial(plan_list);
- if (!IsA(pstmt, PlannedStmt) ||
- pstmt->commandType != CMD_SELECT ||
- pstmt->utilityStmt != NULL)
+ Assert(IsA(pstmt, PlannedStmt));
+ if (pstmt->commandType != CMD_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
{
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
- if (IsA(pstmt, PlannedStmt))
+ Assert(IsA(pstmt, PlannedStmt));
+ if (pstmt->commandType != CMD_UTILITY)
ExplainOnePlan(pstmt, into, es, query_string, paramLI, NULL);
else
- ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
+ ExplainOneUtility(pstmt->utilityStmt, into, es, query_string, paramLI);
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
/*
* CREATE SCHEMA
+ *
+ * Note: caller should pass in location information for the whole
+ * CREATE SCHEMA statement, which in turn we pass down as the location
+ * of the component commands. This comports with our general plan of
+ * reporting location/len for the whole command even when executing
+ * a subquery.
*/
Oid
-CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
+CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
+ int stmt_location, int stmt_len)
{
const char *schemaName = stmt->schemaname;
Oid namespaceId;
foreach(parsetree_item, parsetree_list)
{
Node *stmt = (Node *) lfirst(parsetree_item);
+ PlannedStmt *wrapper;
+
+ /* need to make a wrapper PlannedStmt */
+ wrapper = makeNode(PlannedStmt);
+ wrapper->commandType = CMD_UTILITY;
+ wrapper->canSetTag = false;
+ wrapper->utilityStmt = stmt;
+ wrapper->stmt_location = stmt_location;
+ wrapper->stmt_len = stmt_len;
/* do this step */
- ProcessUtility(stmt,
+ ProcessUtility(wrapper,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
None_Receiver,
NULL);
+
/* make sure later steps can see the object created here */
CommandCounterIncrement();
}
querytree_list = NIL;
foreach(list_item, raw_parsetree_list)
{
- Node *stmt = (Node *) lfirst(list_item);
+ RawStmt *rs = (RawStmt *) lfirst(list_item);
+ Node *stmt = rs->stmt;
if (IsA(stmt, IndexStmt))
querytree_list = lappend(querytree_list,
AlterTableStmt *atstmt = makeNode(AlterTableStmt);
AlterTableCmd *atcmd = makeNode(AlterTableCmd);
Constraint *fkcon = makeNode(Constraint);
+ PlannedStmt *wrapper = makeNode(PlannedStmt);
ereport(NOTICE,
(errmsg("converting trigger group into constraint \"%s\" %s",
fkcon->skip_validation = false;
fkcon->initially_valid = true;
+ /* finally, wrap it in a dummy PlannedStmt */
+ wrapper->commandType = CMD_UTILITY;
+ wrapper->canSetTag = false;
+ wrapper->utilityStmt = (Node *) atstmt;
+ wrapper->stmt_location = -1;
+ wrapper->stmt_len = -1;
+
/* ... and execute it */
- ProcessUtility((Node *) atstmt,
+ ProcessUtility(wrapper,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
PROCESS_UTILITY_SUBCOMMAND, NULL,
None_Receiver, NULL);
* Execute a CREATE VIEW command.
*/
ObjectAddress
-DefineView(ViewStmt *stmt, const char *queryString)
+DefineView(ViewStmt *stmt, const char *queryString,
+ int stmt_location, int stmt_len)
{
+ RawStmt *rawstmt;
Query *viewParse;
RangeVar *view;
ListCell *cell;
* Since parse analysis scribbles on its input, copy the raw parse tree;
* this ensures we don't corrupt a prepared statement, for example.
*/
- viewParse = parse_analyze((Node *) copyObject(stmt->query),
- queryString, NULL, 0);
+ rawstmt = makeNode(RawStmt);
+ rawstmt->stmt = (Node *) copyObject(stmt->query);
+ rawstmt->stmt_location = stmt_location;
+ rawstmt->stmt_len = stmt_len;
+
+ viewParse = parse_analyze(rawstmt, queryString, NULL, 0);
/*
* The grammar should ensure that the result is a single SELECT Query.
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("views must not contain SELECT INTO")));
- if (viewParse->commandType != CMD_SELECT ||
- viewParse->utilityStmt != NULL)
+ if (viewParse->commandType != CMD_SELECT)
elog(ERROR, "unexpected parse analysis result");
/*
pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL;
- pstmt->utilityStmt = NULL;
pstmt->subplans = NIL;
pstmt->rewindPlanIDs = NULL;
pstmt->rowMarks = NIL;
pstmt->relationOids = NIL;
pstmt->invalItems = NIL; /* workers can't replan anyway... */
pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
+ pstmt->utilityStmt = NULL;
+ pstmt->stmt_location = -1;
+ pstmt->stmt_len = -1;
/* Return serialized copy of our dummy PlannedStmt. */
return nodeToString(pstmt);
ExecStatus status;
bool setsResult; /* true if this query produces func's result */
bool lazyEval; /* true if should fetch one row at a time */
- Node *stmt; /* PlannedStmt or utility statement */
+ PlannedStmt *stmt; /* plan for this query */
QueryDesc *qd; /* null unless status == RUN */
} execution_state;
foreach(lc2, qtlist)
{
Query *queryTree = (Query *) lfirst(lc2);
- Node *stmt;
+ PlannedStmt *stmt;
execution_state *newes;
Assert(IsA(queryTree, Query));
/* Plan the query if needed */
if (queryTree->commandType == CMD_UTILITY)
- stmt = queryTree->utilityStmt;
+ {
+ /* Utility commands require no planning. */
+ stmt = makeNode(PlannedStmt);
+ stmt->commandType = CMD_UTILITY;
+ stmt->canSetTag = queryTree->canSetTag;
+ stmt->utilityStmt = queryTree->utilityStmt;
+ stmt->stmt_location = queryTree->stmt_location;
+ stmt->stmt_len = queryTree->stmt_len;
+ }
else
- stmt = (Node *) pg_plan_query(queryTree,
+ stmt = pg_plan_query(queryTree,
fcache->readonly_func ? CURSOR_OPT_PARALLEL_OK : 0,
- NULL);
+ NULL);
/*
* Precheck all commands for validity in a function. This should
* generally match the restrictions spi.c applies.
*/
- if (IsA(stmt, CopyStmt) &&
- ((CopyStmt *) stmt)->filename == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ if (stmt->commandType == CMD_UTILITY)
+ {
+ if (IsA(stmt->utilityStmt, CopyStmt) &&
+ ((CopyStmt *) stmt->utilityStmt)->filename == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot COPY to/from client in a SQL function")));
- if (IsA(stmt, TransactionStmt))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- /* translator: %s is a SQL statement name */
- errmsg("%s is not allowed in a SQL function",
- CreateCommandTag(stmt))));
+ if (IsA(stmt->utilityStmt, TransactionStmt))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is a SQL statement name */
+ errmsg("%s is not allowed in a SQL function",
+ CreateCommandTag(stmt->utilityStmt))));
+ }
if (fcache->readonly_func && !CommandIsReadOnly(stmt))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag(stmt))));
+ CreateCommandTag((Node *) stmt))));
if (IsInParallelMode() && !CommandIsReadOnly(stmt))
- PreventCommandIfParallelMode(CreateCommandTag(stmt));
+ PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
/* OK, build the execution_state for this query */
newes = (execution_state *) palloc(sizeof(execution_state));
{
lasttages->setsResult = true;
if (lazyEvalOK &&
- IsA(lasttages->stmt, PlannedStmt))
- {
- PlannedStmt *ps = (PlannedStmt *) lasttages->stmt;
-
- if (ps->commandType == CMD_SELECT &&
- ps->utilityStmt == NULL &&
- !ps->hasModifyingCTE)
- fcache->lazyEval = lasttages->lazyEval = true;
- }
+ lasttages->stmt->commandType == CMD_SELECT &&
+ !lasttages->stmt->hasModifyingCTE)
+ fcache->lazyEval = lasttages->lazyEval = true;
}
return eslist;
flat_query_list = NIL;
foreach(lc, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(lc);
+ RawStmt *parsetree = (RawStmt *) lfirst(lc);
List *queryTree_sublist;
queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
else
dest = None_Receiver;
- if (IsA(es->stmt, PlannedStmt))
- es->qd = CreateQueryDesc((PlannedStmt *) es->stmt,
- fcache->src,
- GetActiveSnapshot(),
- InvalidSnapshot,
- dest,
- fcache->paramLI, 0);
- else
- es->qd = CreateUtilityQueryDesc(es->stmt,
- fcache->src,
- GetActiveSnapshot(),
- dest,
- fcache->paramLI);
+ es->qd = CreateQueryDesc(es->stmt,
+ fcache->src,
+ GetActiveSnapshot(),
+ InvalidSnapshot,
+ dest,
+ fcache->paramLI, 0);
/* Utility commands don't need Executor. */
- if (es->qd->utilitystmt == NULL)
+ if (es->qd->operation != CMD_UTILITY)
{
/*
* In lazyEval mode, do not let the executor set up an AfterTrigger
{
bool result;
- if (es->qd->utilitystmt)
+ if (es->qd->operation == CMD_UTILITY)
{
- /* ProcessUtility needs the PlannedStmt for DECLARE CURSOR */
- ProcessUtility((es->qd->plannedstmt ?
- (Node *) es->qd->plannedstmt :
- es->qd->utilitystmt),
+ ProcessUtility(es->qd->plannedstmt,
fcache->src,
PROCESS_UTILITY_QUERY,
es->qd->params,
es->status = F_EXEC_DONE;
/* Utility commands don't need Executor. */
- if (es->qd->utilitystmt == NULL)
+ if (es->qd->operation != CMD_UTILITY)
{
ExecutorFinish(es->qd);
ExecutorEnd(es->qd);
* entities.
*/
if (parse &&
- parse->commandType == CMD_SELECT &&
- parse->utilityStmt == NULL)
+ parse->commandType == CMD_SELECT)
{
tlist_ptr = &parse->targetList;
tlist = parse->targetList;
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
{
if (list_length(stmt_list) == 1 &&
- IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+ ((PlannedStmt *) linitial(stmt_list))->commandType != CMD_UTILITY &&
((PlannedStmt *) linitial(stmt_list))->rowMarks == NIL &&
ExecSupportsBackwardScan(((PlannedStmt *) linitial(stmt_list))->planTree))
portal->cursorOptions |= CURSOR_OPT_SCROLL;
if (portal->cursorOptions & CURSOR_OPT_SCROLL)
{
if (list_length(stmt_list) == 1 &&
- IsA((Node *) linitial(stmt_list), PlannedStmt) &&
+ ((PlannedStmt *) linitial(stmt_list))->commandType != CMD_UTILITY &&
((PlannedStmt *) linitial(stmt_list))->rowMarks != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
foreach(lc, stmt_list)
{
- Node *pstmt = (Node *) lfirst(lc);
+ PlannedStmt *pstmt = (PlannedStmt *) lfirst(lc);
if (!CommandIsReadOnly(pstmt))
{
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag(pstmt))));
+ CreateCommandTag((Node *) pstmt))));
else
- PreventCommandIfParallelMode(CreateCommandTag(pstmt));
+ PreventCommandIfParallelMode(CreateCommandTag((Node *) pstmt));
}
}
}
foreach(list_item, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(list_item);
+ RawStmt *parsetree = (RawStmt *) lfirst(list_item);
List *stmt_list;
CachedPlanSource *plansource;
*/
plansource = CreateCachedPlan(parsetree,
src,
- CreateCommandTag(parsetree));
+ CreateCommandTag(parsetree->stmt));
/*
* Parameter datatypes are driven by parserSetup hook if provided,
foreach(list_item, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(list_item);
+ RawStmt *parsetree = (RawStmt *) lfirst(list_item);
CachedPlanSource *plansource;
plansource = CreateOneShotCachedPlan(parsetree,
src,
- CreateCommandTag(parsetree));
+ CreateCommandTag(parsetree->stmt));
plancache_list = lappend(plancache_list, plansource);
}
*/
if (plan->oneshot)
{
- Node *parsetree = plansource->raw_parse_tree;
+ RawStmt *parsetree = plansource->raw_parse_tree;
const char *src = plansource->query_string;
List *stmt_list;
foreach(lc2, stmt_list)
{
- Node *stmt = (Node *) lfirst(lc2);
- bool canSetTag;
+ PlannedStmt *stmt = (PlannedStmt *) lfirst(lc2);
+ bool canSetTag = stmt->canSetTag;
DestReceiver *dest;
_SPI_current->processed = 0;
_SPI_current->lastoid = InvalidOid;
_SPI_current->tuptable = NULL;
- if (IsA(stmt, PlannedStmt))
+ if (stmt->utilityStmt)
{
- canSetTag = ((PlannedStmt *) stmt)->canSetTag;
- }
- else
- {
- /* utilities are canSetTag if only thing in list */
- canSetTag = (list_length(stmt_list) == 1);
-
- if (IsA(stmt, CopyStmt))
+ if (IsA(stmt->utilityStmt, CopyStmt))
{
- CopyStmt *cstmt = (CopyStmt *) stmt;
+ CopyStmt *cstmt = (CopyStmt *) stmt->utilityStmt;
if (cstmt->filename == NULL)
{
goto fail;
}
}
- else if (IsA(stmt, TransactionStmt))
+ else if (IsA(stmt->utilityStmt, TransactionStmt))
{
my_res = SPI_ERROR_TRANSACTION;
goto fail;
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
errmsg("%s is not allowed in a non-volatile function",
- CreateCommandTag(stmt))));
+ CreateCommandTag((Node *) stmt))));
if (IsInParallelMode() && !CommandIsReadOnly(stmt))
- PreventCommandIfParallelMode(CreateCommandTag(stmt));
+ PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
/*
* If not read-only mode, advance the command counter before each
dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
- if (IsA(stmt, PlannedStmt) &&
- ((PlannedStmt *) stmt)->utilityStmt == NULL)
+ if (stmt->utilityStmt == NULL)
{
QueryDesc *qdesc;
Snapshot snap;
else
snap = InvalidSnapshot;
- qdesc = CreateQueryDesc((PlannedStmt *) stmt,
+ qdesc = CreateQueryDesc(stmt,
plansource->query_string,
snap, crosscheck_snapshot,
dest,
* Some utility statements return a row count, even though the
* tuples are not returned to the caller.
*/
- if (IsA(stmt, CreateTableAsStmt))
+ if (IsA(stmt->utilityStmt, CreateTableAsStmt))
{
- CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt;
+ CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
if (strncmp(completionTag, "SELECT ", 7) == 0)
_SPI_current->processed =
if (ctastmt->is_select_into)
res = SPI_OK_SELINTO;
}
- else if (IsA(stmt, CopyStmt))
+ else if (IsA(stmt->utilityStmt, CopyStmt))
{
Assert(strncmp(completionTag, "COPY ", 5) == 0);
_SPI_current->processed = pg_strtouint64(completionTag + 5,
switch (operation)
{
case CMD_SELECT:
- Assert(queryDesc->plannedstmt->utilityStmt == NULL);
if (queryDesc->dest->mydest != DestSPI)
{
/* Don't return SPI_OK_SELECT if we're discarding result */
COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations);
- COPY_NODE_FIELD(utilityStmt);
COPY_NODE_FIELD(subplans);
COPY_BITMAPSET_FIELD(rewindPlanIDs);
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(relationOids);
COPY_NODE_FIELD(invalItems);
COPY_SCALAR_FIELD(nParamExec);
+ COPY_NODE_FIELD(utilityStmt);
+ COPY_LOCATION_FIELD(stmt_location);
+ COPY_LOCATION_FIELD(stmt_len);
return newnode;
}
COPY_NODE_FIELD(setOperations);
COPY_NODE_FIELD(constraintDeps);
COPY_NODE_FIELD(withCheckOptions);
+ COPY_LOCATION_FIELD(stmt_location);
+ COPY_LOCATION_FIELD(stmt_len);
+
+ return newnode;
+}
+
+static RawStmt *
+_copyRawStmt(const RawStmt *from)
+{
+ RawStmt *newnode = makeNode(RawStmt);
+
+ COPY_NODE_FIELD(stmt);
+ COPY_LOCATION_FIELD(stmt_location);
+ COPY_LOCATION_FIELD(stmt_len);
return newnode;
}
case T_Query:
retval = _copyQuery(from);
break;
+ case T_RawStmt:
+ retval = _copyRawStmt(from);
+ break;
case T_InsertStmt:
retval = _copyInsertStmt(from);
break;
COMPARE_NODE_FIELD(setOperations);
COMPARE_NODE_FIELD(constraintDeps);
COMPARE_NODE_FIELD(withCheckOptions);
+ COMPARE_LOCATION_FIELD(stmt_location);
+ COMPARE_LOCATION_FIELD(stmt_len);
+
+ return true;
+}
+
+static bool
+_equalRawStmt(const RawStmt *a, const RawStmt *b)
+{
+ COMPARE_NODE_FIELD(stmt);
+ COMPARE_LOCATION_FIELD(stmt_location);
+ COMPARE_LOCATION_FIELD(stmt_len);
return true;
}
case T_Query:
retval = _equalQuery(a, b);
break;
+ case T_RawStmt:
+ retval = _equalRawStmt(a, b);
+ break;
case T_InsertStmt:
retval = _equalInsertStmt(a, b);
break;
WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations);
- WRITE_NODE_FIELD(utilityStmt);
WRITE_NODE_FIELD(subplans);
WRITE_BITMAPSET_FIELD(rewindPlanIDs);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_INT_FIELD(nParamExec);
+ WRITE_NODE_FIELD(utilityStmt);
+ WRITE_LOCATION_FIELD(stmt_location);
+ WRITE_LOCATION_FIELD(stmt_len);
}
/*
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
WRITE_NODE_FIELD(constraintDeps);
+ /* withCheckOptions intentionally omitted, see comment in parsenodes.h */
+ WRITE_LOCATION_FIELD(stmt_location);
+ WRITE_LOCATION_FIELD(stmt_len);
}
static void
READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
READ_NODE_FIELD(constraintDeps);
+ /* withCheckOptions intentionally omitted, see comment in parsenodes.h */
+ READ_LOCATION_FIELD(stmt_location);
+ READ_LOCATION_FIELD(stmt_len);
READ_DONE();
}
READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations);
- READ_NODE_FIELD(utilityStmt);
READ_NODE_FIELD(subplans);
READ_BITMAPSET_FIELD(rewindPlanIDs);
READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(relationOids);
READ_NODE_FIELD(invalItems);
READ_INT_FIELD(nParamExec);
+ READ_NODE_FIELD(utilityStmt);
+ READ_LOCATION_FIELD(stmt_location);
+ READ_LOCATION_FIELD(stmt_len);
READ_DONE();
}
ListCell *lp,
*lr;
- /* Cursor options may come from caller or from DECLARE CURSOR stmt */
- if (parse->utilityStmt &&
- IsA(parse->utilityStmt, DeclareCursorStmt))
- cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;
-
/*
* Set up global state for this planner invocation. This data is needed
* across all levels of sub-Query that might exist in the given command,
IsUnderPostmaster &&
dynamic_shared_memory_type != DSM_IMPL_NONE &&
parse->commandType == CMD_SELECT &&
- parse->utilityStmt == NULL &&
!parse->hasModifyingCTE &&
max_parallel_workers_per_gather > 0 &&
!IsParallelWorker() &&
result->planTree = top_plan;
result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations;
- result->utilityStmt = parse->utilityStmt;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->rowMarks = glob->finalrowmarks;
result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems;
result->nParamExec = glob->nParamExec;
+ /* utilityStmt should be null, but we might as well copy it */
+ result->utilityStmt = parse->utilityStmt;
+ result->stmt_location = parse->stmt_location;
+ result->stmt_len = parse->stmt_len;
return result;
}
* Let's just make sure it's a valid subselect ...
*/
if (!IsA(subquery, Query) ||
- subquery->commandType != CMD_SELECT ||
- subquery->utilityStmt != NULL)
+ subquery->commandType != CMD_SELECT)
elog(ERROR, "subquery is bogus");
/*
/* Let's just make sure it's a valid subselect ... */
if (!IsA(subquery, Query) ||
- subquery->commandType != CMD_SELECT ||
- subquery->utilityStmt != NULL)
+ subquery->commandType != CMD_SELECT)
elog(ERROR, "subquery is bogus");
/* Is it a set-operation query at all? */
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->utilityStmt ||
querytree->hasAggs ||
querytree->hasWindowFuncs ||
querytree->hasTargetSRFs ||
* The single command must be a plain SELECT.
*/
if (!IsA(querytree, Query) ||
- querytree->commandType != CMD_SELECT ||
- querytree->utilityStmt)
+ querytree->commandType != CMD_SELECT)
goto fail;
/*
/* Hook for plugins to get control at end of parse analysis */
post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
+static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree);
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
static List *transformInsertRow(ParseState *pstate, List *exprlist,
* a dummy CMD_UTILITY Query node.
*/
Query *
-parse_analyze(Node *parseTree, const char *sourceText,
+parse_analyze(RawStmt *parseTree, const char *sourceText,
Oid *paramTypes, int numParams)
{
ParseState *pstate = make_parsestate(NULL);
* be modified or enlarged (via repalloc).
*/
Query *
-parse_analyze_varparams(Node *parseTree, const char *sourceText,
+parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams)
{
ParseState *pstate = make_parsestate(NULL);
* transformTopLevelStmt -
* transform a Parse tree into a Query tree.
*
+ * This function is just responsible for transferring statement location data
+ * from the RawStmt into the finished Query.
+ */
+Query *
+transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree)
+{
+ Query *result;
+
+ /* We're at top level, so allow SELECT INTO */
+ result = transformOptionalSelectInto(pstate, parseTree->stmt);
+
+ result->stmt_location = parseTree->stmt_location;
+ result->stmt_len = parseTree->stmt_len;
+
+ return result;
+}
+
+/*
+ * transformOptionalSelectInto -
+ * If SELECT has INTO, convert it to CREATE TABLE AS.
+ *
* The only thing we do here that we don't do in transformStmt() is to
* convert SELECT ... INTO into CREATE TABLE AS. Since utility statements
* aren't allowed within larger statements, this is only allowed at the top
* of the parse tree, and so we only try it before entering the recursive
* transformStmt() processing.
*/
-Query *
-transformTopLevelStmt(ParseState *pstate, Node *parseTree)
+static Query *
+transformOptionalSelectInto(ParseState *pstate, Node *parseTree)
{
if (IsA(parseTree, SelectStmt))
{
* Classification here should match transformStmt().
*/
bool
-analyze_requires_snapshot(Node *parseTree)
+analyze_requires_snapshot(RawStmt *parseTree)
{
bool result;
- switch (nodeTag(parseTree))
+ switch (nodeTag(parseTree->stmt))
{
/*
* Optimizable statements
* Special cases
*/
case T_DeclareCursorStmt:
- /* yes, because it's analyzed just like SELECT */
- result = true;
- break;
-
case T_ExplainStmt:
case T_CreateTableAsStmt:
/* yes, because we must analyze the contained statement */
/* The grammar should have produced a SELECT */
if (!IsA(selectQuery, Query) ||
- selectQuery->commandType != CMD_SELECT ||
- selectQuery->utilityStmt != NULL)
+ selectQuery->commandType != CMD_SELECT)
elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
/*
* transformDeclareCursorStmt -
* transform a DECLARE CURSOR Statement
*
- * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not
- * significantly different from a SELECT) as far as parsing/rewriting/planning
- * are concerned, but it's not passed to the executor and so in that sense is
- * a utility statement. We transform it into a Query exactly as if it were
- * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt
- * field to carry the cursor name and options.
+ * DECLARE CURSOR is like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node; however, we must first transform the contained
+ * query. We used to postpone that until execution, but it's really necessary
+ * to do it during the normal parse analysis phase to ensure that side effects
+ * of parser hooks happen at the expected time.
*/
static Query *
transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
{
Query *result;
+ Query *query;
/*
* Don't allow both SCROLL and NO SCROLL to be specified
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("cannot specify both SCROLL and NO SCROLL")));
- result = transformStmt(pstate, stmt->query);
+ /* Transform contained query, not allowing SELECT INTO */
+ query = transformStmt(pstate, stmt->query);
+ stmt->query = (Node *) query;
/* Grammar should not have allowed anything but SELECT */
- if (!IsA(result, Query) ||
- result->commandType != CMD_SELECT ||
- result->utilityStmt != NULL)
+ if (!IsA(query, Query) ||
+ query->commandType != CMD_SELECT)
elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
/*
* allowed, but the semantics of when the updates occur might be
* surprising.)
*/
- if (result->hasModifyingCTE)
+ if (query->hasModifyingCTE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
/* FOR UPDATE and WITH HOLD are not compatible */
- if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
+ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------
translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported",
LCS_asString(((RowMarkClause *)
- linitial(result->rowMarks))->strength)),
+ linitial(query->rowMarks))->strength)),
errdetail("Holdable cursors must be READ ONLY.")));
/* FOR UPDATE and SCROLL are not compatible */
- if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
+ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------
translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE SCROLL CURSOR ... %s is not supported",
LCS_asString(((RowMarkClause *)
- linitial(result->rowMarks))->strength)),
+ linitial(query->rowMarks))->strength)),
errdetail("Scrollable cursors must be READ ONLY.")));
/* FOR UPDATE and INSENSITIVE are not compatible */
- if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
+ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/*------
translator: %s is a SQL row locking clause such as FOR UPDATE */
errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported",
LCS_asString(((RowMarkClause *)
- linitial(result->rowMarks))->strength)),
+ linitial(query->rowMarks))->strength)),
errdetail("Insensitive cursors must be READ ONLY.")));
- /* We won't need the raw querytree any more */
- stmt->query = NULL;
-
+ /* represent the command as a utility Query */
+ result = makeNode(Query);
+ result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt;
return result;
Query *result;
/* transform contained query, allowing SELECT INTO */
- stmt->query = (Node *) transformTopLevelStmt(pstate, stmt->query);
+ stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query);
/* represent the command as a utility Query */
result = makeNode(Query);
* transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW
* Statement
*
- * As with EXPLAIN, transform the contained statement now.
+ * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now.
*/
static Query *
transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
Query *result;
Query *query;
- /* transform contained query */
+ /* transform contained query, not allowing SELECT INTO */
query = transformStmt(pstate, stmt->query);
stmt->query = (Node *) query;
/*
* The above macro assigns -1 (unknown) as the parse location of any
- * nonterminal that was reduced from an empty rule. This is problematic
+ * nonterminal that was reduced from an empty rule, or whose leftmost
+ * component was reduced from an empty rule. This is problematic
* for nonterminals defined like
* OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ;
* because we'll set -1 as the location during the first reduction and then
* (Although we have many nonterminals that follow this pattern, we only
* bother with fixing @$ like this when the nonterminal's parse location
* is actually referenced in some rule.)
+ *
+ * A cleaner answer would be to make YYLLOC_DEFAULT scan all the Rhs
+ * locations until it's found one that's not -1. Then we'd get a correct
+ * location for any nonterminal that isn't entirely empty. But this way
+ * would add overhead to every rule reduction, and so far there's not been
+ * a compelling reason to pay that overhead.
*/
/*
static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner,
const char *msg);
+static RawStmt *makeRawStmt(Node *stmt, int stmt_location);
+static void updateRawStmtEnd(RawStmt *rs, int end_location);
static Node *makeColumnRef(char *colname, List *indirection,
int location, core_yyscan_t yyscanner);
static Node *makeTypeCast(Node *arg, TypeName *typename, int location);
}
;
-/* the thrashing around here is to discard "empty" statements... */
+/*
+ * At top level, we wrap each stmt with a RawStmt node carrying start location
+ * and length of the stmt's text. Notice that the start loc/len are driven
+ * entirely from semicolon locations (@2). It would seem natural to use
+ * @1 or @3 to get the true start location of a stmt, but that doesn't work
+ * for statements that can start with empty nonterminals (opt_with_clause is
+ * the main offender here); as noted in the comments for YYLLOC_DEFAULT,
+ * we'd get -1 for the location in such cases.
+ * We also take care to discard empty statements entirely.
+ */
stmtmulti: stmtmulti ';' stmt
{
+ if ($1 != NIL)
+ {
+ /* update length of previous stmt */
+ updateRawStmtEnd((RawStmt *) llast($1), @2);
+ }
if ($3 != NULL)
- $$ = lappend($1, $3);
+ $$ = lappend($1, makeRawStmt($3, @2 + 1));
else
$$ = $1;
}
| stmt
{
if ($1 != NULL)
- $$ = list_make1($1);
+ $$ = list_make1(makeRawStmt($1, 0));
else
$$ = NIL;
}
parser_yyerror(msg);
}
+static RawStmt *
+makeRawStmt(Node *stmt, int stmt_location)
+{
+ RawStmt *rs = makeNode(RawStmt);
+
+ rs->stmt = stmt;
+ rs->stmt_location = stmt_location;
+ rs->stmt_len = 0; /* might get changed later */
+ return rs;
+}
+
+/* Adjust a RawStmt to reflect that it doesn't run to the end of the string */
+static void
+updateRawStmtEnd(RawStmt *rs, int end_location)
+{
+ /*
+ * If we already set the length, don't change it. This is for situations
+ * like "select foo ;; select bar" where the same statement will be last
+ * in the string for more than one semicolon.
+ */
+ if (rs->stmt_len > 0)
+ return;
+
+ /* OK, update length of RawStmt */
+ rs->stmt_len = end_location - rs->stmt_location;
+}
+
static Node *
makeColumnRef(char *colname, List *indirection,
int location, core_yyscan_t yyscanner)
pstate->p_expr_kind = EXPR_KIND_NONE;
/*
- * Check that we got something reasonable. Many of these conditions are
- * impossible given restrictions of the grammar, but check 'em anyway.
+ * Check that we got a SELECT. Anything else should be impossible given
+ * restrictions of the grammar, but check anyway.
*/
if (!IsA(query, Query) ||
- query->commandType != CMD_SELECT ||
- query->utilityStmt != NULL)
+ query->commandType != CMD_SELECT)
elog(ERROR, "unexpected non-SELECT command in subquery in FROM");
/*
qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
/*
- * Check that we got something reasonable. Many of these conditions are
- * impossible given restrictions of the grammar, but check 'em anyway.
+ * Check that we got a SELECT. Anything else should be impossible given
+ * restrictions of the grammar, but check anyway.
*/
if (!IsA(qtree, Query) ||
- qtree->commandType != CMD_SELECT ||
- qtree->utilityStmt != NULL)
+ qtree->commandType != CMD_SELECT)
elog(ERROR, "unexpected non-SELECT command in SubLink");
sublink->subselect = (Node *) qtree;
*/
if (list_length(raw_parsetree_list) != 1)
goto fail;
- stmt = (SelectStmt *) linitial(raw_parsetree_list);
+ stmt = (SelectStmt *) ((RawStmt *) linitial(raw_parsetree_list))->stmt;
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL ||
* raw_parser
* Given a query in string form, do lexical and grammatical analysis.
*
- * Returns a list of raw (un-analyzed) parse trees.
+ * Returns a list of raw (un-analyzed) parse trees. The immediate elements
+ * of the list are always RawStmt nodes.
*/
List *
raw_parser(const char *str)
*/
query = (Query *) linitial(action);
if (!is_instead ||
- query->commandType != CMD_SELECT ||
- query->utilityStmt != NULL)
+ query->commandType != CMD_SELECT)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("rules on SELECT must have action INSTEAD SELECT")));
static void start_xact_command(void);
static void finish_xact_command(void);
static bool IsTransactionExitStmt(Node *parsetree);
-static bool IsTransactionExitStmtList(List *parseTrees);
-static bool IsTransactionStmtList(List *parseTrees);
+static bool IsTransactionExitStmtList(List *pstmts);
+static bool IsTransactionStmtList(List *pstmts);
static void drop_unnamed_stmt(void);
static void SigHupHandler(SIGNAL_ARGS);
static void log_disconnections(int code, Datum arg);
/*
* Do raw parsing (only).
*
- * A list of parsetrees is returned, since there might be multiple
- * commands in the given string.
+ * A list of parsetrees (RawStmt nodes) is returned, since there might be
+ * multiple commands in the given string.
*
* NOTE: for interactive queries, it is important to keep this routine
* separate from the analysis & rewrite stages. Analysis and rewriting
* NOTE: for reasons mentioned above, this must be separate from raw parsing.
*/
List *
-pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
+pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
Oid *paramTypes, int numParams)
{
Query *query;
* hooks instead of a fixed list of parameter datatypes.
*/
List *
-pg_analyze_and_rewrite_params(Node *parsetree,
+pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg)
/*
* Generate plans for a list of already-rewritten queries.
*
- * Normal optimizable statements generate PlannedStmt entries in the result
- * list. Utility statements are simply represented by their statement nodes.
+ * For normal optimizable statements, invoke the planner. For utility
+ * statements, just make a wrapper PlannedStmt node.
+ *
+ * The result is a list of PlannedStmt nodes.
*/
List *
pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
foreach(query_list, querytrees)
{
Query *query = (Query *) lfirst(query_list);
- Node *stmt;
+ PlannedStmt *stmt;
if (query->commandType == CMD_UTILITY)
{
- /* Utility commands have no plans. */
- stmt = query->utilityStmt;
+ /* Utility commands require no planning. */
+ stmt = makeNode(PlannedStmt);
+ stmt->commandType = CMD_UTILITY;
+ stmt->canSetTag = query->canSetTag;
+ stmt->utilityStmt = query->utilityStmt;
+ stmt->stmt_location = query->stmt_location;
+ stmt->stmt_len = query->stmt_len;
}
else
{
- stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
+ stmt = pg_plan_query(query, cursorOptions, boundParams);
}
stmt_list = lappend(stmt_list, stmt);
*/
foreach(parsetree_item, parsetree_list)
{
- Node *parsetree = (Node *) lfirst(parsetree_item);
+ RawStmt *parsetree = (RawStmt *) lfirst(parsetree_item);
bool snapshot_set = false;
const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
* do any special start-of-SQL-command processing needed by the
* destination.
*/
- commandTag = CreateCommandTag(parsetree);
+ commandTag = CreateCommandTag(parsetree->stmt);
set_ps_display(commandTag, false);
* state, but not many...)
*/
if (IsAbortedTransactionBlockState() &&
- !IsTransactionExitStmt(parsetree))
+ !IsTransactionExitStmt(parsetree->stmt))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
* backward compatibility...)
*/
format = 0; /* TEXT is default */
- if (IsA(parsetree, FetchStmt))
+ if (IsA(parsetree->stmt, FetchStmt))
{
- FetchStmt *stmt = (FetchStmt *) parsetree;
+ FetchStmt *stmt = (FetchStmt *) parsetree->stmt;
if (!stmt->ismove)
{
PortalDrop(portal, false);
- if (IsA(parsetree, TransactionStmt))
+ if (IsA(parsetree->stmt, TransactionStmt))
{
/*
* If this was a transaction control statement, commit it. We will
MemoryContext unnamed_stmt_context = NULL;
MemoryContext oldcontext;
List *parsetree_list;
- Node *raw_parse_tree;
+ RawStmt *raw_parse_tree;
const char *commandTag;
List *querytree_list;
CachedPlanSource *psrc;
bool snapshot_set = false;
int i;
- raw_parse_tree = (Node *) linitial(parsetree_list);
+ raw_parse_tree = (RawStmt *) linitial(parsetree_list);
/*
* Get the command name for possible use in status display.
*/
- commandTag = CreateCommandTag(raw_parse_tree);
+ commandTag = CreateCommandTag(raw_parse_tree->stmt);
/*
* If we are in an aborted transaction, reject all commands except
* state, but not many...)
*/
if (IsAbortedTransactionBlockState() &&
- !IsTransactionExitStmt(raw_parse_tree))
+ !IsTransactionExitStmt(raw_parse_tree->stmt))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
* functions.
*/
if (IsAbortedTransactionBlockState() &&
- (!IsTransactionExitStmt(psrc->raw_parse_tree) ||
+ (!IsTransactionExitStmt(psrc->raw_parse_tree->stmt) ||
numParams != 0))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
foreach(parsetree_item, raw_parsetree_list)
{
- Node *parsetree = (Node *) lfirst(parsetree_item);
+ RawStmt *parsetree = (RawStmt *) lfirst(parsetree_item);
- if (IsA(parsetree, ExecuteStmt))
+ if (IsA(parsetree->stmt, ExecuteStmt))
{
- ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
+ ExecuteStmt *stmt = (ExecuteStmt *) parsetree->stmt;
PreparedStatement *pstmt;
pstmt = FetchPreparedStatement(stmt->name, false);
return false;
}
-/* Test a list that might contain Query nodes or bare parsetrees */
+/* Test a list that contains PlannedStmt nodes */
static bool
-IsTransactionExitStmtList(List *parseTrees)
+IsTransactionExitStmtList(List *pstmts)
{
- if (list_length(parseTrees) == 1)
+ if (list_length(pstmts) == 1)
{
- Node *stmt = (Node *) linitial(parseTrees);
-
- if (IsA(stmt, Query))
- {
- Query *query = (Query *) stmt;
+ PlannedStmt *pstmt = (PlannedStmt *) linitial(pstmts);
- if (query->commandType == CMD_UTILITY &&
- IsTransactionExitStmt(query->utilityStmt))
- return true;
- }
- else if (IsTransactionExitStmt(stmt))
+ Assert(IsA(pstmt, PlannedStmt));
+ if (pstmt->commandType == CMD_UTILITY &&
+ IsTransactionExitStmt(pstmt->utilityStmt))
return true;
}
return false;
}
-/* Test a list that might contain Query nodes or bare parsetrees */
+/* Test a list that contains PlannedStmt nodes */
static bool
-IsTransactionStmtList(List *parseTrees)
+IsTransactionStmtList(List *pstmts)
{
- if (list_length(parseTrees) == 1)
+ if (list_length(pstmts) == 1)
{
- Node *stmt = (Node *) linitial(parseTrees);
+ PlannedStmt *pstmt = (PlannedStmt *) linitial(pstmts);
- if (IsA(stmt, Query))
- {
- Query *query = (Query *) stmt;
-
- if (query->commandType == CMD_UTILITY &&
- IsA(query->utilityStmt, TransactionStmt))
- return true;
- }
- else if (IsA(stmt, TransactionStmt))
+ Assert(IsA(pstmt, PlannedStmt));
+ if (pstmt->commandType == CMD_UTILITY &&
+ IsA(pstmt->utilityStmt, TransactionStmt))
return true;
}
return false;
DestReceiver *dest);
static uint64 PortalRunSelect(Portal portal, bool forward, long count,
DestReceiver *dest);
-static void PortalRunUtility(Portal portal, Node *utilityStmt,
+static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, char *completionTag);
static void PortalRunMulti(Portal portal,
qd->operation = plannedstmt->commandType; /* operation */
qd->plannedstmt = plannedstmt; /* plan */
- qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
qd->sourceText = sourceText; /* query text */
qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
/* RI check snapshot */
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
*/
* ChoosePortalStrategy
* Select portal execution strategy given the intended statement list.
*
- * The list elements can be Querys, PlannedStmts, or utility statements.
+ * The list elements can be Querys or PlannedStmts.
* That's more general than portals need, but plancache.c uses this too.
*
* See the comments in portal.h.
if (query->canSetTag)
{
- if (query->commandType == CMD_SELECT &&
- query->utilityStmt == NULL)
+ if (query->commandType == CMD_SELECT)
{
if (query->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH;
else
return PORTAL_ONE_SELECT;
}
- if (query->commandType == CMD_UTILITY &&
- query->utilityStmt != NULL)
+ if (query->commandType == CMD_UTILITY)
{
if (UtilityReturnsTuples(query->utilityStmt))
return PORTAL_UTIL_SELECT;
if (pstmt->canSetTag)
{
- if (pstmt->commandType == CMD_SELECT &&
- pstmt->utilityStmt == NULL)
+ if (pstmt->commandType == CMD_SELECT)
{
if (pstmt->hasModifyingCTE)
return PORTAL_ONE_MOD_WITH;
else
return PORTAL_ONE_SELECT;
}
+ if (pstmt->commandType == CMD_UTILITY)
+ {
+ if (UtilityReturnsTuples(pstmt->utilityStmt))
+ return PORTAL_UTIL_SELECT;
+ /* it can't be ONE_RETURNING, so give up */
+ return PORTAL_MULTI_QUERY;
+ }
}
}
else
- {
- /* must be a utility command; assume it's canSetTag */
- if (UtilityReturnsTuples(stmt))
- return PORTAL_UTIL_SELECT;
- /* it can't be ONE_RETURNING, so give up */
- return PORTAL_MULTI_QUERY;
- }
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
}
/*
{
if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */
- if (query->returningList == NIL)
+ if (query->commandType == CMD_UTILITY ||
+ query->returningList == NIL)
return PORTAL_MULTI_QUERY; /* no need to look further */
}
}
{
if (++nSetTag > 1)
return PORTAL_MULTI_QUERY; /* no need to look further */
- if (!pstmt->hasReturning)
+ if (pstmt->commandType == CMD_UTILITY ||
+ !pstmt->hasReturning)
return PORTAL_MULTI_QUERY; /* no need to look further */
}
}
- /* otherwise, utility command, assumed not canSetTag */
+ else
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
}
if (nSetTag == 1)
return PORTAL_ONE_RETURNING;
if (portal->strategy == PORTAL_MULTI_QUERY)
return NIL;
/* get the primary statement and find out what it returns */
- return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
+ return FetchStatementTargetList((Node *) PortalGetPrimaryStmt(portal));
}
/*
* Given a statement that returns tuples, extract the query targetlist.
* Returns NIL if the statement doesn't have a determinable targetlist.
*
- * This can be applied to a Query, a PlannedStmt, or a utility statement.
+ * This can be applied to a Query or a PlannedStmt.
* That's more general than portals need, but plancache.c uses this too.
*
* Note: do not modify the result.
{
Query *query = (Query *) stmt;
- if (query->commandType == CMD_UTILITY &&
- query->utilityStmt != NULL)
+ if (query->commandType == CMD_UTILITY)
{
/* transfer attention to utility statement */
stmt = query->utilityStmt;
}
else
{
- if (query->commandType == CMD_SELECT &&
- query->utilityStmt == NULL)
+ if (query->commandType == CMD_SELECT)
return query->targetList;
if (query->returningList)
return query->returningList;
{
PlannedStmt *pstmt = (PlannedStmt *) stmt;
- if (pstmt->commandType == CMD_SELECT &&
- pstmt->utilityStmt == NULL)
- return pstmt->planTree->targetlist;
- if (pstmt->hasReturning)
- return pstmt->planTree->targetlist;
- return NIL;
+ if (pstmt->commandType == CMD_UTILITY)
+ {
+ /* transfer attention to utility statement */
+ stmt = pstmt->utilityStmt;
+ }
+ else
+ {
+ if (pstmt->commandType == CMD_SELECT)
+ return pstmt->planTree->targetlist;
+ if (pstmt->hasReturning)
+ return pstmt->planTree->targetlist;
+ return NIL;
+ }
}
if (IsA(stmt, FetchStmt))
{
{
PlannedStmt *pstmt;
- pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
- Assert(IsA(pstmt, PlannedStmt));
+ pstmt = PortalGetPrimaryStmt(portal);
portal->tupDesc =
ExecCleanTypeFromTL(pstmt->planTree->targetlist,
false);
* take care of it if needed.
*/
{
- Node *ustmt = PortalGetPrimaryStmt(portal);
+ PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
- Assert(!IsA(ustmt, PlannedStmt));
- portal->tupDesc = UtilityTupleDescriptor(ustmt);
+ Assert(pstmt->commandType == CMD_UTILITY);
+ portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
}
/*
break;
case PORTAL_UTIL_SELECT:
- PortalRunUtility(portal, (Node *) linitial(portal->stmts),
+ PortalRunUtility(portal, (PlannedStmt *) linitial(portal->stmts),
isTopLevel, true, treceiver, completionTag);
break;
* Execute a utility statement inside a portal.
*/
static void
-PortalRunUtility(Portal portal, Node *utilityStmt,
+PortalRunUtility(Portal portal, PlannedStmt *pstmt,
bool isTopLevel, bool setHoldSnapshot,
DestReceiver *dest, char *completionTag)
{
+ Node *utilityStmt = pstmt->utilityStmt;
Snapshot snapshot;
/*
else
snapshot = NULL;
- ProcessUtility(utilityStmt,
+ ProcessUtility(pstmt,
portal->sourceText,
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
portal->portalParams,
*/
foreach(stmtlist_item, portal->stmts)
{
- Node *stmt = (Node *) lfirst(stmtlist_item);
+ PlannedStmt *pstmt = (PlannedStmt *) lfirst(stmtlist_item);
/*
* If we got a cancel signal in prior command, quit
*/
CHECK_FOR_INTERRUPTS();
- if (IsA(stmt, PlannedStmt) &&
- ((PlannedStmt *) stmt)->utilityStmt == NULL)
+ if (pstmt->utilityStmt == NULL)
{
/*
* process a plannable query.
*/
- PlannedStmt *pstmt = (PlannedStmt *) stmt;
-
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
if (log_executor_stats)
/*
* process utility functions (create, destroy, etc..)
*
- * These are assumed canSetTag if they're the only stmt in the
- * portal.
- *
* We must not set a snapshot here for utility commands (if one is
* needed, PortalRunUtility will do it). If a utility command is
* alone in a portal then everything's fine. The only case where
* whether it has a snapshot or not, so we just leave the current
* snapshot alone if we have one.
*/
- if (list_length(portal->stmts) == 1)
+ if (pstmt->canSetTag)
{
Assert(!active_snapshot_set);
/* statement can set tag string */
- PortalRunUtility(portal, stmt, isTopLevel, false,
+ PortalRunUtility(portal, pstmt, isTopLevel, false,
dest, completionTag);
}
else
{
- Assert(IsA(stmt, NotifyStmt));
+ Assert(IsA(pstmt->utilityStmt, NotifyStmt));
/* stmt added by rewrite cannot set tag */
- PortalRunUtility(portal, stmt, isTopLevel, false,
+ PortalRunUtility(portal, pstmt, isTopLevel, false,
altdest, NULL);
}
}
/* local function declarations */
static void ProcessUtilitySlow(ParseState *pstate,
- Node *parsetree,
+ PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
* the query must be *in truth* read-only, because the caller wishes
* not to do CommandCounterIncrement for it.
*
- * Note: currently no need to support Query nodes here
+ * Note: currently no need to support raw or analyzed queries here
*/
bool
-CommandIsReadOnly(Node *parsetree)
+CommandIsReadOnly(PlannedStmt *pstmt)
{
- if (IsA(parsetree, PlannedStmt))
+ Assert(IsA(pstmt, PlannedStmt));
+ switch (pstmt->commandType)
{
- PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
- switch (stmt->commandType)
- {
- case CMD_SELECT:
- if (stmt->rowMarks != NIL)
- return false; /* SELECT FOR [KEY] UPDATE/SHARE */
- else if (stmt->hasModifyingCTE)
- return false; /* data-modifying CTE */
- else
- return true;
- case CMD_UPDATE:
- case CMD_INSERT:
- case CMD_DELETE:
- return false;
- default:
- elog(WARNING, "unrecognized commandType: %d",
- (int) stmt->commandType);
- break;
- }
+ case CMD_SELECT:
+ if (pstmt->rowMarks != NIL)
+ return false; /* SELECT FOR [KEY] UPDATE/SHARE */
+ else if (pstmt->hasModifyingCTE)
+ return false; /* data-modifying CTE */
+ else
+ return true;
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ return false;
+ case CMD_UTILITY:
+ /* For now, treat all utility commands as read/write */
+ return false;
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) pstmt->commandType);
+ break;
}
- /* For now, treat all utility commands as read/write */
return false;
}
* ProcessUtility
* general utility function invoker
*
- * parsetree: the parse tree for the utility statement
+ * pstmt: PlannedStmt wrapper for the utility statement
* queryString: original source text of command
* context: identifies source of statement (toplevel client command,
* non-toplevel client command, subcommand of a larger utility command)
* completionTag may be NULL if caller doesn't want a status string.
*/
void
-ProcessUtility(Node *parsetree,
+ProcessUtility(PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
+ Assert(IsA(pstmt, PlannedStmt));
+ Assert(pstmt->commandType == CMD_UTILITY);
Assert(queryString != NULL); /* required as of 8.4 */
/*
* call standard_ProcessUtility().
*/
if (ProcessUtility_hook)
- (*ProcessUtility_hook) (parsetree, queryString,
+ (*ProcessUtility_hook) (pstmt, queryString,
context, params,
dest, completionTag);
else
- standard_ProcessUtility(parsetree, queryString,
+ standard_ProcessUtility(pstmt, queryString,
context, params,
dest, completionTag);
}
* which requires being in a valid transaction.
*/
void
-standard_ProcessUtility(Node *parsetree,
+standard_ProcessUtility(PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
+ Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
ParseState *pstate;
/*
* Portal (cursor) manipulation
- *
- * Note: DECLARE CURSOR is processed mostly as a SELECT, and
- * therefore what we will get here is a PlannedStmt not a bare
- * DeclareCursorStmt.
*/
- case T_PlannedStmt:
- {
- PlannedStmt *stmt = (PlannedStmt *) parsetree;
-
- if (stmt->utilityStmt == NULL ||
- !IsA(stmt->utilityStmt, DeclareCursorStmt))
- elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
- PerformCursorOpen(stmt, params, queryString, isTopLevel);
- }
+ case T_DeclareCursorStmt:
+ PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
+ queryString, isTopLevel);
break;
case T_ClosePortalStmt:
{
uint64 processed;
- DoCopy(pstate, (CopyStmt *) parsetree, &processed);
+ DoCopy(pstate, (CopyStmt *) parsetree,
+ pstmt->stmt_location, pstmt->stmt_len,
+ &processed);
if (completionTag)
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
"COPY " UINT64_FORMAT, processed);
case T_PrepareStmt:
CheckRestrictedOperation("PREPARE");
- PrepareQuery((PrepareStmt *) parsetree, queryString);
+ PrepareQuery((PrepareStmt *) parsetree, queryString,
+ pstmt->stmt_location, pstmt->stmt_len);
break;
case T_ExecuteStmt:
GrantStmt *stmt = (GrantStmt *) parsetree;
if (EventTriggerSupportsGrantObjectType(stmt->objtype))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
- ExecuteGrantStmt((GrantStmt *) parsetree);
+ ExecuteGrantStmt(stmt);
}
break;
DropStmt *stmt = (DropStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->removeType))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
RenameStmt *stmt = (RenameStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->renameType))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objectType))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
CommentStmt *stmt = (CommentStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objtype))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
- CommentObject((CommentStmt *) parsetree);
+ CommentObject(stmt);
break;
}
SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
if (EventTriggerSupportsObjectType(stmt->objtype))
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
else
default:
/* All other statement types have event trigger support */
- ProcessUtilitySlow(pstate, parsetree, queryString,
+ ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
dest, completionTag);
break;
*/
static void
ProcessUtilitySlow(ParseState *pstate,
- Node *parsetree,
+ PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
+ Node *parsetree = pstmt->utilityStmt;
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
bool needCleanup;
*/
case T_CreateSchemaStmt:
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
- queryString);
+ queryString,
+ pstmt->stmt_location,
+ pstmt->stmt_len);
/*
* EventTriggerCollectSimpleCommand called by
* call will stash the objects so created into our
* event trigger context.
*/
- ProcessUtility(stmt,
+ PlannedStmt *wrapper;
+
+ wrapper = makeNode(PlannedStmt);
+ wrapper->commandType = CMD_UTILITY;
+ wrapper->canSetTag = false;
+ wrapper->utilityStmt = stmt;
+ wrapper->stmt_location = pstmt->stmt_location;
+ wrapper->stmt_len = pstmt->stmt_len;
+
+ ProcessUtility(wrapper,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
* queued commands is consistent with the way
* they are executed here.
*/
+ PlannedStmt *wrapper;
+
EventTriggerAlterTableEnd();
- ProcessUtility(stmt,
+ wrapper = makeNode(PlannedStmt);
+ wrapper->commandType = CMD_UTILITY;
+ wrapper->canSetTag = false;
+ wrapper->utilityStmt = stmt;
+ wrapper->stmt_location = pstmt->stmt_location;
+ wrapper->stmt_len = pstmt->stmt_len;
+ ProcessUtility(wrapper,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
case T_ViewStmt: /* CREATE VIEW */
EventTriggerAlterTableStart(parsetree);
- address = DefineView((ViewStmt *) parsetree, queryString);
+ address = DefineView((ViewStmt *) parsetree, queryString,
+ pstmt->stmt_location, pstmt->stmt_len);
EventTriggerCollectSimpleCommand(address, secondaryObject,
parsetree);
/* stashed internally */
case T_AlterTSConfigurationStmt:
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
+
/*
* Commands are stashed in MakeConfigurationMapping and
* DropConfigurationMapping, which are called from
switch (parsetree->commandType)
{
case CMD_SELECT:
- /* returns tuples ... unless it's DECLARE CURSOR */
- if (parsetree->utilityStmt == NULL)
- return true;
- break;
+ /* returns tuples */
+ return true;
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
switch (nodeTag(parsetree))
{
+ case T_DeclareCursorStmt:
+ qry = (Query *) ((DeclareCursorStmt *) parsetree)->query;
+ Assert(IsA(qry, Query));
+ if (qry->commandType == CMD_UTILITY)
+ return UtilityContainsQuery(qry->utilityStmt);
+ return qry;
+
case T_ExplainStmt:
qry = (Query *) ((ExplainStmt *) parsetree)->query;
Assert(IsA(qry, Query));
/*
* CreateCommandTag
* utility to get a string representation of the command operation,
- * given either a raw (un-analyzed) parsetree or a planned query.
+ * given either a raw (un-analyzed) parsetree, an analyzed Query,
+ * or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
switch (nodeTag(parsetree))
{
+ /* recurse if we're given a RawStmt */
+ case T_RawStmt:
+ tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
+ break;
+
/* raw plannable queries */
case T_InsertStmt:
tag = "INSERT";
* will be useful for complaints about read-only
* statements
*/
- if (stmt->utilityStmt != NULL)
- {
- Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
- tag = "DECLARE CURSOR";
- }
- else if (stmt->rowMarks != NIL)
+ if (stmt->rowMarks != NIL)
{
/* not 100% but probably close enough */
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
case CMD_DELETE:
tag = "DELETE";
break;
+ case CMD_UTILITY:
+ tag = CreateCommandTag(stmt->utilityStmt);
+ break;
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
* will be useful for complaints about read-only
* statements
*/
- if (stmt->utilityStmt != NULL)
- {
- Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
- tag = "DECLARE CURSOR";
- }
- else if (stmt->rowMarks != NIL)
+ if (stmt->rowMarks != NIL)
{
/* not 100% but probably close enough */
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
/*
* GetCommandLogLevel
* utility to get the minimum log_statement level for a command,
- * given either a raw (un-analyzed) parsetree or a planned query.
+ * given either a raw (un-analyzed) parsetree, an analyzed Query,
+ * or a PlannedStmt.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
switch (nodeTag(parsetree))
{
+ /* recurse if we're given a RawStmt */
+ case T_RawStmt:
+ lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
+ break;
+
/* raw plannable queries */
case T_InsertStmt:
case T_DeleteStmt:
/* Look through an EXECUTE to the referenced stmt */
ps = FetchPreparedStatement(stmt->name, false);
if (ps && ps->plansource->raw_parse_tree)
- lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
+ lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
else
lev = LOGSTMT_ALL;
}
lev = LOGSTMT_MOD;
break;
+ case CMD_UTILITY:
+ lev = GetCommandLogLevel(stmt->utilityStmt);
+ break;
+
default:
elog(WARNING, "unrecognized commandType: %d",
(int) stmt->commandType);
*/
#define IsTransactionStmtPlan(plansource) \
((plansource)->raw_parse_tree && \
- IsA((plansource)->raw_parse_tree, TransactionStmt))
+ IsA((plansource)->raw_parse_tree->stmt, TransactionStmt))
/*
* This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
static bool choose_custom_plan(CachedPlanSource *plansource,
ParamListInfo boundParams);
static double cached_plan_cost(CachedPlan *plan, bool include_planner);
+static Query *QueryListGetPrimaryStmt(List *stmts);
static void AcquireExecutorLocks(List *stmt_list, bool acquire);
static void AcquirePlannerLocks(List *stmt_list, bool acquire);
static void ScanQueryForLocks(Query *parsetree, bool acquire);
* commandTag: compile-time-constant tag for query, or NULL if empty query
*/
CachedPlanSource *
-CreateCachedPlan(Node *raw_parse_tree,
+CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag)
{
* commandTag: compile-time-constant tag for query, or NULL if empty query
*/
CachedPlanSource *
-CreateOneShotCachedPlan(Node *raw_parse_tree,
+CreateOneShotCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag)
{
RevalidateCachedQuery(CachedPlanSource *plansource)
{
bool snapshot_set;
- Node *rawtree;
+ RawStmt *rawtree;
List *tlist; /* transient query-tree list */
List *qlist; /* permanent query-tree list */
TupleDesc resultDesc;
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
- if (!IsA(plannedstmt, PlannedStmt))
+ if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */
if (plannedstmt->transientPlan)
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
- if (!IsA(plannedstmt, PlannedStmt))
+ if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */
result += plannedstmt->planTree->total_cost;
List *
CachedPlanGetTargetList(CachedPlanSource *plansource)
{
- Node *pstmt;
+ Query *pstmt;
/* Assert caller is doing things in a sane order */
Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
RevalidateCachedQuery(plansource);
/* Get the primary statement and find out what it returns */
- pstmt = PortalListGetPrimaryStmt(plansource->query_list);
+ pstmt = QueryListGetPrimaryStmt(plansource->query_list);
- return FetchStatementTargetList(pstmt);
+ return FetchStatementTargetList((Node *) pstmt);
+}
+
+/*
+ * QueryListGetPrimaryStmt
+ * Get the "primary" stmt within a list, ie, the one marked canSetTag.
+ *
+ * Returns NULL if no such stmt. If multiple queries within the list are
+ * marked canSetTag, returns the first one. Neither of these cases should
+ * occur in present usages of this function.
+ */
+static Query *
+QueryListGetPrimaryStmt(List *stmts)
+{
+ ListCell *lc;
+
+ foreach(lc, stmts)
+ {
+ Query *stmt = (Query *) lfirst(lc);
+
+ Assert(IsA(stmt, Query));
+
+ if (stmt->canSetTag)
+ return stmt;
+ }
+ return NULL;
}
/*
int rt_index;
ListCell *lc2;
- Assert(!IsA(plannedstmt, Query));
- if (!IsA(plannedstmt, PlannedStmt))
+ Assert(IsA(plannedstmt, PlannedStmt));
+
+ if (plannedstmt->commandType == CMD_UTILITY)
{
/*
* Ignore utility statements, except those (such as EXPLAIN) that
* rule rewriting, because rewriting doesn't change the query
* representation.
*/
- Query *query = UtilityContainsQuery((Node *) plannedstmt);
+ Query *query = UtilityContainsQuery(plannedstmt->utilityStmt);
if (query)
ScanQueryForLocks(query, acquire);
return ExecCleanTypeFromTL(query->targetList, false);
case PORTAL_ONE_RETURNING:
- query = (Query *) PortalListGetPrimaryStmt(stmt_list);
- Assert(IsA(query, Query));
+ query = QueryListGetPrimaryStmt(stmt_list);
Assert(query->returningList);
return ExecCleanTypeFromTL(query->returningList, false);
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
- Assert(!IsA(plannedstmt, Query));
- if (!IsA(plannedstmt, PlannedStmt))
+ if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */
if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL :
list_member_oid(plannedstmt->relationOids, relid))
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
ListCell *lc3;
- Assert(!IsA(plannedstmt, Query));
- if (!IsA(plannedstmt, PlannedStmt))
+ if (plannedstmt->commandType == CMD_UTILITY)
continue; /* Ignore utility statements */
foreach(lc3, plannedstmt->invalItems)
{
}
/*
- * PortalListGetPrimaryStmt
+ * PortalGetPrimaryStmt
* Get the "primary" stmt within a portal, ie, the one marked canSetTag.
*
* Returns NULL if no such stmt. If multiple PlannedStmt structs within the
* portal are marked canSetTag, returns the first one. Neither of these
* cases should occur in present usages of this function.
- *
- * Copes if given a list of Querys --- can't happen in a portal, but this
- * code also supports plancache.c, which needs both cases.
- *
- * Note: the reason this is just handed a List is so that plancache.c
- * can share the code. For use with a portal, use PortalGetPrimaryStmt
- * rather than calling this directly.
*/
-Node *
-PortalListGetPrimaryStmt(List *stmts)
+PlannedStmt *
+PortalGetPrimaryStmt(Portal portal)
{
ListCell *lc;
- foreach(lc, stmts)
+ foreach(lc, portal->stmts)
{
- Node *stmt = (Node *) lfirst(lc);
+ PlannedStmt *stmt = (PlannedStmt *) lfirst(lc);
- if (IsA(stmt, PlannedStmt))
- {
- if (((PlannedStmt *) stmt)->canSetTag)
- return stmt;
- }
- else if (IsA(stmt, Query))
- {
- if (((Query *) stmt)->canSetTag)
- return stmt;
- }
- else
- {
- /* Utility stmts are assumed canSetTag if they're the only stmt */
- if (list_length(stmts) == 1)
- return stmt;
- }
+ Assert(IsA(stmt, PlannedStmt));
+
+ if (stmt->canSetTag)
+ return stmt;
}
return NULL;
}
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201612231
+#define CATALOG_VERSION_NO 201701141
#endif
/* CopyStateData is private in commands/copy.c */
typedef struct CopyStateData *CopyState;
-extern Oid DoCopy(ParseState *state, const CopyStmt *stmt,
+extern void DoCopy(ParseState *state, const CopyStmt *stmt,
+ int stmt_location, int stmt_len,
uint64 *processed);
extern void ProcessCopyOptions(ParseState *pstate, CopyState cstate, bool is_from, List *options);
/* Hook for plugins to get control in ExplainOneQuery() */
typedef void (*ExplainOneQuery_hook_type) (Query *query,
+ int cursorOptions,
IntoClause *into,
ExplainState *es,
const char *queryString,
#include "utils/portal.h"
-extern void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
+extern void PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
const char *queryString, bool isTopLevel);
extern void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest,
/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
-extern void PrepareQuery(PrepareStmt *stmt, const char *queryString);
+extern void PrepareQuery(PrepareStmt *stmt, const char *queryString,
+ int stmt_location, int stmt_len);
extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
const char *queryString, ParamListInfo params,
DestReceiver *dest, char *completionTag);
#include "nodes/parsenodes.h"
extern Oid CreateSchemaCommand(CreateSchemaStmt *parsetree,
- const char *queryString);
+ const char *queryString,
+ int stmt_location, int stmt_len);
extern void RemoveSchemaById(Oid schemaOid);
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);
{
/* These fields are provided by CreateQueryDesc */
CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
- PlannedStmt *plannedstmt; /* planner's output, or null if utility */
- Node *utilitystmt; /* utility statement, or null */
+ PlannedStmt *plannedstmt; /* planner's output (could be utility, too) */
const char *sourceText; /* source text of the query */
Snapshot snapshot; /* snapshot to use for query */
Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */
ParamListInfo params,
int instrument_options);
-extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
- const char *sourceText,
- Snapshot snapshot,
- DestReceiver *dest,
- ParamListInfo params);
-
extern void FreeQueryDesc(QueryDesc *qdesc);
#endif /* EXECDESC_H */
/*
* TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
*/
+ T_RawStmt,
T_Query,
T_PlannedStmt,
T_InsertStmt,
* This is a byte (not character) offset in the original source text, to be
* used for positioning an error cursor when there is an error related to
* the node. Access to the original source text is needed to make use of
- * the location.
+ * the location. At the topmost (statement) level, we also provide a
+ * statement length, likewise measured in bytes, for convenience in
+ * identifying statement boundaries in multi-statement source strings.
*
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* for further processing by the rewriter and planner.
*
* Utility statements (i.e. non-optimizable statements) have the
- * utilityStmt field set, and the Query itself is mostly dummy.
- * DECLARE CURSOR is a special case: it is represented like a SELECT,
- * but the original DeclareCursorStmt is stored in utilityStmt.
+ * utilityStmt field set, and the rest of the Query is mostly dummy.
*
* Planning converts a Query tree into a Plan tree headed by a PlannedStmt
* node --- the Query structure is not used by the executor.
bool canSetTag; /* do I set the command result tag? */
- Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a
- * non-optimizable statement */
+ Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
int resultRelation; /* rtable index of target relation for
* INSERT/UPDATE/DELETE; 0 for SELECT */
* are only added during rewrite and
* therefore are not written out as
* part of Query. */
+
+ /*
+ * The following two fields identify the portion of the source text string
+ * containing this query. They are typically only populated in top-level
+ * Queries, not in sub-queries. When not set, they might both be zero, or
+ * both be -1 meaning "unknown".
+ */
+ int stmt_location; /* start location, or -1 if unknown */
+ int stmt_len; /* length in bytes; 0 means "rest of string" */
} Query;
bool isTable;
} TriggerTransition;
+/*****************************************************************************
+ * Raw Grammar Output Statements
+ *****************************************************************************/
+
+/*
+ * RawStmt --- container for any one statement's raw parse tree
+ *
+ * Parse analysis converts a raw parse tree headed by a RawStmt node into
+ * an analyzed statement headed by a Query node. For optimizable statements,
+ * the conversion is complex. For utility statements, the parser usually just
+ * transfers the raw parse tree (sans RawStmt) into the utilityStmt field of
+ * the Query node, and all the useful work happens at execution time.
+ *
+ * stmt_location/stmt_len identify the portion of the source text string
+ * containing this raw statement (useful for multi-statement strings).
+ */
+typedef struct RawStmt
+{
+ NodeTag type;
+ Node *stmt; /* raw parse tree */
+ int stmt_location; /* start location, or -1 if unknown */
+ int stmt_len; /* length in bytes; 0 means "rest of string" */
+} RawStmt;
+
/*****************************************************************************
* Optimizable Statements
*****************************************************************************/
* statements do need attention from parse analysis, and this is
* done by routines in parser/parse_utilcmd.c after ProcessUtility
* receives the command for execution.
+ * DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are special cases:
+ * they contain optimizable statements, which get processed normally
+ * by parser/analyze.c.
*****************************************************************************/
/*
NodeTag type;
RangeVar *relation; /* the relation to copy */
Node *query; /* the query (SELECT or DML statement with
- * RETURNING) to copy */
+ * RETURNING) to copy, as a raw parse tree */
List *attlist; /* List of column names (as Strings), or NIL
* for all columns */
bool is_from; /* TO or FROM */
/* ----------------------
* Declare Cursor Statement
*
- * Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
- * output. After parse analysis it's set to null, and the Query points to the
- * DeclareCursorStmt, not vice versa.
+ * The "query" field is initially a raw parse tree, and is converted to a
+ * Query node during parse analysis. Note that rewriting and planning
+ * of the query are always postponed until execution.
* ----------------------
*/
#define CURSOR_OPT_BINARY 0x0001 /* BINARY */
NodeTag type;
char *portalname; /* name of the portal (cursor) */
int options; /* bitmask of options (see above) */
- Node *query; /* the raw SELECT query */
+ Node *query; /* the query (see comments above) */
} DeclareCursorStmt;
/* ----------------------
NodeTag type;
RangeVar *view; /* the view to be created */
List *aliases; /* target column names */
- Node *query; /* the SELECT query */
+ Node *query; /* the SELECT query (as a raw parse tree) */
bool replace; /* replace an existing view? */
List *options; /* options from WITH clause */
ViewCheckOption withCheckOption; /* WITH CHECK OPTION */
/* ----------------------
* Explain Statement
*
- * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
- * or a Query node if parse analysis has been done. Note that rewriting and
- * planning of the query are always postponed until execution of EXPLAIN.
+ * The "query" field is initially a raw parse tree, and is converted to a
+ * Query node during parse analysis. Note that rewriting and planning
+ * of the query are always postponed until execution.
* ----------------------
*/
typedef struct ExplainStmt
*
* The output of the planner is a Plan tree headed by a PlannedStmt node.
* PlannedStmt holds the "one time" information needed by the executor.
+ *
+ * For simplicity in APIs, we also wrap utility statements in PlannedStmt
+ * nodes; in such cases, commandType == CMD_UTILITY, the statement itself
+ * is in the utilityStmt field, and the rest of the struct is mostly dummy.
+ * (We do use canSetTag, stmt_location, stmt_len, and possibly queryId.)
* ----------------
*/
typedef struct PlannedStmt
{
NodeTag type;
- CmdType commandType; /* select|insert|update|delete */
+ CmdType commandType; /* select|insert|update|delete|utility */
uint32 queryId; /* query identifier (copied from Query) */
/* rtable indexes of target relations for INSERT/UPDATE/DELETE */
List *resultRelations; /* integer list of RT indexes, or NIL */
- Node *utilityStmt; /* non-null if this is DECLARE CURSOR */
-
List *subplans; /* Plan trees for SubPlan expressions */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
List *invalItems; /* other dependencies, as PlanInvalItems */
int nParamExec; /* number of PARAM_EXEC Params used */
+
+ Node *utilityStmt; /* non-null if this is utility stmt */
+
+ /* statement location in source string (copied from Query) */
+ int stmt_location; /* start location, or -1 if unknown */
+ int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */
extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
-extern Query *parse_analyze(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze(RawStmt *parseTree, const char *sourceText,
Oid *paramTypes, int numParams);
-extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
+extern Query *parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams);
extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
CommonTableExpr *parentCTE,
bool locked_from_parent);
-extern Query *transformTopLevelStmt(ParseState *pstate, Node *parseTree);
+extern Query *transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree);
extern Query *transformStmt(ParseState *pstate, Node *parseTree);
-extern bool analyze_requires_snapshot(Node *parseTree);
+extern bool analyze_requires_snapshot(RawStmt *parseTree);
extern const char *LCS_asString(LockClauseStrength strength);
extern void CheckSelectLocking(Query *qry, LockClauseStrength strength);
extern int log_statement;
extern List *pg_parse_query(const char *query_string);
-extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
+extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
+ const char *query_string,
Oid *paramTypes, int numParams);
-extern List *pg_analyze_and_rewrite_params(Node *parsetree,
+extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg);
} ProcessUtilityContext;
/* Hook for plugins to get control in ProcessUtility() */
-typedef void (*ProcessUtility_hook_type) (Node *parsetree,
+typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
DestReceiver *dest, char *completionTag);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
-extern void ProcessUtility(Node *parsetree, const char *queryString,
+extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag);
-extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
+extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
DestReceiver *dest, char *completionTag);
extern LogStmtLevel GetCommandLogLevel(Node *parsetree);
-extern bool CommandIsReadOnly(Node *parsetree);
+extern bool CommandIsReadOnly(PlannedStmt *pstmt);
#endif /* UTILITY_H */
#include "access/tupdesc.h"
#include "nodes/params.h"
+/* Forward declaration, to avoid including parsenodes.h here */
+struct RawStmt;
+
#define CACHEDPLANSOURCE_MAGIC 195726186
#define CACHEDPLAN_MAGIC 953717834
typedef struct CachedPlanSource
{
int magic; /* should equal CACHEDPLANSOURCE_MAGIC */
- Node *raw_parse_tree; /* output of raw_parser(), or NULL */
+ struct RawStmt *raw_parse_tree; /* output of raw_parser(), or NULL */
const char *query_string; /* source text of query */
const char *commandTag; /* command tag (a constant!), or NULL */
Oid *param_types; /* array of parameter type OIDs, or NULL */
typedef struct CachedPlan
{
int magic; /* should equal CACHEDPLAN_MAGIC */
- List *stmt_list; /* list of statement nodes (PlannedStmts and
- * bare utility statements) */
+ List *stmt_list; /* list of PlannedStmts */
bool is_oneshot; /* is it a "oneshot" plan? */
bool is_saved; /* is CachedPlan in a long-lived context? */
bool is_valid; /* is the stmt_list currently valid? */
extern void InitPlanCache(void);
extern void ResetPlanCache(void);
-extern CachedPlanSource *CreateCachedPlan(Node *raw_parse_tree,
+extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag);
-extern CachedPlanSource *CreateOneShotCachedPlan(Node *raw_parse_tree,
+extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag);
extern void CompleteCachedPlan(CachedPlanSource *plansource,
/* The query or queries the portal will execute */
const char *sourceText; /* text of query (as of 8.4, never NULL) */
const char *commandTag; /* command tag for original query */
- List *stmts; /* PlannedStmts and/or utility statements */
+ List *stmts; /* list of PlannedStmts */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* params to pass to query */
*/
#define PortalGetQueryDesc(portal) ((portal)->queryDesc)
#define PortalGetHeapMemory(portal) ((portal)->heap)
-#define PortalGetPrimaryStmt(portal) PortalListGetPrimaryStmt((portal)->stmts)
/* Prototypes for functions in utils/mmgr/portalmem.c */
const char *commandTag,
List *stmts,
CachedPlan *cplan);
-extern Node *PortalListGetPrimaryStmt(List *stmts);
+extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
extern void PortalCreateHoldStore(Portal portal);
extern void PortalHashTableDeleteAll(void);
extern bool ThereAreNoReadyPortals(void);
if (list_length(cplan->stmt_list) != 1)
return;
stmt = (PlannedStmt *) linitial(cplan->stmt_list);
+ Assert(IsA(stmt, PlannedStmt));
/*
* 2. It must be a RESULT plan --> no scan's required
*/
- if (!IsA(stmt, PlannedStmt))
- return;
if (stmt->commandType != CMD_SELECT)
return;
plan = stmt->planTree;