]> 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 16:29:50 +0000 (11:29 -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

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 b031c1610ea416d42ba49afb23627baa99ddfe9d..08b1bb060f2511e794005b07861d0d556388e452 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 5a31173eca79a1f5671764b84b8ebf9267107f36..47fb6720ba60930e8b0a07df63776a56a29115aa 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 058c1dfd77edffbfe45ab2d398151b1504aa95f4..8d129a9986b73b0eeab66bdff3a6d345c219ca34 100644 (file)
@@ -151,13 +151,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) AS my_function_1 FROM t1",
+            dialect="sqlite",
+        )
+
+        eq_(stmt.selected_columns.keys(), [stmt._raw_columns[0].anon_label])
+
     def test_no_default_message(self):
         class MyThingy(ClauseElement):
             pass
@@ -167,7 +187,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,