diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/copyto.c | 8 | ||||
-rw-r--r-- | src/backend/commands/createas.c | 8 | ||||
-rw-r--r-- | src/backend/commands/explain.c | 14 | ||||
-rw-r--r-- | src/backend/commands/extension.c | 1 | ||||
-rw-r--r-- | src/backend/commands/foreigncmds.c | 3 | ||||
-rw-r--r-- | src/backend/commands/policy.c | 8 | ||||
-rw-r--r-- | src/backend/commands/portalcmds.c | 8 | ||||
-rw-r--r-- | src/backend/commands/prepare.c | 5 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 1 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 3 | ||||
-rw-r--r-- | src/backend/commands/view.c | 5 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 1 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 1 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 36 | ||||
-rw-r--r-- | src/backend/tcop/pquery.c | 1 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 23 | ||||
-rw-r--r-- | src/include/tcop/utility.h | 6 |
17 files changed, 47 insertions, 85 deletions
diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index 89a4f8f810e..b6eacd5baa8 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -438,14 +438,8 @@ BeginCopyTo(ParseState *pstate, /* * Run parse analysis and rewrite. Note this also acquires sufficient * locks on the source table(s). - * - * Because the parser and planner tend to scribble on their input, we - * make a preliminary copy of the source querytree. This prevents - * problems in the case that the COPY is in a portal or plpgsql - * function and is executed repeatedly. (See also the same hack in - * DECLARE CURSOR and PREPARE.) XXX FIXME someday. */ - rewritten = pg_analyze_and_rewrite(copyObject(raw_query), + rewritten = pg_analyze_and_rewrite(raw_query, pstate->p_sourcetext, NULL, 0, NULL); diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index dce882012e6..09828517153 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -299,14 +299,8 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt, * 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 CTAS is in a portal or plpgsql function - * and is executed repeatedly. (See also the same hack in EXPLAIN and - * PREPARE.) */ - rewritten = QueryRewrite(copyObject(query)); + rewritten = QueryRewrite(query); /* SELECT should never rewrite to more or less than one SELECT query */ if (list_length(rewritten) != 1) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 9a60865d191..e81b9900925 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -256,14 +256,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, * 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 EXPLAIN is in a portal or plpgsql function and is - * executed repeatedly. (See also the same hack in DECLARE CURSOR and - * PREPARE.) XXX FIXME someday. */ - rewritten = QueryRewrite(castNode(Query, copyObject(stmt->query))); + rewritten = QueryRewrite(castNode(Query, stmt->query)); /* emit opening boilerplate */ ExplainBeginOutput(es); @@ -427,7 +421,8 @@ ExplainOneQuery(Query *query, int cursorOptions, * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt. * * This is exported because it's called back from prepare.c in the - * EXPLAIN EXECUTE case. + * EXPLAIN EXECUTE case. In that case, we'll be dealing with a statement + * that's in the plan cache, so we have to ensure we don't modify it. */ void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, @@ -441,8 +436,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, { /* * We have to rewrite the contained SELECT and then pass it back to - * ExplainOneQuery. It's probably not really necessary to copy the - * contained parsetree another time, but let's be safe. + * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case. */ CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt; List *rewritten; diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 008505368c4..41857feda9f 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -786,6 +786,7 @@ execute_sql_string(const char *sql) ProcessUtility(stmt, sql, + false, PROCESS_UTILITY_QUERY, NULL, NULL, diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index eb7103fd3b1..bc36311d383 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -1570,8 +1570,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt) pstmt->stmt_len = rs->stmt_len; /* Execute statement */ - ProcessUtility(pstmt, - cmd, + ProcessUtility(pstmt, cmd, false, PROCESS_UTILITY_SUBCOMMAND, NULL, NULL, None_Receiver, NULL); diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 5cacc088cd8..5d469309ce1 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -747,12 +747,12 @@ CreatePolicy(CreatePolicyStmt *stmt) addNSItemToQuery(with_check_pstate, nsitem, false, true, true); qual = transformWhereClause(qual_pstate, - copyObject(stmt->qual), + stmt->qual, EXPR_KIND_POLICY, "POLICY"); with_check_qual = transformWhereClause(with_check_pstate, - copyObject(stmt->with_check), + stmt->with_check, EXPR_KIND_POLICY, "POLICY"); @@ -922,7 +922,7 @@ AlterPolicy(AlterPolicyStmt *stmt) addNSItemToQuery(qual_pstate, nsitem, false, true, true); - qual = transformWhereClause(qual_pstate, copyObject(stmt->qual), + qual = transformWhereClause(qual_pstate, stmt->qual, EXPR_KIND_POLICY, "POLICY"); @@ -946,7 +946,7 @@ AlterPolicy(AlterPolicyStmt *stmt) addNSItemToQuery(with_check_pstate, nsitem, false, true, true); with_check_qual = transformWhereClause(with_check_pstate, - copyObject(stmt->with_check), + stmt->with_check, EXPR_KIND_POLICY, "POLICY"); diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index d34cc39fdea..3ea30bcbc99 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -76,14 +76,8 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa * 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)); + rewritten = QueryRewrite(query); /* SELECT should never rewrite to more or less than one query */ if (list_length(rewritten) != 1) diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 65d8ac65b18..5e03c7c5aaa 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -78,12 +78,9 @@ PrepareQuery(ParseState *pstate, PrepareStmt *stmt, /* * 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 = stmt->query; rawstmt->stmt_location = stmt_location; rawstmt->stmt_len = stmt_len; diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index a60eb161e4a..6c6ab9ee349 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -191,6 +191,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, /* do this step */ ProcessUtility(wrapper, queryString, + false, PROCESS_UTILITY_SUBCOMMAND, NULL, NULL, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 028e8ac46b3..4e23c7fce5f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4408,8 +4408,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * Copy the original subcommand for each table. This avoids conflicts * when different child tables need to make different parse * transformations (for example, the same column may have different column - * numbers in different children). It also ensures that we don't corrupt - * the original parse tree, in case it is saved in plancache. + * numbers in different children). */ cmd = copyObject(cmd); diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index f2642dba6c9..4df05a0b33d 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -417,12 +417,9 @@ DefineView(ViewStmt *stmt, const char *queryString, /* * Run parse analysis to convert the raw parse tree to a Query. Note this * also acquires sufficient locks on the source table(s). - * - * Since parse analysis scribbles on its input, copy the raw parse tree; - * this ensures we don't corrupt a prepared statement, for example. */ rawstmt = makeNode(RawStmt); - rawstmt->stmt = (Node *) copyObject(stmt->query); + rawstmt->stmt = stmt->query; rawstmt->stmt_location = stmt_location; rawstmt->stmt_len = stmt_len; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index e2ea51aafe5..296e54e60a4 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -886,6 +886,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) { ProcessUtility(es->qd->plannedstmt, fcache->src, + false, PROCESS_UTILITY_QUERY, es->qd->params, es->qd->queryEnv, diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index b8bd05e8942..bf619d3a65a 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -2545,6 +2545,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI, InitializeQueryCompletion(&qc); ProcessUtility(stmt, plansource->query_string, + true, /* protect plancache's node tree */ context, paramLI, _SPI_current->queryEnv, diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index c9708e38f46..81d3e7990c6 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -11,10 +11,6 @@ * Hence these functions are now called at the start of execution of their * respective utility commands. * - * NOTE: in general we must avoid scribbling on the passed-in raw parse - * tree, since it might be in a plan cache. The simplest solution is - * a quick copyObject() call before manipulating the query tree. - * * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -177,12 +173,6 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) Oid existing_relid; ParseCallbackState pcbstate; - /* - * We must not scribble on the passed-in CreateStmt, so copy it. (This is - * overkill, but easy.) - */ - stmt = copyObject(stmt); - /* Set up pstate */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; @@ -2824,12 +2814,6 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) if (stmt->transformed) return stmt; - /* - * We must not scribble on the passed-in IndexStmt, so copy it. (This is - * overkill, but easy.) - */ - stmt = copyObject(stmt); - /* Set up pstate */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; @@ -2925,12 +2909,6 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString) if (stmt->transformed) return stmt; - /* - * We must not scribble on the passed-in CreateStatsStmt, so copy it. - * (This is overkill, but easy.) - */ - stmt = copyObject(stmt); - /* Set up pstate */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; @@ -2993,9 +2971,6 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString) * * actions and whereClause are output parameters that receive the * transformed results. - * - * Note that we must not scribble on the passed-in RuleStmt, so we do - * copyObject() on the actions and WHERE clause. */ void transformRuleStmt(RuleStmt *stmt, const char *queryString, @@ -3070,7 +3045,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, /* take care of the where clause */ *whereClause = transformWhereClause(pstate, - (Node *) copyObject(stmt->whereClause), + stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); /* we have to fix its collations too */ @@ -3142,8 +3117,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, addNSItemToQuery(sub_pstate, newnsitem, false, true, false); /* Transform the rule action statement */ - top_subqry = transformStmt(sub_pstate, - (Node *) copyObject(action)); + top_subqry = transformStmt(sub_pstate, action); /* * We cannot support utility-statement actions (eg NOTIFY) with @@ -3325,12 +3299,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, AlterTableCmd *newcmd; ParseNamespaceItem *nsitem; - /* - * We must not scribble on the passed-in AlterTableStmt, so copy it. (This - * is overkill, but easy.) - */ - stmt = copyObject(stmt); - /* Caller is responsible for locking the relation */ rel = relation_open(relid, NoLock); tupdesc = RelationGetDescr(rel); diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index f7f08e6c05f..ed43920264a 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -1146,6 +1146,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt, ProcessUtility(pstmt, portal->sourceText, + (portal->cplan != NULL), /* protect tree if in plancache */ isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY, portal->portalParams, portal->queryEnv, diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 1a8fc167733..7a2da9dab43 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -476,6 +476,7 @@ CheckRestrictedOperation(const char *cmdname) * * pstmt: PlannedStmt wrapper for the utility statement * queryString: original source text of command + * readOnlyTree: if true, pstmt's node tree must not be modified * context: identifies source of statement (toplevel client command, * non-toplevel client command, subcommand of a larger utility command) * params: parameters to use during execution @@ -501,6 +502,7 @@ CheckRestrictedOperation(const char *cmdname) void ProcessUtility(PlannedStmt *pstmt, const char *queryString, + bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, @@ -518,11 +520,11 @@ ProcessUtility(PlannedStmt *pstmt, * call standard_ProcessUtility(). */ if (ProcessUtility_hook) - (*ProcessUtility_hook) (pstmt, queryString, + (*ProcessUtility_hook) (pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc); else - standard_ProcessUtility(pstmt, queryString, + standard_ProcessUtility(pstmt, queryString, readOnlyTree, context, params, queryEnv, dest, qc); } @@ -541,13 +543,14 @@ ProcessUtility(PlannedStmt *pstmt, void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, + bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc) { - Node *parsetree = pstmt->utilityStmt; + Node *parsetree; bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL); bool isAtomicContext = (!(context == PROCESS_UTILITY_TOPLEVEL || context == PROCESS_UTILITY_QUERY_NONATOMIC) || IsTransactionBlock()); ParseState *pstate; @@ -556,6 +559,18 @@ standard_ProcessUtility(PlannedStmt *pstmt, /* This can recurse, so check for excessive recursion */ check_stack_depth(); + /* + * If the given node tree is read-only, make a copy to ensure that parse + * transformations don't damage the original tree. This could be + * refactored to avoid making unnecessary copies in more cases, but it's + * not clear that it's worth a great deal of trouble over. Statements + * that are complex enough to be expensive to copy are exactly the ones + * we'd need to copy, so that only marginal savings seem possible. + */ + if (readOnlyTree) + pstmt = copyObject(pstmt); + parsetree = pstmt->utilityStmt; + /* Prohibit read/write commands in read-only states. */ readonly_flags = ClassifyUtilityCommandAsReadOnly(parsetree); if (readonly_flags != COMMAND_IS_STRICTLY_READ_ONLY && @@ -1211,6 +1226,7 @@ ProcessUtilitySlow(ParseState *pstate, ProcessUtility(wrapper, queryString, + false, PROCESS_UTILITY_SUBCOMMAND, params, NULL, @@ -1918,6 +1934,7 @@ ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context) ProcessUtility(wrapper, context->queryString, + false, PROCESS_UTILITY_SUBCOMMAND, context->params, context->queryEnv, diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h index 841062b4b35..212e9b32806 100644 --- a/src/include/tcop/utility.h +++ b/src/include/tcop/utility.h @@ -69,17 +69,21 @@ typedef struct AlterTableUtilityContext /* Hook for plugins to get control in ProcessUtility() */ typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt, - const char *queryString, ProcessUtilityContext context, + const char *queryString, + bool readOnlyTree, + ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc); extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook; extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString, + bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc); extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, + bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc); |