Thread: [PATCH] plpythonu datatype conversion improvements

[PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
<font face="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">Patch for plpythonu<br /><br /> Primary
motivationof the attached patch is to support handling bytea conversion allowing for embedded nulls, which in turn
allowsfor supporting the marshal module.<br /><br /> Secondary motivation is slightly improved performance for
conversionroutines of basic datatypes that have simple mappings between postgres/python.<br /><br /> Primary design is
tochange the conversion routines from being based on cstrings to datums, eg:<br />     PLyBool_FromString(const char *)
 =>PLyBool_FromBool(PLyDatumToOb, Datum);<br /><br /> Thanks, <br />    Caleb<br /><br /> -----<br /><br /> Index:
plpython.c<br/> ===================================================================<br /> RCS file:
/projects/cvsroot/pgsql/src/pl/plpython/plpython.c,v<br/> retrieving revision 1.120<br /> diff -c -r1.120 plpython.c<br
/>*** plpython.c    3 Apr 2009 16:59:42 -0000    1.120<br /> --- plpython.c    26 May 2009 22:58:52 -0000<br />
***************<br/> *** 78,84 ****<br />    * objects.<br />    */<br />   <br /> ! typedef PyObject
*(*PLyDatumToObFunc)(const char *);<br />   <br />   typedef struct PLyDatumToOb<br />   {<br /> --- 78,85 ----<br />
   *objects.<br />    */<br />   <br /> ! struct PLyDatumToOb;<br /> ! typedef PyObject *(*PLyDatumToObFunc) (struct
PLyDatumToOb*,Datum);<br />   <br />   typedef struct PLyDatumToOb<br />   {<br /> ***************<br /> *** 104,111
****<br/> --- 105,120 ----<br />   /* convert PyObject to a Postgresql Datum or tuple.<br />    * output from Python<br
/>   */<br /> + <br /> + struct PLyObToDatum;<br /> + struct PLyProcedure;<br /> + typedef Datum (*PLyObToDatumFunc)
(structPLyProcedure*, <br /> +                                    struct PLyObToDatum*, <br /> +
                                   PyObject*, bool *isnull);<br /> + <br />   typedef struct PLyObToDatum<br />   {<br
/>+     PLyObToDatumFunc func;<br />       FmgrInfo    typfunc;        /* The type's input function */<br />       Oid
           typoid;           /* The OID of the type */<br />       Oid            typioparam;<br /> ***************<br
/>*** 255,270 ****<br />   static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);<br />   <br />   /* conversion
functions*/<br />   static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);<br /> ! static PyObject
*PLyBool_FromString(constchar *);<br /> ! static PyObject *PLyFloat_FromString(const char *);<br /> ! static PyObject
*PLyInt_FromString(constchar *);<br /> ! static PyObject *PLyLong_FromString(const char *);<br /> ! static PyObject
*PLyString_FromString(constchar *);<br /> ! <br /> ! static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);<br
/>! static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);<br /> ! static HeapTuple
PLyObject_ToTuple(PLyTypeInfo*, PyObject *);<br />   <br />   /*<br />    * Currently active plpython function<br />
---264,295 ----<br />   static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);<br />   <br />   /* conversion
functions*/<br /> + static PyObject *PLyBool_FromBool(PLyDatumToOb *arg, Datum d);<br /> + static PyObject
*PLyFloat_FromFloat4(PLyDatumToOb*arg, Datum d);<br /> + static PyObject *PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum
d);<br/> + static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);<br /> + static PyObject
*PLyInt_FromInt16(PLyDatumToOb*arg, Datum d);<br /> + static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);<br
/>+ static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);<br /> + static PyObject
*PLyString_FromText(PLyDatumToOb*arg, Datum d);<br /> + static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum
d);<br/> + <br />   static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);<br /> ! <br /> ! static
DatumPLyObject_ToVoid(PLyProcedure *, PLyObToDatum *, <br /> !                               PyObject *, bool
*isnull);<br/> ! static Datum PLyObject_ToBool(PLyProcedure *, PLyObToDatum *, <br /> !
                              PyObject*, bool *isnull);<br /> ! static Datum PLyObject_ToBytea(PLyProcedure *,
PLyObToDatum*, <br /> !                                PyObject *, bool *isnull);<br /> ! static Datum
PLyObject_ToText(PLyProcedure*, PLyObToDatum *, <br /> !                               PyObject *, bool *isnull);<br />
!static Datum PLyObject_ToDatum(PLyProcedure *, PLyObToDatum *, <br /> !                                PyObject *,
bool*isnull);<br /> ! <br /> ! static HeapTuple PLyMapping_ToTuple(PLyProcedure *, PyObject *);<br /> ! static
HeapTuplePLySequence_ToTuple(PLyProcedure *, PyObject *);<br /> ! static HeapTuple PLyObject_ToTuple(PLyProcedure *,
PyObject*);<br />   <br />   /*<br />    * Currently active plpython function<br /> ***************<br /> *** 507,514
****<br/>   <br />           for (i = 0; i < natts; i++)<br />           {<br /> -             char       *src;<br
/>- <br />               platt = PyList_GetItem(plkeys, i);<br />               if (!PyString_Check(platt))<br />
                  ereport(ERROR,<br/> --- 532,537 ----<br /> ***************<br /> *** 533,564 ****<br />
                  modvalues[i]= (Datum) 0;<br />                   modnulls[i] = 'n';<br />               }<br /> !
            elseif (plval != Py_None)<br />               {<br /> !                 plstr = PyObject_Str(plval);<br />
!                if (!plstr)<br /> !                     PLy_elog(ERROR, "could not compute string representation of
Pythonobject in PL/Python function \"%s\" while modifying trigger row",<br /> !
                             proc->proname);<br/> !                 src = PyString_AsString(plstr);<br /> ! <br /> !
                modvalues[i]=<br /> !
                    InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,<br/> !
                                      src,<br/> !
                                    proc->result.out.r.atts[atti].typioparam,<br/> !
                                      tupdesc->attrs[atti]->atttypmod);<br/> !                 modnulls[i] = '
';<br/> ! <br /> !                 Py_DECREF(plstr);<br /> !                 plstr = NULL;<br /> !             }<br />
!            else<br /> !             {<br /> !                 modvalues[i] =<br /> !
                    InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,<br/> !
                                      NULL,<br/> !
                                    proc->result.out.r.atts[atti].typioparam,<br/> !
                                      tupdesc->attrs[atti]->atttypmod);<br/> !                 modnulls[i] =
'n';<br/>               }<br />   <br />               Py_DECREF(plval);<br /> --- 556,565 ----<br />
                  modvalues[i]= (Datum) 0;<br />                   modnulls[i] = 'n';<br />               }<br /> !
            else<br />               {<br /> !                 PLyObToDatum *att =
&proc->result.out.r.atts[atti];<br/> !                 modvalues[i] = (att->func) (proc, att, plval,
&modnulls[i]);<br/>               }<br />   <br />               Py_DECREF(plval);<br /> ***************<br /> ***
784,791****<br />       Datum        rv;<br />       PyObject   *volatile plargs = NULL;<br />       PyObject
  *volatileplrv = NULL;<br /> -     PyObject   *volatile plrv_so = NULL;<br /> -     char       *plrv_sc;<br />   <br
/>      PG_TRY();<br />       {<br /> --- 785,790 ----<br /> ***************<br /> *** 862,868 ****<br />   <br />
                  Py_XDECREF(plargs);<br/>                   Py_XDECREF(plrv);<br /> -
                Py_XDECREF(plrv_so);<br/>   <br />                   PLy_function_delete_args(proc);<br />   <br /> ---
861,866----<br /> ***************<br /> *** 876,922 ****<br />               }<br />           }<br />   <br /> !
        /*<br/> !          * If the function is declared to return void, the Python return value<br /> !          *
mustbe None. For void-returning functions, we also treat a None<br /> !          * return value as a special "void
datum"rather than NULL (as is the<br /> !          * case for non-void-returning functions).<br /> !          */<br />
!        if (proc->result.out.d.typoid == VOIDOID)<br /> !         {<br /> !             if (plrv != Py_None)<br />
!                ereport(ERROR,<br /> !                         (errcode(ERRCODE_DATATYPE_MISMATCH),<br /> !
                       errmsg("PL/Pythonfunction with return type \"void\" did not return None")));<br /> ! <br /> !
            fcinfo->isnull= false;<br /> !             rv = (Datum) 0;<br /> !         }<br /> !         else if
(plrv== Py_None)<br /> !         {<br /> !             fcinfo->isnull = true;<br /> !             if
(proc->result.is_rowtype< 1)<br /> !                 rv =
InputFunctionCall(&proc->result.out.d.typfunc,<br/> !                                        NULL,<br /> !
                                       proc->result.out.d.typioparam,<br/> !
                                       -1);<br/> !             else<br /> !                 /* Tuple as None */<br /> !
                rv= (Datum) NULL;<br /> !         }<br /> !         else if (proc->result.is_rowtype >= 1)<br />
          {<br/>               HeapTuple    tuple = NULL;<br />   <br /> !             if (PySequence_Check(plrv))<br
/>                  /* composite type as sequence (tuple, list etc) */<br /> !                 tuple =
PLySequence_ToTuple(&proc->result,plrv);<br />               else if (PyMapping_Check(plrv))<br />
                  /*composite type as mapping (currently only dict) */<br /> !                 tuple =
PLyMapping_ToTuple(&proc->result,plrv);<br />               else<br />                   /* returned as smth,
mustprovide method __getattr__(name) */<br /> !                 tuple = PLyObject_ToTuple(&proc->result,
plrv);<br/>   <br />               if (tuple != NULL)<br />               {<br /> --- 874,895 ----<br />
              }<br/>           }<br />   <br /> !         /* Convert python return value into postgres datatypes */<br
