Thread: plpython triggers are broken for composite-type columns

plpython triggers are broken for composite-type columns

From
Tom Lane
Date:
Don't know if anybody noticed bug #6559
http://archives.postgresql.org/pgsql-bugs/2012-03/msg00180.php

I've confirmed that the given test case works in 9.0 but fails in
9.1 and HEAD.  It's not terribly sensitive to the details of the
SQL: any non-null value for the composite column fails, for instance
you can tryINSERT INTO tbl VALUES (row(3), 4);
and it spits up just the same.  The long and the short of it is that
PLy_modify_tuple fails to make sense of what PLyDict_FromTuple
produced for the table row.

I tried to trace through things to see exactly where it was going wrong,
and noted that

(1) When converting the table row to a Python dict, the composite
column value is fed through the generic PLyString_FromDatum() function,
which seems likely to be the wrong choice.

(2) When converting back, the composite column value is routed to
PLyObject_ToTuple, which decides it is a Python sequence, which seems
a bit improbable considering it was merely a string a moment ago.

I find this code pretty unreadable, though, and know nothing to
speak of about the Python side of things anyhow.  So somebody else
had better pick this up.
        regards, tom lane


Re: plpython triggers are broken for composite-type columns

From
Jan Urbański
Date:
On 10/04/12 04:20, Tom Lane wrote:
> Don't know if anybody noticed bug #6559
> http://archives.postgresql.org/pgsql-bugs/2012-03/msg00180.php
>
> I've confirmed that the given test case works in 9.0 but fails in
> 9.1 and HEAD.
>
> I find this code pretty unreadable, though, and know nothing to
> speak of about the Python side of things anyhow.  So somebody else
> had better pick this up.

I'll look into that.

Cheers,
Jan


Re: plpython triggers are broken for composite-type columns

From
Jan Urbański
Date:
On 10/04/12 07:35, Jan Urbański wrote:
> On 10/04/12 04:20, Tom Lane wrote:
>> Don't know if anybody noticed bug #6559
>> http://archives.postgresql.org/pgsql-bugs/2012-03/msg00180.php
>>
>> I've confirmed that the given test case works in 9.0 but fails in
>> 9.1 and HEAD.

So, I know what's going on, I still don't know what's the best way to 
handle it.

The function that converts Python objects to PG data checks what type 
it's supposed to produce and acts accordingly. In 9.0 it checked for 
bool, bytea and arrays, in 9.1 it also takes composite types into account.

This has been done to support functions returning composite types - to 
do that they need to return a dictionary or a list, for instance 
{'col1': 1, 'col2': 2}.

The problem is that the routine that converts PG data into Python 
objects does not handle composite type inputs all that well - it just 
bails and returns the string representation, hence '(3)' appearing in 
Python land.

Now previously, the Python->PG function did not see that the given 
conversion is supposed to return a composite so it also bailed and used 
a default text->composite conversion, so '(3)' was converted to ROW(3) 
and all went well. The new code tries to treat what it gets as a 
dictionary/list/tuple and fails in a more or less random way.

Now that I understand what's been going on, I'll try to think of a 
non-invasive way of fixing that...

Cheers,
Jan


Re: plpython triggers are broken for composite-type columns

From
Tom Lane
Date:
Jan Urbański <wulczer@wulczer.org> writes:
>> On 10/04/12 04:20, Tom Lane wrote:
>>> Don't know if anybody noticed bug #6559
>>> http://archives.postgresql.org/pgsql-bugs/2012-03/msg00180.php

> So, I know what's going on, I still don't know what's the best way to 
> handle it.

> The function that converts Python objects to PG data checks what type 
> it's supposed to produce and acts accordingly. In 9.0 it checked for 
> bool, bytea and arrays, in 9.1 it also takes composite types into account.

> This has been done to support functions returning composite types - to 
> do that they need to return a dictionary or a list, for instance 
> {'col1': 1, 'col2': 2}.

