--- /dev/null
+.. 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`.
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 "")
)
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,
)
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,
)
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
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,