/>!         if (proc->result.is_rowtype >= 1)<br />           {<br />               HeapTuple    tuple = NULL;<br
/>  <br /> !             if (plrv == Py_None)<br /> !                 tuple = NULL;<br /> !             else if
(PySequence_Check(plrv))<br/>                   /* composite type as sequence (tuple, list etc) */<br /> !
                tuple= PLySequence_ToTuple(proc, plrv);<br />               else if (PyMapping_Check(plrv))<br />
                  /*composite type as mapping (currently only dict) */<br /> !                 tuple =
PLyMapping_ToTuple(proc,plrv);<br />               else<br />                   /* returned as smth, must provide
method__getattr__(name) */<br /> !                 tuple = PLyObject_ToTuple(proc, plrv);<br />   <br />
              if(tuple != NULL)<br />               {<br /> ***************<br /> *** 931,952 ****<br />           }<br
/>          else<br />           {<br /> !             fcinfo->isnull = false;<br /> !             plrv_so =
PyObject_Str(plrv);<br/> !             if (!plrv_so)<br /> !                 PLy_elog(ERROR, "could not create string
representationof Python object in PL/Python function \"%s\" while creating return value", proc->proname);<br /> !
            plrv_sc= PyString_AsString(plrv_so);<br /> !             rv =
InputFunctionCall(&proc->result.out.d.typfunc,<br/> !                                    plrv_sc,<br /> !
                                   proc->result.out.d.typioparam,<br/> !                                    -1);<br
/>          }<br />       }<br />       PG_CATCH();<br />       {<br />           Py_XDECREF(plargs);<br />
          Py_XDECREF(plrv);<br/> -         Py_XDECREF(plrv_so);<br />   <br />           PG_RE_THROW();<br />
      }<br/> --- 904,919 ----<br />           }<br />           else<br />           {<br /> !             rv =
(proc->result.out.d.func)(proc,<br /> !                                             &proc->result.out.d, <br
/>!                                             plrv,<br /> !
                                            &fcinfo->isnull);<br/>           }<br />       }<br />
      PG_CATCH();<br/>       {<br />           Py_XDECREF(plargs);<br />           Py_XDECREF(plrv);<br />   <br />
          PG_RE_THROW();<br/>       }<br /> ***************<br /> *** 954,960 ****<br />   <br />
      Py_XDECREF(plargs);<br/>       Py_DECREF(plrv);<br /> -     Py_XDECREF(plrv_so);<br />   <br />       return
rv;<br/>   }<br /> --- 921,926 ----<br /> ***************<br /> *** 1037,1048 ****<br />                       arg =
NULL;<br/>                   else<br />                   {<br /> !                     char       *ct;<br /> ! <br />
!                    ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc),<br /> !
                                            fcinfo->arg[i]);<br/> !                     arg =
(proc->args[i].in.d.func)(ct);<br /> !                     pfree(ct);<br />                   }<br />
              }<br/>   <br /> --- 1003,1010 ----<br />                       arg = NULL;<br />
                  else<br/>                   {<br /> !                     arg = (proc->args[i].in.d.func)
(&(proc->args[i].in.d),<br/> !                                                      fcinfo->arg[i]);<br />
                  }<br/>               }<br />   <br /> ***************<br /> *** 1593,1598 ****<br /> --- 1555,1589
----<br/>       arg->typoid = HeapTupleGetOid(typeTup);<br />       arg->typioparam = getTypeIOParam(typeTup);<br
/>      arg->typbyval = typeStruct->typbyval;<br /> + <br /> +     /* Determine which kind of Python object we
willconvert to */<br /> +     switch (arg->typoid)<br /> +     {<br /> +         case VOIDOID:<br /> +
            arg->func= PLyObject_ToVoid;<br /> +             break;<br /> +         case BOOLOID:<br /> +
            arg->func= PLyObject_ToBool;<br /> +             break;<br /> +         case BYTEAOID:<br /> +
            arg->func= PLyObject_ToBytea;<br /> +             break;<br /> +         case BPCHAROID:<br /> +
        caseVARCHAROID:<br /> +         case TEXTOID:<br /> +             arg->func = PLyObject_ToText;<br /> +
            break;<br/> + <br /> +         case FLOAT4OID:<br /> +         case FLOAT8OID:<br /> +         case
NUMERICOID:<br/> +         case INT2OID:<br /> +         case INT4OID:<br /> +         case INT8OID:<br /> +
        default:<br/> +             arg->func = PLyObject_ToDatum;<br /> +             break;<br /> +     }<br />
  }<br/>   <br />   static void<br /> ***************<br /> *** 1619,1644 ****<br />       switch (typeOid)<br />
      {<br/>           case BOOLOID:<br /> !             arg->func = PLyBool_FromString;<br />
              break;<br/>           case FLOAT4OID:<br />           case FLOAT8OID:<br />           case NUMERICOID:<br
/>!             arg->func = PLyFloat_FromString;<br />               break;<br />           case INT2OID:<br />
          caseINT4OID:<br /> !             arg->func = PLyInt_FromString;<br />               break;<br />
          caseINT8OID:<br /> !             arg->func = PLyLong_FromString;<br />               break;<br />
          default:<br/> !             arg->func = PLyString_FromString;<br />               break;<br />       }<br
/>  }<br />   <br />   static void<br />   PLy_typeinfo_init(PLyTypeInfo * arg)<br />   {<br /> --- 1610,1648 ----<br
/>      switch (typeOid)<br />       {<br />           case BOOLOID:<br /> !             arg->func =
PLyBool_FromBool;<br/>               break;<br />           case FLOAT4OID:<br /> +             arg->func =
PLyFloat_FromFloat4;<br/> +             break;<br />           case FLOAT8OID:<br /> +             arg->func =
PLyFloat_FromFloat8;<br/> +             break;<br />           case NUMERICOID:<br /> !             arg->func =
PLyFloat_FromNumeric;<br/>               break;<br />           case INT2OID:<br /> +             arg->func =
PLyInt_FromInt16;<br/> +             break;<br />           case INT4OID:<br /> !             arg->func =
PLyInt_FromInt32;<br/>               break;<br />           case INT8OID:<br /> !             arg->func =
PLyLong_FromInt64;<br/> !             break;<br /> !         case BPCHAROID:<br /> !         case VARCHAROID:<br /> !
        caseTEXTOID:<br /> !         case BYTEAOID:<br /> !             arg->func = PLyString_FromText;<br />
              break;<br/>           default:<br /> !             arg->func = PLyString_FromDatum;<br />
              break;<br/>       }<br />   }<br />   <br /> + <br />   static void<br />   PLy_typeinfo_init(PLyTypeInfo
*arg)<br />   {<br /> ***************<br /> *** 1660,1716 ****<br />       }<br />   }<br />   <br /> - /* assumes that
abool is always returned as a 't' or 'f' */<br />   static PyObject *<br /> ! PLyBool_FromString(const char *src)<br />
  {<br/>       /*<br />        * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for<br />        *
generatingSQL from trigger functions, but those are only supported in<br />        * Python >= 2.3, and we support
olderversions.<br />        * <a
href="http://docs.python.org/api/boolObjects.html">http://docs.python.org/api/boolObjects.html</a><br/>        */<br />
!    if (src[0] == 't')<br />           return PyBool_FromLong(1);<br /> !     return PyBool_FromLong(0);<br />   }<br
/>  <br />   static PyObject *<br /> ! PLyFloat_FromString(const char *src)<br />   {<br /> !     double        v;<br
/>!     char       *eptr;<br />   <br /> !     errno = 0;<br /> !     v = strtod(src, &eptr);<br /> !     if (*eptr
!='\0' || errno)<br /> !         return NULL;<br /> !     return PyFloat_FromDouble(v);<br />   }<br />   <br />
  staticPyObject *<br /> ! PLyInt_FromString(const char *src)<br />   {<br /> !     long        v;<br /> !     char
      *eptr;<br/>   <br /> !     errno = 0;<br /> !     v = strtol(src, &eptr, 0);<br /> !     if (*eptr != '\0' ||
errno)<br/> !         return NULL;<br /> !     return PyInt_FromLong(v);<br />   }<br />   <br />   static PyObject
*<br/> ! PLyLong_FromString(const char *src)<br />   {<br /> !     return PyLong_FromString((char *) src, NULL, 0);<br
/>  }<br />   <br />   static PyObject *<br /> ! PLyString_FromString(const char *src)<br />   {<br /> !     return
PyString_FromString(src);<br/>   }<br />   <br />   static PyObject *<br /> --- 1664,1758 ----<br />       }<br />
  }<br/>   <br />   static PyObject *<br /> ! PLyBool_FromBool(PLyDatumToOb *arg, Datum d)<br />   {<br /> +     bool x
=DatumGetBool(d);<br /> +     arg = 0;  /* unused */<br /> + <br />       /*<br />        * We would like to use
Py_RETURN_TRUEand Py_RETURN_FALSE here for<br />        * generating SQL from trigger functions, but those are only
supportedin<br />        * Python >= 2.3, and we support older versions.<br />        * <a
href="http://docs.python.org/api/boolObjects.html">http://docs.python.org/api/boolObjects.html</a><br/>        */<br />
!    if (x)<br />           return PyBool_FromLong(1);<br /> !     else<br /> !         return PyBool_FromLong(0);<br
/>  }<br />   <br />   static PyObject *<br /> ! PLyFloat_FromFloat4(PLyDatumToOb *arg, Datum d)<br />   {<br /> !
    arg= 0;  /* unused */<br /> !     return PyFloat_FromDouble(DatumGetFloat4(d));<br /> ! }<br />   <br /> ! static
PyObject*<br /> ! PLyFloat_FromFloat8(PLyDatumToOb *arg, Datum d)<br /> ! {<br /> !     arg = 0;  /* unused */<br /> !
    returnPyFloat_FromDouble(DatumGetFloat8(d));<br />   }<br />   <br />   static PyObject *<br /> !