> The problem is that the routine that converts PG data into Python 
> objects does not handle composite type inputs all that well - it just 
> bails and returns the string representation, hence '(3)' appearing in 
> Python land.

> Now previously, the Python->PG function did not see that the given 
> conversion is supposed to return a composite so it also bailed and used 
> a default text->composite conversion, so '(3)' was converted to ROW(3) 
> and all went well. The new code tries to treat what it gets as a 
> dictionary/list/tuple and fails in a more or less random way.

> Now that I understand what's been going on, I'll try to think of a 
> non-invasive way of fixing that...

ISTM that conversion of a composite value to Python ought to produce a
dict, now that the other direction expects a dict.  I can see that this
is probably infeasible for compatibility reasons in 9.1, but it's not
too late to fix it for 9.2.  We might have to leave the bug unfixed in
9.1, since anything we do about it will represent a compatibility break.
        regards, tom lane


Re: plpython triggers are broken for composite-type columns

From
Tom Lane
Date:
I wrote:
> Jan Urbański <wulczer@wulczer.org> writes:
>> Now that I understand what's been going on, I'll try to think of a 
>> non-invasive way of fixing that...

> ISTM that conversion of a composite value to Python ought to produce a
> dict, now that the other direction expects a dict.  I can see that this
> is probably infeasible for compatibility reasons in 9.1, but it's not
> too late to fix it for 9.2.  We might have to leave the bug unfixed in
> 9.1, since anything we do about it will represent a compatibility break.

On reflection, can't we fix this as follows: if the value coming in from
Python is a string, just feed it to record_in, the same as we used to.
When I traced through the logic before, it seemed like it was failing
to distinguish strings from sequences, but I would hope that Python
is more strongly typed than that.

I still think the conversion in the other direction ought to yield a
dict, but that's clearly not back-patch material.
        regards, tom lane


Re: plpython triggers are broken for composite-type columns

From
Jan Urbański
Date:
On 10/04/12 20:47, Tom Lane wrote:
> I wrote:
>> Jan Urbański<wulczer@wulczer.org>  writes:
>>> Now that I understand what's been going on, I'll try to think of a
>>> non-invasive way of fixing that...
>
>> ISTM that conversion of a composite value to Python ought to produce a
>> dict, now that the other direction expects a dict.  I can see that this
>> is probably infeasible for compatibility reasons in 9.1, but it's not
>> too late to fix it for 9.2.  We might have to leave the bug unfixed in
>> 9.1, since anything we do about it will represent a compatibility break.
>
> On reflection, can't we fix this as follows: if the value coming in from
> Python is a string, just feed it to record_in, the same as we used to.
> When I traced through the logic before, it seemed like it was failing
> to distinguish strings from sequences, but I would hope that Python
> is more strongly typed than that.

Yeah, we can fix PLyObject_ToTuple to check for strings too and use the 
default PG input function. The reason it was complaining about length is 
that we're checking if the object passed implements the sequence 
protocol, which Python strings do (length, iteration, etc). Sticking a 
if branch that will catch the string case above that should be sufficient.

>
> I still think the conversion in the other direction ought to yield a
> dict, but that's clearly not back-patch material.

Yes, that would be ideal, even though not backwards-compatible. 
Back-patching is out of the question, but do we want to change trigger 
functions to receive dictionaries in NEW? If so, should this be 9.2 
material, or just a TODO?


Re: plpython triggers are broken for composite-type columns

From
Tom Lane
Date:
Jan Urbański <wulczer@wulczer.org> writes:
> On 10/04/12 20:47, Tom Lane wrote:
>> On reflection, can't we fix this as follows: if the value coming in from
>> Python is a string, just feed it to record_in, the same as we used to.
>> When I traced through the logic before, it seemed like it was failing
>> to distinguish strings from sequences, but I would hope that Python
>> is more strongly typed than that.

> Yeah, we can fix PLyObject_ToTuple to check for strings too and use the 
> default PG input function. The reason it was complaining about length is 
> that we're checking if the object passed implements the sequence 
> protocol, which Python strings do (length, iteration, etc). Sticking a 
> if branch that will catch the string case above that should be sufficient.

