Skip to content

Commit 9f18fa9

Browse files
committed
Reduce leakage during PL/pgSQL function compilation.
format_procedure leaks memory, so run it in a short-lived context not the session-lifespan cache context for the PL/pgSQL function. parse_datatype called the core parser in the function's cache context, thus leaking potentially a lot of storage into that context. We were also being a bit careless with the TypeName structures made in that code path and others. Most of the time we don't need to retain the TypeName, so make sure it is made in the short-lived temp context, and copy it only if we do need to retain it. These are far from the only leaks in PL/pgSQL compilation, but they're the biggest as far as I've seen, and further improvement looks like it'd require delicate and bug-prone surgery. Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/285483.1746756246@sss.pgh.pa.us
1 parent db01c90 commit 9f18fa9

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

src/pl/plpgsql/src/pl_comp.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
177177
yyscan_t scanner;
178178
Datum prosrcdatum;
179179
char *proc_source;
180+
char *proc_signature;
180181
HeapTuple typeTup;
181182
Form_pg_type typeStruct;
182183
PLpgSQL_variable *var;
@@ -223,6 +224,9 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
223224
plpgsql_check_syntax = forValidator;
224225
plpgsql_curr_compile = function;
225226

227+
/* format_procedure leaks memory, so run it in temp context */
228+
proc_signature = format_procedure(fcinfo->flinfo->fn_oid);
229+
226230
/*
227231
* All the permanent output of compilation (e.g. parse tree) is kept in a
228232
* per-function memory context, so it can be reclaimed easily.
@@ -237,7 +241,7 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
237241
ALLOCSET_DEFAULT_SIZES);
238242
plpgsql_compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
239243

240-
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
244+
function->fn_signature = pstrdup(proc_signature);
241245
MemoryContextSetIdentifier(func_cxt, function->fn_signature);
242246
function->fn_oid = fcinfo->flinfo->fn_oid;
243247
function->fn_input_collation = fcinfo->fncollation;
@@ -1673,6 +1677,11 @@ plpgsql_parse_wordrowtype(char *ident)
16731677
{
16741678
Oid classOid;
16751679
Oid typOid;
1680+
TypeName *typName;
1681+
MemoryContext oldCxt;
1682+
1683+
/* Avoid memory leaks in long-term function context */
1684+
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
16761685

16771686
/*
16781687
* Look up the relation. Note that because relation rowtypes have the
@@ -1695,9 +1704,12 @@ plpgsql_parse_wordrowtype(char *ident)
16951704
errmsg("relation \"%s\" does not have a composite type",
16961705
ident)));
16971706

1707+
typName = makeTypeName(ident);
1708+
1709+
MemoryContextSwitchTo(oldCxt);
1710+
16981711
/* Build and return the row type struct */
1699-
return plpgsql_build_datatype(typOid, -1, InvalidOid,
1700-
makeTypeName(ident));
1712+
return plpgsql_build_datatype(typOid, -1, InvalidOid, typName);
17011713
}
17021714

17031715
/* ----------
@@ -1711,6 +1723,7 @@ plpgsql_parse_cwordrowtype(List *idents)
17111723
Oid classOid;
17121724
Oid typOid;
17131725
RangeVar *relvar;
1726+
TypeName *typName;
17141727
MemoryContext oldCxt;
17151728

17161729
/*
@@ -1733,11 +1746,12 @@ plpgsql_parse_cwordrowtype(List *idents)
17331746
errmsg("relation \"%s\" does not have a composite type",
17341747
relvar->relname)));
17351748

1749+
typName = makeTypeNameFromNameList(idents);
1750+
17361751
MemoryContextSwitchTo(oldCxt);
17371752

17381753
/* Build and return the row type struct */
1739-
return plpgsql_build_datatype(typOid, -1, InvalidOid,
1740-
makeTypeNameFromNameList(idents));
1754+
return plpgsql_build_datatype(typOid, -1, InvalidOid, typName);
17411755
}
17421756

17431757
/*
@@ -1952,6 +1966,8 @@ plpgsql_build_recfield(PLpgSQL_rec *rec, const char *fldname)
19521966
* origtypname is the parsed form of what the user wrote as the type name.
19531967
* It can be NULL if the type could not be a composite type, or if it was
19541968
* identified by OID to begin with (e.g., it's a function argument type).
1969+
* origtypname is in short-lived storage and must be copied if we choose
1970+
* to incorporate it into the function's parse tree.
19551971
*/
19561972
PLpgSQL_type *
19571973
plpgsql_build_datatype(Oid typeOid, int32 typmod,
@@ -2070,7 +2086,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
20702086
errmsg("type %s is not composite",
20712087
format_type_be(typ->typoid))));
20722088

2073-
typ->origtypname = origtypname;
2089+
typ->origtypname = copyObject(origtypname);
20742090
typ->tcache = typentry;
20752091
typ->tupdesc_id = typentry->tupDesc_identifier;
20762092
}

src/pl/plpgsql/src/pl_gram.y

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3853,6 +3853,7 @@ parse_datatype(const char *string, int location, yyscan_t yyscanner)
38533853
int32 typmod;
38543854
sql_error_callback_arg cbarg;
38553855
ErrorContextCallback syntax_errcontext;
3856+
MemoryContext oldCxt;
38563857

38573858
cbarg.location = location;
38583859
cbarg.yyscanner = yyscanner;
@@ -3862,9 +3863,14 @@ parse_datatype(const char *string, int location, yyscan_t yyscanner)
38623863
syntax_errcontext.previous = error_context_stack;
38633864
error_context_stack = &syntax_errcontext;
38643865

3865-
/* Let the main parser try to parse it under standard SQL rules */
3866+
/*
3867+
* Let the main parser try to parse it under standard SQL rules. The
3868+
* parser leaks memory, so run it in temp context.
3869+
*/
3870+
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
38663871
typeName = typeStringToTypeName(string, NULL);
38673872
typenameTypeIdAndMod(NULL, typeName, &type_id, &typmod);
3873+
MemoryContextSwitchTo(oldCxt);
38683874

38693875
/* Restore former ereport callback */
38703876
error_context_stack = syntax_errcontext.previous;

0 commit comments

Comments
 (0)