From dbdae3a75512bddbf0b75ea6354fb3fc4bdb53cf Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 26 Sep 2014 08:43:50 +0200 Subject: [PATCH] Fixed #22738 -- Abstracted boolean field type introspection Thanks maxi for the report, Shai Berger for his help with the patch and Tim Graham for the review. --- django/db/backends/__init__.py | 4 ++-- django/db/backends/mysql/base.py | 2 +- django/db/backends/oracle/base.py | 28 ++++++++++++++-------------- tests/inspectdb/tests.py | 9 +++++---- tests/schema/tests.py | 11 ++--------- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9873d9cba6..8bdf6eed3c 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -614,8 +614,8 @@ class BaseDatabaseFeatures(object): # Can the backend introspect an BinaryField, instead of an TextField? can_introspect_binary_field = True - # Can the backend introspect an BooleanField, instead of an IntegerField? - can_introspect_boolean_field = True + # What is the type returned when the backend introspect a BooleanField? + introspected_boolean_field_type = 'BooleanField' # Can the backend introspect an DecimalField, instead of an FloatField? can_introspect_decimal_field = True diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 7223780081..0bd9c16e56 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -178,7 +178,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supports_regex_backreferencing = False supports_date_lookup_using_string = False can_introspect_binary_field = False - can_introspect_boolean_field = False + introspected_boolean_field_type = 'IntegerField' can_introspect_small_integer_field = True supports_timezones = False requires_explicit_null_ordering_when_grouping = True diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 083ba18daa..c9a59d3970 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -125,6 +125,20 @@ class DatabaseFeatures(BaseDatabaseFeatures): # select for update with limit can be achieved on Oracle, but not with the current backend. supports_select_for_update_with_limit = False + @cached_property + def introspected_boolean_field_type(self): + """ + Some versions of Oracle -- we've seen this on 11.2.0.1 and suspect + it goes back -- have a weird bug where, when an integer column is + defined with a default, its precision is later reported on introspection + as 0, regardless of the real precision. For Django introspection, this + means that such columns are reported as IntegerField even if they are + really BigIntegerField or BooleanField. + + The bug is solved in Oracle 11.2.0.2 and up. + """ + return 'IntegerField' if self.connection.oracle_full_version < '11.2.0.2' else 'BooleanField' + class DatabaseOperations(BaseDatabaseOperations): compiler_module = "django.db.backends.oracle.compiler" @@ -735,20 +749,6 @@ class DatabaseWrapper(BaseDatabaseWrapper): except ValueError: return None - @cached_property - def version_has_default_introspection_bug(self): - """ - Some versions of Oracle -- we've seen this on 11.2.0.1 and suspect - it goes back -- have a weird bug where, when an integer column is - defined with a default, its precision is later reported on introspection - as 0, regardless of the real precision. For Django introspection, this - means that such columns are reported as IntegerField even if they are - really BigIntegerField or BooleanField. - - The bug is solved in Oracle 11.2.0.2 and up. - """ - return self.oracle_full_version < '11.2.0.2' - class OracleParam(object): """ diff --git a/tests/inspectdb/tests.py b/tests/inspectdb/tests.py index fb72b3756f..8b187191ae 100644 --- a/tests/inspectdb/tests.py +++ b/tests/inspectdb/tests.py @@ -87,18 +87,19 @@ class InspectDBTestCase(TestCase): else: assertFieldType('big_int_field', "models.IntegerField()") - if connection.features.can_introspect_boolean_field: + if connection.features.introspected_boolean_field_type == 'BooleanField': assertFieldType('bool_field', "models.BooleanField()") if connection.features.can_introspect_null: assertFieldType('null_bool_field', "models.NullBooleanField()") else: assertFieldType('null_bool_field', "models.BooleanField()") else: - assertFieldType('bool_field', "models.IntegerField()") + field_type = connection.features.introspected_boolean_field_type + assertFieldType('bool_field', "models.{}()".format(field_type)) if connection.features.can_introspect_null: - assertFieldType('null_bool_field', "models.IntegerField(blank=True, null=True)") + assertFieldType('null_bool_field', "models.{}(blank=True, null=True)".format(field_type)) else: - assertFieldType('null_bool_field', "models.IntegerField()") + assertFieldType('null_bool_field', "models.{}()".format(field_type)) if connection.features.can_introspect_decimal_field: assertFieldType('decimal_field', "models.DecimalField(max_digits=6, decimal_places=1)") diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 02207ff6e9..82377041dc 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -313,15 +313,8 @@ class SchemaTests(TransactionTestCase): # Ensure the field is right afterwards columns = self.column_classes(Author) # BooleanField are stored as TINYINT(1) on MySQL. - field_type, field_info = columns['awesome'] - if connection.vendor == 'mysql': - self.assertEqual(field_type, 'IntegerField') - self.assertEqual(field_info.precision, 1) - elif connection.vendor == 'oracle' and connection.version_has_default_introspection_bug: - self.assertEqual(field_type, 'IntegerField') - self.assertEqual(field_info.precision, 0) - else: - self.assertEqual(field_type, 'BooleanField') + field_type = columns['awesome'][0] + self.assertEqual(field_type, connection.features.introspected_boolean_field_type) def test_add_field_default_transform(self): """