diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-08-22 12:47:13 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-08-22 12:47:13 -0400 |
| commit | 8712ef2f81498fe59b9636ba150833d779e60781 (patch) | |
| tree | 8e42639b148196a22d930fdef851b44686886279 /lib/sqlalchemy/sql | |
| parent | e0a8030048f4ad0690d3084929441bda4c21aba2 (diff) | |
| download | sqlalchemy-8712ef2f81498fe59b9636ba150833d779e60781.tar.gz | |
- Added new checks for the common error case of passing mapped classes
or mapped instances into contexts where they are interpreted as
SQL bound parameters; a new exception is raised for this.
fixes #3321
Diffstat (limited to 'lib/sqlalchemy/sql')
| -rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/default_comparator.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 6 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/sqltypes.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/type_api.py | 3 |
5 files changed, 25 insertions, 10 deletions
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index d3c46e643..4717b777f 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -281,6 +281,8 @@ class _CompileLabel(visitors.Visitable): def type(self): return self.element.type + def self_group(self, **kw): + return self class SQLCompiler(Compiled): diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py index 09f639163..125fec33f 100644 --- a/lib/sqlalchemy/sql/default_comparator.py +++ b/lib/sqlalchemy/sql/default_comparator.py @@ -15,7 +15,7 @@ from .elements import BindParameter, True_, False_, BinaryExpression, \ Null, _const_expr, _clause_element_as_expr, \ ClauseList, ColumnElement, TextClause, UnaryExpression, \ collate, _is_literal, _literal_as_text, ClauseElement, and_, or_, \ - Slice + Slice, Visitable from .selectable import SelectBase, Alias, Selectable, ScalarSelect @@ -304,7 +304,7 @@ def _check_literal(expr, operator, other): if isinstance(other, (SelectBase, Alias)): return other.as_scalar() - elif not isinstance(other, (ColumnElement, TextClause)): + elif not isinstance(other, Visitable): return expr._bind_param(operator, other) else: return other diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 00c749b40..e2d81afc1 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -1145,8 +1145,7 @@ class BindParameter(ColumnElement): _compared_to_type.coerce_compared_value( _compared_to_operator, value) else: - self.type = type_api._type_map.get(type(value), - type_api.NULLTYPE) + self.type = type_api._resolve_value_to_type(value) elif isinstance(type_, type): self.type = type_() else: @@ -1161,8 +1160,7 @@ class BindParameter(ColumnElement): cloned.callable = None cloned.required = False if cloned.type is type_api.NULLTYPE: - cloned.type = type_api._type_map.get(type(value), - type_api.NULLTYPE) + cloned.type = type_api._resolve_value_to_type(value) return cloned @property diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index ec7dea300..b5c575143 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -9,7 +9,6 @@ """ -import collections import datetime as dt import codecs @@ -18,6 +17,7 @@ from .elements import quoted_name, type_coerce, _defer_name from .. import exc, util, processors from .base import _bind_or_error, SchemaEventTarget from . import operators +from .. import inspection from .. import event from ..util import pickle import decimal @@ -1736,6 +1736,21 @@ else: _type_map[unicode] = Unicode() _type_map[str] = String() +_type_map_get = _type_map.get + + +def _resolve_value_to_type(value): + _result_type = _type_map_get(type(value), False) + if _result_type is False: + # use inspect() to detect SQLAlchemy built-in + # objects. + insp = inspection.inspect(value, False) + if insp is not None: + raise exc.ArgumentError( + "Object %r is not legal as a SQL literal value" % value) + return NULLTYPE + else: + return _result_type # back-assign to type_api from . import type_api @@ -1745,6 +1760,5 @@ type_api.INTEGERTYPE = INTEGERTYPE type_api.NULLTYPE = NULLTYPE type_api.MATCHTYPE = MATCHTYPE type_api.INDEXABLE = Indexable -type_api._type_map = _type_map - +type_api._resolve_value_to_type = _resolve_value_to_type TypeEngine.Comparator.BOOLEANTYPE = BOOLEANTYPE diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py index c4e830b7f..b9826e585 100644 --- a/lib/sqlalchemy/sql/type_api.py +++ b/lib/sqlalchemy/sql/type_api.py @@ -21,6 +21,7 @@ NULLTYPE = None STRINGTYPE = None MATCHTYPE = None INDEXABLE = None +_resolve_value_to_type = None class TypeEngine(Visitable): @@ -454,7 +455,7 @@ class TypeEngine(Visitable): end-user customization of this behavior. """ - _coerced_type = _type_map.get(type(value), NULLTYPE) + _coerced_type = _resolve_value_to_type(value) if _coerced_type is NULLTYPE or _coerced_type._type_affinity \ is self._type_affinity: return self |
