From 7748e9e7e5aef280bea4e204017e8ac7dca14177 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 1 Jun 2001 18:17:44 +0000 Subject: [PATCH] pltcl, plperl, and plpython all suffer the same bug previously fixed in plpgsql: they fail for datatypes that have old-style I/O functions due to caching FmgrInfo structs with wrong fn_mcxt lifetime. Although the plpython fix seems straightforward, I can't check it here since I don't have Python installed --- would someone check it? --- src/pl/plperl/plperl.c | 31 +++++++++++++++++++++++----- src/pl/plpython/plpython.c | 42 ++++++++++++++++++++++++++++++-------- src/pl/tcl/pltcl.c | 31 +++++++++++++++++++++++----- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 091b4e4071..bb9ab670ae 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -33,7 +33,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plperl/plperl.c,v 1.19 2001/03/22 04:01:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plperl/plperl.c,v 1.20 2001/06/01 18:17:44 tgl Exp $ * **********************************************************************/ @@ -163,6 +163,27 @@ static void plperl_set_tuple_values(Tcl_Interp *interp, char *arrayname, #endif +/* + * This routine is a crock, and so is everyplace that calls it. The problem + * is that the cached form of plperl functions/queries is allocated permanently + * (mostly via malloc()) and never released until backend exit. Subsidiary + * data structures such as fmgr info records therefore must live forever + * as well. A better implementation would store all this stuff in a per- + * function memory context that could be reclaimed at need. In the meantime, + * fmgr_info must be called in TopMemoryContext so that whatever it might + * allocate, and whatever the eventual function might allocate using fn_mcxt, + * will live forever too. + */ +static void +perm_fmgr_info(Oid functionId, FmgrInfo *finfo) +{ + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + fmgr_info(functionId, finfo); + MemoryContextSwitchTo(oldcontext); +} + /********************************************************************** * plperl_init_all() - Initialize all **********************************************************************/ @@ -562,7 +583,7 @@ plperl_func_handler(PG_FUNCTION_ARGS) elog(ERROR, "plperl: return types of tuples not supported yet"); } - fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); + perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_in_elem = (Oid) (typeStruct->typelem); prodesc->result_in_len = typeStruct->typlen; @@ -595,7 +616,7 @@ plperl_func_handler(PG_FUNCTION_ARGS) else prodesc->arg_is_rel[i] = 0; - fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); + perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem); prodesc->arg_out_len[i] = typeStruct->typlen; ReleaseSysCache(typeTup); @@ -1560,8 +1581,8 @@ plperl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, if (!HeapTupleIsValid(typeTup)) elog(ERROR, "plperl: Cache lookup of type %s failed", args[i]); qdesc->argtypes[i] = typeTup->t_data->t_oid; - fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, - &(qdesc->arginfuncs[i])); + perm_fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, + &(qdesc->arginfuncs[i])); qdesc->argtypelems[i] = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; qdesc->argvalues[i] = (Datum) NULL; qdesc->arglen[i] = (int) (((Form_pg_type) GETSTRUCT(typeTup))->typlen); diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index dea0dc285f..ea0b7d5ed9 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,10 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/pl/plpython/plpython.c,v 1.3 2001/05/25 15:48:33 momjian Exp $ */ - -/* +/********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * IDENTIFICATION - * * This software is copyright by Andrew Bosma * but is really shameless cribbed from pltcl.c by Jan Weick, and * plperl.c by Mark Hollomon. @@ -32,7 +28,11 @@ * AND THE AUTHOR AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - **********************************************************************/ + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/pl/plpython/plpython.c,v 1.4 2001/06/01 18:17:44 tgl Exp $ + * + ********************************************************************* + */ #include "postgres.h" @@ -296,8 +296,32 @@ volatile int func_enter_calls = 0; volatile int func_leave_calls = 0; #endif -/* the function definitions +/* + * the function definitions */ + +/* + * This routine is a crock, and so is everyplace that calls it. The problem + * is that the cached form of plpython functions/queries is allocated permanently + * (mostly via malloc()) and never released until backend exit. Subsidiary + * data structures such as fmgr info records therefore must live forever + * as well. A better implementation would store all this stuff in a per- + * function memory context that could be reclaimed at need. In the meantime, + * fmgr_info must be called in TopMemoryContext so that whatever it might + * allocate, and whatever the eventual function might allocate using fn_mcxt, + * will live forever too. + */ +static void +perm_fmgr_info(Oid functionId, FmgrInfo *finfo) +{ + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + fmgr_info(functionId, finfo); + MemoryContextSwitchTo(oldcontext); +} + + Datum plpython_call_handler(PG_FUNCTION_ARGS) { @@ -1282,7 +1306,7 @@ PLy_output_datum_func2(PLyObToDatum *arg, Form_pg_type typeStruct) { enter(); - fmgr_info(typeStruct->typinput, &arg->typfunc); + perm_fmgr_info(typeStruct->typinput, &arg->typfunc); arg->typelem = (Oid) typeStruct->typelem; arg->typlen = typeStruct->typlen; } @@ -1304,7 +1328,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Form_pg_type typeStruct) char *type; arg->typoutput = typeStruct->typoutput; - fmgr_info(typeStruct->typoutput, &arg->typfunc); + perm_fmgr_info(typeStruct->typoutput, &arg->typfunc); arg->typlen = typeStruct->typlen; arg->typelem = typeStruct->typelem; diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 5c44cbe7ab..30985a434c 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -31,7 +31,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.35 2001/05/11 23:38:06 petere Exp $ + * $Header: /cvsroot/pgsql/src/pl/tcl/pltcl.c,v 1.36 2001/06/01 18:17:44 tgl Exp $ * **********************************************************************/ @@ -145,6 +145,27 @@ static void pltcl_set_tuple_values(Tcl_Interp *interp, char *arrayname, static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, Tcl_DString *retval); +/* + * This routine is a crock, and so is everyplace that calls it. The problem + * is that the cached form of pltcl functions/queries is allocated permanently + * (mostly via malloc()) and never released until backend exit. Subsidiary + * data structures such as fmgr info records therefore must live forever + * as well. A better implementation would store all this stuff in a per- + * function memory context that could be reclaimed at need. In the meantime, + * fmgr_info must be called in TopMemoryContext so that whatever it might + * allocate, and whatever the eventual function might allocate using fn_mcxt, + * will live forever too. + */ +static void +perm_fmgr_info(Oid functionId, FmgrInfo *finfo) +{ + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + fmgr_info(functionId, finfo); + MemoryContextSwitchTo(oldcontext); +} + /********************************************************************** * pltcl_init_all() - Initialize all **********************************************************************/ @@ -508,7 +529,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) elog(ERROR, "pltcl: return types of tuples not supported yet"); } - fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); + perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_in_elem = typeStruct->typelem; ReleaseSysCache(typeTup); @@ -549,7 +570,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS) else prodesc->arg_is_rel[i] = 0; - fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); + perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); prodesc->arg_out_elem[i] = (Oid) (typeStruct->typelem); prodesc->arg_out_len[i] = typeStruct->typlen; @@ -1760,8 +1781,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, if (!HeapTupleIsValid(typeTup)) elog(ERROR, "pltcl: Cache lookup of type %s failed", args[i]); qdesc->argtypes[i] = typeTup->t_data->t_oid; - fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, - &(qdesc->arginfuncs[i])); + perm_fmgr_info(((Form_pg_type) GETSTRUCT(typeTup))->typinput, + &(qdesc->arginfuncs[i])); qdesc->argtypelems[i] = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; qdesc->argvalues[i] = (Datum) NULL; qdesc->arglen[i] = (int) (((Form_pg_type) GETSTRUCT(typeTup))->typlen);