Обсуждение: Rows from a stored procedure result are stringified

Поиск
Список
Период
Сортировка

Rows from a stored procedure result are stringified

От
Christopher David Howie
Дата:
Hello,

Please copy me on replies as I'm not subscribed to this list.

I'm working on an application using psycopg2 2.2.1.  Regarding stored
procedures, I'm seeing some odd behavior.  I am calling the procedure like:

SELECT sp_foobar(1, 2, 3);

In this case, the stored procedure is declared to return a "setof" a
custom type, and it returns using "RETURN QUERY SELECT ...".

I would expect the rows to come back from cursor.fetchall() in this
structure:

[(1,2),(3,4),(5,6)]

But they are returned like this:

[('(1,2)'),('(3,4)'),('(5,6)')]

This makes no sense to me.  Is this expected/documented behavior?

--
Chris Howie
http://www.chrishowie.com
http://en.wikipedia.org/wiki/User:Crazycomputers

If you correspond with me on a regular basis, please read this document:
http://www.chrishowie.com/email-preferences/

PGP fingerprint: 2B7A B280 8B12 21CC 260A DF65 6FCE 505A CF83 38F5

------------------------------------------------------------------------
                    IMPORTANT INFORMATION/DISCLAIMER

This document should be read only by those persons to whom it is
addressed.  If you have received this message it was obviously addressed
to you and therefore you can read it.

Additionally, by sending an email to ANY of my addresses or to ANY
mailing lists to which I am subscribed, whether intentionally or
accidentally, you are agreeing that I am "the intended recipient," and
that I may do whatever I wish with the contents of any message received
from you, unless a pre-existing agreement prohibits me from so doing.

This overrides any disclaimer or statement of confidentiality that may
be included on your message.

Re: Rows from a stored procedure result are stringified

От
Daniele Varrazzo
Дата:


On Dec 21, 2011 8:52 PM, "Christopher David Howie" <me@chrishowie.com> wrote:
>
> I'm working on an application using psycopg2 2.2.1.  Regarding stored
> procedures, I'm seeing some odd behavior.  I am calling the procedure like:
>
> SELECT sp_foobar(1, 2, 3);
>
> In this case, the stored procedure is declared to return a "setof" a
> custom type, and it returns using "RETURN QUERY SELECT ...".
>
> I would expect the rows to come back from cursor.fetchall() in this
> structure:
>
> [(1,2),(3,4),(5,6)]
>
> But they are returned like this:
>
> [('(1,2)'),('(3,4)'),('(5,6)')]
>
> This makes no sense to me.  Is this expected/documented behavior?

Yes: it is expected: if psycopg finds a type it doesn't know, it will just return the string passed by the database. Doesn't it make sense? :-)

You can extend psycopg adding your custom type using register_composite(): see < http://initd.org/psycopg/docs/extras.html#composite-types-casting>. It requires psycopg 2.4 though. Alternatively you can write and register your own type parser: see  <http://initd.org/psycopg/docs/advanced.html#type-casting-of-sql-types-into-python-objects>.

-- Daniele

Re: Rows from a stored procedure result are stringified

От
Christopher David Howie
Дата:
On 12/21/2011 03:14 PM, Daniele Varrazzo wrote:
>> I would expect the rows to come back from cursor.fetchall() in this
>> structure:
>>
>> [(1,2),(3,4),(5,6)]
>>
>> But they are returned like this:
>>
>> [('(1,2)'),('(3,4)'),('(5,6)')]
>>
>> This makes no sense to me.  Is this expected/documented behavior?
>
> Yes: it is expected: if psycopg finds a type it doesn't know, it will
> just return the string passed by the database. Doesn't it make sense? :-)
>
> You can extend psycopg adding your custom type using
> register_composite(): see <
> http://initd.org/psycopg/docs/extras.html#composite-types-casting>. It
> requires psycopg 2.4 though. Alternatively you can write and register
> your own type parser: see
> <http://initd.org/psycopg/docs/advanced.html#type-casting-of-sql-types-into-python-objects>.

Hmm.  This makes sense to some degree.  I do note that strings coming
back from the database are not quoted.  Is there a parsing routine in
psycopg that I can leverage to turn this kind of database-provided
tuple-as-a-string representation into a proper tuple/list?

--
Chris Howie
http://www.chrishowie.com
http://en.wikipedia.org/wiki/User:Crazycomputers

If you correspond with me on a regular basis, please read this document:
http://www.chrishowie.com/email-preferences/

PGP fingerprint: 2B7A B280 8B12 21CC 260A DF65 6FCE 505A CF83 38F5

------------------------------------------------------------------------
                    IMPORTANT INFORMATION/DISCLAIMER

This document should be read only by those persons to whom it is
addressed.  If you have received this message it was obviously addressed
to you and therefore you can read it.

Additionally, by sending an email to ANY of my addresses or to ANY
mailing lists to which I am subscribed, whether intentionally or
accidentally, you are agreeing that I am "the intended recipient," and
that I may do whatever I wish with the contents of any message received
from you, unless a pre-existing agreement prohibits me from so doing.

This overrides any disclaimer or statement of confidentiality that may
be included on your message.

Re: Rows from a stored procedure result are stringified

От
Daniele Varrazzo
Дата:
On Wed, Dec 21, 2011 at 8:54 PM, Christopher David Howie
<me@chrishowie.com> wrote:

Hi Chris. Please consider subscribing to the ML, or I will have to
authorize every single message of yours. After subscribing, you may
also disable receiving mail if you want.

