Commit 220e6bfc authored by Tom Lane's avatar Tom Lane

Fix plperl to do recursion safely, and fix a problem with array results.

Add suitable regression tests.  Andrew Dunstan
parent a1a64bb7
...@@ -367,3 +367,56 @@ SELECT * from perl_spi_func(); ...@@ -367,3 +367,56 @@ SELECT * from perl_spi_func();
2 2
(2 rows) (2 rows)
---
--- Test recursion via SPI
---
CREATE OR REPLACE FUNCTION recurse(i int) RETURNS SETOF TEXT LANGUAGE plperl
AS $$
my $i = shift;
foreach my $x (1..$i)
{
return_next "hello $x";
}
if ($i > 2)
{
my $z = $i-1;
my $cursor = spi_query("select * from recurse($z)");
while (defined(my $row = spi_fetchrow($cursor)))
{
return_next "recurse $i: $row->{recurse}";
}
}
return undef;
$$;
SELECT * FROM recurse(2);
recurse
---------
hello 1
hello 2
(2 rows)
SELECT * FROM recurse(3);
recurse
--------------------
hello 1
hello 2
hello 3
recurse 3: hello 1
recurse 3: hello 2
(5 rows)
---
--- Test arrary return
---
CREATE OR REPLACE FUNCTION array_of_text() RETURNS TEXT[][]
LANGUAGE plperl as $$
return [['a"b','c,d'],['e\\f','g']];
$$;
SELECT array_of_text();
array_of_text
-----------------------------
{{"a\"b","c,d"},{"e\\f",g}}
(1 row)
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
* ENHANCEMENTS, OR MODIFICATIONS. * ENHANCEMENTS, OR MODIFICATIONS.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.84 2005/07/10 16:13:13 momjian Exp $ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.85 2005/07/12 01:16:21 tgl Exp $
* *
**********************************************************************/ **********************************************************************/
...@@ -90,9 +90,6 @@ typedef struct plperl_proc_desc ...@@ -90,9 +90,6 @@ typedef struct plperl_proc_desc
FmgrInfo arg_out_func[FUNC_MAX_ARGS]; FmgrInfo arg_out_func[FUNC_MAX_ARGS];
bool arg_is_rowtype[FUNC_MAX_ARGS]; bool arg_is_rowtype[FUNC_MAX_ARGS];
SV *reference; SV *reference;
FunctionCallInfo caller_info;
Tuplestorestate *tuple_store;
TupleDesc tuple_desc;
} plperl_proc_desc; } plperl_proc_desc;
...@@ -106,8 +103,11 @@ static HV *plperl_proc_hash = NULL; ...@@ -106,8 +103,11 @@ static HV *plperl_proc_hash = NULL;
static bool plperl_use_strict = false; static bool plperl_use_strict = false;
/* this is saved and restored by plperl_call_handler */ /* these are saved and restored by plperl_call_handler */
static plperl_proc_desc *plperl_current_prodesc = NULL; static plperl_proc_desc *plperl_current_prodesc = NULL;
static FunctionCallInfo plperl_current_caller_info;
static Tuplestorestate *plperl_current_tuple_store;
static TupleDesc plperl_current_tuple_desc;
/********************************************************************** /**********************************************************************
* Forward declarations * Forward declarations
...@@ -577,10 +577,16 @@ plperl_call_handler(PG_FUNCTION_ARGS) ...@@ -577,10 +577,16 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{ {
Datum retval; Datum retval;
plperl_proc_desc *save_prodesc; plperl_proc_desc *save_prodesc;
FunctionCallInfo save_caller_info;
Tuplestorestate *save_tuple_store;
TupleDesc save_tuple_desc;
plperl_init_all(); plperl_init_all();
save_prodesc = plperl_current_prodesc; save_prodesc = plperl_current_prodesc;
save_caller_info = plperl_current_caller_info;
save_tuple_store = plperl_current_tuple_store;
save_tuple_desc = plperl_current_tuple_desc;
PG_TRY(); PG_TRY();
{ {
...@@ -592,11 +598,17 @@ plperl_call_handler(PG_FUNCTION_ARGS) ...@@ -592,11 +598,17 @@ plperl_call_handler(PG_FUNCTION_ARGS)
PG_CATCH(); PG_CATCH();
{ {
plperl_current_prodesc = save_prodesc; plperl_current_prodesc = save_prodesc;
plperl_current_caller_info = save_caller_info;
plperl_current_tuple_store = save_tuple_store;
plperl_current_tuple_desc = save_tuple_desc;
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
plperl_current_prodesc = save_prodesc; plperl_current_prodesc = save_prodesc;
plperl_current_caller_info = save_caller_info;
plperl_current_tuple_store = save_tuple_store;
plperl_current_tuple_desc = save_tuple_desc;
return retval; return retval;
} }
...@@ -897,6 +909,7 @@ plperl_func_handler(PG_FUNCTION_ARGS) ...@@ -897,6 +909,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
SV *perlret; SV *perlret;
Datum retval; Datum retval;
ReturnSetInfo *rsi; ReturnSetInfo *rsi;
SV* array_ret = NULL;
if (SPI_connect() != SPI_OK_CONNECT) if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager"); elog(ERROR, "could not connect to SPI manager");
...@@ -904,9 +917,9 @@ plperl_func_handler(PG_FUNCTION_ARGS) ...@@ -904,9 +917,9 @@ plperl_func_handler(PG_FUNCTION_ARGS)
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false); prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
plperl_current_prodesc = prodesc; plperl_current_prodesc = prodesc;
prodesc->caller_info = fcinfo; plperl_current_caller_info = fcinfo;
prodesc->tuple_store = 0; plperl_current_tuple_store = 0;
prodesc->tuple_desc = 0; plperl_current_tuple_desc = 0;
perlret = plperl_call_perl_func(prodesc, fcinfo); perlret = plperl_call_perl_func(prodesc, fcinfo);
...@@ -958,10 +971,10 @@ plperl_func_handler(PG_FUNCTION_ARGS) ...@@ -958,10 +971,10 @@ plperl_func_handler(PG_FUNCTION_ARGS)
} }
rsi->returnMode = SFRM_Materialize; rsi->returnMode = SFRM_Materialize;
if (prodesc->tuple_store) if (plperl_current_tuple_store)
{ {
rsi->setResult = prodesc->tuple_store; rsi->setResult = plperl_current_tuple_store;
rsi->setDesc = prodesc->tuple_desc; rsi->setDesc = plperl_current_tuple_desc;
} }
retval = (Datum)0; retval = (Datum)0;
} }
...@@ -1006,7 +1019,6 @@ plperl_func_handler(PG_FUNCTION_ARGS) ...@@ -1006,7 +1019,6 @@ plperl_func_handler(PG_FUNCTION_ARGS)
{ {
/* Return a perl string converted to a Datum */ /* Return a perl string converted to a Datum */
char *val; char *val;
SV* array_ret;
if (prodesc->fn_retisarray && SvTYPE(SvRV(perlret)) == SVt_PVAV) if (prodesc->fn_retisarray && SvTYPE(SvRV(perlret)) == SVt_PVAV)
...@@ -1024,7 +1036,9 @@ plperl_func_handler(PG_FUNCTION_ARGS) ...@@ -1024,7 +1036,9 @@ plperl_func_handler(PG_FUNCTION_ARGS)
Int32GetDatum(-1)); Int32GetDatum(-1));
} }
if (array_ret == NULL)
SvREFCNT_dec(perlret); SvREFCNT_dec(perlret);
return retval; return retval;
} }
...@@ -1526,7 +1540,7 @@ void ...@@ -1526,7 +1540,7 @@ void
plperl_return_next(SV *sv) plperl_return_next(SV *sv)
{ {
plperl_proc_desc *prodesc = plperl_current_prodesc; plperl_proc_desc *prodesc = plperl_current_prodesc;
FunctionCallInfo fcinfo = prodesc->caller_info; FunctionCallInfo fcinfo = plperl_current_caller_info;
ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo; ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
MemoryContext cxt; MemoryContext cxt;
HeapTuple tuple; HeapTuple tuple;
...@@ -1553,8 +1567,9 @@ plperl_return_next(SV *sv) ...@@ -1553,8 +1567,9 @@ plperl_return_next(SV *sv)
cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
if (!prodesc->tuple_store) if (!plperl_current_tuple_store)
prodesc->tuple_store = tuplestore_begin_heap(true, false, work_mem); plperl_current_tuple_store =
tuplestore_begin_heap(true, false, work_mem);
if (prodesc->fn_retistuple) if (prodesc->fn_retistuple)
{ {
...@@ -1590,10 +1605,10 @@ plperl_return_next(SV *sv) ...@@ -1590,10 +1605,10 @@ plperl_return_next(SV *sv)
tuple = heap_form_tuple(tupdesc, &ret, &isNull); tuple = heap_form_tuple(tupdesc, &ret, &isNull);
} }
if (!prodesc->tuple_desc) if (!plperl_current_tuple_desc)
prodesc->tuple_desc = tupdesc; plperl_current_tuple_desc = tupdesc;
tuplestore_puttuple(prodesc->tuple_store, tuple); tuplestore_puttuple(plperl_current_tuple_store, tuple);
heap_freetuple(tuple); heap_freetuple(tuple);
MemoryContextSwitchTo(cxt); MemoryContextSwitchTo(cxt);
} }
......
...@@ -260,3 +260,44 @@ while (defined ($y = spi_fetchrow($x))) { ...@@ -260,3 +260,44 @@ while (defined ($y = spi_fetchrow($x))) {
return; return;
$$ LANGUAGE plperl; $$ LANGUAGE plperl;
SELECT * from perl_spi_func(); SELECT * from perl_spi_func();
---
--- Test recursion via SPI
---
CREATE OR REPLACE FUNCTION recurse(i int) RETURNS SETOF TEXT LANGUAGE plperl
AS $$
my $i = shift;
foreach my $x (1..$i)
{
return_next "hello $x";
}
if ($i > 2)
{
my $z = $i-1;
my $cursor = spi_query("select * from recurse($z)");
while (defined(my $row = spi_fetchrow($cursor)))
{
return_next "recurse $i: $row->{recurse}";
}
}
return undef;
$$;
SELECT * FROM recurse(2);
SELECT * FROM recurse(3);
---
--- Test arrary return
---
CREATE OR REPLACE FUNCTION array_of_text() RETURNS TEXT[][]
LANGUAGE plperl as $$
return [['a"b','c,d'],['e\\f','g']];
$$;
SELECT array_of_text();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment