summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-08-22 12:47:13 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-08-22 12:47:13 -0400
commit8712ef2f81498fe59b9636ba150833d779e60781 (patch)
tree8e42639b148196a22d930fdef851b44686886279 /lib/sqlalchemy/sql
parente0a8030048f4ad0690d3084929441bda4c21aba2 (diff)
downloadsqlalchemy-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.py2
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py4
-rw-r--r--lib/sqlalchemy/sql/elements.py6
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py20
-rw-r--r--lib/sqlalchemy/sql/type_api.py3
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