]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed regression whereby the "annotation" system used by the ORM was leaking
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 29 Jan 2014 22:33:28 +0000 (17:33 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 29 Jan 2014 22:33:28 +0000 (17:33 -0500)
into the names used by standard functions in :mod:`sqlalchemy.sql.functions`,
such as ``func.coalesce()`` and ``func.max()``.  Using these functions
in ORM attributes and thus producing annotated versions of them could
corrupt the actual function name rendered in the SQL. [ticket:2927]

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/sql/functions.py
test/orm/test_query.py
test/sql/test_functions.py

index b6bc07847ef5076917c461da29039f210c05a726..8f1daf0037fb3669c23e4ee9896301486927bc25 100644 (file)
 .. changelog::
     :version: 0.9.2
 
+    .. change::
+        :tags: bug, sql
+        :tickets: 2927
+
+        Fixed regression whereby the "annotation" system used by the ORM was leaking
+        into the names used by standard functions in :mod:`sqlalchemy.sql.functions`,
+        such as ``func.coalesce()`` and ``func.max()``.  Using these functions
+        in ORM attributes and thus producing annotated versions of them could
+        corrupt the actual function name rendered in the SQL.
+
     .. change::
         :tags: bug, sql
         :tickets: 2924, 2848
index f300e24165049f29c66b8a69d57c3d10744230d6..a9b88b13b94dfac8feec3a4e91344bf45013eec0 100644 (file)
@@ -17,6 +17,7 @@ from .selectable import FromClause, Select
 from . import operators
 from .visitors import VisitableType
 from .. import util
+from . import annotation
 
 _registry = util.defaultdict(dict)
 
@@ -308,16 +309,16 @@ class Function(FunctionElement):
                                 _compared_to_type=self.type,
                                 unique=True)
 
-
 class _GenericMeta(VisitableType):
     def __init__(cls, clsname, bases, clsdict):
-        cls.name = name = clsdict.get('name', clsname)
-        cls.identifier = identifier = clsdict.get('identifier', name)
-        package = clsdict.pop('package', '_default')
-        # legacy
-        if '__return_type__' in clsdict:
-            cls.type = clsdict['__return_type__']
-        register_function(identifier, cls, package)
+        if annotation.Annotated not in cls.__mro__:
+            cls.name = name = clsdict.get('name', clsname)
+            cls.identifier = identifier = clsdict.get('identifier', name)
+            package = clsdict.pop('package', '_default')
+            # legacy
+            if '__return_type__' in clsdict:
+                cls.type = clsdict['__return_type__']
+            register_function(identifier, cls, package)
         super(_GenericMeta, cls).__init__(clsname, bases, clsdict)
 
 
@@ -407,7 +408,6 @@ class GenericFunction(util.with_metaclass(_GenericMeta, Function)):
         self.type = sqltypes.to_instance(
             kwargs.pop("type_", None) or getattr(self, 'type', None))
 
-
 register_function("cast", Cast)
 register_function("extract", Extract)
 
index fea2337caad5b719a919da322a0e6eebc4a1dcfa..64b8cfdc460ba8f874dab60e9c3028d84fd583c5 100644 (file)
@@ -348,6 +348,20 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
             checkparams={"name": "ed"}
         )
 
+    def test_col_prop_builtin_function(self):
+        class Foo(object):
+            pass
+
+        mapper(Foo, self.tables.users, properties={
+                'foob': column_property(func.coalesce(self.tables.users.c.name))
+            })
+
+        self.assert_compile(
+            select([Foo]).where(Foo.foob == 'somename').order_by(Foo.foob),
+            "SELECT users.id, users.name FROM users "
+            "WHERE coalesce(users.name) = :coalesce_1 ORDER BY coalesce(users.name)"
+        )
+
 class GetTest(QueryTest):
     def test_get(self):
         User = self.classes.User
index ee1d61f85e7d008c723d29c1d4fb669b2978fd6e..3e47e018bdc0173d0cae4444636c42b44a0d35bb 100644 (file)
@@ -76,6 +76,11 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
         ]:
             self.assert_compile(func.random(), ret, dialect=dialect)
 
+    def test_generic_annotation(self):
+        fn = func.coalesce('x', 'y')._annotate({"foo": "bar"})
+        self.assert_compile(
+            fn, "coalesce(:param_1, :param_2)"
+        )
     def test_custom_default_namespace(self):
         class myfunc(GenericFunction):
             pass