> On 12/21/2011 03:14 PM, Daniele Varrazzo wrote:
>>> I would expect the rows to come back from cursor.fetchall() in this
>>> structure:
>>>
>>> [(1,2),(3,4),(5,6)]
>>>
>>> But they are returned like this:
>>>
>>> [('(1,2)'),('(3,4)'),('(5,6)')]
>>>
>> You can extend psycopg adding your custom type

This is still true, however it may not be the most convenient way.
Sorry but from your example I may have missed the picture. Let's say
you have a SRF of some type, e.g.:

    test=> create type mytype as (a int, b text);
    CREATE TYPE
    test=> create function myfunc (x int, y int)
    returns setof mytype as $$
    select n, repeat('x', n) from generate_series($1,$2) n;
    $$ language sql;

You can now use this function either returning the whole result in a
single field:

    test=> select myfunc(1,4);
      myfunc
    ----------
     (1,x)
     (2,xx)
     (3,xxx)
     (4,xxxx)
    (4 rows)

or access to the components of the returned type:

    test=> select * from myfunc(1,4);
     a |  b
    ---+------
     1 | x
     2 | xx
     3 | xxx
     4 | xxxx
    (4 rows)

(you can also specify the fields such as "select b from myfunc"... you
got the picture).

Psycopg receives the same data displayed by psql: in the first case
you get the type returned by the function in a single field:

    In [5]: cur.execute("select myfunc(1,4)")

    In [6]: print cur.fetchall()
    [('(1,x)',), ('(2,xx)',), ('(3,xxx)',), ('(4,xxxx)',)]

and this is your "surprising version". If everything you have to do is
to get these data unpacked, you can use the second version of the
query, specifying the fields list:

    In [6]: cur.execute("select a,b from myfunc(1,4)")

    In [7]: cur.fetchall()
    Out[7]: [(1, 'x'), (2, 'xx'), (3, 'xxx'), (4, 'xxxx')]

and this is probably everything you need, if you just want to receive
the function result, and it could have been the first answer to your
question yesterday.

If, instead, you want to keep the identity of the type you get from
the function, you can do what suggested before: attaching a parser to
the oid of "mytype". Together with the result, Postgres passes to the
client some metadata about the returned type, so that e.g. we can tell
if "1" was the string "1" or the number 1:

    In [8]: cur.execute("select myfunc(1,4)")

    In [9]: cur.description
    Out[9]: (Column(name='myfunc', type_code=897686,
display_size=None, internal_size=-1, precision=None, scale=None,
null_ok=None),)

    In [10]: cur.fetchone()
    Out[10]: ('(1,x)',)

so we can attach a fancy parser to parse strings such as "(1,x)"
whenever we see a column of data with oid "897686" (which is the oid
in *my* database: in yours it will be different). All these operations
(getting the oid of mytype, inspecting what is composed of, generate a
parser for it, attach the parser to its oid) are performed by
register_composite().


> Hmm.  This makes sense to some degree.  I do note that strings coming
> back from the database are not quoted.  Is there a parsing routine in
> psycopg that I can leverage to turn this kind of database-provided
> tuple-as-a-string representation into a proper tuple/list?

The psycopg2.extras.CompositeCaster class has the parsing function,
but it is not part of the API. You may read the code to see what it
does, but chances are that you either just want to use
register_composite() or you don't want to get composites at all, using
the unpacking syntax in the query.


-- Daniele

Re: Rows from a stored procedure result are stringified

От
Christopher David Howie
Дата:
On 12/22/2011 10:34 AM, Daniele Varrazzo wrote:
> On Wed, Dec 21, 2011 at 8:54 PM, Christopher David Howie
> <me@chrishowie.com> wrote:
>
> Hi Chris. Please consider subscribing to the ML, or I will have to
> authorize every single message of yours. After subscribing, you may
> also disable receiving mail if you want.

I may do that in the future, as there's a good change I'll have more
questions, but for now this will be my last message, because:

>> On 12/21/2011 03:14 PM, Daniele Varrazzo wrote:
> or access to the components of the returned type:
>
>     test=> select * from myfunc(1,4);
>      a |  b
>     ---+------
>      1 | x
>      2 | xx
>      3 | xxx
>      4 | xxxx
>     (4 rows)

This is exactly the solution I was looking for.  I'm not sure how I
missed it, the syntax is pretty obvious.  I didn't even notice when
fiddling with psql that "SELECT myfunc()" was in fact returning a string
and not many columns.  Thanks for the pointer.

The other information in your message was useful as well.  I will likely
be using composite types as columns soon and I'll need to map them to
Python types.  So thanks for the detailed explanation.  :)

Cheers,

--
Chris Howie
http://www.chrishowie.com
http://en.wikipedia.org/wiki/User:Crazycomputers

If you correspond with me on a regular basis, please read this document:
http://www.chrishowie.com/email-preferences/

PGP fingerprint: 2B7A B280 8B12 21CC 260A DF65 6FCE 505A CF83 38F5

------------------------------------------------------------------------
                    IMPORTANT INFORMATION/DISCLAIMER

This document should be read only by those persons to whom it is
addressed.  If you have received this message it was obviously addressed
to you and therefore you can read it.

Additionally, by sending an email to ANY of my addresses or to ANY
mailing lists to which I am subscribed, whether intentionally or
accidentally, you are agreeing that I am "the intended recipient," and
that I may do whatever I wish with the contents of any message received
from you, unless a pre-existing agreement prohibits me from so doing.

This overrides any disclaimer or statement of confidentiality that may
be included on your message.