diff --git a/src/pl/plpython/expected/plpython_setof.out b/src/pl/plpython/expected/plpython_setof.out index ac9765fc882..b3bbdd81238 100644 --- a/src/pl/plpython/expected/plpython_setof.out +++ b/src/pl/plpython/expected/plpython_setof.out @@ -124,3 +124,19 @@ SELECT test_setof_spi_in_iterator(); World (4 rows) +-- setof function with an SPI result set (used to crash because of +-- memory management issues across multiple calls) +CREATE OR REPLACE FUNCTION get_user_records() +RETURNS SETOF users +AS $$ + return plpy.execute("SELECT * FROM users ORDER BY username") +$$ LANGUAGE plpythonu; +SELECT get_user_records(); + get_user_records +---------------------- + (jane,doe,j_doe,1) + (john,doe,johnd,2) + (rick,smith,slash,4) + (willem,doe,w_doe,3) +(4 rows) + diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c index cde3c08f967..4bc3d96d589 100644 --- a/src/pl/plpython/plpy_spi.c +++ b/src/pl/plpython/plpy_spi.c @@ -11,6 +11,7 @@ #include "executor/spi_priv.h" #include "mb/pg_wchar.h" #include "parser/parse_type.h" +#include "utils/memutils.h" #include "utils/syscache.h" #include "plpython.h" @@ -403,7 +404,17 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) oldcontext = CurrentMemoryContext; PG_TRY(); { + MemoryContext oldcontext2; + + /* + * Save tuple descriptor for later use by result set metadata + * functions. Save it in TopMemoryContext so that it survives + * outside of an SPI context. We trust that PLy_result_dealloc() + * will clean it up when the time is right. + */ + oldcontext2 = MemoryContextSwitchTo(TopMemoryContext); result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc); + MemoryContextSwitchTo(oldcontext2); if (rows) { diff --git a/src/pl/plpython/sql/plpython_setof.sql b/src/pl/plpython/sql/plpython_setof.sql index 80a8d5b4c1e..243f711dcc6 100644 --- a/src/pl/plpython/sql/plpython_setof.sql +++ b/src/pl/plpython/sql/plpython_setof.sql @@ -62,3 +62,15 @@ SELECT test_setof_as_iterator(2, 'list'); SELECT test_setof_as_iterator(2, null); SELECT test_setof_spi_in_iterator(); + + +-- setof function with an SPI result set (used to crash because of +-- memory management issues across multiple calls) + +CREATE OR REPLACE FUNCTION get_user_records() +RETURNS SETOF users +AS $$ + return plpy.execute("SELECT * FROM users ORDER BY username") +$$ LANGUAGE plpythonu; + +SELECT get_user_records();