On 04/14/2017 03:49 AM, Lele Gaifax wrote:
mike bayer <mike...@zzzcomputing.com> writes:

However, we can improve upon the situation by adding a CAST, which seems to
send psycopg2 a little more information:

row = s.query(
    cast(
        func.array_agg(tuple_(A.x, A.y)), ARRAY(Unicode)
    )
).scalar()
print row

For one new application I'm writing I chose the "new" asyncpg[1] driver.

The major problem I hit was related to this CASTing need ([2] and [3]): due to
the way asyncpg talks to the database, in some cases it cannot guess the right
format because on the PG side things may be ambiguous (e.g. when an operator
is overloaded and works on different types of data, like the @> operator I
used in [3]).

To solve that I wrote a very simple custom PGDialect+PGCompiler duo to handle
a new "paramstyle", basically translating the following SA construct::

  oneyearago = date.today() - timedelta(days=365)
  q = sa.select([users.c.id]) \
      .where(users.c.name == 'test') \
      .where(users.c.validity.contains(oneyearago))

into this SQL statement::

  SELECT auth.users.id
  FROM auth.users
  WHERE auth.users.name = $1::VARCHAR(64) AND (auth.users.validity @> $2::DATE)

I'd note that the above seems to *not* be casting "users.id" in the SELECT. The problem we are having with the array/json/tuple etc. is that the typing information is not in the result description, so that's really the only place a CAST is sometimes needed when dealing with psycopg2.

I'm not sure what asyncpgs' issue is with input types, there should not be a reason to parse the SQL statement and look at operators, you have the Python type of object coming in, you use that to determine the general behavior to take with basic types like strings and dates.

Additionally, the Python DBAPI already supports a method for dealing with the general problem of "what types should be applied to bound parameters?", it's called setinputsizes: https://www.python.org/dev/peps/pep-0249/#id26 . SQLAlchemy has generalized support for this for DBAPIs that require it, which currently includes cx_Oracle.

but that's pep-249, which asyncpg unfortunately simply disregards.

So, what you have above is very easy to accomplish with a simple @compiles recipe that merely adds CAST to the rendering of bindparam():


from sqlalchemy import *
from sqlalchemy.orm import *
from datetime import date, timedelta
from sqlalchemy.sql.elements import BindParameter
from sqlalchemy.ext.compiler import compiles


@compiles(BindParameter)
def inject_cast(element, compiler, _already_wrapped=False, **kw):
    if _already_wrapped:
        return compiler.visit_bindparam(element, **kw)
    else:
        return compiler.process(
            cast(element, element.type), _already_wrapped=True, **kw)


m = MetaData()

users = Table(
    'users', m,
    Column('id', Integer, primary_key=True),
    Column('name', String(64)),
    Column('validity', DateTime)
)


oneyearago = date.today() - timedelta(days=365)
q = select([users.c.id]) \
   .where(users.c.name == 'test') \
   .where(users.c.validity.contains(oneyearago))


print q


SELECT users.id
FROM users
WHERE users.name = CAST(:name_1 AS VARCHAR(64)) AND (users.validity LIKE '%' + CAST(:validity_1 AS DATE) || '%')









Since it eventually boils down to overriding a single "private" method
(_apply_numbered_params), I wonder if it would be a welcome addition to
standard SA, maybe allowing to set "paramstyle" to an user's arbitrary
callable.

What do you think?

Thank you,
ciao, lele.

[1] https://github.com/MagicStack/asyncpg
[2] https://github.com/CanopyTax/asyncpgsa/issues/12
[3] https://github.com/MagicStack/asyncpg/issues/32


--
SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to