mirror of
https://github.com/postgres/postgres.git
synced 2025-08-30 06:01:21 +03:00
Change representation of statement lists, and add statement location info.
This patch makes several changes that improve the consistency of representation of lists of statements. It's always been the case that the output of parse analysis is a list of Query nodes, whatever the types of the individual statements in the list. This patch brings similar consistency to the outputs of raw parsing and planning steps: * The output of raw parsing is now always a list of RawStmt nodes; the statement-type-dependent nodes are one level down from that. * The output of pg_plan_queries() is now always a list of PlannedStmt nodes, even for utility statements. In the case of a utility statement, "planning" just consists of wrapping a CMD_UTILITY PlannedStmt around the utility node. This list representation is now used in Portal and CachedPlan plan lists, replacing the former convention of intermixing PlannedStmts with bare utility-statement nodes. Now, every list of statements has a consistent head-node type depending on how far along it is in processing. This allows changing many places that formerly used generic "Node *" pointers to use a more specific pointer type, thus reducing the number of IsA() tests and casts needed, as well as improving code clarity. Also, the post-parse-analysis representation of DECLARE CURSOR is changed so that it looks more like EXPLAIN, PREPARE, etc. That is, the contained SELECT remains a child of the DeclareCursorStmt rather than getting flipped around to be the other way. It's now true for both Query and PlannedStmt that utilityStmt is non-null if and only if commandType is CMD_UTILITY. That allows simplifying a lot of places that were testing both fields. (I think some of those were just defensive programming, but in many places, it was actually necessary to avoid confusing DECLARE CURSOR with SELECT.) Because PlannedStmt carries a canSetTag field, we're also able to get rid of some ad-hoc rules about how to reconstruct canSetTag for a bare utility statement; specifically, the assumption that a utility is canSetTag if and only if it's the only one in its list. While I see no near-term need for relaxing that restriction, it's nice to get rid of the ad-hocery. The API of ProcessUtility() is changed so that what it's passed is the wrapper PlannedStmt not just the bare utility statement. This will affect all users of ProcessUtility_hook, but the changes are pretty trivial; see the affected contrib modules for examples of the minimum change needed. (Most compilers should give pointer-type-mismatch warnings for uncorrected code.) There's also a change in the API of ExplainOneQuery_hook, to pass through cursorOptions instead of expecting hook functions to know what to pick. This is needed because of the DECLARE CURSOR changes, but really should have been done in 9.6; it's unlikely that any extant hook functions know about using CURSOR_OPT_PARALLEL_OK. Finally, teach gram.y to save statement boundary locations in RawStmt nodes, and pass those through to Query and PlannedStmt nodes. This allows more intelligent handling of cases where a source query string contains multiple statements. This patch doesn't actually do anything with the information, but a follow-on patch will. (Passing this information through cleanly is the true motivation for these changes; while I think this is all good cleanup, it's unlikely we'd have bothered without this end goal.) catversion bump because addition of location fields to struct Query affects stored rules. This patch is by me, but it owes a good deal to Fabien Coelho who did a lot of preliminary work on the problem, and also reviewed the patch. Discussion: https://postgr.es/m/alpine.DEB.2.20.1612200926310.29821@lancre
This commit is contained in:
@@ -183,8 +183,8 @@ static int errdetail_recovery_conflict(void);
|
||||
static void start_xact_command(void);
|
||||
static void finish_xact_command(void);
|
||||
static bool IsTransactionExitStmt(Node *parsetree);
|
||||
static bool IsTransactionExitStmtList(List *parseTrees);
|
||||
static bool IsTransactionStmtList(List *parseTrees);
|
||||
static bool IsTransactionExitStmtList(List *pstmts);
|
||||
static bool IsTransactionStmtList(List *pstmts);
|
||||
static void drop_unnamed_stmt(void);
|
||||
static void SigHupHandler(SIGNAL_ARGS);
|
||||
static void log_disconnections(int code, Datum arg);
|
||||
@@ -588,8 +588,8 @@ ProcessClientWriteInterrupt(bool blocked)
|
||||
/*
|
||||
* Do raw parsing (only).
|
||||
*
|
||||
* A list of parsetrees is returned, since there might be multiple
|
||||
* commands in the given string.
|
||||
* A list of parsetrees (RawStmt nodes) is returned, since there might be
|
||||
* multiple commands in the given string.
|
||||
*
|
||||
* NOTE: for interactive queries, it is important to keep this routine
|
||||
* separate from the analysis & rewrite stages. Analysis and rewriting
|
||||
@@ -641,7 +641,7 @@ pg_parse_query(const char *query_string)
|
||||
* NOTE: for reasons mentioned above, this must be separate from raw parsing.
|
||||
*/
|
||||
List *
|
||||
pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
||||
pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
|
||||
Oid *paramTypes, int numParams)
|
||||
{
|
||||
Query *query;
|
||||
@@ -676,7 +676,7 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
|
||||
* hooks instead of a fixed list of parameter datatypes.
|
||||
*/
|
||||
List *
|
||||
pg_analyze_and_rewrite_params(Node *parsetree,
|
||||
pg_analyze_and_rewrite_params(RawStmt *parsetree,
|
||||
const char *query_string,
|
||||
ParserSetupHook parserSetup,
|
||||
void *parserSetupArg)
|
||||
@@ -833,8 +833,10 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
|
||||
/*
|
||||
* Generate plans for a list of already-rewritten queries.
|
||||
*
|
||||
* Normal optimizable statements generate PlannedStmt entries in the result
|
||||
* list. Utility statements are simply represented by their statement nodes.
|
||||
* For normal optimizable statements, invoke the planner. For utility
|
||||
* statements, just make a wrapper PlannedStmt node.
|
||||
*
|
||||
* The result is a list of PlannedStmt nodes.
|
||||
*/
|
||||
List *
|
||||
pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
|
||||
@@ -845,16 +847,21 @@ pg_plan_queries(List *querytrees, int cursorOptions, ParamListInfo boundParams)
|
||||
foreach(query_list, querytrees)
|
||||
{
|
||||
Query *query = (Query *) lfirst(query_list);
|
||||
Node *stmt;
|
||||
PlannedStmt *stmt;
|
||||
|
||||
if (query->commandType == CMD_UTILITY)
|
||||
{
|
||||
/* Utility commands have no plans. */
|
||||
stmt = query->utilityStmt;
|
||||
/* Utility commands require no planning. */
|
||||
stmt = makeNode(PlannedStmt);
|
||||
stmt->commandType = CMD_UTILITY;
|
||||
stmt->canSetTag = query->canSetTag;
|
||||
stmt->utilityStmt = query->utilityStmt;
|
||||
stmt->stmt_location = query->stmt_location;
|
||||
stmt->stmt_len = query->stmt_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams);
|
||||
stmt = pg_plan_query(query, cursorOptions, boundParams);
|
||||
}
|
||||
|
||||
stmt_list = lappend(stmt_list, stmt);
|
||||
@@ -955,7 +962,7 @@ exec_simple_query(const char *query_string)
|
||||
*/
|
||||
foreach(parsetree_item, parsetree_list)
|
||||
{
|
||||
Node *parsetree = (Node *) lfirst(parsetree_item);
|
||||
RawStmt *parsetree = (RawStmt *) lfirst(parsetree_item);
|
||||
bool snapshot_set = false;
|
||||
const char *commandTag;
|
||||
char completionTag[COMPLETION_TAG_BUFSIZE];
|
||||
@@ -971,7 +978,7 @@ exec_simple_query(const char *query_string)
|
||||
* do any special start-of-SQL-command processing needed by the
|
||||
* destination.
|
||||
*/
|
||||
commandTag = CreateCommandTag(parsetree);
|
||||
commandTag = CreateCommandTag(parsetree->stmt);
|
||||
|
||||
set_ps_display(commandTag, false);
|
||||
|
||||
@@ -986,7 +993,7 @@ exec_simple_query(const char *query_string)
|
||||
* state, but not many...)
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
!IsTransactionExitStmt(parsetree))
|
||||
!IsTransactionExitStmt(parsetree->stmt))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("current transaction is aborted, "
|
||||
@@ -1061,9 +1068,9 @@ exec_simple_query(const char *query_string)
|
||||
* backward compatibility...)
|
||||
*/
|
||||
format = 0; /* TEXT is default */
|
||||
if (IsA(parsetree, FetchStmt))
|
||||
if (IsA(parsetree->stmt, FetchStmt))
|
||||
{
|
||||
FetchStmt *stmt = (FetchStmt *) parsetree;
|
||||
FetchStmt *stmt = (FetchStmt *) parsetree->stmt;
|
||||
|
||||
if (!stmt->ismove)
|
||||
{
|
||||
@@ -1102,7 +1109,7 @@ exec_simple_query(const char *query_string)
|
||||
|
||||
PortalDrop(portal, false);
|
||||
|
||||
if (IsA(parsetree, TransactionStmt))
|
||||
if (IsA(parsetree->stmt, TransactionStmt))
|
||||
{
|
||||
/*
|
||||
* If this was a transaction control statement, commit it. We will
|
||||
@@ -1194,7 +1201,7 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
MemoryContext unnamed_stmt_context = NULL;
|
||||
MemoryContext oldcontext;
|
||||
List *parsetree_list;
|
||||
Node *raw_parse_tree;
|
||||
RawStmt *raw_parse_tree;
|
||||
const char *commandTag;
|
||||
List *querytree_list;
|
||||
CachedPlanSource *psrc;
|
||||
@@ -1279,12 +1286,12 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
bool snapshot_set = false;
|
||||
int i;
|
||||
|
||||
raw_parse_tree = (Node *) linitial(parsetree_list);
|
||||
raw_parse_tree = (RawStmt *) linitial(parsetree_list);
|
||||
|
||||
/*
|
||||
* Get the command name for possible use in status display.
|
||||
*/
|
||||
commandTag = CreateCommandTag(raw_parse_tree);
|
||||
commandTag = CreateCommandTag(raw_parse_tree->stmt);
|
||||
|
||||
/*
|
||||
* If we are in an aborted transaction, reject all commands except
|
||||
@@ -1295,7 +1302,7 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
* state, but not many...)
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
!IsTransactionExitStmt(raw_parse_tree))
|
||||
!IsTransactionExitStmt(raw_parse_tree->stmt))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("current transaction is aborted, "
|
||||
@@ -1552,7 +1559,7 @@ exec_bind_message(StringInfo input_message)
|
||||
* functions.
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
(!IsTransactionExitStmt(psrc->raw_parse_tree) ||
|
||||
(!IsTransactionExitStmt(psrc->raw_parse_tree->stmt) ||
|
||||
numParams != 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
@@ -2140,11 +2147,11 @@ errdetail_execute(List *raw_parsetree_list)
|
||||
|
||||
foreach(parsetree_item, raw_parsetree_list)
|
||||
{
|
||||
Node *parsetree = (Node *) lfirst(parsetree_item);
|
||||
RawStmt *parsetree = (RawStmt *) lfirst(parsetree_item);
|
||||
|
||||
if (IsA(parsetree, ExecuteStmt))
|
||||
if (IsA(parsetree->stmt, ExecuteStmt))
|
||||
{
|
||||
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
||||
ExecuteStmt *stmt = (ExecuteStmt *) parsetree->stmt;
|
||||
PreparedStatement *pstmt;
|
||||
|
||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
||||
@@ -2488,45 +2495,33 @@ IsTransactionExitStmt(Node *parsetree)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Test a list that might contain Query nodes or bare parsetrees */
|
||||
/* Test a list that contains PlannedStmt nodes */
|
||||
static bool
|
||||
IsTransactionExitStmtList(List *parseTrees)
|
||||
IsTransactionExitStmtList(List *pstmts)
|
||||
{
|
||||
if (list_length(parseTrees) == 1)
|
||||
if (list_length(pstmts) == 1)
|
||||
{
|
||||
Node *stmt = (Node *) linitial(parseTrees);
|
||||
PlannedStmt *pstmt = (PlannedStmt *) linitial(pstmts);
|
||||
|
||||
if (IsA(stmt, Query))
|
||||
{
|
||||
Query *query = (Query *) stmt;
|
||||
|
||||
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;
|
||||
|
@@ -43,7 +43,7 @@ static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
|
||||
DestReceiver *dest);
|
||||
static uint64 PortalRunSelect(Portal portal, bool forward, long count,
|
||||
DestReceiver *dest);
|
||||
static void PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
|
||||
bool isTopLevel, bool setHoldSnapshot,
|
||||
DestReceiver *dest, char *completionTag);
|
||||
static void PortalRunMulti(Portal portal,
|
||||
@@ -73,7 +73,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
|
||||
|
||||
qd->operation = plannedstmt->commandType; /* operation */
|
||||
qd->plannedstmt = plannedstmt; /* plan */
|
||||
qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
|
||||
qd->sourceText = sourceText; /* query text */
|
||||
qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
|
||||
/* RI check snapshot */
|
||||
@@ -92,37 +91,6 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
|
||||
return qd;
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateUtilityQueryDesc
|
||||
*/
|
||||
QueryDesc *
|
||||
CreateUtilityQueryDesc(Node *utilitystmt,
|
||||
const char *sourceText,
|
||||
Snapshot snapshot,
|
||||
DestReceiver *dest,
|
||||
ParamListInfo params)
|
||||
{
|
||||
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
|
||||
|
||||
qd->operation = CMD_UTILITY; /* operation */
|
||||
qd->plannedstmt = NULL;
|
||||
qd->utilitystmt = utilitystmt; /* utility command */
|
||||
qd->sourceText = sourceText; /* query text */
|
||||
qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
|
||||
qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
|
||||
qd->dest = dest; /* output dest */
|
||||
qd->params = params; /* parameter values passed into query */
|
||||
qd->instrument_options = false; /* uninteresting for utilities */
|
||||
|
||||
/* null these fields until set by ExecutorStart */
|
||||
qd->tupDesc = NULL;
|
||||
qd->estate = NULL;
|
||||
qd->planstate = NULL;
|
||||
qd->totaltime = NULL;
|
||||
|
||||
return qd;
|
||||
}
|
||||
|
||||
/*
|
||||
* FreeQueryDesc
|
||||
*/
|
||||
@@ -236,7 +204,7 @@ ProcessQuery(PlannedStmt *plan,
|
||||
* ChoosePortalStrategy
|
||||
* Select portal execution strategy given the intended statement list.
|
||||
*
|
||||
* The list elements can be Querys, PlannedStmts, or utility statements.
|
||||
* The list elements can be Querys or PlannedStmts.
|
||||
* That's more general than portals need, but plancache.c uses this too.
|
||||
*
|
||||
* See the comments in portal.h.
|
||||
@@ -263,16 +231,14 @@ ChoosePortalStrategy(List *stmts)
|
||||
|
||||
if (query->canSetTag)
|
||||
{
|
||||
if (query->commandType == CMD_SELECT &&
|
||||
query->utilityStmt == NULL)
|
||||
if (query->commandType == CMD_SELECT)
|
||||
{
|
||||
if (query->hasModifyingCTE)
|
||||
return PORTAL_ONE_MOD_WITH;
|
||||
else
|
||||
return PORTAL_ONE_SELECT;
|
||||
}
|
||||
if (query->commandType == CMD_UTILITY &&
|
||||
query->utilityStmt != NULL)
|
||||
if (query->commandType == CMD_UTILITY)
|
||||
{
|
||||
if (UtilityReturnsTuples(query->utilityStmt))
|
||||
return PORTAL_UTIL_SELECT;
|
||||
@@ -287,24 +253,24 @@ ChoosePortalStrategy(List *stmts)
|
||||
|
||||
if (pstmt->canSetTag)
|
||||
{
|
||||
if (pstmt->commandType == CMD_SELECT &&
|
||||
pstmt->utilityStmt == NULL)
|
||||
if (pstmt->commandType == CMD_SELECT)
|
||||
{
|
||||
if (pstmt->hasModifyingCTE)
|
||||
return PORTAL_ONE_MOD_WITH;
|
||||
else
|
||||
return PORTAL_ONE_SELECT;
|
||||
}
|
||||
if (pstmt->commandType == CMD_UTILITY)
|
||||
{
|
||||
if (UtilityReturnsTuples(pstmt->utilityStmt))
|
||||
return PORTAL_UTIL_SELECT;
|
||||
/* it can't be ONE_RETURNING, so give up */
|
||||
return PORTAL_MULTI_QUERY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* must be a utility command; assume it's canSetTag */
|
||||
if (UtilityReturnsTuples(stmt))
|
||||
return PORTAL_UTIL_SELECT;
|
||||
/* it can't be ONE_RETURNING, so give up */
|
||||
return PORTAL_MULTI_QUERY;
|
||||
}
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -325,7 +291,8 @@ ChoosePortalStrategy(List *stmts)
|
||||
{
|
||||
if (++nSetTag > 1)
|
||||
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||
if (query->returningList == NIL)
|
||||
if (query->commandType == CMD_UTILITY ||
|
||||
query->returningList == NIL)
|
||||
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||
}
|
||||
}
|
||||
@@ -337,11 +304,13 @@ ChoosePortalStrategy(List *stmts)
|
||||
{
|
||||
if (++nSetTag > 1)
|
||||
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||
if (!pstmt->hasReturning)
|
||||
if (pstmt->commandType == CMD_UTILITY ||
|
||||
!pstmt->hasReturning)
|
||||
return PORTAL_MULTI_QUERY; /* no need to look further */
|
||||
}
|
||||
}
|
||||
/* otherwise, utility command, assumed not canSetTag */
|
||||
else
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
|
||||
}
|
||||
if (nSetTag == 1)
|
||||
return PORTAL_ONE_RETURNING;
|
||||
@@ -364,7 +333,7 @@ FetchPortalTargetList(Portal portal)
|
||||
if (portal->strategy == PORTAL_MULTI_QUERY)
|
||||
return NIL;
|
||||
/* get the primary statement and find out what it returns */
|
||||
return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
|
||||
return FetchStatementTargetList((Node *) PortalGetPrimaryStmt(portal));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -372,7 +341,7 @@ FetchPortalTargetList(Portal portal)
|
||||
* Given a statement that returns tuples, extract the query targetlist.
|
||||
* Returns NIL if the statement doesn't have a determinable targetlist.
|
||||
*
|
||||
* This can be applied to a Query, a PlannedStmt, or a utility statement.
|
||||
* This can be applied to a Query or a PlannedStmt.
|
||||
* That's more general than portals need, but plancache.c uses this too.
|
||||
*
|
||||
* Note: do not modify the result.
|
||||
@@ -388,16 +357,14 @@ FetchStatementTargetList(Node *stmt)
|
||||
{
|
||||
Query *query = (Query *) stmt;
|
||||
|
||||
if (query->commandType == CMD_UTILITY &&
|
||||
query->utilityStmt != NULL)
|
||||
if (query->commandType == CMD_UTILITY)
|
||||
{
|
||||
/* transfer attention to utility statement */
|
||||
stmt = query->utilityStmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (query->commandType == CMD_SELECT &&
|
||||
query->utilityStmt == NULL)
|
||||
if (query->commandType == CMD_SELECT)
|
||||
return query->targetList;
|
||||
if (query->returningList)
|
||||
return query->returningList;
|
||||
@@ -408,12 +375,19 @@ FetchStatementTargetList(Node *stmt)
|
||||
{
|
||||
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||
|
||||
if (pstmt->commandType == CMD_SELECT &&
|
||||
pstmt->utilityStmt == NULL)
|
||||
return pstmt->planTree->targetlist;
|
||||
if (pstmt->hasReturning)
|
||||
return pstmt->planTree->targetlist;
|
||||
return NIL;
|
||||
if (pstmt->commandType == CMD_UTILITY)
|
||||
{
|
||||
/* transfer attention to utility statement */
|
||||
stmt = pstmt->utilityStmt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pstmt->commandType == CMD_SELECT)
|
||||
return pstmt->planTree->targetlist;
|
||||
if (pstmt->hasReturning)
|
||||
return pstmt->planTree->targetlist;
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
if (IsA(stmt, FetchStmt))
|
||||
{
|
||||
@@ -566,8 +540,7 @@ PortalStart(Portal portal, ParamListInfo params,
|
||||
{
|
||||
PlannedStmt *pstmt;
|
||||
|
||||
pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
|
||||
Assert(IsA(pstmt, PlannedStmt));
|
||||
pstmt = PortalGetPrimaryStmt(portal);
|
||||
portal->tupDesc =
|
||||
ExecCleanTypeFromTL(pstmt->planTree->targetlist,
|
||||
false);
|
||||
@@ -588,10 +561,10 @@ PortalStart(Portal portal, ParamListInfo params,
|
||||
* take care of it if needed.
|
||||
*/
|
||||
{
|
||||
Node *ustmt = PortalGetPrimaryStmt(portal);
|
||||
PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
|
||||
|
||||
Assert(!IsA(ustmt, PlannedStmt));
|
||||
portal->tupDesc = UtilityTupleDescriptor(ustmt);
|
||||
Assert(pstmt->commandType == CMD_UTILITY);
|
||||
portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1047,7 +1020,7 @@ FillPortalStore(Portal portal, bool isTopLevel)
|
||||
break;
|
||||
|
||||
case PORTAL_UTIL_SELECT:
|
||||
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
|
||||
PortalRunUtility(portal, (PlannedStmt *) linitial(portal->stmts),
|
||||
isTopLevel, true, treceiver, completionTag);
|
||||
break;
|
||||
|
||||
@@ -1143,10 +1116,11 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
|
||||
* Execute a utility statement inside a portal.
|
||||
*/
|
||||
static void
|
||||
PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
PortalRunUtility(Portal portal, PlannedStmt *pstmt,
|
||||
bool isTopLevel, bool setHoldSnapshot,
|
||||
DestReceiver *dest, char *completionTag)
|
||||
{
|
||||
Node *utilityStmt = pstmt->utilityStmt;
|
||||
Snapshot snapshot;
|
||||
|
||||
/*
|
||||
@@ -1186,7 +1160,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
else
|
||||
snapshot = NULL;
|
||||
|
||||
ProcessUtility(utilityStmt,
|
||||
ProcessUtility(pstmt,
|
||||
portal->sourceText,
|
||||
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
|
||||
portal->portalParams,
|
||||
@@ -1241,21 +1215,18 @@ PortalRunMulti(Portal portal,
|
||||
*/
|
||||
foreach(stmtlist_item, portal->stmts)
|
||||
{
|
||||
Node *stmt = (Node *) lfirst(stmtlist_item);
|
||||
PlannedStmt *pstmt = (PlannedStmt *) lfirst(stmtlist_item);
|
||||
|
||||
/*
|
||||
* If we got a cancel signal in prior command, quit
|
||||
*/
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
if (IsA(stmt, PlannedStmt) &&
|
||||
((PlannedStmt *) stmt)->utilityStmt == NULL)
|
||||
if (pstmt->utilityStmt == NULL)
|
||||
{
|
||||
/*
|
||||
* process a plannable query.
|
||||
*/
|
||||
PlannedStmt *pstmt = (PlannedStmt *) stmt;
|
||||
|
||||
TRACE_POSTGRESQL_QUERY_EXECUTE_START();
|
||||
|
||||
if (log_executor_stats)
|
||||
@@ -1320,9 +1291,6 @@ PortalRunMulti(Portal portal,
|
||||
/*
|
||||
* process utility functions (create, destroy, etc..)
|
||||
*
|
||||
* These are assumed canSetTag if they're the only stmt in the
|
||||
* portal.
|
||||
*
|
||||
* We must not set a snapshot here for utility commands (if one is
|
||||
* needed, PortalRunUtility will do it). If a utility command is
|
||||
* alone in a portal then everything's fine. The only case where
|
||||
@@ -1331,18 +1299,18 @@ PortalRunMulti(Portal portal,
|
||||
* whether it has a snapshot or not, so we just leave the current
|
||||
* snapshot alone if we have one.
|
||||
*/
|
||||
if (list_length(portal->stmts) == 1)
|
||||
if (pstmt->canSetTag)
|
||||
{
|
||||
Assert(!active_snapshot_set);
|
||||
/* statement can set tag string */
|
||||
PortalRunUtility(portal, stmt, isTopLevel, false,
|
||||
PortalRunUtility(portal, pstmt, isTopLevel, false,
|
||||
dest, completionTag);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(IsA(stmt, NotifyStmt));
|
||||
Assert(IsA(pstmt->utilityStmt, NotifyStmt));
|
||||
/* stmt added by rewrite cannot set tag */
|
||||
PortalRunUtility(portal, stmt, isTopLevel, false,
|
||||
PortalRunUtility(portal, pstmt, isTopLevel, false,
|
||||
altdest, NULL);
|
||||
}
|
||||
}
|
||||
|
@@ -72,7 +72,7 @@ ProcessUtility_hook_type ProcessUtility_hook = NULL;
|
||||
|
||||
/* local function declarations */
|
||||
static void ProcessUtilitySlow(ParseState *pstate,
|
||||
Node *parsetree,
|
||||
PlannedStmt *pstmt,
|
||||
const char *queryString,
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
@@ -88,35 +88,33 @@ static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
|
||||
* the query must be *in truth* read-only, because the caller wishes
|
||||
* not to do CommandCounterIncrement for it.
|
||||
*
|
||||
* Note: currently no need to support Query nodes here
|
||||
* Note: currently no need to support raw or analyzed queries here
|
||||
*/
|
||||
bool
|
||||
CommandIsReadOnly(Node *parsetree)
|
||||
CommandIsReadOnly(PlannedStmt *pstmt)
|
||||
{
|
||||
if (IsA(parsetree, PlannedStmt))
|
||||
Assert(IsA(pstmt, PlannedStmt));
|
||||
switch (pstmt->commandType)
|
||||
{
|
||||
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
||||
|
||||
switch (stmt->commandType)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
if (stmt->rowMarks != NIL)
|
||||
return false; /* SELECT FOR [KEY] UPDATE/SHARE */
|
||||
else if (stmt->hasModifyingCTE)
|
||||
return false; /* data-modifying CTE */
|
||||
else
|
||||
return true;
|
||||
case CMD_UPDATE:
|
||||
case CMD_INSERT:
|
||||
case CMD_DELETE:
|
||||
return false;
|
||||
default:
|
||||
elog(WARNING, "unrecognized commandType: %d",
|
||||
(int) stmt->commandType);
|
||||
break;
|
||||
}
|
||||
case CMD_SELECT:
|
||||
if (pstmt->rowMarks != NIL)
|
||||
return false; /* SELECT FOR [KEY] UPDATE/SHARE */
|
||||
else if (pstmt->hasModifyingCTE)
|
||||
return false; /* data-modifying CTE */
|
||||
else
|
||||
return true;
|
||||
case CMD_UPDATE:
|
||||
case CMD_INSERT:
|
||||
case CMD_DELETE:
|
||||
return false;
|
||||
case CMD_UTILITY:
|
||||
/* For now, treat all utility commands as read/write */
|
||||
return false;
|
||||
default:
|
||||
elog(WARNING, "unrecognized commandType: %d",
|
||||
(int) pstmt->commandType);
|
||||
break;
|
||||
}
|
||||
/* For now, treat all utility commands as read/write */
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -297,7 +295,7 @@ CheckRestrictedOperation(const char *cmdname)
|
||||
* ProcessUtility
|
||||
* general utility function invoker
|
||||
*
|
||||
* parsetree: the parse tree for the utility statement
|
||||
* pstmt: PlannedStmt wrapper for the utility statement
|
||||
* queryString: original source text of command
|
||||
* context: identifies source of statement (toplevel client command,
|
||||
* non-toplevel client command, subcommand of a larger utility command)
|
||||
@@ -315,13 +313,15 @@ CheckRestrictedOperation(const char *cmdname)
|
||||
* completionTag may be NULL if caller doesn't want a status string.
|
||||
*/
|
||||
void
|
||||
ProcessUtility(Node *parsetree,
|
||||
ProcessUtility(PlannedStmt *pstmt,
|
||||
const char *queryString,
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
DestReceiver *dest,
|
||||
char *completionTag)
|
||||
{
|
||||
Assert(IsA(pstmt, PlannedStmt));
|
||||
Assert(pstmt->commandType == CMD_UTILITY);
|
||||
Assert(queryString != NULL); /* required as of 8.4 */
|
||||
|
||||
/*
|
||||
@@ -330,11 +330,11 @@ ProcessUtility(Node *parsetree,
|
||||
* call standard_ProcessUtility().
|
||||
*/
|
||||
if (ProcessUtility_hook)
|
||||
(*ProcessUtility_hook) (parsetree, queryString,
|
||||
(*ProcessUtility_hook) (pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
standard_ProcessUtility(parsetree, queryString,
|
||||
standard_ProcessUtility(pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
}
|
||||
@@ -351,13 +351,14 @@ ProcessUtility(Node *parsetree,
|
||||
* which requires being in a valid transaction.
|
||||
*/
|
||||
void
|
||||
standard_ProcessUtility(Node *parsetree,
|
||||
standard_ProcessUtility(PlannedStmt *pstmt,
|
||||
const char *queryString,
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
DestReceiver *dest,
|
||||
char *completionTag)
|
||||
{
|
||||
Node *parsetree = pstmt->utilityStmt;
|
||||
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
|
||||
ParseState *pstate;
|
||||
|
||||
@@ -486,20 +487,10 @@ standard_ProcessUtility(Node *parsetree,
|
||||
|
||||
/*
|
||||
* Portal (cursor) manipulation
|
||||
*
|
||||
* Note: DECLARE CURSOR is processed mostly as a SELECT, and
|
||||
* therefore what we will get here is a PlannedStmt not a bare
|
||||
* DeclareCursorStmt.
|
||||
*/
|
||||
case T_PlannedStmt:
|
||||
{
|
||||
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
||||
|
||||
if (stmt->utilityStmt == NULL ||
|
||||
!IsA(stmt->utilityStmt, DeclareCursorStmt))
|
||||
elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
|
||||
PerformCursorOpen(stmt, params, queryString, isTopLevel);
|
||||
}
|
||||
case T_DeclareCursorStmt:
|
||||
PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
|
||||
queryString, isTopLevel);
|
||||
break;
|
||||
|
||||
case T_ClosePortalStmt:
|
||||
@@ -545,7 +536,9 @@ standard_ProcessUtility(Node *parsetree,
|
||||
{
|
||||
uint64 processed;
|
||||
|
||||
DoCopy(pstate, (CopyStmt *) parsetree, &processed);
|
||||
DoCopy(pstate, (CopyStmt *) parsetree,
|
||||
pstmt->stmt_location, pstmt->stmt_len,
|
||||
&processed);
|
||||
if (completionTag)
|
||||
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
|
||||
"COPY " UINT64_FORMAT, processed);
|
||||
@@ -554,7 +547,8 @@ standard_ProcessUtility(Node *parsetree,
|
||||
|
||||
case T_PrepareStmt:
|
||||
CheckRestrictedOperation("PREPARE");
|
||||
PrepareQuery((PrepareStmt *) parsetree, queryString);
|
||||
PrepareQuery((PrepareStmt *) parsetree, queryString,
|
||||
pstmt->stmt_location, pstmt->stmt_len);
|
||||
break;
|
||||
|
||||
case T_ExecuteStmt:
|
||||
@@ -808,11 +802,11 @@ standard_ProcessUtility(Node *parsetree,
|
||||
GrantStmt *stmt = (GrantStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsGrantObjectType(stmt->objtype))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
ExecuteGrantStmt((GrantStmt *) parsetree);
|
||||
ExecuteGrantStmt(stmt);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -821,7 +815,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
DropStmt *stmt = (DropStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->removeType))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -834,7 +828,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
RenameStmt *stmt = (RenameStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->renameType))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -847,7 +841,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->objectType))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -860,7 +854,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->objectType))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -873,7 +867,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->objectType))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -886,11 +880,11 @@ standard_ProcessUtility(Node *parsetree,
|
||||
CommentStmt *stmt = (CommentStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->objtype))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
CommentObject((CommentStmt *) parsetree);
|
||||
CommentObject(stmt);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -899,7 +893,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
|
||||
|
||||
if (EventTriggerSupportsObjectType(stmt->objtype))
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
else
|
||||
@@ -909,7 +903,7 @@ standard_ProcessUtility(Node *parsetree,
|
||||
|
||||
default:
|
||||
/* All other statement types have event trigger support */
|
||||
ProcessUtilitySlow(pstate, parsetree, queryString,
|
||||
ProcessUtilitySlow(pstate, pstmt, queryString,
|
||||
context, params,
|
||||
dest, completionTag);
|
||||
break;
|
||||
@@ -925,13 +919,14 @@ standard_ProcessUtility(Node *parsetree,
|
||||
*/
|
||||
static void
|
||||
ProcessUtilitySlow(ParseState *pstate,
|
||||
Node *parsetree,
|
||||
PlannedStmt *pstmt,
|
||||
const char *queryString,
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
DestReceiver *dest,
|
||||
char *completionTag)
|
||||
{
|
||||
Node *parsetree = pstmt->utilityStmt;
|
||||
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
|
||||
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
|
||||
bool needCleanup;
|
||||
@@ -955,7 +950,9 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
*/
|
||||
case T_CreateSchemaStmt:
|
||||
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
|
||||
queryString);
|
||||
queryString,
|
||||
pstmt->stmt_location,
|
||||
pstmt->stmt_len);
|
||||
|
||||
/*
|
||||
* EventTriggerCollectSimpleCommand called by
|
||||
@@ -1036,7 +1033,16 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
* call will stash the objects so created into our
|
||||
* event trigger context.
|
||||
*/
|
||||
ProcessUtility(stmt,
|
||||
PlannedStmt *wrapper;
|
||||
|
||||
wrapper = makeNode(PlannedStmt);
|
||||
wrapper->commandType = CMD_UTILITY;
|
||||
wrapper->canSetTag = false;
|
||||
wrapper->utilityStmt = stmt;
|
||||
wrapper->stmt_location = pstmt->stmt_location;
|
||||
wrapper->stmt_len = pstmt->stmt_len;
|
||||
|
||||
ProcessUtility(wrapper,
|
||||
queryString,
|
||||
PROCESS_UTILITY_SUBCOMMAND,
|
||||
params,
|
||||
@@ -1105,8 +1111,16 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
* queued commands is consistent with the way
|
||||
* they are executed here.
|
||||
*/
|
||||
PlannedStmt *wrapper;
|
||||
|
||||
EventTriggerAlterTableEnd();
|
||||
ProcessUtility(stmt,
|
||||
wrapper = makeNode(PlannedStmt);
|
||||
wrapper->commandType = CMD_UTILITY;
|
||||
wrapper->canSetTag = false;
|
||||
wrapper->utilityStmt = stmt;
|
||||
wrapper->stmt_location = pstmt->stmt_location;
|
||||
wrapper->stmt_len = pstmt->stmt_len;
|
||||
ProcessUtility(wrapper,
|
||||
queryString,
|
||||
PROCESS_UTILITY_SUBCOMMAND,
|
||||
params,
|
||||
@@ -1376,7 +1390,8 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
|
||||
case T_ViewStmt: /* CREATE VIEW */
|
||||
EventTriggerAlterTableStart(parsetree);
|
||||
address = DefineView((ViewStmt *) parsetree, queryString);
|
||||
address = DefineView((ViewStmt *) parsetree, queryString,
|
||||
pstmt->stmt_location, pstmt->stmt_len);
|
||||
EventTriggerCollectSimpleCommand(address, secondaryObject,
|
||||
parsetree);
|
||||
/* stashed internally */
|
||||
@@ -1480,6 +1495,7 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
|
||||
case T_AlterTSConfigurationStmt:
|
||||
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
|
||||
|
||||
/*
|
||||
* Commands are stashed in MakeConfigurationMapping and
|
||||
* DropConfigurationMapping, which are called from
|
||||
@@ -1736,10 +1752,8 @@ QueryReturnsTuples(Query *parsetree)
|
||||
switch (parsetree->commandType)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
/* returns tuples ... unless it's DECLARE CURSOR */
|
||||
if (parsetree->utilityStmt == NULL)
|
||||
return true;
|
||||
break;
|
||||
/* returns tuples */
|
||||
return true;
|
||||
case CMD_INSERT:
|
||||
case CMD_UPDATE:
|
||||
case CMD_DELETE:
|
||||
@@ -1780,6 +1794,13 @@ UtilityContainsQuery(Node *parsetree)
|
||||
|
||||
switch (nodeTag(parsetree))
|
||||
{
|
||||
case T_DeclareCursorStmt:
|
||||
qry = (Query *) ((DeclareCursorStmt *) parsetree)->query;
|
||||
Assert(IsA(qry, Query));
|
||||
if (qry->commandType == CMD_UTILITY)
|
||||
return UtilityContainsQuery(qry->utilityStmt);
|
||||
return qry;
|
||||
|
||||
case T_ExplainStmt:
|
||||
qry = (Query *) ((ExplainStmt *) parsetree)->query;
|
||||
Assert(IsA(qry, Query));
|
||||
@@ -1931,7 +1952,8 @@ AlterObjectTypeCommandTag(ObjectType objtype)
|
||||
/*
|
||||
* CreateCommandTag
|
||||
* utility to get a string representation of the command operation,
|
||||
* given either a raw (un-analyzed) parsetree or a planned query.
|
||||
* given either a raw (un-analyzed) parsetree, an analyzed Query,
|
||||
* or a PlannedStmt.
|
||||
*
|
||||
* This must handle all command types, but since the vast majority
|
||||
* of 'em are utility commands, it seems sensible to keep it here.
|
||||
@@ -1946,6 +1968,11 @@ CreateCommandTag(Node *parsetree)
|
||||
|
||||
switch (nodeTag(parsetree))
|
||||
{
|
||||
/* recurse if we're given a RawStmt */
|
||||
case T_RawStmt:
|
||||
tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
|
||||
break;
|
||||
|
||||
/* raw plannable queries */
|
||||
case T_InsertStmt:
|
||||
tag = "INSERT";
|
||||
@@ -2608,12 +2635,7 @@ CreateCommandTag(Node *parsetree)
|
||||
* will be useful for complaints about read-only
|
||||
* statements
|
||||
*/
|
||||
if (stmt->utilityStmt != NULL)
|
||||
{
|
||||
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
|
||||
tag = "DECLARE CURSOR";
|
||||
}
|
||||
else if (stmt->rowMarks != NIL)
|
||||
if (stmt->rowMarks != NIL)
|
||||
{
|
||||
/* not 100% but probably close enough */
|
||||
switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
|
||||
@@ -2647,6 +2669,9 @@ CreateCommandTag(Node *parsetree)
|
||||
case CMD_DELETE:
|
||||
tag = "DELETE";
|
||||
break;
|
||||
case CMD_UTILITY:
|
||||
tag = CreateCommandTag(stmt->utilityStmt);
|
||||
break;
|
||||
default:
|
||||
elog(WARNING, "unrecognized commandType: %d",
|
||||
(int) stmt->commandType);
|
||||
@@ -2670,12 +2695,7 @@ CreateCommandTag(Node *parsetree)
|
||||
* will be useful for complaints about read-only
|
||||
* statements
|
||||
*/
|
||||
if (stmt->utilityStmt != NULL)
|
||||
{
|
||||
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
|
||||
tag = "DECLARE CURSOR";
|
||||
}
|
||||
else if (stmt->rowMarks != NIL)
|
||||
if (stmt->rowMarks != NIL)
|
||||
{
|
||||
/* not 100% but probably close enough */
|
||||
switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
|
||||
@@ -2735,7 +2755,8 @@ CreateCommandTag(Node *parsetree)
|
||||
/*
|
||||
* GetCommandLogLevel
|
||||
* utility to get the minimum log_statement level for a command,
|
||||
* given either a raw (un-analyzed) parsetree or a planned query.
|
||||
* given either a raw (un-analyzed) parsetree, an analyzed Query,
|
||||
* or a PlannedStmt.
|
||||
*
|
||||
* This must handle all command types, but since the vast majority
|
||||
* of 'em are utility commands, it seems sensible to keep it here.
|
||||
@@ -2747,6 +2768,11 @@ GetCommandLogLevel(Node *parsetree)
|
||||
|
||||
switch (nodeTag(parsetree))
|
||||
{
|
||||
/* recurse if we're given a RawStmt */
|
||||
case T_RawStmt:
|
||||
lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
|
||||
break;
|
||||
|
||||
/* raw plannable queries */
|
||||
case T_InsertStmt:
|
||||
case T_DeleteStmt:
|
||||
@@ -2850,7 +2876,7 @@ GetCommandLogLevel(Node *parsetree)
|
||||
/* Look through an EXECUTE to the referenced stmt */
|
||||
ps = FetchPreparedStatement(stmt->name, false);
|
||||
if (ps && ps->plansource->raw_parse_tree)
|
||||
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
|
||||
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
|
||||
else
|
||||
lev = LOGSTMT_ALL;
|
||||
}
|
||||
@@ -3157,6 +3183,10 @@ GetCommandLogLevel(Node *parsetree)
|
||||
lev = LOGSTMT_MOD;
|
||||
break;
|
||||
|
||||
case CMD_UTILITY:
|
||||
lev = GetCommandLogLevel(stmt->utilityStmt);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARNING, "unrecognized commandType: %d",
|
||||
(int) stmt->commandType);
|
||||
|
Reference in New Issue
Block a user