tuple_() needs to record heterogeneous types

Issue #2977 resolved
Mike Bayer repo owner created an issue
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class HashTest(Base):
    __tablename__ = 'hash_test'

    hash_val = Column(Binary, primary_key=True)
    hash_type = Column(String, primary_key=True)

e = create_engine("postgresql://scott@localhost/test", echo=True)

Base.metadata.drop_all(e)
Base.metadata.create_all(e)

s = Session(e)
s.add_all([
    HashTest(hash_val='im binary 1', hash_type='im string 1'),
    HashTest(hash_val='im binary 2', hash_type='im string 2'),
])
s.commit()

filter_cols = tuple_(HashTest.hash_val, HashTest.hash_type)
requested_cols = ('im binary 1', 'im string 1')

print s.query(HashTest).filter(filter_cols.in_([requested_cols])).all()

patch:

diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index f2ce061..96c51bd 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -1859,6 +1859,8 @@ class Tuple(ClauseList, ColumnElement):
         self.type = kw.pop('type_', None)
         if self.type is None:
             self.type = _type_from_args(clauses)
+        self._type_tuple = [arg.type for arg in clauses]
+
         super(Tuple, self).__init__(*clauses, **kw)

     @property
@@ -1868,8 +1870,8 @@ class Tuple(ClauseList, ColumnElement):
     def _bind_param(self, operator, obj):
         return Tuple(*[
             BindParameter(None, o, _compared_to_operator=operator,
-                             _compared_to_type=self.type, unique=True)
-            for o in obj
+                             _compared_to_type=type_, unique=True)
+            for o, type_ in zip(obj, self._type_tuple)
         ]).self_group()

consider that we should try to whack ".type" off of Tuple entirely as the type system can't represent that right now.

Comments (4)

  1. Mike Bayer reporter
    • Fixed bug in :func:.tuple_ construct where the "type" of essentially the first SQL expression would be applied as the "comparison type" to a compared tuple value; this has the effect in some cases of an inappropriate "type coersion" occurring, such as when a tuple that has a mix of String and Binary values improperly coerces target values to Binary even though that's not what they are on the left side. :func:.tuple_ now expects heterogeneous types within its list of values. fixes #2977

    → <<cset e21cd0d95fb6>>

  2. Mike Bayer reporter
    • Fixed bug in :func:.tuple_ construct where the "type" of essentially the first SQL expression would be applied as the "comparison type" to a compared tuple value; this has the effect in some cases of an inappropriate "type coersion" occurring, such as when a tuple that has a mix of String and Binary values improperly coerces target values to Binary even though that's not what they are on the left side. :func:.tuple_ now expects heterogeneous types within its list of values. fixes #2977

    Conflicts: lib/sqlalchemy/sql/elements.py test/sql/test_operators.py

    → <<cset 3ba1385520a0>>

  3. Log in to comment