PLyFloat_FromNumeric(PLyDatumToOb*arg, Datum d)<br />   {<br /> !     /* <br /> !      * Numeric is cast to a PyFloat:
<br/> !      *   This results in a loss of precision<br /> !      *   Would it be better to cast to PyString? <br /> !
     */<br/> !     Datum  f = DirectFunctionCall1(numeric_float8, d);<br /> !     double x = DatumGetFloat8(f);<br /> !
    arg= 0;  /* unused */<br /> !     return PyFloat_FromDouble(x);<br /> ! }<br />   <br /> ! static PyObject *<br />
!PLyInt_FromInt16(PLyDatumToOb *arg, Datum d)<br /> ! {<br /> !     arg = 0;  /* unused */<br /> !     return
PyInt_FromLong(DatumGetInt16(d));<br/>   }<br />   <br />   static PyObject *<br /> ! PLyInt_FromInt32(PLyDatumToOb
*arg,Datum d)<br />   {<br /> !     arg = 0;  /* unused */<br /> !     return PyInt_FromLong(DatumGetInt32(d));<br />
  }<br/>   <br />   static PyObject *<br /> ! PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)<br />   {<br /> !     arg =
0; /* unused */<br /> ! <br /> !     /* on 32 bit platforms "long" may be too small */<br /> !     if (sizeof(int64)
>sizeof(long))<br /> !         return PyLong_FromLongLong(DatumGetInt64(d));<br /> !     else<br /> !         return
PyLong_FromLong(DatumGetInt64(d));<br/> ! }<br /> ! <br /> ! static PyObject *<br /> ! PLyString_FromText(PLyDatumToOb
*arg,Datum d)<br /> ! {<br /> !     text     *txt = DatumGetTextP(d);<br /> !     char     *str = VARDATA(txt);<br /> !
    size_t   size = VARSIZE(txt) - VARHDRSZ;<br /> ! <br /> !     return PyString_FromStringAndSize(str, size);<br /> !
}<br/> ! <br /> ! static PyObject *<br /> ! PLyString_FromDatum(PLyDatumToOb *arg, Datum d)<br /> ! {<br /> !     char
    *x= OutputFunctionCall(&arg->typfunc, d);<br /> !     PyObject *r = PyString_FromString(x);<br /> !
    pfree(x);<br/> !     return r;<br />   }<br />   <br />   static PyObject *<br /> ***************<br /> ***
