]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Use UnsupportedCompilationError for no default compiler
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 14 Jan 2021 16:18:58 +0000 (11:18 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 14 Jan 2021 18:05:52 +0000 (13:05 -0500)
Fixed issue where the stringification that is sometimes called when
attempting to generate the "key" for the ``.c`` collection on a selectable
would fail if the column were an unlabeled custom SQL construct using the
``sqlalchemy.ext.compiler`` extension, and did not provide a default
compilation form; while this seems like an unusual case, it can get invoked
for some ORM scenarios such as when the expression is used in an "order by"
in combination with joined eager loading.  The issue is that the lack of a
default compiler function was raising :class:`.CompileError` and not
:class:`.UnsupportedCompilationError`.

Fixes: #5836
Change-Id: I5af243b2c70c7dcca4b212a3869c3017a50c132b
(cherry picked from commit b4c6cfc2fde6c652e79ca157f8023a3f8941bc3c)

doc/build/changelog/unreleased_13/5836.rst [new file with mode: 0644]
lib/sqlalchemy/exc.py
lib/sqlalchemy/ext/compiler.py
test/ext/test_compiler.py

diff --git a/doc/build/changelog/unreleased_13/5836.rst b/doc/build/changelog/unreleased_13/5836.rst
new file mode 100644 (file)
index 0000000..0ddfb9a
--- /dev/null
@@ -0,0 +1,13 @@
+.. change::
+    :tags: bug, ext
+    :tickets: 5836
+
+    Fixed issue where the stringification that is sometimes called when
+    attempting to generate the "key" for the ``.c`` collection on a selectable
+    would fail if the column were an unlabeled custom SQL construct using the
+    ``sqlalchemy.ext.compiler`` extension, and did not provide a default
+    compilation form; while this seems like an unusual case, it can get invoked
+    for some ORM scenarios such as when the expression is used in an "order by"
+    in combination with joined eager loading.  The issue is that the lack of a
+    default compiler function was raising :class:`.CompileError` and not
+    :class:`.UnsupportedCompilationError`.
index 4d6121da3cbc3c3ad5911a252791528e336118bb..aa3d8b62ac2a3e324617d07a265e70a93fd6d5f0 100644 (file)
@@ -179,10 +179,10 @@ class UnsupportedCompilationError(CompileError):
 
     code = "l7de"
 
-    def __init__(self, compiler, element_type):
+    def __init__(self, compiler, element_type, message=None):
         super(UnsupportedCompilationError, self).__init__(
-            "Compiler %r can't render element of type %s"
-            % (compiler, element_type)
+            "Compiler %r can't render element of type %s%s"
+            % (compiler, element_type, ": %s" % message if message else "")
         )
 
 
index d1c6f4385bd62dfa9cbddc985d4030e29721d206..8045d3d4fca9c6f9908b1059c54f8e1a3cd64b25 100644 (file)
@@ -425,9 +425,11 @@ def compiles(class_, *specs):
                         return existing_dispatch(element, compiler, **kw)
                     except exc.UnsupportedCompilationError as uce:
                         util.raise_(
-                            exc.CompileError(
-                                "%s construct has no default "
-                                "compilation handler." % type(element)
+                            exc.UnsupportedCompilationError(
+                                compiler,
+                                type(element),
+                                message="%s construct has no default "
+                                "compilation handler." % type(element),
                             ),
                             from_=uce,
                         )
@@ -476,9 +478,11 @@ class _dispatcher(object):
                 fn = self.specs["default"]
             except KeyError as ke:
                 util.raise_(
-                    exc.CompileError(
-                        "%s construct has no default "
-                        "compilation handler." % type(element)
+                    exc.UnsupportedCompilationError(
+                        compiler,
+                        type(element),
+                        message="%s construct has no default "
+                        "compilation handler." % type(element),
                     ),
                     replace_context=ke,
                 )
index ac6af0a96daa16fbb08a1ddb88ae44e1ba827d5a..7d844b018cfdea6c8005f04947a709778ed28afc 100644 (file)
@@ -148,13 +148,33 @@ class UserDefinedTest(fixtures.TestBase, AssertsCompiledSQL):
             return "mythingy"
 
         assert_raises_message(
-            exc.CompileError,
+            exc.UnsupportedCompilationError,
             "<class 'test.ext.test_compiler..*MyThingy'> "
             "construct has no default compilation handler.",
             str,
             MyThingy(),
         )
 
+    def test_no_default_proxy_generation(self):
+        class my_function(FunctionElement):
+            name = "my_function"
+            type = Numeric()
+
+        @compiles(my_function, "sqlite")
+        def sqlite_my_function(element, compiler, **kw):
+            return "my_function(%s)" % compiler.process(element.clauses, **kw)
+
+        t1 = table("t1", column("q"))
+        stmt = select([my_function(t1.c.q)])
+
+        self.assert_compile(
+            stmt,
+            "SELECT my_function(t1.q) FROM t1",
+            dialect="sqlite",
+        )
+
+        eq_(stmt.c.keys(), [stmt._raw_columns[0].anon_label])
+
     def test_no_default_message(self):
         class MyThingy(ClauseElement):
             pass
@@ -164,7 +184,8 @@ class UserDefinedTest(fixtures.TestBase, AssertsCompiledSQL):
             return "mythingy"
 
         assert_raises_message(
-            exc.CompileError,
+            exc.UnsupportedCompilationError,
+            "Compiler .*StrSQLCompiler.* can't .* "
             "<class 'test.ext.test_compiler..*MyThingy'> "
             "construct has no default compilation handler.",
             str,