Ah, makes sense then.  (Perhaps the dict case ought to be tested before
the sequence case, too, just to be safe?)

>> I still think the conversion in the other direction ought to yield a
>> dict, but that's clearly not back-patch material.

> Yes, that would be ideal, even though not backwards-compatible. 
> Back-patching is out of the question, but do we want to change trigger 
> functions to receive dictionaries in NEW?

Hm, I was not thinking of this as being trigger-specific, but more a
general principle that composite columns of tuples ought to be handled
in a recursive fashion.

> If so, should this be 9.2 material, or just a TODO?

If it can be done quickly and with not much risk, I'd vote for
squeezing it into 9.2, because it seems to me to be a clear bug that the
two directions are not handled consistently.  If you don't have time for
it now or you don't think it would be a small/safe patch, we'd better
just put it on TODO.
        regards, tom lane


Re: plpython triggers are broken for composite-type columns

From
Jan Urbański
Date:
On 10/04/12 21:27, Tom Lane wrote:
> Jan Urbański<wulczer@wulczer.org>  writes:
>> Yes, that would be ideal, even though not backwards-compatible.
>> Back-patching is out of the question, but do we want to change trigger
>> functions to receive dictionaries in NEW?
>
> Hm, I was not thinking of this as being trigger-specific, but more a
> general principle that composite columns of tuples ought to be handled
> in a recursive fashion.

Sure, that would be the way.

>> If so, should this be 9.2 material, or just a TODO?
>
> If it can be done quickly and with not much risk, I'd vote for
> squeezing it into 9.2, because it seems to me to be a clear bug that the
> two directions are not handled consistently.  If you don't have time for
> it now or you don't think it would be a small/safe patch, we'd better
> just put it on TODO.

I'll see if making the conversion function recursive is easy and 
independently whip up a patch to check for strings and routes them 
through InputFunctionCall, for back-patching purposes.

Cheers,
Jan


Re: plpython triggers are broken for composite-type columns

From
Jan Urbański
Date:
On 10/04/12 21:47, Jan Urbański wrote:
> On 10/04/12 21:27, Tom Lane wrote:
>> =?UTF-8?B?SmFuIFVyYmHFhHNraQ==?=<wulczer@wulczer.org> writes:
>>> Yes, that would be ideal, even though not backwards-compatible.
>>> Back-patching is out of the question, but do we want to change trigger
>>> functions to receive dictionaries in NEW?
>>
>> Hm, I was not thinking of this as being trigger-specific, but more a
>> general principle that composite columns of tuples ought to be handled
>> in a recursive fashion.
>
> Sure, that would be the way.
>
>>> If so, should this be 9.2 material, or just a TODO?
>>
>> If it can be done quickly and with not much risk, I'd vote for
>> squeezing it into 9.2, because it seems to me to be a clear bug that the
>> two directions are not handled consistently. If you don't have time for
>> it now or you don't think it would be a small/safe patch, we'd better
>> just put it on TODO.

It turned out not to be as straightforward as I though :(

The I/O code in PL/Python is a bit of a mess and that's something that
I'd like to address somewhere in the 9.3 development cycle. Right now
making the conversion function recursive is not possible without some
deep surgery (or kludgery...) so I limited myself to producing
regression-fixing patches for 9.2 and 9.1 (attached).

Cheers,
Jan

Attachment

Re: plpython triggers are broken for composite-type columns

From
Peter Eisentraut
Date:
On mån, 2012-04-23 at 02:25 +0200, Jan Urbański wrote:
> It turned out not to be as straightforward as I though :(

Yeah, been there ...
> 
> The I/O code in PL/Python is a bit of a mess and that's something that
> I'd like to address somewhere in the 9.3 development cycle. Right now 
> making the conversion function recursive is not possible without some 
> deep surgery (or kludgery...) so I limited myself to producing 
> regression-fixing patches for 9.2 and 9.1 (attached). 

Committed.