diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 59ca5cd5a9..f7202cc9e7 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -717,9 +717,21 @@ execute_sql_string(const char *sql) foreach(lc1, raw_parsetree_list) { RawStmt *parsetree = lfirst_node(RawStmt, lc1); + MemoryContext per_parsetree_context, + oldcontext; List *stmt_list; ListCell *lc2; + /* + * We do the work for each parsetree in a short-lived context, to + * limit the memory used when there are many commands in the string. + */ + per_parsetree_context = + AllocSetContextCreate(CurrentMemoryContext, + "execute_sql_string per-statement context", + ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(per_parsetree_context); + /* Be sure parser can see any DDL done so far */ CommandCounterIncrement(); @@ -772,6 +784,10 @@ execute_sql_string(const char *sql) PopActiveSnapshot(); } + + /* Clean up per-parsetree context. */ + MemoryContextSwitchTo(oldcontext); + MemoryContextDelete(per_parsetree_context); } /* 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..ffd84d877c 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -1070,6 +1070,7 @@ exec_simple_query(const char *query_string) bool snapshot_set = false; const char *commandTag; char completionTag[COMPLETION_TAG_BUFSIZE]; + MemoryContext per_parsetree_context = NULL; List *querytree_list, *plantree_list; Portal portal; @@ -1132,10 +1133,25 @@ 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 + * (these can't be in the transaction context, as that will get reset + * when the command is COMMIT/ROLLBACK). If we have multiple + * parsetrees, we use a separate context for each one, so that we can + * free that memory before moving on to the next one. But for the + * last (or only) parsetree, just use MessageContext, which will be + * reset shortly after completion anyway. In event of an error, the + * per_parsetree_context will be deleted when MessageContext is reset. */ - oldcontext = MemoryContextSwitchTo(MessageContext); + if (lnext(parsetree_item) != NULL) + { + per_parsetree_context = + AllocSetContextCreate(MessageContext, + "per-parsetree message context", + ALLOCSET_DEFAULT_SIZES); + oldcontext = MemoryContextSwitchTo(per_parsetree_context); + } + else + oldcontext = MemoryContextSwitchTo(MessageContext); querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0, NULL); @@ -1160,8 +1176,8 @@ exec_simple_query(const char *query_string) /* * We don't have to copy anything into the portal, because everything - * we are passing here is in MessageContext, which will outlive the - * portal anyway. + * we are passing here is in MessageContext or the + * per_parsetree_context, and so will outlive the portal anyway. */ PortalDefineQuery(portal, NULL, @@ -1263,6 +1279,10 @@ exec_simple_query(const char *query_string) * aborted by error will not send an EndCommand report at all.) */ EndCommand(completionTag, dest); + + /* Now we may drop the per-parsetree context, if one was created. */ + if (per_parsetree_context) + MemoryContextDelete(per_parsetree_context); } /* end loop over parsetrees */ /*