]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed an 0.9 regression where the automatic aliasing applied by
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 23 Jan 2014 19:49:04 +0000 (14:49 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 23 Jan 2014 19:49:04 +0000 (14:49 -0500)
:class:`.Query` and in other situations where selects or joins
were aliased (such as joined table inheritance) could fail if a
user-defined :class:`.Column` subclass were used in the expression.
In this case, the subclass would fail to propagate ORM-specific
"annotations" along needed by the adaptation.  The "expression
annotations" system has been corrected to account for this case.
[ticket:2918]

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/sql/annotation.py
test/orm/inheritance/test_assorted_poly.py
test/sql/test_selectable.py

index 893e3970fee263bfbd22f76d026bce760f9aa327..a321f2747274455b0ca5aa0a3a959e3503e21a69 100644 (file)
 .. changelog::
     :version: 0.9.2
 
+    .. change::
+        :tags: bug, orm
+        :tickets: 2918
+
+        Fixed an 0.9 regression where the automatic aliasing applied by
+        :class:`.Query` and in other situations where selects or joins
+        were aliased (such as joined table inheritance) could fail if a
+        user-defined :class:`.Column` subclass were used in the expression.
+        In this case, the subclass would fail to propagate ORM-specific
+        "annotations" along needed by the adaptation.  The "expression
+        annotations" system has been corrected to account for this case.
+
     .. change::
         :tags: feature, orm
 
index 6bd465e9c79d751190f84c8c1436f8adcb540498..23fe9315a52a9bebf41af64dd04f5a888a5fe0c6 100644 (file)
@@ -229,7 +229,6 @@ class Query(object):
         have been applied within this query."""
 
         adapters = []
-
         # do we adapt all expression elements or only those
         # tagged as 'ORM' constructs ?
         orm_only = getattr(self, '_orm_only_adapt', orm_only)
index b5b7849d21e7c5c52f1f81ea6a5c96a1ecd07563..11b06667509252e6a5f71146f6564e1efa66c1f2 100644 (file)
@@ -167,9 +167,18 @@ def _new_annotation_type(cls, base_cls):
         return cls
     elif cls in annotated_classes:
         return annotated_classes[cls]
+
+    for super_ in cls.__mro__:
+        # check if an Annotated subclass more specific than
+        # the given base_cls is already registered, such
+        # as AnnotatedColumnElement.
+        if super_ in annotated_classes:
+            base_cls = annotated_classes[super_]
+            break
+
     annotated_classes[cls] = anno_cls = type(
-                                "Annotated%s" % cls.__name__,
-                                (base_cls, cls), {})
+                            "Annotated%s" % cls.__name__,
+                            (base_cls, cls), {})
     globals()["Annotated%s" % cls.__name__] = anno_cls
     return anno_cls
 
index da0e3b1a3c640dcf56ca014ccaa019f75009d7a2..c3ed73c9ca0ba60d9b3ae92c6184bb946ab65d6a 100644 (file)
@@ -1521,3 +1521,39 @@ class Ticket2419Test(fixtures.DeclarativeMappedTest):
             q.all(),
             [(b, True)]
         )
+
+class ColSubclassTest(fixtures.DeclarativeMappedTest, testing.AssertsCompiledSQL):
+    """Test [ticket:2918]'s test case."""
+
+    run_create_tables = None
+    __dialect__ = 'default'
+
+    @classmethod
+    def setup_classes(cls):
+        from sqlalchemy.schema import Column
+        Base = cls.DeclarativeBasic
+
+        class A(Base):
+            __tablename__ = 'a'
+
+            id = Column(Integer, primary_key=True)
+
+        class MySpecialColumn(Column):
+            pass
+
+        class B(A):
+            __tablename__ = 'b'
+
+            id = Column(ForeignKey('a.id'), primary_key=True)
+            x = MySpecialColumn(String)
+
+    def test_polymorphic_adaptation(self):
+        A, B = self.classes.A, self.classes.B
+
+        s = Session()
+        self.assert_compile(
+            s.query(A).join(B).filter(B.x == 'test'),
+            "SELECT a.id AS a_id FROM a JOIN "
+            "(a AS a_1 JOIN b AS b_1 ON a_1.id = b_1.id) "
+            "ON a.id = b_1.id WHERE b_1.x = :x_1"
+        )
index 8c7bf43b04c405c3d528963c1d7dc4f87b97cc9c..3c29d9784e5ed432a39b76bf712cabaa5f747b7a 100644 (file)
@@ -1497,6 +1497,29 @@ class AnnotationsTest(fixtures.TestBase):
         annot_2 = s1._annotate({})
         assert isinstance(annot_2.c.foo, Column)
 
+    def test_custom_construction_correct_anno_subclass(self):
+        # [ticket:2918]
+        from sqlalchemy.schema import Column
+        from sqlalchemy.sql.elements import AnnotatedColumnElement
+        class MyColumn(Column):
+            pass
+
+        assert isinstance(
+                    MyColumn('x', Integer)._annotate({"foo": "bar"}),
+                    AnnotatedColumnElement)
+
+    def test_custom_construction_correct_anno_expr(self):
+        # [ticket:2918]
+        from sqlalchemy.schema import Column
+        class MyColumn(Column):
+            pass
+
+        col = MyColumn('x', Integer)
+        binary_1 = col == 5
+        col_anno = MyColumn('x', Integer)._annotate({"foo": "bar"})
+        binary_2 = col_anno == 5
+        eq_(binary_2.left._annotations, {"foo": "bar"})
+
     def test_annotated_corresponding_column(self):
         table1 = table('table1', column("col1"))