plperl better array support - Mailing list pgsql-patches
From | Andrew Dunstan |
---|---|
Subject | plperl better array support |
Date | |
Msg-id | 42B68718.5010209@dunslane.net Whole thread Raw |
Responses |
Re: plperl better array support
Re: plperl better array support |
List | pgsql-patches |
The attached patch (submitted for comment) is somewhat adapted from one submitted last October. This allows returning a perl array where a postgres array is expected. example: andrew=# create function blurfl() returns text[] language plperl as $$ andrew$# return ['a','b','c','a"b\c']; andrew$# $$; CREATE FUNCTION andrew=# select blurfl(); blurfl ------------------- {a,b,c,"a\"b\\c"} Unlike the patch from October, this patch does not implement ANYARRAY or ANYELEMENT pseudotypes. However it does escape/quote array elements where necessary. It also preserves the old behaviour (if the plperl function returns a string it is just passed through). I'm not happy about constructing a string which we then parse out again into an array - that strikes me as quite inefficient. (And there are other inelegancies that I'd like to get rid of.) Much better would be to use some array contruction calls directly - any pointers on how to do that would be apprciated :-) cheers andrew Index: plperl.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plperl/plperl.c,v retrieving revision 1.76 diff -c -r1.76 plperl.c *** plperl.c 5 Jun 2005 03:16:35 -0000 1.76 --- plperl.c 20 Jun 2005 08:54:15 -0000 *************** *** 80,85 **** --- 80,86 ---- bool lanpltrusted; bool fn_retistuple; /* true, if function returns tuple */ bool fn_retisset; /* true, if function returns set */ + bool fn_retisarray; /* true if function returns array */ Oid result_oid; /* Oid of result type */ FmgrInfo result_in_func; /* I/O function and arg for result type */ Oid result_typioparam; *************** *** 323,328 **** --- 324,408 ---- return tup; } + /* substitute(string, pattern) + * + * Used for =~ operations that modify their left-hand side (s/// and tr///) + * + * Returns the number of successful matches, and + * modifies the input string if there were any. + * + * (almost) straight from perlembed man page. + */ + + static I32 + plperl_substitute(SV **string, char *pattern) + { + SV *command = NEWSV(1099, 0); + I32 retval; + STRLEN n_a; + + sv_setpvf(command, "$_plp_string = '%s'; ($_plp_string =~ %s)", + SvPV(*string,n_a), pattern); + + retval = eval_sv(command, TRUE); + + *string = get_sv("_plp_string", FALSE); + return retval; + } + + /* + * convert perl array to postgres string representation + */ + static SV* + plperl_convert_to_pg_array(SV *src) + { + SV* rv; + SV** val; + AV* internal; + int len, + i; + + internal=(AV*)SvRV(src); + len = av_len(internal)+1; + + rv = newSVpv("{ ",0); + for(i=0; i<len; i++) + { + val = av_fetch(internal, i, FALSE); + if (SvTYPE(*val)==SVt_RV) + { + /* + * If there's a reference type val, call this func + * recursively to handle a nested array, and error out on any + * other reference type. + */ + + if (SvTYPE(SvRV(*val))==SVt_PVAV) + sv_catpvf(rv, "%s", + SvPV(plperl_convert_to_pg_array(*val),PL_na) ); + else + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("returned array contains non-array ref"))); + } + else + { + /* non-reference case - append the stringified value */ + SV * copyval; + + copyval= newSVpv(SvPV(*val,PL_na),0); + plperl_substitute(©val,"s/([\"\\\\])/\\\\$1/g"); + sv_catpvf(rv, "\"%s\"", SvPV(copyval,PL_na)); + } + + if (i != len-1) sv_catpvf(rv, ","); + } + + sv_catpvf(rv, "}"); + + return rv; + } + /* Set up the arguments for a trigger call. */ *************** *** 817,823 **** rsi = (ReturnSetInfo *)fcinfo->resultinfo; ! if (prodesc->fn_retisset) { if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) --- 897,904 ---- rsi = (ReturnSetInfo *)fcinfo->resultinfo; ! if (prodesc->fn_retisset) ! { if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) *************** *** 838,844 **** int i = 0; SV **svp = 0; AV *rav = (AV *)SvRV(perlret); ! while ((svp = av_fetch(rav, i, FALSE)) != NULL) { plperl_return_next(*svp); i++; } --- 919,926 ---- int i = 0; SV **svp = 0; AV *rav = (AV *)SvRV(perlret); ! while ((svp = av_fetch(rav, i, FALSE)) != NULL) ! { plperl_return_next(*svp); i++; } *************** *** 852,858 **** } rsi->returnMode = SFRM_Materialize; ! if (prodesc->tuple_store) { rsi->setResult = prodesc->tuple_store; rsi->setDesc = prodesc->tuple_desc; } --- 934,941 ---- } rsi->returnMode = SFRM_Materialize; ! if (prodesc->tuple_store) ! { rsi->setResult = prodesc->tuple_store; rsi->setDesc = prodesc->tuple_desc; } *************** *** 897,904 **** } else { ! /* Return a perl string converted to a Datum */ ! char *val = SvPV(perlret, PL_na); retval = FunctionCall3(&prodesc->result_in_func, CStringGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), --- 980,999 ---- } else { ! /* Return a perl string converted to a Datum */ ! char *val; ! SV* array_ret; ! ! ! if (prodesc->fn_retisarray && SvTYPE(SvRV(perlret)) == SVt_PVAV) ! { ! array_ret = plperl_convert_to_pg_array(perlret); ! SvREFCNT_dec(perlret); ! perlret = array_ret; ! } ! ! val = SvPV(perlret, PL_na); ! retval = FunctionCall3(&prodesc->result_in_func, CStringGetDatum(val), ObjectIdGetDatum(prodesc->result_typioparam), *************** *** 1156,1161 **** --- 1251,1259 ---- prodesc->fn_retistuple = (typeStruct->typtype == 'c' || procStruct->prorettype == RECORDOID); + prodesc->fn_retisarray = + (typeStruct->typlen == -1 && typeStruct->typelem) ; + perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_typioparam = getTypeIOParam(typeTup);
pgsql-patches by date: