From eea0f44bbdb759368996dcdb241e837c7c809fb9 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 14 Nov 2022 08:54:56 -0500 Subject: add informative exception context for literal render An informative re-raise is now thrown in the case where any "literal bindparam" render operation fails, indicating the value itself and the datatype in use, to assist in debugging when literal params are being rendered in a statement. Fixes: #8800 Change-Id: Id658f8b03359312353ddbb0c7563026239579f7b --- lib/sqlalchemy/sql/compiler.py | 22 ++++++++++++++++++---- lib/sqlalchemy/sql/sqltypes.py | 7 +------ lib/sqlalchemy/sql/util.py | 6 ++++++ 3 files changed, 25 insertions(+), 10 deletions(-) (limited to 'lib/sqlalchemy/sql') diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 97397e9cf..9a00afc91 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -63,6 +63,7 @@ from . import roles from . import schema from . import selectable from . import sqltypes +from . import util as sql_util from ._typing import is_column_element from ._typing import is_dml from .base import _from_objects @@ -1530,7 +1531,8 @@ class SQLCompiler(Compiled): replacement_expressions[ escaped_name ] = self.render_literal_bindparam( - parameter, render_literal_value=value + parameter, + render_literal_value=value, ) continue @@ -3154,10 +3156,22 @@ class SQLCompiler(Compiled): processor = type_._cached_literal_processor(self.dialect) if processor: - return processor(value) + try: + return processor(value) + except Exception as e: + raise exc.CompileError( + f"Could not render literal value " + f'"{sql_util._repr_single_value(value)}" ' + f"with datatype " + f"{type_}; see parent stack trace for " + "more detail." + ) from e + else: - raise NotImplementedError( - "Don't know how to literal-quote value %r" % value + raise exc.CompileError( + f"No literal value renderer is available for literal value " + f'"{sql_util._repr_single_value(value)}" ' + f"with datatype {type_}" ) def _truncate_bindparam(self, bindparam): diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 414ff03c3..b98a16b6f 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -3381,12 +3381,7 @@ class NullType(TypeEngine[None]): _isnull = True def literal_processor(self, dialect): - def process(value): - raise exc.CompileError( - "Don't know how to render literal SQL value: %r" % (value,) - ) - - return process + return None class Comparator(TypeEngine.Comparator[_T]): __slots__ = () diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 55c6a35f8..1f9944529 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -581,6 +581,12 @@ class _repr_base: return rep +def _repr_single_value(value): + rp = _repr_base() + rp.max_chars = 300 + return rp.trunc(value) + + class _repr_row(_repr_base): """Provide a string view of a row.""" -- cgit v1.2.1