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:

Previous
From: Neil Conway
Date:
Subject: code cleanup for tz
Next
From: "Dave Page"
Date:
Subject: Default database patch