1730,1737****<br />       {<br />           for (i = 0; i < info->in.r.natts; i++)<br />           {<br /> !
            char      *key,<br /> !                        *vsrc;<br />               Datum        vattr;<br />
              bool       is_null;<br />               PyObject   *value;<br /> --- 1772,1778 ----<br />       {<br />
          for(i = 0; i < info->in.r.natts; i++)<br />           {<br /> !             char       *key;<br />
              Datum       vattr;<br />               bool        is_null;<br />               PyObject   *value;<br />
***************<br/> *** 1746,1759 ****<br />                   PyDict_SetItemString(dict, key, Py_None);<br />
              else<br/>               {<br /> !                 vsrc =
OutputFunctionCall(&info->in.r.atts[i].typfunc,<br/> !                                           vattr);<br /> !
<br/> !                 /*<br /> !                  * no exceptions allowed<br /> !                  */<br /> !
                value= info->in.r.atts[i].func(vsrc);<br /> !                 pfree(vsrc);<br />
                  PyDict_SetItemString(dict,key, value);<br />                   Py_DECREF(value);<br />
              }<br/> --- 1787,1793 ----<br />                   PyDict_SetItemString(dict, key, Py_None);<br />
              else<br/>               {<br /> !                 value = (info->in.r.atts[i].func)
(&info->in.r.atts[i],vattr);<br />                   PyDict_SetItemString(dict, key, value);<br />
                  Py_DECREF(value);<br/>               }<br /> ***************<br /> *** 1769,1777 ****<br />
      returndict;<br />   }<br />   <br />   <br />   static HeapTuple<br /> ! PLyMapping_ToTuple(PLyTypeInfo * info,
PyObject* mapping)<br />   {<br />       TupleDesc    desc;<br />       HeapTuple    tuple;<br /> --- 1803,2017 ----<br
/>      return dict;<br />   }<br />   <br /> + static Datum <br /> + PLyObject_ToVoid(PLyProcedure *proc, <br /> +
                 PLyObToDatum*arg, <br /> +                  PyObject *plrv, <br /> +                  bool *isnull)<br
/>+ {<br /> +     /* <br /> +      * If the function is declared to return void, the Python return value must<br /> +
     *be None.  For void-returning functions, we also treat a None return value<br /> +      * as a special "void
datum"rather than NULL (as is the case for the <br /> +      * non-void-returning functions).<br /> +      */<br /> +
    if(plrv != Py_None)<br /> +         ereport(ERROR,<br /> +                 (errcode(ERRCODE_DATATYPE_MISMATCH),<br
/>+                  errmsg("PL/Python function with return type \"void\" did not "<br /> +
                        "returnNone")));<br /> + <br /> +     *isnull = false;<br /> +     return (Datum) 0;<br /> +
}<br/> + <br /> + static Datum <br /> + PLyObject_ToBool(PLyProcedure *proc, <br /> +                  PLyObToDatum
*arg,<br /> +                  PyObject *plrv, <br /> +                  bool *isnull)<br /> + {<br /> +     bool rv;
<br/> + <br /> +     if (plrv == Py_None)<br /> +     {<br /> +         *isnull = true;<br /> +         return (Datum)
0;<br/> +     }<br /> + <br /> +     rv = PyObject_IsTrue(plrv);<br /> +     *isnull = false;<br /> +     return
BoolGetDatum(rv);<br/> + }<br /> + <br /> + <br /> + static Datum <br /> + PLyObject_ToBytea(PLyProcedure *proc, <br />
+                  PLyObToDatum *arg, <br /> +                   PyObject *plrv, <br /> +                   bool
*isnull)<br/> + {<br /> +     PyObject   *volatile plrv_so = NULL;<br /> +     Datum       rv;<br /> + <br /> +     if
(plrv== Py_None)<br /> +     {<br /> +         *isnull = true;<br /> +         return (Datum) 0;<br /> +     }<br /> +
<br/> +     plrv_so = PyObject_Str(plrv);<br /> +     if (!plrv_so)<br /> +     {<br /> +         ereport(ERROR,<br />
+                (errcode(ERRCODE_DATATYPE_MISMATCH),<br /> +                  errmsg("could not create string
representationof Python "<br /> +                         "object in PL/Python function \"%s\" while creating "<br /> +
                        "returnvalue", proc->proname)));<br /> +     }<br /> + <br /> +     PG_TRY();<br /> +
    {<br/> +         char *plrv_sc = PyString_AsString(plrv_so);<br /> +         size_t len =
PyString_Size(plrv_so);<br/> +         size_t size = len + VARHDRSZ;<br /> +         bytea *result = (bytea*)
palloc(size);<br/> + <br /> +         SET_VARSIZE(result, size);<br /> +         memcpy(VARDATA(result), plrv_sc,
len);<br/> +         rv = PointerGetDatum(result);<br /> +     }<br /> +     PG_CATCH();<br /> +     {<br /> +
        Py_XDECREF(plrv_so);<br/> +         PG_RE_THROW();<br /> +     }<br /> +     PG_END_TRY();<br /> + <br /> +
    Py_XDECREF(plrv_so);<br/> + <br /> +     *isnull = false;<br /> +     return rv;<br /> + }<br /> + <br /> + static
Datum<br /> + PLyObject_ToText(PLyProcedure *proc, <br /> +                  PLyObToDatum *arg, <br /> +
                 PyObject*plrv, <br /> +                  bool *isnull)<br /> + {<br /> +     PyObject   *volatile
plrv_so= NULL;<br /> +     Datum       rv;<br /> + <br /> +     if (plrv == Py_None)<br /> +     {<br /> +
        *isnull= true;<br /> +         return (Datum) 0;<br /> +     }<br /> + <br /> +     plrv_so =
PyObject_Str(plrv);<br/> +     if (!plrv_so)<br /> +     {<br /> +         ereport(ERROR,<br /> +
                (errcode(ERRCODE_DATATYPE_MISMATCH),<br/> +                  errmsg("could not create string
representationof Python "<br /> +                         "object in PL/Python function \"%s\" while creating "<br /> +
                        "returnvalue", proc->proname)));<br /> +     }<br /> + <br /> +     PG_TRY();<br /> +
    {<br/> +         char *plrv_sc = PyString_AsString(plrv_so);<br /> +         size_t len    =
PyString_Size(plrv_so);<br/> +         size_t size   = len + VARHDRSZ;<br /> +         text *result;<br /> + <br /> +
        if(strlen(plrv_sc) != (size_t) len)<br /> +         {<br /> +             ereport(ERROR,<br /> +
                    (errcode(ERRCODE_DATATYPE_MISMATCH),<br/> +                      errmsg("PL/Python function \"%s\"
couldnot convert "<br /> +                             "Python object into text: expected string without "<br /> +
                            "nullbytes", proc->proname)));<br /> +         }<br /> + <br /> +         result =
(bytea*)palloc(size);<br /> +         SET_VARSIZE(result, size);<br /> +         memcpy(VARDATA(result), plrv_sc,
len);<br/> +         rv = PointerGetDatum(result);<br /> +     }<br /> +     PG_CATCH();<br /> +     {<br /> +
        Py_XDECREF(plrv_so);<br/> +         PG_RE_THROW();<br /> +     }<br /> +     PG_END_TRY();<br /> + <br /> +
    Py_XDECREF(plrv_so);<br/> + <br /> +     *isnull = false;<br /> +     return rv;<br /> + }<br /> + <br /> + /* <br
/>+  * Generic conversion function:<br /> +  *  - Cast PyObject to cstring and cstring into postgres type.<br /> +
 */<br/> + static Datum <br /> + PLyObject_ToDatum(PLyProcedure *proc, <br /> +                   PLyObToDatum *arg,
<br/> +                   PyObject *plrv, <br /> +                   bool *isnull)<br /> + {<br /> +     PyObject
*volatileplrv_so = NULL;<br /> +     Datum     rv;<br /> + <br /> +     if (plrv == Py_None)<br /> +     {<br /> +
        *isnull= true;<br /> +         return (Datum) 0;<br /> +     }<br /> + <br /> +     plrv_so =
PyObject_Str(plrv);<br/> +     if (!plrv_so)<br /> +     {<br /> +         ereport(ERROR,<br /> +
                (errcode(ERRCODE_DATATYPE_MISMATCH),<br/> +                  errmsg("could not create string
representationof Python "<br /> +                         "object in PL/Python function \"%s\" while creating "<br /> +
                        "returnvalue", proc->proname)));<br /> +     }<br /> + <br /> +     PG_TRY();<br /> +
    {<br/> +         char *plrv_sc = PyString_AsString(plrv_so);<br /> +         size_t len    =
PyString_Size(plrv_so);       <br /> + <br /> +         if (strlen(plrv_sc) != (size_t) len)<br /> +         {<br /> +
            ereport(ERROR,<br/> +                     (errcode(ERRCODE_DATATYPE_MISMATCH),<br /> +
                     errmsg("PL/Pythonfunction \"%s\" could not convert "<br /> +                             "Python
objectinto cstring: expected string without "<br /> +                             "null bytes", proc->proname)));<br
/>+         }<br /> +         rv = InputFunctionCall(&arg->typfunc, plrv_sc, arg->typioparam, -1);<br /> +
    }<br/> +     PG_CATCH();<br /> +     {<br /> +         Py_XDECREF(plrv_so);<br /> +         PG_RE_THROW();<br /> +
    }<br/> +     PG_END_TRY();<br /> + <br /> +     Py_XDECREF(plrv_so);<br /> + <br /> +     *isnull = false;<br /> +
    returnrv;<br /> + }<br />   <br />   static HeapTuple<br /> ! PLyMapping_ToTuple(PLyProcedure *proc, PyObject
*mapping)<br/>   {<br />       TupleDesc    desc;<br />       HeapTuple    tuple;<br /> ***************<br /> ***
1781,1840****<br />   <br />       Assert(PyMapping_Check(mapping));<br />   <br /> !     desc =
lookup_rowtype_tupdesc(info->out.d.typoid,-1);<br /> !     if (info->is_rowtype == 2)<br /> !
        PLy_output_tuple_funcs(info,desc);<br /> !     Assert(info->is_rowtype == 1);<br />   <br />       /* Build
tuple*/<br />       values = palloc(sizeof(Datum) * desc->natts);<br />       nulls = palloc(sizeof(bool) *
desc->natts);<br/>       for (i = 0; i < desc->natts; ++i)<br />       {<br /> !         char       *key;<br
/>!         PyObject   *volatile value,<br /> !                    *volatile so;<br />   <br />           key =
NameStr(desc->attrs[i]->attname);<br/> !         value = so = NULL;<br />           PG_TRY();<br />
          {<br/>               value = PyMapping_GetItemString(mapping, key);<br /> !             if (value ==
Py_None)<br/>               {<br /> -                 values[i] = (Datum) NULL;<br /> -                 nulls[i] =
true;<br/> -             }<br /> -             else if (value)<br /> -             {<br /> -                 char
      *valuestr;<br/> - <br /> -                 so = PyObject_Str(value);<br /> -                 if (so == NULL)<br
/>-                     PLy_elog(ERROR, "could not compute string representation of Python object");<br /> -
                valuestr= PyString_AsString(so);<br /> - <br /> -                 values[i] =
InputFunctionCall(&info->out.r.atts[i].typfunc<br/> -                                               ,valuestr<br
/>-                                               ,info->out.r.atts[i].typioparam<br /> -
                                              ,-1);<br/> -                 Py_DECREF(so);<br /> -                 so =
NULL;<br/> -                 nulls[i] = false;<br /> -             }<br /> -             else<br />
                  ereport(ERROR,<br/>                           (errcode(ERRCODE_UNDEFINED_COLUMN),<br />
                           errmsg("key\"%s\" not found in mapping", key),<br />                            errhint("To
returnnull in a column, "<br /> !                       "add the value None to the mapping with the key named after the
column.")));<br/>   <br />               Py_XDECREF(value);<br />               value = NULL;<br />           }<br />
          PG_CATCH();<br/>           {<br /> -             Py_XDECREF(so);<br />               Py_XDECREF(value);<br />
              PG_RE_THROW();<br/>           }<br /> --- 2021,2062 ----<br />   <br />
      Assert(PyMapping_Check(mapping));<br/>   <br /> !     desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
-1);<br/> !     if (proc->result.is_rowtype == 2)<br /> !         PLy_output_tuple_funcs(&proc->result,
desc);<br/> !     Assert(proc->result.is_rowtype == 1);<br />   <br />       /* Build tuple */<br />       values =
palloc(sizeof(Datum)* desc->natts);<br />       nulls = palloc(sizeof(bool) * desc->natts);<br />       for (i =
0;i < desc->natts; ++i)<br />       {<br /> !         char         *key;<br /> !         PLyObToDatum *att;<br />
!        PyObject     *volatile value;<br />   <br /> +         att = &proc->result.out.r.atts[i];<br />
          key= NameStr(desc->attrs[i]->attname);<br /> !         value = NULL;<br />           PG_TRY();<br />
          {<br/>               value = PyMapping_GetItemString(mapping, key);<br /> !             if (!value)<br />
              {<br/>                   ereport(ERROR,<br />
                          (errcode(ERRCODE_UNDEFINED_COLUMN),<br/>                            errmsg("key \"%s\" not
foundin mapping", key),<br />                            errhint("To return null in a column, "<br /> !
                                 "addthe value None to the mapping with the "<br /> !
                                 "keynamed after the column.")));<br /> !             }<br /> !             values[i] =
(att->func)(proc, att, value, &nulls[i]);<br />   <br />               Py_XDECREF(value);<br />
              value= NULL;<br />           }<br />           PG_CATCH();<br />           {<br />
              Py_XDECREF(value);<br/>               PG_RE_THROW();<br />           }<br /> ***************<br /> ***
1851,1857****<br />   <br />   <br />   static HeapTuple<br /> ! PLySequence_ToTuple(PLyTypeInfo * info, PyObject *
sequence)<br/>   {<br />       TupleDesc    desc;<br />       HeapTuple    tuple;<br /> --- 2073,2079 ----<br />   <br
/>  <br />   static HeapTuple<br /> ! PLySequence_ToTuple(PLyProcedure *proc, PyObject *sequence)<br />   {<br />
      TupleDesc   desc;<br />       HeapTuple    tuple;<br /> ***************<br /> *** 1866,1922 ****<br />        *
canignore exceeding items or assume missing ones as null but to avoid<br />        * plpython developer's errors we are
stricthere<br />        */<br /> !     desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);<br />       if
(PySequence_Length(sequence)!= desc->natts)<br />           ereport(ERROR,<br />
                  (errcode(ERRCODE_DATATYPE_MISMATCH),<br/>           errmsg("length of returned sequence did not match
numberof columns in row")));<br />   <br /> !     if (info->is_rowtype == 2)<br /> !
        PLy_output_tuple_funcs(info,desc);<br /> !     Assert(info->is_rowtype == 1);<br />   <br />       /* Build
tuple*/<br />       values = palloc(sizeof(Datum) * desc->natts);<br />       nulls = palloc(sizeof(bool) *
desc->natts);<br/>       for (i = 0; i < desc->natts; ++i)<br />       {<br /> !         PyObject   *volatile
value,<br/> !                    *volatile so;<br />   <br /> !         value = so = NULL;<br />           PG_TRY();<br
/>          {<br />               value = PySequence_GetItem(sequence, i);<br />               Assert(value);<br /> !
            if(value == Py_None)<br /> !             {<br /> !                 values[i] = (Datum) NULL;<br /> !
                nulls[i]= true;<br /> !             }<br /> !             else if (value)<br /> !             {<br /> !
                char      *valuestr;<br /> ! <br /> !                 so = PyObject_Str(value);<br /> !
                if(so == NULL)<br /> !                     PLy_elog(ERROR, "could not compute string representation of
Pythonobject");<br /> !                 valuestr = PyString_AsString(so);<br /> !                 values[i] =
InputFunctionCall(&info->out.r.atts[i].typfunc<br/> !                                               ,valuestr<br
/>!                                               ,info->out.r.atts[i].typioparam<br /> !
                                              ,-1);<br/> !                 Py_DECREF(so);<br /> !                 so =
NULL;<br/> !                 nulls[i] = false;<br /> !             }<br />   <br />               Py_XDECREF(value);<br
/>              value = NULL;<br />           }<br />           PG_CATCH();<br />           {<br /> -
            Py_XDECREF(so);<br/>               Py_XDECREF(value);<br />               PG_RE_THROW();<br />
          }<br/> --- 2088,2124 ----<br />        * can ignore exceeding items or assume missing ones as null but to
avoid<br/>        * plpython developer's errors we are strict here<br />        */<br /> !     desc =
lookup_rowtype_tupdesc(proc->result.out.d.typoid,-1);<br />       if (PySequence_Length(sequence) !=
desc->natts)<br/>           ereport(ERROR,<br />                   (errcode(ERRCODE_DATATYPE_MISMATCH),<br />
          errmsg("lengthof returned sequence did not match number of columns in row")));<br />   <br /> !     if
(proc->result.is_rowtype== 2)<br /> !         PLy_output_tuple_funcs(&proc->result, desc);<br /> !
    Assert(proc->result.is_rowtype== 1);<br />   <br />       /* Build tuple */<br />       values =
palloc(sizeof(Datum)* desc->natts);<br />       nulls = palloc(sizeof(bool) * desc->natts);<br />       for (i =
0;i < desc->natts; ++i)<br />       {<br /> !         PLyObToDatum *att;<br /> !         PyObject     *volatile
value;<br/>   <br /> !         att = &proc->result.out.r.atts[i];<br /> !         value = NULL;<br />
          PG_TRY();<br/>           {<br />               value = PySequence_GetItem(sequence, i);<br />
              Assert(value);<br/> !             values[i] = (att->func) (proc, att, value, &nulls[i]);<br />
  <br/>               Py_XDECREF(value);<br />               value = NULL;<br />           }<br />
          PG_CATCH();<br/>           {<br />               Py_XDECREF(value);<br />               PG_RE_THROW();<br />
          }<br/> ***************<br /> *** 1933,1939 ****<br />   <br />   <br />   static HeapTuple<br /> !
PLyObject_ToTuple(PLyTypeInfo* info, PyObject * object)<br />   {<br />       TupleDesc    desc;<br />       HeapTuple
   tuple;<br/> --- 2135,2141 ----<br />   <br />   <br />   static HeapTuple<br /> ! PLyObject_ToTuple(PLyProcedure
*proc,PyObject *object)<br />   {<br />       TupleDesc    desc;<br />       HeapTuple    tuple;<br />
***************<br/> *** 1941,1962 ****<br />       bool       *nulls;<br />       volatile int i;<br />   <br /> !
    desc= lookup_rowtype_tupdesc(info->out.d.typoid, -1);<br /> !     if (info->is_rowtype == 2)<br /> !
        PLy_output_tuple_funcs(info,desc);<br /> !     Assert(info->is_rowtype == 1);<br />   <br />       /* Build
tuple*/<br />       values = palloc(sizeof(Datum) * desc->natts);<br />       nulls = palloc(sizeof(bool) *
desc->natts);<br/>       for (i = 0; i < desc->natts; ++i)<br />       {<br /> !         char       *key;<br
/>!         PyObject   *volatile value,<br /> !                    *volatile so;<br />   <br />           key =
NameStr(desc->attrs[i]->attname);<br/> !         value = so = NULL;<br />           PG_TRY();<br />
          {<br/>               value = PyObject_GetAttrString(object, key);<br /> --- 2143,2165 ----<br />       bool
      *nulls;<br/>       volatile int i;<br />   <br /> !     desc =
lookup_rowtype_tupdesc(proc->result.out.d.typoid,-1);<br /> !     if (proc->result.is_rowtype == 2)<br /> !
        PLy_output_tuple_funcs(&proc->result,desc);<br /> !     Assert(proc->result.is_rowtype == 1);<br />
  <br/>       /* Build tuple */<br />       values = palloc(sizeof(Datum) * desc->natts);<br />       nulls =
palloc(sizeof(bool)* desc->natts);<br />       for (i = 0; i < desc->natts; ++i)<br />       {<br /> !
        char        *key;<br /> !         PLyObToDatum *att;<br /> !         PyObject     *volatile value;<br />   <br
/>+         att = &proc->result.out.r.atts[i];<br />           key = NameStr(desc->attrs[i]->attname);<br
/>!         value = NULL;<br />           PG_TRY();<br />           {<br />               value =
PyObject_GetAttrString(object,key);<br /> ***************<br /> *** 1965,2000 ****<br />                   values[i] =
(Datum)NULL;<br />                   nulls[i] = true;<br />               }<br /> !             else if (value)<br />
              {<br/> -                 char       *valuestr;<br /> - <br /> -                 so =
PyObject_Str(value);<br/> -                 if (so == NULL)<br /> -                     PLy_elog(ERROR, "could not
computestring representation of Python object");<br /> -                 valuestr = PyString_AsString(so);<br /> -
                values[i]= InputFunctionCall(&info->out.r.atts[i].typfunc<br /> -
                                              ,valuestr<br/> -
                                              ,info->out.r.atts[i].typioparam<br/> -
                                              ,-1);<br/> -                 Py_DECREF(so);<br /> -                 so =
NULL;<br/> -                 nulls[i] = false;<br /> -             }<br /> -             else<br />
                  ereport(ERROR,<br/>                           (errcode(ERRCODE_UNDEFINED_COLUMN),<br /> !
                         errmsg("attribute\"%s\" does not exist in Python object", key),<br />
                           errhint("Toreturn null in a column, "<br /> !                                  "let the
returnedobject have an attribute named "<br /> !                                  "after column with value
None.")));<br/>   <br />               Py_XDECREF(value);<br />               value = NULL;<br />           }<br />
          PG_CATCH();<br/>           {<br /> -             Py_XDECREF(so);<br />               Py_XDECREF(value);<br />
              PG_RE_THROW();<br/>           }<br /> --- 2168,2190 ----<br />                   values[i] = (Datum)
NULL;<br/>                   nulls[i] = true;<br />               }<br /> !             else if (!value)<br />
              {<br/>                   ereport(ERROR,<br />
                          (errcode(ERRCODE_UNDEFINED_COLUMN),<br/> !                          errmsg("key \"%s\" not
foundin object", key),<br />                            errhint("To return null in a column, "<br /> !
                                 "addthe value None to the mapping with the "<br /> !
                                 "keynamed after the column.")));<br /> !             }<br /> !             else<br />
!                values[i] = (att->func) (proc, att, value, &nulls[i]);<br />   <br />
              Py_XDECREF(value);<br/>               value = NULL;<br />           }<br />           PG_CATCH();<br />
          {<br/>               Py_XDECREF(value);<br />               PG_RE_THROW();<br />           }<br /> Index:
expected/plpython_function.out<br/> ===================================================================<br /> RCS file:
/projects/cvsroot/pgsql/src/pl/plpython/expected/plpython_function.out,v<br/> retrieving revision 1.12<br /> diff -c
-r1.12plpython_function.out<br /> *** expected/plpython_function.out    3 Apr 2009 16:59:42 -0000    1.12<br /> ---
expected/plpython_function.out   26 May 2009 22:58:52 -0000<br /> ***************<br /> *** 450,452 ****<br /> ---
450,470----<br />   CREATE FUNCTION test_inout_params(first inout text) AS $$<br />   return first + '_inout';<br />
  $$LANGUAGE plpythonu;<br /> + CREATE FUNCTION test_type_conversion_bool(x bool) returns bool AS $$ return x $$
languageplpythonu;<br /> + CREATE FUNCTION test_type_conversion_char(x char) returns char AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int2(x int2) returns int2 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int4(x int4) returns int4 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int8(x int8) returns int8 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_float4(x float4) returns float4 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_float8(x float8) returns float8 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_numeric(x numeric) returns numeric AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_text(x text) returns text AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_bytea(x bytea) returns bytea AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_marshal() returns bytea AS $$ <br /> + import marshal<br /> + return
marshal.dumps('helloworld')<br /> + $$ language plpythonu;<br /> + CREATE FUNCTION test_type_unmarshal(x bytea) returns
textAS $$<br /> + import marshal<br /> + return marshal.loads(x)<br /> + $$ language plpythonu;<br /> Index:
expected/plpython_test.out<br/> ===================================================================<br /> RCS file:
/projects/cvsroot/pgsql/src/pl/plpython/expected/plpython_test.out,v<br/> retrieving revision 1.8<br /> diff -c -r1.8
plpython_test.out<br/> *** expected/plpython_test.out    3 Apr 2009 16:59:42 -0000    1.8<br /> ---
expected/plpython_test.out   26 May 2009 22:58:52 -0000<br /> ***************<br /> *** 559,561 ****<br /> --- 559,693
----<br/>    test_in_inout<br />   (1 row)<br />   <br /> + SELECT * FROM test_type_conversion_bool(true);<br /> +
 test_type_conversion_bool<br /> + ---------------------------<br /> +  t<br /> + (1 row)<br /> + <br /> + SELECT *
FROMtest_type_conversion_bool(false);<br /> +  test_type_conversion_bool <br /> + ---------------------------<br /> +
 f<br/> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_bool(null);<br /> +  test_type_conversion_bool
<br/> + ---------------------------<br /> +  <br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_char('a');<br/> +  test_type_conversion_char <br /> + ---------------------------<br /> +  a<br />
+(1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_char(null);<br /> +  test_type_conversion_char <br /> +
---------------------------<br/> +  <br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_int2(100::int2);<br/> +  test_type_conversion_int2 <br /> + ---------------------------<br /> +
                       100<br/> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_int2(null);<br /> +
 test_type_conversion_int2<br /> + ---------------------------<br /> +                           <br /> + (1 row)<br />
+<br /> + SELECT * FROM test_type_conversion_int4(100);<br /> +  test_type_conversion_int4 <br /> +
---------------------------<br/> +                        100<br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_int4(null);<br/> +  test_type_conversion_int4 <br /> + ---------------------------<br /> +
                          <br/> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_int8(100);<br /> +
 test_type_conversion_int8<br /> + ---------------------------<br /> +                        100<br /> + (1 row)<br />
+<br /> + SELECT * FROM test_type_conversion_int8(null);<br /> +  test_type_conversion_int8 <br /> +
---------------------------<br/> +                           <br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_float4(100);<br/> +  test_type_conversion_float4 <br /> + -----------------------------<br /> +
                         100<br/> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_float4(null);<br /> +
 test_type_conversion_float4<br /> + -----------------------------<br /> +                             <br /> + (1
row)<br/> + <br /> + SELECT * FROM test_type_conversion_float8(100);<br /> +  test_type_conversion_float8 <br /> +
-----------------------------<br/> +                          100<br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_float8(null);<br/> +  test_type_conversion_float8 <br /> + -----------------------------<br /> +
                            <br/> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_numeric(100);<br /> +
 test_type_conversion_numeric<br /> + ------------------------------<br /> +                         100.0<br /> + (1
row)<br/> + <br /> + SELECT * FROM test_type_conversion_numeric(null);<br /> +  test_type_conversion_numeric <br /> +
------------------------------<br/> +                              <br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_text('helloworld');<br /> +  test_type_conversion_text <br /> + ---------------------------<br />
+ hello world<br /> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_text(null);<br /> +
 test_type_conversion_text<br /> + ---------------------------<br /> +  <br /> + (1 row)<br /> + <br /> + SELECT * FROM
test_type_conversion_bytea('helloworld');<br /> +  test_type_conversion_bytea <br /> + ----------------------------<br
/>+  hello world<br /> + (1 row)<br /> + <br /> + SELECT * FROM test_type_conversion_bytea(null);<br /> +
 test_type_conversion_bytea<br /> + ----------------------------<br /> +  <br /> + (1 row)<br /> + <br /> + SELECT
test_type_unmarshal(x)FROM test_type_marshal() x;<br /> +  test_type_unmarshal <br /> + ---------------------<br /> +
 helloworld<br /> + (1 row)<br /> + <br /> Index: sql/plpython_function.sql<br />
===================================================================<br/> RCS file:
/projects/cvsroot/pgsql/src/pl/plpython/sql/plpython_function.sql,v<br/> retrieving revision 1.12<br /> diff -c -r1.12
plpython_function.sql<br/> *** sql/plpython_function.sql    3 Apr 2009 16:59:43 -0000    1.12<br /> ---
sql/plpython_function.sql   26 May 2009 22:58:52 -0000<br /> ***************<br /> *** 497,499 ****<br /> --- 497,518
----<br/>   CREATE FUNCTION test_inout_params(first inout text) AS $$<br />   return first + '_inout';<br />   $$
LANGUAGEplpythonu;<br /> + <br /> + CREATE FUNCTION test_type_conversion_bool(x bool) returns bool AS $$ return x $$
languageplpythonu;<br /> + CREATE FUNCTION test_type_conversion_char(x char) returns char AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int2(x int2) returns int2 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int4(x int4) returns int4 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_int8(x int8) returns int8 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_float4(x float4) returns float4 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_float8(x float8) returns float8 AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_numeric(x numeric) returns numeric AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_text(x text) returns text AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_conversion_bytea(x bytea) returns bytea AS $$ return x $$ language
plpythonu;<br/> + CREATE FUNCTION test_type_marshal() returns bytea AS $$ <br /> + import marshal<br /> + return
marshal.dumps('helloworld')<br /> + $$ language plpythonu;<br /> + CREATE FUNCTION test_type_unmarshal(x bytea) returns
textAS $$<br /> + import marshal<br /> + return marshal.loads(x)<br /> + $$ language plpythonu;<br /> Index:
sql/plpython_test.sql<br/> ===================================================================<br /> RCS file:
/projects/cvsroot/pgsql/src/pl/plpython/sql/plpython_test.sql,v<br/> retrieving revision 1.5<br /> diff -c -r1.5
plpython_test.sql<br/> *** sql/plpython_test.sql    3 Apr 2009 16:59:43 -0000    1.5<br /> --- sql/plpython_test.sql
   26May 2009 22:58:52 -0000<br /> ***************<br /> *** 149,151 ****<br /> --- 149,176 ----<br />   -- this
doesn'twork yet :-(<br />   SELECT * FROM test_in_out_params_multi('test_in');<br />   SELECT * FROM
test_inout_params('test_in');<br/> + <br /> + SELECT * FROM test_type_conversion_bool(true);<br /> + SELECT * FROM
test_type_conversion_bool(false);<br/> + SELECT * FROM test_type_conversion_bool(null);<br /> + SELECT * FROM
test_type_conversion_char('a');<br/> + SELECT * FROM test_type_conversion_char(null);<br /> + SELECT * FROM
test_type_conversion_int2(100::int2);<br/> + SELECT * FROM test_type_conversion_int2(null);<br /> + SELECT * FROM
test_type_conversion_int4(100);<br/> + SELECT * FROM test_type_conversion_int4(null);<br /> + SELECT * FROM
test_type_conversion_int8(100);<br/> + SELECT * FROM test_type_conversion_int8(null);<br /> + SELECT * FROM
test_type_conversion_float4(100);<br/> + SELECT * FROM test_type_conversion_float4(null);<br /> + SELECT * FROM
test_type_conversion_float8(100);<br/> + SELECT * FROM test_type_conversion_float8(null);<br /> + SELECT * FROM
test_type_conversion_numeric(100);<br/> + SELECT * FROM test_type_conversion_numeric(null);<br /> + SELECT * FROM
test_type_conversion_text('helloworld');<br /> + SELECT * FROM test_type_conversion_text(null);<br /> + SELECT * FROM
test_type_conversion_bytea('helloworld');<br /> + SELECT * FROM test_type_conversion_bytea(null);<br /> + SELECT
test_type_unmarshal(x)FROM test_type_marshal() x;<br /> + <br /> + <br /></span></font> 

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On Wednesday 27 May 2009 02:07:33 Caleb Welton wrote:
> Patch for plpythonu
>
> Primary motivation of the attached patch is to support handling bytea
> conversion allowing for embedded nulls, which in turn allows for supporting
> the marshal module.
>
> Secondary motivation is slightly improved performance for conversion
> routines of basic datatypes that have simple mappings between
> postgres/python.
>
> Primary design is to change the conversion routines from being based on
> cstrings to datums, eg: PLyBool_FromString(const char *)  =>
> PLyBool_FromBool(PLyDatumToOb, Datum);

Makes sense; please add it to the next commit fest.

Are there any compatibility implications, that is, do any of the conversions 
work differently from before (except when they were broken before, as in the 
case of bytea)?


Re: [PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
<font face="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">All data types should map to the same
pythonobject types as they did before, so int32->PyInt, int64->PyLong, numeric->PyFloat, etc.<br /><br /> The
conversionroutines are slightly different, eg int32 is initialized via PyInt_FromLong() instead of first converting the
integerto a string then calling PyInt_FromString, this is a little faster, but shouldn’t result in differences, if
anythingthis should be more correct.  <br /><br /> Previously numeric->string->PyFloat_FromString, now
numeric->double->PyFloat_FromDouble,which makes use of postgres numeric->double routines rather than python
string->doubleroutines, and it is conceivable that there are precision variations between the two.  My own feeling
onthe matter is that PyFloat is the wrong mapping for numeric, but I didn’t want to muddy this patch by changing that.
 <br/><br /> The main compatibility issue is with Python Strings that contain embedded nulls.  Conversion to bytea will
nowwork correctly and build the bytea using PyString_FromStringAndSize instead of PyString_FromString.  Other datatypes
willnow error if the PyString contains embedded nulls, previously they would silently truncate.<br /><br /> Thanks for
thecomments,<br />   Caleb<br /><br /><br /> On 5/27/09 3:11 AM, "Peter Eisentraut" <<a
href="peter_e@gmx.net">peter_e@gmx.net</a>>wrote:<br /><br /></span></font><blockquote><font face="Calibri, Verdana,
Helvetica,Arial"><span style="font-size:11pt">On Wednesday 27 May 2009 02:07:33 Caleb Welton wrote:<br /> > Patch
forplpythonu<br /> ><br /> > Primary motivation of the attached patch is to support handling bytea<br /> >
conversionallowing for embedded nulls, which in turn allows for supporting<br /> > the marshal module.<br /> ><br
/>> Secondary motivation is slightly improved performance for conversion<br /> > routines of basic datatypes that
havesimple mappings between<br /> > postgres/python.<br /> ><br /> > Primary design is to change the
conversionroutines from being based on<br /> > cstrings to datums, eg: PLyBool_FromString(const char *)  =><br />
>PLyBool_FromBool(PLyDatumToOb, Datum);<br /><br /> Makes sense; please add it to the next commit fest.<br /><br />
Arethere any compatibility implications, that is, do any of the conversions<br /> work differently from before (except
whenthey were broken before, as in the<br /> case of bytea)?<br /><br /></span></font></blockquote> 

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On Wednesday 27 May 2009 21:53:31 Caleb Welton wrote:
> Previously numeric->string->PyFloat_FromString, now
> numeric->double->PyFloat_FromDouble, which makes use of postgres
> numeric->double routines rather than python string->double routines, and it
> is conceivable that there are precision variations between the two.  My own
> feeling on the matter is that PyFloat is the wrong mapping for numeric, but
> I didn't want to muddy this patch by changing that.

Yeah, that one had me wondering for a while as well, but as you say it is 
better to address that separately.



Re: [PATCH] plpythonu datatype conversion improvements

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> On Wednesday 27 May 2009 21:53:31 Caleb Welton wrote:
>> ... My own
>> feeling on the matter is that PyFloat is the wrong mapping for numeric, but
>> I didn't want to muddy this patch by changing that.

> Yeah, that one had me wondering for a while as well, but as you say it is 
> better to address that separately.

That was making me itch as well, in my very cursory look at the patch.
Does Python have a saner mapping for it?
        regards, tom lane


Re: [PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
<font face="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">Yes, in Python >= 2.4 there is the
Decimaldatatype.  <br /><br /> However, unlike the other mappings employed by plpythonu, Decimal requires an import
statementto be in scope.<br /><br /> -Caleb<br /><br /> On 5/27/09 2:07 PM, "Tom Lane" <<a
href="tgl@sss.pgh.pa.us">tgl@sss.pgh.pa.us</a>>wrote:<br /><br /></span></font><blockquote><font face="Calibri,
Verdana,Helvetica, Arial"><span style="font-size:11pt">Peter Eisentraut <<a
href="peter_e@gmx.net">peter_e@gmx.net</a>>writes:<br /> > On Wednesday 27 May 2009 21:53:31 Caleb Welton
wrote:<br/> >> ... My own<br /> >> feeling on the matter is that PyFloat is the wrong mapping for numeric,
but<br/> >> I didn't want to muddy this patch by changing that.<br /><br /> > Yeah, that one had me wondering
fora while as well, but as you say it is<br /> > better to address that separately.<br /><br /> That was making me
itchas well, in my very cursory look at the patch.<br /> Does Python have a saner mapping for it?<br /><br />
                        regards,tom lane<br /><br /></span></font></blockquote> 

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On Wednesday 27 May 2009 02:07:33 Caleb Welton wrote:
> Patch for plpythonu

This patch doesn't apply; I think it got mangled during email transport.  
(Tabs changed to spaces, it looks like.)  Could you resend the patch as a 
separate attachment in a way that it doesn't get mangled?



Re: [PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
Sorry about that.  Here it is again as an attachment.

-Caleb


On 7/16/09 7:16 AM, "Peter Eisentraut" <peter_e@gmx.net> wrote:

On Wednesday 27 May 2009 02:07:33 Caleb Welton wrote:
> Patch for plpythonu

This patch doesn't apply; I think it got mangled during email transport.
(Tabs changed to spaces, it looks like.)  Could you resend the patch as a
separate attachment in a way that it doesn't get mangled?


Attachment

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On tis, 2009-05-26 at 16:07 -0700, Caleb Welton wrote:
> Patch for plpythonu
>
> Primary motivation of the attached patch is to support handling bytea
> conversion allowing for embedded nulls, which in turn allows for
> supporting the marshal module.
>
> Secondary motivation is slightly improved performance for conversion
> routines of basic datatypes that have simple mappings between
> postgres/python.
>
> Primary design is to change the conversion routines from being based
> on cstrings to datums, eg:
>     PLyBool_FromString(const char *)  =>
> PLyBool_FromBool(PLyDatumToOb, Datum);

I have reworked this patch a bit and extended the plpython test suite
around it.  Current copy attached.

The remaining problem is that the patch loses domain checking on the
return types, because some paths no longer go through the data type's
input function.  I have marked these places as FIXME, and the regression
tests also contain a failing test case for this.

What's needed here, I think, is an API that takes a datum plus type
information and checks whether the datum is valid within the domain.  I
haven't found one that is exported, but maybe someone could give a tip.

Attachment

Re: [PATCH] plpythonu datatype conversion improvements

From
Pierre Frédéric Caillaud
Date:
>> Primary motivation of the attached patch is to support handling bytea
>> conversion allowing for embedded nulls, which in turn allows for
>> supporting the marshal module.
>>
>> Secondary motivation is slightly improved performance for conversion
>> routines of basic datatypes that have simple mappings between
>> postgres/python.
>>
>> Primary design is to change the conversion routines from being based
>> on cstrings to datums, eg:
>>     PLyBool_FromString(const char *)  =>
>> PLyBool_FromBool(PLyDatumToOb, Datum);
>
> I have reworked this patch a bit and extended the plpython test suite
> around it.  Current copy attached.
>
> The remaining problem is that the patch loses domain checking on the
> return types, because some paths no longer go through the data type's
> input function.  I have marked these places as FIXME, and the regression
> tests also contain a failing test case for this.
>
> What's needed here, I think, is an API that takes a datum plus type
> information and checks whether the datum is valid within the domain.  I
> haven't found one that is exported, but maybe someone could give a tip.


I see an intersection between the work I'm currently doing on COPY BINARY  
and this.

Basically if you have an INT, you aren't going to make lots of checks.

However, for a TEXT, postgres needs to reject it if it has a NULL in it  
(which doesn't bother Python at all), or if it is has chars which are not  
valid in the current encoding, etc.
Many other types like TIMESTAMP have checks which are absolutely necessary  
for correctness...

> What's needed here, I think, is an API that takes a datum plus type
> information and checks whether the datum is valid within the domain.  I
> haven't found one that is exported, but maybe someone could give a tip.

Problems :

- If the data you're trying to put in the Datum doesn't fit (example : out  
of range error, varchar too small, etc), and you want a  
datum-type-specific function to check your datum and reject it, how are  
you going to build the datum ? perhaps you can't, since your value doesn't  
fit. It's a chicken and egg problem : the check function that you expect  
to reject your invalid datum will not know it's invalid, since you've  
trimmed it at the edges to make it fit in the required Datum type...

- you are going to build a datum that is perhaps valid, and perhaps not,  
and send this to a function... having known-invalid datums moving around  
could be not such a good idea...

Why not use the copy binary format to communicate between python and pg ?

-> you write code to serialize python objects to binary form
-> you call the recv function to get a postgres datum
-> recv function throws an error if there is any problem

-> as a bonus, you release your python object <-> postgres binary code as  
a separate library so people can use it to output data readable by COPY  
BINARY and parse COPY BINARY dumps.


Re: [PATCH] plpythonu datatype conversion improvements

From
James Pye
Date:
On Aug 15, 2009, at 4:44 PM, Peter Eisentraut wrote:
> What's needed here, I think, is an API that takes a datum plus type
> information and checks whether the datum is valid within the domain.

/agree =)


Re: [PATCH] plpythonu datatype conversion improvements

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> The remaining problem is that the patch loses domain checking on the
> return types, because some paths no longer go through the data type's
> input function.  I have marked these places as FIXME, and the regression
> tests also contain a failing test case for this.

For the record, I think this entire patch is a bad idea.  PLs should not
be so much in bed with the internal representation of datatypes.  To
take just one example, this *will* break when/if we change text to carry
some internal locale indicator.  There has been absolutely zero evidence
presented to justify that there's a need to break abstraction to gain
performance in this area.

> What's needed here, I think, is an API that takes a datum plus type
> information and checks whether the datum is valid within the domain.  I
> haven't found one that is exported, but maybe someone could give a tip.

There isn't one, but maybe you could expose domain_state_setup and
domain_check_input, or some simple wrapper around them.
        regards, tom lane


Re: [PATCH] plpythonu datatype conversion improvements

From
Andrew Dunstan
Date:

Tom Lane wrote:
>
> For the record, I think this entire patch is a bad idea.  PLs should not
> be so much in bed with the internal representation of datatypes.  
>   

I thought there was some suggestion in the past that we should move some 
in that direction. The discussion context was Theo Schlossnagle's 
complaint about the overhead of passing bytea to and from PLPerl, 
although that might be ameliorated by the hex gadget. The other major 
case that would benefit would be passing Array values as the PL's native 
array type, and Composite values as the PL's associative array type. 
That would save PL users a lot of highly error-prone coding 
deconstructing the text representation of such objects.

cheers

andrew


Re: [PATCH] plpythonu datatype conversion improvements

From
Tom Lane
Date:
Andrew Dunstan <andrew@dunslane.net> writes:
> Tom Lane wrote:
>> For the record, I think this entire patch is a bad idea.  PLs should not
>> be so much in bed with the internal representation of datatypes.  

> I thought there was some suggestion in the past that we should move some 
> in that direction.

There's been some discussion about functional improvements like
translating arrays to arrays.  I don't know what we'd have to do
to manage that, but possibly some API extensions to the array code
would make it feasible without violating abstractions.  The present
patch, however, doesn't appear to have any reason to live other than an
undocumented amount of performance improvement.  My feeling about that
is if you're concerned about micro-performance, why are you coding in
python to begin with?  It isn't the best choice out there.
        regards, tom lane


Re: [PATCH] plpythonu datatype conversion improvements

From
Alvaro Herrera
Date:
Peter Eisentraut wrote:

> I have reworked this patch a bit and extended the plpython test suite
> around it.  Current copy attached.

I think the errcontext bits should be committed separately to get them
out of the way (and to ensure that they get in, regardless of objections
to other parts of the patch).

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support


Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On mån, 2009-08-17 at 10:42 -0400, Tom Lane wrote:
> For the record, I think this entire patch is a bad idea.  PLs should not
> be so much in bed with the internal representation of datatypes.  To
> take just one example, this *will* break when/if we change text to carry
> some internal locale indicator.  There has been absolutely zero evidence
> presented to justify that there's a need to break abstraction to gain
> performance in this area.

The motivation for this patch has nothing to do with performance.  The
point is to pass data types into and out of PL/Python sensibly.  In
particular, passing bytea into and out of PL/Python is currently
completely broken, in the sense that what you get in Python is not a
byte string that you can process sensibly.

We could argue that peeking inside the internal representation of data
types might be inappropriate.  In which case the solution would be to
run the data through the data type output function and have PL/Python
parse that back in.  That would just be a localized change in the patch,
however.  (It might be less than ideal for passing float types,
perhaps.)  We do, however, expose a data types binary format through the
binary protocol, so perhaps we should be using the send/recv functions
instead of input/output.  Which would require hardcoding the bytea
binary format, at least.  Either of these solutions would probably solve
the domains problem, though.

Note also that we have historically broken the bytea text format twice
as often as the bytea binary format. ;-)



Re: [PATCH] plpythonu datatype conversion improvements

From
Pavel Stehule
Date:
2009/8/18 Peter Eisentraut <peter_e@gmx.net>:
> On mån, 2009-08-17 at 10:42 -0400, Tom Lane wrote:
>> For the record, I think this entire patch is a bad idea.  PLs should not
>> be so much in bed with the internal representation of datatypes.  To
>> take just one example, this *will* break when/if we change text to carry
>> some internal locale indicator.  There has been absolutely zero evidence
>> presented to justify that there's a need to break abstraction to gain
>> performance in this area.
>

I thing, so communication based on text type is bad. Maybe we should
to use binary communication based on send and recv function? It's
should be better and maybe stable than direct transfer - but maybe
little bit slower.


> The motivation for this patch has nothing to do with performance.  The
> point is to pass data types into and out of PL/Python sensibly.  In
> particular, passing bytea into and out of PL/Python is currently
> completely broken, in the sense that what you get in Python is not a
> byte string that you can process sensibly.
>

I thing so bytea should be very well optimized. You can expect, so
there be moved bigger block of data.

regards
Pavel
> We could argue that peeking inside the internal representation of data
> types might be inappropriate.  In which case the solution would be to
> run the data through the data type output function and have PL/Python
> parse that back in.  That would just be a localized change in the patch,
> however.  (It might be less than ideal for passing float types,
> perhaps.)  We do, however, expose a data types binary format through the
> binary protocol, so perhaps we should be using the send/recv functions
> instead of input/output.  Which would require hardcoding the bytea
> binary format, at least.  Either of these solutions would probably solve
> the domains problem, though.
>
> Note also that we have historically broken the bytea text format twice
> as often as the bytea binary format. ;-)
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>


Re: [PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
<font face="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">As documented in the patch, the primary
motivationwas support of BYTEA datatype, which when cast through cstring was truncating python strings with embedded
nulls,<br /> performance was only a secondary consideration.<br /><br /> Regards, <br />   Caleb<br /><br /> (Sorry for
myslow entry on this thread, I’m on vacation right now.)<br /><br /> On 8/17/09 8:12 AM, "Tom Lane" <<a
href="tgl@sss.pgh.pa.us">tgl@sss.pgh.pa.us</a>>wrote:<br /><br /></span></font><blockquote><font face="Calibri,
Verdana,Helvetica, Arial"><span style="font-size:11pt">Andrew Dunstan <<a
href="andrew@dunslane.net">andrew@dunslane.net</a>>writes:<br /> > Tom Lane wrote:<br /> >> For the record,
Ithink this entire patch is a bad idea.  PLs should not<br /> >> be so much in bed with the internal
representationof datatypes. <br /><br /> > I thought there was some suggestion in the past that we should move
some<br/> > in that direction.<br /><br /> There's been some discussion about functional improvements like<br />
translatingarrays to arrays.  I don't know what we'd have to do<br /> to manage that, but possibly some API extensions
tothe array code<br /> would make it feasible without violating abstractions.  The present<br /> patch, however,
doesn'tappear to have any reason to live other than an<br /> undocumented amount of performance improvement.  My
feelingabout that<br /> is if you're concerned about micro-performance, why are you coding in<br /> python to begin
with? It isn't the best choice out there.<br /><br />                         regards, tom lane<br /><br
/></span></font></blockquote>

Re: [PATCH] plpythonu datatype conversion improvements

From
Greg Stark
Date:
On Sat, Aug 22, 2009 at 11:45 AM, Caleb Welton<cwelton@greenplum.com> wrote:
> As documented in the patch, the primary motivation was support of BYTEA
> datatype, which when cast through cstring was truncating python strings with
> embedded nulls,
> performance was only a secondary consideration.

The alternative to attaching to the internal representation would be
to marshal and unmarshal the text representation where nuls are
escaped as \000.

However I dispute this this is "micro-performance" that we're talking
about. On any given small datum it may be a small incremental amount
of time but it's not incremental time that matters, it's aggregate. If
you're processing 1TB of data and you have to marshal and unmarshal
all 1TB it doesn't matter that you're doing it in 100 byte chunks. And
in any case there are plenty of people throwing around multi-megabyte
bytea blobs and having to marshal and unmarshal them every time they
go from the database into a PL or back would be a noticeable delay and
risk of out-of-memory errors.

If we want PLs to not be overly in bed with Postgres data types then
the way to do it is to have data types provide abstract methods for
accessing their internals. At least for bytea and text that would be
fairly straightforward. For numeric I don't see that it would really
buy much since it wouldn't really let us completely change
representations.

-- 
greg
http://mit.edu/~gsstark/resume.pdf


Re: [PATCH] plpythonu datatype conversion improvements

From
Tom Lane
Date:
Greg Stark <gsstark@mit.edu> writes:
> On Sat, Aug 22, 2009 at 11:45 AM, Caleb Welton<cwelton@greenplum.com> wrote:
>> As documented in the patch, the primary motivation was support of BYTEA
>> datatype, which when cast through cstring was truncating python strings with
>> embedded nulls,
>> performance was only a secondary consideration.

> The alternative to attaching to the internal representation would be
> to marshal and unmarshal the text representation where nuls are
> escaped as \000.

I don't actually have a problem with depending on the internal
representation of bytea.  What I'm unhappy about is that (despite
Caleb's assertions that this is only about bytea) the patch proceeds
to make plpython intimate with the internal representation of a bunch
of *other* datatypes, some of which we have good reason to think may
change in future.  If it were only touching bytea I would not have
complained.
        regards, tom lane


Re: [PATCH] plpythonu datatype conversion improvements

From
Caleb Welton
Date:
<font face="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">I didn’t say that it _only_ affects bytea,
Isaid that was the _primary motivation_ for it.  <br /><br /> Converting from postgres=>python this change affects
boolean,float4, float8, numeric, int16, int32, int64, text, and bytea.  The code to handle this goes through
DatumGetXXXfor the native C type for the datatype, with the exception of the Varlena types (special case) and Numeric
whichcalls numeric_float8() to convert the numeric to a native C double precision float.  As mentioned in the original
postI do not think that this is appropriate for numeric, and I would prefer a better mapping, but this was a
pre-existingissue and is not a change in behavior for the patch.  Since this is a separate issue I opted not to change
itto keep the patch concise.   <br /><br /> Converting from python=>postgres this change effects void, bool, bytea,
andtext.  <br /><br /> The reason for this asymmetry is that there is not a 1:1 mapping of Postgres datatypes to Python
datatypesand conciseness of the patch.<br /><br /> All other datatypes (including arrays unfortunately) go through the
sametext input functions that they did before.<br /><br /> Of the above I would expect the only type that we would have
goodreason to expect to change would be numeric, and this patch _doesn’t_ rely on it’s internal representation: it
callsnumeric_float8().<br /><br /> I think it would be good to have mappings for other datatypes, depending on internal
representationor not, but thought that was beyond the scope of the patch.<br /><br /> Regards,<br />   Caleb<br /><br
/>On 8/22/09 7:03 AM, "Tom Lane" <<a href="tgl@sss.pgh.pa.us">tgl@sss.pgh.pa.us</a>> wrote:<br /><br
/></span></font><blockquote><fontface="Calibri, Verdana, Helvetica, Arial"><span style="font-size:11pt">Greg Stark
<<ahref="gsstark@mit.edu">gsstark@mit.edu</a>> writes:<br /> > On Sat, Aug 22, 2009 at 11:45 AM, Caleb
Welton<<ahref="cwelton@greenplum.com">cwelton@greenplum.com</a>> wrote:<br /> >> As documented in the
patch,the primary motivation was support of BYTEA<br /> >> datatype, which when cast through cstring was
truncatingpython strings with<br /> >> embedded nulls,<br /> >> performance was only a secondary
consideration.<br/><br /> > The alternative to attaching to the internal representation would be<br /> > to
marshaland unmarshal the text representation where nuls are<br /> > escaped as \000.<br /><br /> I don't actually
havea problem with depending on the internal<br /> representation of bytea.  What I'm unhappy about is that (despite<br
/>Caleb's assertions that this is only about bytea) the patch proceeds<br /> to make plpython intimate with the
internalrepresentation of a bunch<br /> of *other* datatypes, some of which we have good reason to think may<br />
changein future.  If it were only touching bytea I would not have<br /> complained.<br /><br />
                        regards,tom lane<br /><br /></span></font></blockquote> 

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On mån, 2009-08-17 at 11:55 -0400, Alvaro Herrera wrote:
> Peter Eisentraut wrote:
> 
> > I have reworked this patch a bit and extended the plpython test suite
> > around it.  Current copy attached.
> 
> I think the errcontext bits should be committed separately to get them
> out of the way (and to ensure that they get in, regardless of objections
> to other parts of the patch).

Done that now.



Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On sön, 2009-08-16 at 02:44 +0300, Peter Eisentraut wrote:
> The remaining problem is that the patch loses domain checking on the
> return types, because some paths no longer go through the data type's
> input function.  I have marked these places as FIXME, and the regression
> tests also contain a failing test case for this.
>
> What's needed here, I think, is an API that takes a datum plus type
> information and checks whether the datum is valid within the domain.  I
> haven't found one that is exported, but maybe someone could give a tip.

Got that fixed now.  Updated patch is attached.  I will sleep over it,
but I think it's good to go.

Attachment

Re: [PATCH] plpythonu datatype conversion improvements

From
Peter Eisentraut
Date:
On mån, 2009-08-31 at 23:41 +0300, Peter Eisentraut wrote:
> On sön, 2009-08-16 at 02:44 +0300, Peter Eisentraut wrote:
> > The remaining problem is that the patch loses domain checking on the
> > return types, because some paths no longer go through the data type's
> > input function.  I have marked these places as FIXME, and the regression
> > tests also contain a failing test case for this.
> > 
> > What's needed here, I think, is an API that takes a datum plus type
> > information and checks whether the datum is valid within the domain.  I
> > haven't found one that is exported, but maybe someone could give a tip.
> 
> Got that fixed now.  Updated patch is attached.  I will sleep over it,
> but I think it's good to go.

committed



Re: [PATCH] plpythonu datatype conversion improvements

From
Hannu Krosing
Date:
On Wed, 2009-05-27 at 14:25 -0700, Caleb Welton wrote:
> Yes, in Python >= 2.4 there is the Decimal datatype.  
> 
> However, unlike the other mappings employed by plpythonu, Decimal
> requires an import statement to be in scope.

adding it as already-imported module should not be hard 

I think that moving to saner mappings should at least be discussed

and even if it is not in scope for the user-defined function body there
is nothing that prevents one from using it for conversion.

The Decimal _type_ needs not to be in scope for using Decimal
_instances_

maybe this should/could be controlled by a GUC.



btw, can we currently use funtions in setting GUC parameters ?

if we can , then we could define some python environment initializing
function and then do

ALTER USER xxx SET pyinit = initialise_python_for_xxx()


> -Caleb
> 
> On 5/27/09 2:07 PM, "Tom Lane" <tgl@sss.pgh.pa.us> wrote:
> 
>         Peter Eisentraut <peter_e@gmx.net> writes:
>         > On Wednesday 27 May 2009 21:53:31 Caleb Welton wrote:
>         >> ... My own
>         >> feeling on the matter is that PyFloat is the wrong mapping
>         for numeric, but
>         >> I didn't want to muddy this patch by changing that.
>         
>         > Yeah, that one had me wondering for a while as well, but as
>         you say it is
>         > better to address that separately.
>         
>         That was making me itch as well, in my very cursory look at
>         the patch.
>         Does Python have a saner mapping for it?
>         
>                                 regards, tom lane
>         
-- 
Hannu Krosing   http://www.2ndQuadrant.com
PostgreSQL Scalability and Availability   Services, Consulting and Training