Thread: PL/Python

PL/Python

From
Tom Jenkins
Date:
Hey all,
In 7.2 we found a bug in PL/Python (i don't know if its still there in 7.3 - i
haven't downloaded the sources yet).

When we create a function like so:

CREATE OR REPLACE FUNCTION jojo (timestamp) RETURNS varchar AS '
pln = plpy.prepare("UPDATE jtab SET jfld = $1",["timestamp"])
rcs = plpy.execute(pln,[args[0]])
return "YES"' LANGUAGE 'PLPYTHON';

SELECT jojo(now());
works fine but

SELECT jojo(NULL);
errors out with
NOTICE:  plpython: in function __plpython_procedure_jojo_49818349:
plpy.Error: Unknown error in PLy_spi_execute_plan
ERROR:  Bad timestamp external representation 'None'

in plpython.c the function PLy_spi_execute_plan has the following snippet:
elem = PySequence_GetItem(list, i);
so = PyObject_Str(elem);
sv = PyString_AsString(so);
/*
 * FIXME -- if this can elog, we have leak
 */
plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc),
                CStringGetDatum(sv),
               ObjectIdGetDatum(plan->args[i].out.d.typelem),
                 Int32GetDatum(-1));

this looks to me that the None object is getting converted into 'None'
(through PyString_AsString call).

I tried a quick fix but it crashes the backend:
elem = PySequence_GetItem(list, i);
if ( elem != Py_None )
{
    so = PyObject_Str(elem);
    sv = PyString_AsString(so);
    /*
     * FIXME -- if this can elog, we have leak
     */
    plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc),
                        CStringGetDatum(sv),
                       ObjectIdGetDatum(plan->args[i].out.d.typelem),
                        Int32GetDatum(-1));

    Py_DECREF(so);
}
else
{
    plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc),
                        PointerGetDatum(NULL),
                   ObjectIdGetDatum(plan->args[i].out.d.typelem),
                        Int32GetDatum(-1));
}

Py_DECREF(elem);

I believe the logic is correct (if the python object is None then send to the
typfunc a NULL) but I don't know how to code it correctly.

Thanks for any help,

Tom



Re: PL/Python

From
Tom Lane
Date:
Tom Jenkins <tjenkins@devis.com> writes:
> I believe the logic is correct (if the python object is None then send
> to the typfunc a NULL) but I don't know how to code it correctly.

You shouldn't call the typfunc at all if you want a NULL --- just set
the null flag for that datum slot.

            regards, tom lane

Re: PL/Python

From
Tom Jenkins
Date:
On Tuesday 21 January 2003 12:49 pm, Tom Lane wrote:
> Tom Jenkins <tjenkins@devis.com> writes:
> > I believe the logic is correct (if the python object is None then send
> > to the typfunc a NULL) but I don't know how to code it correctly.
>
> You shouldn't call the typfunc at all if you want a NULL --- just set
> the null flag for that datum slot.
>
>             regards, tom lane

please pardon me if this is really obvious, but i can't seem to find a null
flag in the structures i have in that function.  I don't get how to set the
null flag for the datum slot.  I'm still learning how to work with the
backend code.

any help is appreciated,

Tom

Re: PL/Python

From
Tom Lane
Date:
Tom Jenkins <tjenkins@devis.com> writes:
> please pardon me if this is really obvious, but i can't seem to find a null
> flag in the structures i have in that function.  I don't get how to set the
> null flag for the datum slot.  I'm still learning how to work with the
> backend code.

It looks like whoever wrote the plpython code was still learning, too.
Parts of it handle NULLs correctly, but PLy_spi_execute_plan doesn't
seem to cope at all.  It looks like you ought to add a nulls flag array
to PLyPlanObject, fill it during PLy_spi_execute_plan, and pass it to
SPI_execp.  IIRC it's a char array, containing 'n' for a null arg and
' ' for not-null, but check SPI_execp to be sure.

Also, the code that explicitly pfree's the typfunc's results here is a
waste of space, if not actively dangerous...

            regards, tom lane