Ticket #10161: 10161-r9848.diff

File 10161-r9848.diff, 5.1 KB (added by russellm, 7 years ago)

First cut at a patch to fix Oracle expression problems

  • django/db/backends/__init__.py

    diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
    index 99f8f27..6b027de 100644
    a b class BaseDatabaseOperations(object): 
    408408        """
    409409        pass
    410410
     411    def combine_expression(self, connector, sub_expressions):
     412        """Combine a list of subexpressions into a single expression, using
     413        the provided connecting operator. This is required because operators
     414        can vary between backends (e.g., Oracle with %% and &) and between
     415        subexpression types (e.g., date expressions)
     416        """
     417        conn = ' %s ' % connector
     418        return conn.join(sub_expressions)
     419
    411420class BaseDatabaseIntrospection(object):
    412421    """
    413422    This class encapsulates all backend-specific introspection utilities
  • django/db/backends/oracle/base.py

    diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
    index d6bd3ea..02c25ec 100644
    a b WHEN (new.%(col_name)s IS NULL) 
    221221        second = '%s-12-31'
    222222        return [first % value, second % value]
    223223
     224    def combine_expression(self, connector, sub_expressions):
     225        "Oracle requires special cases for %% and & operators in query expressions"
     226        if connector == '%%':
     227            return 'MOD(%s)' % ','.join(sub_expressions)
     228        elif:
     229            return 'BITAND(%s)' % ','.join(sub_expressions)
     230        return super(DatabaseOperations, self).expression(connector, sub_expressions, parameters)
    224231
    225232class DatabaseWrapper(BaseDatabaseWrapper):
    226233
  • django/db/models/sql/expressions.py

    diff --git a/django/db/models/sql/expressions.py b/django/db/models/sql/expressions.py
    index ef9fcb0..f011db2 100644
    a b class SQLEvaluator(object): 
    7474            if sql:
    7575                expressions.append(format % sql)
    7676                expression_params.extend(params)
    77         conn = ' %s ' % node.connector
    7877
    79         return conn.join(expressions), expression_params
     78        return connection.ops.combine_expression(node.connector, expressions), expression_params
    8079
    8180    def evaluate_leaf(self, node, qn):
    8281        if not qn:
  • tests/modeltests/expressions/models.py

    diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py
    index 4043f5e..cccb05d 100644
    a b __test__ = {'API_TESTS': """ 
    3737>>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
    3838...     ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()
    3939
     40>>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')
     41
    4042# We can filter for companies where the number of employees is greater than the
    4143# number of chairs.
     44>>> company_query.filter(num_employees__gt=F('num_chairs'))
     45[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}]
    4246
    43 >>> Company.objects.filter(num_employees__gt=F('num_chairs'))
    44 [<Company: Example Inc.>, <Company: Test GmbH>]
     47# We can set one field to have the value of another field
     48# Make sure we have enough chairs
     49>>> company_query.update(num_chairs=F('num_employees'))
     503
     51>>> company_query
     52[{'num_chairs': 2300, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 3, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 32, 'name': u'Test GmbH', 'num_employees': 32}]
    4553
    46 # The relation of a foreign key can become copied over to an other foreign key.
     54# We can perform arithmetic operations in expressions
     55# Make sure we have 2 spare chairs
     56>>> company_query.update(num_chairs=F('num_employees')+2)
     573
     58>>> company_query
     59[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]
    4760
     61# The relation of a foreign key can become copied over to an other foreign key.
    4862>>> Company.objects.update(point_of_contact=F('ceo'))
    49633
    5064
  • tests/regressiontests/expressions_regress/models.py

    diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py
    index dfd0df0..ae75cdb 100644
    a b  
    11"""
    22Spanning tests for all the operations that F() expressions can perform.
    33"""
    4 
     4from django.conf import settings
    55from django.db import models
    66
    77#
    Complex expressions of different connection types are possible. 
    123123>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer'))
    124124>>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers
    125125<Number: 10, 15.500>
     126"""}
     127
     128# Oracle doesn't support the Bitwise OR operator.
     129if settings.DATABASE_ENGINE != 'oracle':
     130    __test__['API_TESTS'] += """
    126131
    127132>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
    128133>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer'))
    129134>>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers
    130135<Number: 47, 15.500>
    131136
    132 
    133 """}
     137"""
Back to Top