|
|
|
@ -1,7 +1,7 @@ |
|
|
|
|
/**********************************************************************
|
|
|
|
|
* plperl.c - perl as a procedural language for PostgreSQL |
|
|
|
|
* |
|
|
|
|
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.115 2006/08/12 04:16:45 momjian Exp $ |
|
|
|
|
* $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.116 2006/08/13 02:37:11 momjian Exp $ |
|
|
|
|
* |
|
|
|
|
**********************************************************************/ |
|
|
|
|
|
|
|
|
@ -52,6 +52,7 @@ typedef struct plperl_proc_desc |
|
|
|
|
FmgrInfo result_in_func; /* I/O function and arg for result type */ |
|
|
|
|
Oid result_typioparam; |
|
|
|
|
int nargs; |
|
|
|
|
int num_out_args; /* number of out arguments */ |
|
|
|
|
FmgrInfo arg_out_func[FUNC_MAX_ARGS]; |
|
|
|
|
bool arg_is_rowtype[FUNC_MAX_ARGS]; |
|
|
|
|
SV *reference; |
|
|
|
@ -115,6 +116,9 @@ static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc); |
|
|
|
|
static void plperl_init_shared_libs(pTHX); |
|
|
|
|
static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); |
|
|
|
|
|
|
|
|
|
static SV *plperl_convert_to_pg_array(SV *src); |
|
|
|
|
static SV *plperl_transform_result(plperl_proc_desc *prodesc, SV *result); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 |
|
|
|
@ -404,7 +408,12 @@ plperl_build_tuple_result(HV *perlhash, AttInMetadata *attinmeta) |
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN), |
|
|
|
|
errmsg("Perl hash contains nonexistent column \"%s\"", |
|
|
|
|
key))); |
|
|
|
|
if (SvOK(val) && SvTYPE(val) != SVt_NULL) |
|
|
|
|
|
|
|
|
|
/* if value is ref on array do to pg string array conversion */ |
|
|
|
|
if (SvTYPE(val) == SVt_RV && |
|
|
|
|
SvTYPE(SvRV(val)) == SVt_PVAV) |
|
|
|
|
values[attn - 1] = SvPV(plperl_convert_to_pg_array(val), PL_na); |
|
|
|
|
else if (SvOK(val) && SvTYPE(val) != SVt_NULL) |
|
|
|
|
values[attn - 1] = SvPV(val, PL_na); |
|
|
|
|
} |
|
|
|
|
hv_iterinit(perlhash); |
|
|
|
@ -681,12 +690,7 @@ plperl_validator(PG_FUNCTION_ARGS) |
|
|
|
|
HeapTuple tuple; |
|
|
|
|
Form_pg_proc proc; |
|
|
|
|
char functyptype; |
|
|
|
|
int numargs; |
|
|
|
|
Oid *argtypes; |
|
|
|
|
char **argnames; |
|
|
|
|
char *argmodes; |
|
|
|
|
bool istrigger = false; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
/* Get the new function's pg_proc entry */ |
|
|
|
|
tuple = SearchSysCache(PROCOID, |
|
|
|
@ -714,18 +718,6 @@ plperl_validator(PG_FUNCTION_ARGS) |
|
|
|
|
format_type_be(proc->prorettype)))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Disallow pseudotypes in arguments (either IN or OUT) */ |
|
|
|
|
numargs = get_func_arg_info(tuple, |
|
|
|
|
&argtypes, &argnames, &argmodes); |
|
|
|
|
for (i = 0; i < numargs; i++) |
|
|
|
|
{ |
|
|
|
|
if (get_typtype(argtypes[i]) == 'p') |
|
|
|
|
ereport(ERROR, |
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
|
|
|
|
errmsg("plperl functions cannot take type %s", |
|
|
|
|
format_type_be(argtypes[i])))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple); |
|
|
|
|
|
|
|
|
|
/* Postpone body checks if !check_function_bodies */ |
|
|
|
@ -1128,6 +1120,8 @@ plperl_func_handler(PG_FUNCTION_ARGS) |
|
|
|
|
/* Return a perl string converted to a Datum */ |
|
|
|
|
char *val; |
|
|
|
|
|
|
|
|
|
perlret = plperl_transform_result(prodesc, perlret); |
|
|
|
|
|
|
|
|
|
if (prodesc->fn_retisarray && SvROK(perlret) && |
|
|
|
|
SvTYPE(SvRV(perlret)) == SVt_PVAV) |
|
|
|
|
{ |
|
|
|
@ -1256,7 +1250,6 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) |
|
|
|
|
char internal_proname[64]; |
|
|
|
|
int proname_len; |
|
|
|
|
plperl_proc_desc *prodesc = NULL; |
|
|
|
|
int i; |
|
|
|
|
SV **svp; |
|
|
|
|
|
|
|
|
|
/* We'll need the pg_proc tuple in any case... */ |
|
|
|
@ -1319,6 +1312,12 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) |
|
|
|
|
Datum prosrcdatum; |
|
|
|
|
bool isnull; |
|
|
|
|
char *proc_source; |
|
|
|
|
int i; |
|
|
|
|
int numargs; |
|
|
|
|
Oid *argtypes; |
|
|
|
|
char **argnames; |
|
|
|
|
char *argmodes; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
* Allocate a new procedure description block |
|
|
|
@ -1337,6 +1336,25 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) |
|
|
|
|
prodesc->fn_readonly = |
|
|
|
|
(procStruct->provolatile != PROVOLATILE_VOLATILE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Disallow pseudotypes in arguments (either IN or OUT) */ |
|
|
|
|
/* Count number of out arguments */ |
|
|
|
|
numargs = get_func_arg_info(procTup, |
|
|
|
|
&argtypes, &argnames, &argmodes); |
|
|
|
|
for (i = 0; i < numargs; i++) |
|
|
|
|
{ |
|
|
|
|
if (get_typtype(argtypes[i]) == 'p') |
|
|
|
|
ereport(ERROR, |
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
|
|
|
|
errmsg("plperl functions cannot take type %s", |
|
|
|
|
format_type_be(argtypes[i])))); |
|
|
|
|
|
|
|
|
|
if (argmodes && argmodes[i] == PROARGMODE_OUT) |
|
|
|
|
prodesc->num_out_args++; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
* Lookup the pg_language tuple by Oid |
|
|
|
|
************************************************************/ |
|
|
|
@ -1676,6 +1694,8 @@ plperl_return_next(SV *sv) |
|
|
|
|
fcinfo = current_call_data->fcinfo; |
|
|
|
|
rsi = (ReturnSetInfo *) fcinfo->resultinfo; |
|
|
|
|
|
|
|
|
|
sv = plperl_transform_result(prodesc, sv); |
|
|
|
|
|
|
|
|
|
if (!prodesc->fn_retisset) |
|
|
|
|
ereport(ERROR, |
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR), |
|
|
|
@ -1753,7 +1773,16 @@ plperl_return_next(SV *sv) |
|
|
|
|
|
|
|
|
|
if (SvOK(sv) && SvTYPE(sv) != SVt_NULL) |
|
|
|
|
{ |
|
|
|
|
char *val = SvPV(sv, PL_na); |
|
|
|
|
char *val; |
|
|
|
|
SV *array_ret; |
|
|
|
|
|
|
|
|
|
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV ) |
|
|
|
|
{ |
|
|
|
|
array_ret = plperl_convert_to_pg_array(sv); |
|
|
|
|
sv = array_ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
val = SvPV(sv, PL_na); |
|
|
|
|
|
|
|
|
|
ret = InputFunctionCall(&prodesc->result_in_func, val, |
|
|
|
|
prodesc->result_typioparam, -1); |
|
|
|
@ -2368,3 +2397,46 @@ plperl_spi_freeplan(char *query) |
|
|
|
|
|
|
|
|
|
SPI_freeplan( plan); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If plerl result is hash and fce result is scalar, it's hash form of |
|
|
|
|
* out argument. Then, transform it to scalar |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static SV * |
|
|
|
|
plperl_transform_result(plperl_proc_desc *prodesc, SV *result) |
|
|
|
|
{ |
|
|
|
|
bool exactly_one_field = false; |
|
|
|
|
HV *hvr; |
|
|
|
|
SV *val; |
|
|
|
|
char *key; |
|
|
|
|
I32 klen; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (prodesc->num_out_args == 1 && SvOK(result)
|
|
|
|
|
&& SvTYPE(result) == SVt_RV && SvTYPE(SvRV(result)) == SVt_PVHV) |
|
|
|
|
{ |
|
|
|
|
hvr = (HV *) SvRV(result); |
|
|
|
|
hv_iterinit(hvr); |
|
|
|
|
|
|
|
|
|
while ((val = hv_iternextsv(hvr, &key, &klen))) |
|
|
|
|
{ |
|
|
|
|
if (exactly_one_field) |
|
|
|
|
ereport(ERROR, |
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN), |
|
|
|
|
errmsg("Perl hash contains nonexistent column \"%s\"", |
|
|
|
|
key))); |
|
|
|
|
exactly_one_field = true; |
|
|
|
|
result = val; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!exactly_one_field) |
|
|
|
|
ereport(ERROR, |
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN), |
|
|
|
|
errmsg("Perl hash is empty"))); |
|
|
|
|
|
|
|
|
|
hv_iterinit(hvr); |
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|