Re: flexi adaption/casting scheme

Поиск
Список
Период
Сортировка
От Daniele Varrazzo
Тема Re: flexi adaption/casting scheme
Дата
Msg-id CA+mi_8aTMiz1AKSAJWw_Bhj9XkSTMkjU8taXcOgWqfjNJWzE2g@mail.gmail.com
обсуждение исходный текст
Ответ на flexi adaption/casting scheme  (Tobias Oberstein <tobias.oberstein@gmail.com>)
Ответы Re: flexi adaption/casting scheme  (Ronan Dunklau <rdunklau@gmail.com>)
Re: flexi adaption/casting scheme  (Tobias Oberstein <tobias.oberstein@gmail.com>)
Re: flexi adaption/casting scheme  (Tobias Oberstein <tobias.oberstein@gmail.com>)
Re: flexi adaption/casting scheme  (Tobias Oberstein <tobias.oberstein@gmail.com>)
Список psycopg
On Thu, Sep 20, 2012 at 10:50 PM, Tobias Oberstein
<tobias.oberstein@gmail.com> wrote:
> Hi,
>
> I'd like to implement a certain type adaption/casting scheme
> and struggle with the right approach. Maybe someone can give
> me a hint? Would be great and welcome!
>
> Context: I am _only_ concerned with calling stored procedures,
> so there is the complete type metadata from PG catalog regarding
> the PG side available to use for mapping.

As long as you know a type oid you can associate a different cast function.
>
>
> PG => Py (typecasting):
> -----------------------
>
> 1.1
> hstore => plain Python dict

register_hstore does this


> 1.2
> JSON => json.loads() .. whatever Python object that gives

register_json will do


> 1.3
> composite type => plain Python dict with key/value pairs only
> for all attributes in the composite type that have non-NULL values

register_composite will return a namedtuple: it should be easy to
change that code and return a dict instead.


> 1.4
> everything else => as per-default with Psycopg


> Py => PG (adaption):

Adaptation only works on the class of the Python object: there is no
knowledge about the target type in postgres.

> 2.1
> plain Python dict ..:
>
> 2.1.1
> PG target type = hstore => dict-to-hstore with conversion of keys/values to
> str repr. if needed

you may subclass dict into an "hstore" python type and maybe guard
key/values accepting only strings, or converting the items according
to your rules.


> 2.1.2
> PG target type = JSON => json.dumps() whatever str that produces

same as hstore, with the extra nuance that the py object may be a
dict, a list and much more.


> 2.1.3
> PG target type = composite type => for every key in the Python dict that is
> an attribute in the composite type, fill in the value from the dict; for
> every attribute in the composite type where there is no key in the Python
> dict, fill in NULL

You can do it, but again you will need a dict subclass for it.


> 2.2
> everything else => as per-default with Psycopg
>
> ==
>
> Above should work with nested PG types (array of composite type with
> an attribute again composite type etc etc).

The composite caster already deals correctly with nested types: the
change to make it return dicts instead of tuples should be a few lines
of code.


> It should work with IN, OUT, INOUT parameters and array, setof, etc
> returning procedures.

Procedures with OUT parameters don't return a specific oid: they
return a generic "record" oid (2249).

    test=> create or replace function mysp (out a int, out b text) as
$$select 10::int, 'ciao'::text;$$ language sql;
    CREATE FUNCTION
    test=> select mysp(), pg_typeof(mysp());
       mysp    | pg_typeof
    -----------+-----------
     (10,ciao) | record
    (1 row)


If you want to return the identity of that record, you will have to
create a Postgres type and cast your function to that record: you can
then register a typecaster against its oid.

    test=> create type myspout as (a int, b text);
    CREATE TYPE

    -- Don't know if there's a better syntax for this
    test=> select (x.*)::myspout from mysp() as x;
         x
    -----------
     (10,ciao)
    (1 row)

In Python:

    In [2]: cnn = psycopg2.connect('dbname=test')

    In [5]: psycopg2.extras.register_composite('myspout', cnn)
    Out[5]: <psycopg2.extras.CompositeCaster object at 0x8fd672c>

    In [6]: cur = cnn.cursor()

    In [7]: cur.execute("select (x.*)::myspout from mysp() as x;")

    In [8]: cur.fetchone()[0]
    Out[8]: myspout(a=10, b='ciao')


> How do I tackle this? Or even more fundamental: is it sane / doable at all
> (using public Psycopg hooks only)?

The only impossible thing is the "PG target type" idea: there is no
knowledge about where a parameter will go in a query.

Hope this helps.

-- Daniele


В списке psycopg по дате отправления:

Предыдущее
От: Daniele Varrazzo
Дата:
Сообщение: Dealing with a change in Python 3.3 memoryview
Следующее
От: Ronan Dunklau
Дата:
Сообщение: Re: flexi adaption/casting scheme