From 2249bd275cbae6b73716bf36f5f36def1bb4222e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sat, 27 Oct 2012 04:51:14 +0300 Subject: [PATCH 1/4] Fixed Oracle failure for "%" in table name --- django/db/backends/oracle/base.py | 4 ++++ tests/regressiontests/backends/tests.py | 8 ++++++++ tests/regressiontests/inspectdb/tests.py | 11 +++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 6bf2e815a73..aad52992ddf 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -256,6 +256,10 @@ WHEN (new.%(col_name)s IS NULL) if not name.startswith('"') and not name.endswith('"'): name = '"%s"' % util.truncate_name(name.upper(), self.max_name_length()) + # Oracle puts the query text into a (query % args) construct, so % signs + # in names need to be escaped. The '%%' will be collapsed back to '%' at + # that stage so we aren't really making the name longer here. + name = name.replace('%','%%') return name.upper() def random_function_sql(self): diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index d284cfaac88..4766dcf44e3 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -25,6 +25,14 @@ from . import models class OracleChecks(unittest.TestCase): + @unittest.skipUnless(connection.vendor == 'oracle', + "No need to check Oracle quote_name semantics") + def test_quote_name(self): + # Check that '%' chars are escaped for query execution. + name = '"SOME%NAME"' + quoted_name = connection.ops.quote_name(name) + self.assertEquals(quoted_name % (), name) + @unittest.skipUnless(connection.vendor == 'oracle', "No need to check Oracle cursor semantics") def test_dbms_session(self): diff --git a/tests/regressiontests/inspectdb/tests.py b/tests/regressiontests/inspectdb/tests.py index 484e7f40603..028d2633371 100644 --- a/tests/regressiontests/inspectdb/tests.py +++ b/tests/regressiontests/inspectdb/tests.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals from django.core.management import call_command +from django.db import connection from django.test import TestCase, skipUnlessDBFeature from django.utils.six import StringIO @@ -60,14 +61,16 @@ class InspectDBTestCase(TestCase): self.assertIn("number_45extra = models.CharField", output) def test_special_column_name_introspection(self): - """Introspection of column names containing special characters, - unsuitable for Python identifiers + """ + Introspection of column names containing special characters, + unsuitable for Python identifiers """ out = StringIO() call_command('inspectdb', stdout=out) output = out.getvalue() + base_name = 'Field' if connection.vendor != 'oracle' else 'field' self.assertIn("field = models.IntegerField()", output) - self.assertIn("field_field = models.IntegerField(db_column='Field_')", output) - self.assertIn("field_field_0 = models.IntegerField(db_column='Field__')", output) + self.assertIn("field_field = models.IntegerField(db_column='%s_')" % base_name, output) + self.assertIn("field_field_0 = models.IntegerField(db_column='%s__')" % base_name, output) self.assertIn("field_field_1 = models.IntegerField(db_column='__field')", output) self.assertIn("prc_x = models.IntegerField(db_column='prc(%) x')", output) From c159d9cec0baab7bbd04d5d51a92a51e354a722a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sat, 27 Oct 2012 04:52:12 +0300 Subject: [PATCH 2/4] Fixed Oracle failure caused by None converted to '' in select_related case --- django/db/models/query.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index dc1ddf16061..da4c69f3621 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1418,8 +1418,15 @@ def get_cached_row(row, index_start, using, klass_info, offset=0): fields = row[index_start : index_start + field_count] # If all the select_related columns are None, then the related # object must be non-existent - set the relation to None. - # Otherwise, construct the related object. - if fields == (None,) * field_count: + # Otherwise, construct the related object. Also, some backends treat '' + # and None equivalently for char fields, so we have to be prepared for + # '' values. + if connections[using].features.interprets_empty_strings_as_nulls: + vals = tuple([None if f == '' else f for f in fields]) + else: + vals = fields + + if vals == (None,) * field_count: obj = None else: if field_names: From a5152bb64677fb9b976f6b35e80b11b368ef1e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sat, 27 Oct 2012 18:10:02 +0300 Subject: [PATCH 3/4] Marked a test as expectedFailure on Oracle --- tests/regressiontests/introspection/tests.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/regressiontests/introspection/tests.py b/tests/regressiontests/introspection/tests.py index a54e0c670b4..4b8a3277e2a 100644 --- a/tests/regressiontests/introspection/tests.py +++ b/tests/regressiontests/introspection/tests.py @@ -4,10 +4,15 @@ from functools import update_wrapper from django.db import connection from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature -from django.utils import six +from django.utils import six, unittest from .models import Reporter, Article +if connection.vendor == 'oracle': + expectedFailureOnOracle = unittest.expectedFailure +else: + expectedFailureOnOracle = lambda f: f + # # The introspection module is optional, so methods tested here might raise # NotImplementedError. This is perfectly acceptable behavior for the backend @@ -89,7 +94,13 @@ class IntrospectionTests(six.with_metaclass(IgnoreNotimplementedError, TestCase) [datatype(r[1], r) for r in desc], ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField'] ) - # Check also length of CharFields + + # The following test fails on Oracle due to #17202 (can't correctly + # inspect the length of character columns). + @expectedFailureOnOracle + def test_get_table_description_col_lengths(self): + cursor = connection.cursor() + desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table) self.assertEqual( [r[3] for r in desc if datatype(r[1], r) == 'CharField'], [30, 30, 75] From b55de81b9e4d820a223c066022050a0df0ee1dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sat, 27 Oct 2012 18:10:41 +0300 Subject: [PATCH 4/4] Ensured gis tests aren't run on non-gis Oracle --- django/contrib/gis/tests/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/contrib/gis/tests/utils.py b/django/contrib/gis/tests/utils.py index a83ba8a93fc..8355b27fd73 100644 --- a/django/contrib/gis/tests/utils.py +++ b/django/contrib/gis/tests/utils.py @@ -26,7 +26,7 @@ mysql = _default_db == 'mysql' spatialite = _default_db == 'spatialite' HAS_SPATIALREFSYS = True -if oracle: +if oracle and 'gis' in settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE']: from django.contrib.gis.db.backends.oracle.models import SpatialRefSys elif postgis: from django.contrib.gis.db.backends.postgis.models import SpatialRefSys