On Jan 11, 2013, at 4:37 PM, Ryan Kelly wrote:
> I'm trying to figure out the correct way to use these array comparisons
> features specific to postgres, e.g.:
>
> select * from foo where 1 = any(bar);
>
> So I tried this:
>
> from sqlalchemy.sql.expression import func
> session.query(foo).filter(1 == func.any(foo.c.bar))
>
> But that didn't work, as I got this (essentially):
>
> select * from foo where any(bar) = 1;
reading this, it seemed extremely surprising that the = operator would suddenly
not be commutative. So I took a look at "ANY" in the PG docs, and I don't
really see this as the "=" operator anymore, it's a special PG operator that is
essentially "= ANY". So we can use op() for that. Then, to build this with
off-the-shelf pieces, we have to jump through the many hoops of this very
non-standard syntax:
from sqlalchemy.dialects import postgresql
from sqlalchemy import literal, tuple
from sqlalchemy.dialects.postgresql import array
literal(1).op("= ANY")(tuple_(array([foo.c.bar])))
then the NOT case (~ is equivalent to not_()):
expr = (~literal(1)).op("= ANY")(tuple_(array([foo.c.bar])))
seems to work:
print expr.compile(dialect=postgresql.dialect())
NOT %(param_1)s = ANY (ARRAY[foo.bar])
now that's cobbling it together with off the shelf pieces, which you could of
course throw into a function called any() to hide that boilerplate. But also,
we can make this into a construct where we can build up the string directly
(and this you can use to build anything without too much guessing):
from sqlalchemy.types import Boolean
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.compiler import OPERATORS
from sqlalchemy.sql.expression import ColumnElement, literal
class Any(ColumnElement):
type = Boolean()
def __init__(self, left, right, operator=operator.eq):
self.left = literal(left)
assert isinstance(right, array)
self.right = right
self.operator = operator
@compiles(Any, "postgresql")
def compile_any(element, compiler, **kw):
return "%s%sANY(%s)" % (
compiler.process(element.left),
OPERATORS[element.operator],
compiler.process(element.right)
)
print Any(1, array([foo.c.bar])).compile(dialect=postgresql.dialect())
# %(param_1)s = ANY(ARRAY[foo.bar])
print (~Any(1, array([foo.c.bar]))).compile(dialect=postgresql.dialect())
# NOT %(param_1)s = ANY(ARRAY[foo.bar])
These functions (ANY, ALL, etc.) should probably be provided as part of
sqlalchemy.dialects.postgresql to start with as well as be available as methods
from the ARRAY comparator.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.