summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-01-14 11:18:58 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2021-01-14 11:29:50 -0500
commitb4c6cfc2fde6c652e79ca157f8023a3f8941bc3c (patch)
tree8d494f71cb634060584555e6bc0aadbc8e3dfa3a
parent0a41f9bea6602c52c59af0f7b572308b2c2b27ab (diff)
downloadsqlalchemy-b4c6cfc2fde6c652e79ca157f8023a3f8941bc3c.tar.gz
Use UnsupportedCompilationError for no default compiler
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
-rw-r--r--doc/build/changelog/unreleased_13/5836.rst13
-rw-r--r--lib/sqlalchemy/exc.py6
-rw-r--r--lib/sqlalchemy/ext/compiler.py16
-rw-r--r--test/ext/test_compiler.py25
4 files changed, 49 insertions, 11 deletions
diff --git a/doc/build/changelog/unreleased_13/5836.rst b/doc/build/changelog/unreleased_13/5836.rst
new file mode 100644
index 000000000..0ddfb9a8d
--- /dev/null
+++ b/doc/build/changelog/unreleased_13/5836.rst
@@ -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`.
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index b031c1610..08b1bb060 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -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 "")
)
diff --git a/lib/sqlalchemy/ext/compiler.py b/lib/sqlalchemy/ext/compiler.py
index 5a31173ec..47fb6720b 100644
--- a/lib/sqlalchemy/ext/compiler.py
+++ b/lib/sqlalchemy/ext/compiler.py
@@ -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,
)
diff --git a/test/ext/test_compiler.py b/test/ext/test_compiler.py
index 058c1dfd7..8d129a998 100644
--- a/test/ext/test_compiler.py
+++ b/test/ext/test_compiler.py
@@ -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,