diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-10-11 17:56:14 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-10-12 13:39:06 -0400 |
| commit | c63658973c95996b73251e0953cf4c598cdb262b (patch) | |
| tree | 1d9aba0b27f2f096369b95cf0a6dbb95f87825ac /lib/sqlalchemy/sql | |
| parent | 3306d4d34d0f78bdda6f85a0a3be81beb2850676 (diff) | |
| download | sqlalchemy-c63658973c95996b73251e0953cf4c598cdb262b.tar.gz | |
Disallow all ambiguous boolean values for Boolean
In release 1.1, the :class:`.Boolean` type was broken in that
boolean coercion via ``bool()`` would occur for backends that did not
feature "native boolean", but would not occur for native boolean backends,
meaning the string ``"0"`` now behaved inconsistently. After a poll, a
consensus was reached that non-boolean values should be raising an error,
especially in the ambiguous case of string ``"0"``; so the :class:`.Boolean`
datatype will now raise ``ValueError`` if an incoming value is not
within the range ``None, True, False, 1, 0``.
Change-Id: If70c4f79c266f0dd1a0306c0ffe7acb9c66c4cc3
Fixes: #4102
Diffstat (limited to 'lib/sqlalchemy/sql')
| -rw-r--r-- | lib/sqlalchemy/sql/sqltypes.py | 41 |
1 files changed, 37 insertions, 4 deletions
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 061f8de96..2a30bf832 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -1581,9 +1581,22 @@ class Boolean(Emulated, TypeEngine, SchemaType): """A bool datatype. - Boolean typically uses BOOLEAN or SMALLINT on the DDL side, and on + :class:`.Boolean` typically uses BOOLEAN or SMALLINT on the DDL side, and on the Python side deals in ``True`` or ``False``. + The :class:`.Boolean` datatype currently has two levels of assertion + that the values persisted are simple true/false values. For all + backends, only the Python values ``None``, ``True``, ``False``, ``1`` + or ``0`` are accepted as parameter values. For those backends that + don't support a "native boolean" datatype, a CHECK constraint is also + created on the target column. Production of the CHECK constraint + can be disabled by passing the :paramref:`.Boolean.create_constraint` + flag set to ``False``. + + .. versionchanged:: 1.2 the :class:`.Boolean` datatype now asserts that + incoming Python values are already in pure boolean form. + + """ __visit_name__ = 'boolean' @@ -1631,20 +1644,40 @@ class Boolean(Emulated, TypeEngine, SchemaType): def python_type(self): return bool + _strict_bools = frozenset([None, True, False]) + + def _strict_as_bool(self, value): + if value not in self._strict_bools: + if not isinstance(value, int): + raise TypeError( + "Not a boolean value: %r" % value) + else: + raise ValueError( + "Value %r is not None, True, or False" % value) + return value + def literal_processor(self, dialect): compiler = dialect.statement_compiler(dialect, None) true = compiler.visit_true(None) false = compiler.visit_false(None) def process(value): - return true if value else false + return true if self._strict_as_bool(value) else false return process def bind_processor(self, dialect): + _strict_as_bool = self._strict_as_bool if dialect.supports_native_boolean: - return None + _coerce = bool else: - return processors.boolean_to_int + _coerce = int + + def process(value): + value = _strict_as_bool(value) + if value is not None: + value = _coerce(value) + return value + return process def result_processor(self, dialect, coltype): if dialect.supports_native_boolean: |
