From: Mike Bayer Date: Fri, 14 Mar 2014 00:03:48 +0000 (-0400) Subject: - Added support for literal rendering of boolean values, e.g. X-Git-Tag: rel_0_9_4~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=11003828cb65693f6b85818552cab652d8ae1294;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Added support for literal rendering of boolean values, e.g. "true" / "false" or "1" / "0". - added Boolean tests to the test suite --- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 10879e2caf..d082a29f65 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,12 @@ .. changelog:: :version: 0.9.4 + .. change:: + :tags: feature, sql + + Added support for literal rendering of boolean values, e.g. + "true" / "false" or "1" / "0". + .. change:: :tags: feature, sql diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index ec1d66459c..f3468ebc24 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -1274,6 +1274,15 @@ class Boolean(TypeEngine, SchemaType): def python_type(self): return bool + def literal_processor(self, dialect): + if dialect.supports_native_boolean: + def process(value): + return "true" if value else "false" + else: + def process(value): + return str(1 if value else 0) + return process + def bind_processor(self, dialect): if dialect.supports_native_boolean: return None diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py index f7e0270c81..9f0d9b7aab 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -5,7 +5,7 @@ from ..assertions import eq_ from ..config import requirements from sqlalchemy import Integer, Unicode, UnicodeText, select from sqlalchemy import Date, DateTime, Time, MetaData, String, \ - Text, Numeric, Float, literal + Text, Numeric, Float, literal, Boolean from ..schema import Table, Column from ... import testing import decimal @@ -519,10 +519,79 @@ class NumericTest(_LiteralRoundTripFixture, fixtures.TestBase): ) +class BooleanTest(_LiteralRoundTripFixture, fixtures.TablesTest): + __multiple__ = True + + @classmethod + def define_tables(cls, metadata): + Table('boolean_table', metadata, + Column('id', Integer, primary_key=True, autoincrement=False), + Column('value', Boolean), + Column('unconstrained_value', Boolean(create_constraint=False)), + ) + + def test_render_literal_bool(self): + self._literal_round_trip( + Boolean(), + [True, False], + [True, False] + ) + + def test_round_trip(self): + boolean_table = self.tables.boolean_table + + config.db.execute( + boolean_table.insert(), + { + 'id': 1, + 'value': True, + 'unconstrained_value': False + } + ) + + row = config.db.execute( + select([ + boolean_table.c.value, + boolean_table.c.unconstrained_value + ]) + ).first() + + eq_( + row, + (True, False) + ) + assert isinstance(row[0], bool) + + def test_null(self): + boolean_table = self.tables.boolean_table + + config.db.execute( + boolean_table.insert(), + { + 'id': 1, + 'value': None, + 'unconstrained_value': None + } + ) + + row = config.db.execute( + select([ + boolean_table.c.value, + boolean_table.c.unconstrained_value + ]) + ).first() + + eq_( + row, + (None, None) + ) + + + __all__ = ('UnicodeVarcharTest', 'UnicodeTextTest', 'DateTest', 'DateTimeTest', 'TextTest', 'NumericTest', 'IntegerTest', 'DateTimeHistoricTest', 'DateTimeCoercedToDateTimeTest', 'TimeMicrosecondsTest', 'TimeTest', 'DateTimeMicrosecondsTest', - 'DateHistoricTest', 'StringTest') + 'DateHistoricTest', 'StringTest', 'BooleanTest') diff --git a/test/sql/test_types.py b/test/sql/test_types.py index 887e768274..c7e6298378 100644 --- a/test/sql/test_types.py +++ b/test/sql/test_types.py @@ -1692,52 +1692,18 @@ class IntervalTest(fixtures.TestBase, AssertsExecutionResults): eq_(row['non_native_interval'], None) -class BooleanTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL): +class BooleanTest(fixtures.TablesTest, AssertsExecutionResults, AssertsCompiledSQL): + """test edge cases for booleans. Note that the main boolean test suite + is now in testing/suite/test_types.py + + """ @classmethod - def setup_class(cls): - global bool_table - metadata = MetaData(testing.db) - bool_table = Table('booltest', metadata, + def define_tables(cls, metadata): + Table('boolean_table', metadata, Column('id', Integer, primary_key=True, autoincrement=False), Column('value', Boolean), Column('unconstrained_value', Boolean(create_constraint=False)), ) - bool_table.create() - - @classmethod - def teardown_class(cls): - bool_table.drop() - - def teardown(self): - bool_table.delete().execute() - - def test_boolean(self): - bool_table.insert().execute(id=1, value=True) - bool_table.insert().execute(id=2, value=False) - bool_table.insert().execute(id=3, value=True) - bool_table.insert().execute(id=4, value=True) - bool_table.insert().execute(id=5, value=True) - bool_table.insert().execute(id=6, value=None) - - res = select([bool_table.c.id, bool_table.c.value]).where( - bool_table.c.value == True - ).order_by(bool_table.c.id).execute().fetchall() - eq_(res, [(1, True), (3, True), (4, True), (5, True)]) - - res2 = select([bool_table.c.id, bool_table.c.value]).where( - bool_table.c.value == False).execute().fetchall() - eq_(res2, [(2, False)]) - - res3 = select([bool_table.c.id, bool_table.c.value]).\ - order_by(bool_table.c.id).\ - execute().fetchall() - eq_(res3, [(1, True), (2, False), - (3, True), (4, True), - (5, True), (6, None)]) - - # ensure we're getting True/False, not just ints - assert res3[0][1] is True - assert res3[1][1] is False @testing.fails_on('mysql', "The CHECK clause is parsed but ignored by all storage engines.") @@ -1747,12 +1713,12 @@ class BooleanTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiledSQL def test_constraint(self): assert_raises((exc.IntegrityError, exc.ProgrammingError), testing.db.execute, - "insert into booltest (id, value) values(1, 5)") + "insert into boolean_table (id, value) values(1, 5)") @testing.skip_if(lambda: testing.db.dialect.supports_native_boolean) def test_unconstrained(self): testing.db.execute( - "insert into booltest (id, unconstrained_value) values (1, 5)") + "insert into boolean_table (id, unconstrained_value) values (1, 5)") def test_non_native_constraint_custom_type(self): class Foob(object):