diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 59ca5cd5a9..7a5feda2d9 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -719,10 +719,24 @@ execute_sql_string(const char *sql)
 		RawStmt    *parsetree = lfirst_node(RawStmt, lc1);
 		List	   *stmt_list;
 		ListCell   *lc2;
+		MemoryContext oldcontext,
+					  stmtcontext;
 
 		/* Be sure parser can see any DDL done so far */
 		CommandCounterIncrement();
 
+		/*
+		 * OK to analyze, rewrite, and plan this raw statement.
+		 *
+		 * Switch to a temporary context before constructing query and plan
+		 * trees.  Memory allocated during this construction will be released
+		 * before executing the generated plan(s).
+		 */
+		stmtcontext = AllocSetContextCreate(CurrentMemoryContext,
+											"statement parse/plan context",
+											ALLOCSET_DEFAULT_SIZES);
+		oldcontext = MemoryContextSwitchTo(stmtcontext);
+
 		stmt_list = pg_analyze_and_rewrite(parsetree,
 										   sql,
 										   NULL,
@@ -730,6 +744,14 @@ execute_sql_string(const char *sql)
 										   NULL);
 		stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL);
 
+		/*
+		 * Copy the plan trees into the longer-lived context and delete the
+		 * temporary context used to generate them.
+		 */
+		MemoryContextSwitchTo(oldcontext);
+		stmt_list = copyObject(stmt_list);
+		MemoryContextDelete(stmtcontext);
+
 		foreach(lc2, stmt_list)
 		{
 			PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 44a59e1d4f..207a9d6488 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -984,7 +984,8 @@ static void
 exec_simple_query(const char *query_string)
 {
 	CommandDest dest = whereToSendOutput;
-	MemoryContext oldcontext;
+	MemoryContext oldcontext,
+				  stmtcontext;
 	List	   *parsetree_list;
 	ListCell   *parsetree_item;
 	bool		save_log_statement_stats = log_statement_stats;
@@ -1132,10 +1133,14 @@ exec_simple_query(const char *query_string)
 		/*
 		 * OK to analyze, rewrite, and plan this query.
 		 *
-		 * Switch to appropriate context for constructing querytrees (again,
-		 * these must outlive the execution context).
+		 * Switch to a temporary context before constructing query and plan
+		 * trees.  Memory allocated during this construction will be released
+		 * before executing the generated plan(s).
 		 */
-		oldcontext = MemoryContextSwitchTo(MessageContext);
+		stmtcontext = AllocSetContextCreate(MessageContext,
+											"statement parse/plan context",
+											ALLOCSET_DEFAULT_SIZES);
+		oldcontext = MemoryContextSwitchTo(stmtcontext);
 
 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
 												NULL, 0, NULL);
@@ -1143,6 +1148,14 @@ exec_simple_query(const char *query_string)
 		plantree_list = pg_plan_queries(querytree_list,
 										CURSOR_OPT_PARALLEL_OK, NULL);
 
+		/*
+		 * Copy the plan trees into the longer-lived MessageContext and delete
+		 * the temporary context used to generate them.
+		 */
+		MemoryContextSwitchTo(MessageContext);
+		plantree_list = copyObject(plantree_list);
+		MemoryContextDelete(stmtcontext);
+
 		/* Done with the snapshot used for parsing/planning */
 		if (snapshot_set)
 			PopActiveSnapshot();
