Commit 5240b834 authored by Florian Hahn's avatar Florian Hahn Committed by Tim Graham
Browse files

Fixed #17027 -- Added support for the power operator in F expressions.

Thanks dan at dlo.me for the initial patch.

- Added __pow__ and __rpow__ to ExpressionNode
- Added oracle and mysql specific power expressions
- Added used-defined power function for sqlite
parent 1597503a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -386,6 +386,14 @@ class DatabaseOperations(BaseDatabaseOperations):
        items_sql = "(%s)" % ", ".join(["%s"] * len(fields))
        return "VALUES " + ", ".join([items_sql] * num_values)

    def combine_expression(self, connector, sub_expressions):
        """
        MySQL requires special cases for ^ operators in query expressions
        """
        if connector == '^':
            return 'POW(%s)' % ','.join(sub_expressions)
        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)


class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'mysql'
+2 −0
Original line number Diff line number Diff line
@@ -482,6 +482,8 @@ WHEN (new.%(col_name)s IS NULL)
            return 'BITAND(%s)' % ','.join(sub_expressions)
        elif connector == '|':
            raise NotImplementedError("Bit-wise or is not supported in Oracle.")
        elif connector == '^':
            return 'POWER(%s)' % ','.join(sub_expressions)
        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)

    def _get_sequence_name(self, table):
+12 −0
Original line number Diff line number Diff line
@@ -304,6 +304,13 @@ class DatabaseOperations(BaseDatabaseOperations):
        res.extend(["UNION ALL SELECT %s" % ", ".join(["%s"] * len(fields))] * (num_values - 1))
        return " ".join(res)

    def combine_expression(self, connector, sub_expressions):
        # SQLite doesn't have a power function, so we fake it with a
        # user-defined function django_power that's registered in connect().
        if connector == '^':
            return 'django_power(%s)' % ','.join(sub_expressions)
        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)


class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'sqlite'
@@ -376,6 +383,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
        conn.create_function("django_datetime_trunc", 3, _sqlite_datetime_trunc)
        conn.create_function("regexp", 2, _sqlite_regexp)
        conn.create_function("django_format_dtdelta", 5, _sqlite_format_dtdelta)
        conn.create_function("django_power", 2, _sqlite_power)
        return conn

    def init_connection_state(self):
@@ -567,3 +575,7 @@ def _sqlite_format_dtdelta(dt, conn, days, secs, usecs):

def _sqlite_regexp(re_pattern, re_string):
    return bool(re.search(re_pattern, force_text(re_string))) if re_string is not None else False


def _sqlite_power(x, y):
    return x ** y
+7 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ class ExpressionNode(tree.Node):
    SUB = '-'
    MUL = '*'
    DIV = '/'
    POW = '^'
    MOD = '%%'  # This is a quoted % operator - it is quoted
                # because it can be used in strings that also
                # have parameter substitution.
@@ -85,6 +86,9 @@ class ExpressionNode(tree.Node):
    def __mod__(self, other):
        return self._combine(other, self.MOD, False)

    def __pow__(self, other):
        return self._combine(other, self.POW, False)

    def __and__(self, other):
        raise NotImplementedError(
            "Use .bitand() and .bitor() for bitwise logical operations."
@@ -119,6 +123,9 @@ class ExpressionNode(tree.Node):
    def __rmod__(self, other):
        return self._combine(other, self.MOD, True)

    def __rpow__(self, other):
        return self._combine(other, self.POW, True)

    def __rand__(self, other):
        raise NotImplementedError(
            "Use .bitand() and .bitor() for bitwise logical operations."
+4 −0
Original line number Diff line number Diff line
@@ -112,6 +112,10 @@ As well as addition, Django supports subtraction, multiplication, division,
and modulo arithmetic with ``F()`` objects, using Python constants,
variables, and even other ``F()`` objects.

.. versionadded:: 1.7

    The power operator ``**`` is also supported.

``Q()`` objects
===============

Loading