diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index f442e3ecdd..ecf97b62ad 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -181,6 +181,12 @@ def get_date_trunc_sql(lookup_type, field_name): def get_datetime_cast_sql(): return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" +def get_field_cast_sql(db_type): + if db_type.endswith('LOB'): + return "DBMS_LOB.SUBSTR(%s%s)" + else: + return "%s%s" + def get_limit_offset_sql(limit, offset=None): # Limits and offset are too complicated to be handled here. # Instead, they are handled in django/db/backends/oracle/query.py. diff --git a/django/db/models/query.py b/django/db/models/query.py index 7620c366ce..4ad3ee5cbb 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -777,7 +777,7 @@ class QNot(Q): return SortedDict(), [], [] return joins, where2, params -def get_where_clause(lookup_type, table_prefix, field_name, value): +def get_where_clause(lookup_type, table_prefix, field_name, value, db_type): if table_prefix.endswith('.'): table_prefix = backend.quote_name(table_prefix[:-1])+'.' field_name = backend.quote_name(field_name) @@ -785,36 +785,41 @@ def get_where_clause(lookup_type, table_prefix, field_name, value): cast_sql = backend.get_datetime_cast_sql() else: cast_sql = '%s' - if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: - format = 'UPPER(%s%s) %s' + if db_type and hasattr(backend, 'get_field_cast_sql'): + field_cast_sql = backend.get_field_cast_sql(db_type) else: - format = '%s%s %s' + field_cast_sql = '%s%s' + field_sql = field_cast_sql % (table_prefix, field_name) + if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: + format = 'UPPER(%s) %s' + else: + format = '%s %s' try: - return format % (table_prefix, field_name, + return format % (field_sql, backend.OPERATOR_MAPPING[lookup_type] % cast_sql) except KeyError: pass if lookup_type == 'in': in_string = ','.join(['%s' for id in value]) if in_string: - return '%s%s IN (%s)' % (table_prefix, field_name, in_string) + return '%s IN (%s)' % (field_sql, in_string) else: raise EmptyResultSet elif lookup_type in ('range', 'year'): - return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) + return '%s BETWEEN %%s AND %%s' % field_sql elif lookup_type in ('month', 'day'): - return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name) + return "%s = %%s" % backend.get_date_extract_sql(lookup_type, field_sql) elif lookup_type == 'isnull': - return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) + return "%s IS %sNULL" % (field_sql, (not value and 'NOT ' or '')) elif lookup_type == 'search': - return backend.get_fulltext_search_sql(table_prefix + field_name) + return backend.get_fulltext_search_sql(field_sql) elif lookup_type in ('regex', 'iregex'): if settings.DATABASE_ENGINE == 'oracle': if lookup_type == 'regex': match_option = 'c' else: match_option = 'i' - return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option) + return "REGEXP_LIKE(%s, %s, '%s')" % (field_sql, cast_sql, match_option) else: raise NotImplementedError raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) @@ -1071,6 +1076,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column): else: # No elements left in path. Current element is the element on which # the search is being performed. + db_type = None if join_required: # Last query term is a RelatedObject @@ -1100,8 +1106,9 @@ def lookup_inner(path, lookup_type, value, opts, table, column): else: # Last query term was a normal field. column = field.column + db_type = field.db_type() - where.append(get_where_clause(lookup_type, current_table + '.', column, value)) + where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type)) params.extend(field.get_db_prep_lookup(lookup_type, value)) return joins, where, params diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 6a341070a4..12ebd0cf07 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -36,6 +36,13 @@ class Base(models.Model): def __unicode__(self): return "Base %s" % self.name +class Article(models.Model): + name = models.CharField(maxlength = 50) + text = models.TextField() + + def __str__(self): + return "Article %s" % self.name + __test__ = {'API_TESTS': ur""" # Regression test for #1661 and #1662: Check that string form referencing of # models works, both as pre and post reference, on all RelatedField types. @@ -82,4 +89,13 @@ __test__ = {'API_TESTS': ur""" # We can also do the above query using UTF-8 strings. >>> Foo.objects.get(friend__contains='\xc3\xa7') + +# Regression tests for #5087: make sure we can perform queries on TextFields. +>>> a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.') +>>> a.save() +>>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.') + + +>>> Article.objects.get(text__contains='quick brown fox') + """}