diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 59ca5cd5a9..18dd22691c 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 = CurrentMemoryContext,
+					  plancontext = NULL;
 
 		/* Be sure parser can see any DDL done so far */
 		CommandCounterIncrement();
 
+		/*
+		 * If there are more queries to run, use a temporary child context
+		 * that will be reset after executing this query.
+		 */
+		if (lnext(lc1) != NULL)
+		{
+			plancontext = AllocSetContextCreate(CurrentMemoryContext,
+												"statement planning context",
+												ALLOCSET_DEFAULT_SIZES);
+			MemoryContextSwitchTo(plancontext);
+		}
+
 		stmt_list = pg_analyze_and_rewrite(parsetree,
 										   sql,
 										   NULL,
@@ -730,6 +744,10 @@ execute_sql_string(const char *sql)
 										   NULL);
 		stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL);
 
+		/* Switch context for execution. */
+		if (plancontext)
+			MemoryContextSwitchTo(oldcontext);
+
 		foreach(lc2, stmt_list)
 		{
 			PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
@@ -772,6 +790,13 @@ execute_sql_string(const char *sql)
 
 			PopActiveSnapshot();
 		}
+
+		/*
+		 * Delete the planning context unless this is the last statement, in
+		 * which case, deleting the parent context will get the job done.
+		 */
+		if (plancontext)
+			MemoryContextDelete(plancontext);
 	}
 
 	/* Be sure to advance the command counter after the last script command */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 44a59e1d4f..67b46bff8a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1075,6 +1075,7 @@ exec_simple_query(const char *query_string)
 		Portal		portal;
 		DestReceiver *receiver;
 		int16		format;
+		MemoryContext plancontext = NULL;
 
 		/*
 		 * Get the command name for use in status display (it also becomes the
@@ -1132,10 +1133,22 @@ 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 appropriate context for constructing query and plan trees
+		 * (again, these must outlive the execution context).  Normally, it's
+		 * MessageContext, but if there are more queries to plan, we use a
+		 * temporary child context that will be reset after executing this
+		 * query.  We avoid that overhead of setting up a separate context
+		 * for the common case of having just a single query.
 		 */
-		oldcontext = MemoryContextSwitchTo(MessageContext);
+		if (lnext(parsetree_item) != NULL)
+		{
+			plancontext = AllocSetContextCreate(MessageContext,
+												"statement planning context",
+												ALLOCSET_DEFAULT_SIZES);
+			oldcontext = MemoryContextSwitchTo(plancontext);
+		}
+		else
+			oldcontext = MemoryContextSwitchTo(MessageContext);
 
 		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
 												NULL, 0, NULL);
@@ -1143,6 +1156,10 @@ exec_simple_query(const char *query_string)
 		plantree_list = pg_plan_queries(querytree_list,
 										CURSOR_OPT_PARALLEL_OK, NULL);
 
+		/* Switch to MessageContext for creating the portal. */
+		if (plancontext)
+			MemoryContextSwitchTo(MessageContext);
+
 		/* Done with the snapshot used for parsing/planning */
 		if (snapshot_set)
 			PopActiveSnapshot();
@@ -1263,6 +1280,10 @@ exec_simple_query(const char *query_string)
 		 * aborted by error will not send an EndCommand report at all.)
 		 */
 		EndCommand(completionTag, dest);
+
+		/* Delete the planning context if one was created. */
+		if (plancontext)
+			MemoryContextDelete(plancontext);
 	}							/* end loop over parsetrees */
 
 	/*
