]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Ensure (+) is rendered for all right-hand members
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Sep 2017 18:53:29 +0000 (14:53 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Sep 2017 18:53:29 +0000 (14:53 -0400)
Fixed bug where Oracle 8 "non ansi" join mode would not add the
``(+)`` operator to expressions that used an operator other than the
``=`` operator.  The ``(+)`` needs to be on all columns that are part
of the right-hand side.

Change-Id: I952e2369f11b78f5b918456ae3a5b0768d9761ec
Fixes: #4076
doc/build/changelog/unreleased_12/4076.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/oracle/base.py
test/dialect/oracle/test_compiler.py

diff --git a/doc/build/changelog/unreleased_12/4076.rst b/doc/build/changelog/unreleased_12/4076.rst
new file mode 100644 (file)
index 0000000..78cd6bc
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, oracle
+    :tickets: 4076
+
+    Fixed bug where Oracle 8 "non ansi" join mode would not add the
+    ``(+)`` operator to expressions that used an operator other than the
+    ``=`` operator.  The ``(+)`` needs to be on all columns that are part
+    of the right-hand side.
\ No newline at end of file
index b2eb44b5b1d399873957389bb3db6110ed1c1568..dbef1200a99a6d5232aaba138849af4428bacbfc 100644 (file)
@@ -733,12 +733,17 @@ class OracleCompiler(compiler.SQLCompiler):
 
         def visit_join(join):
             if join.isouter:
+                # https://docs.oracle.com/database/121/SQLRF/queries006.htm#SQLRF52354
+                # "apply the outer join operator (+) to all columns of B in
+                # the join condition in the WHERE clause" - that is,
+                # unconditionally regardless of operator or the other side
                 def visit_binary(binary):
-                    if binary.operator == sql_operators.eq:
-                        if join.right.is_derived_from(binary.left.table):
-                            binary.left = _OuterJoinColumn(binary.left)
-                        elif join.right.is_derived_from(binary.right.table):
-                            binary.right = _OuterJoinColumn(binary.right)
+                    if isinstance(binary.left, expression.ColumnClause) \
+                            and join.right.is_derived_from(binary.left.table):
+                        binary.left = _OuterJoinColumn(binary.left)
+                    elif isinstance(binary.right, expression.ColumnClause) \
+                            and join.right.is_derived_from(binary.right.table):
+                        binary.right = _OuterJoinColumn(binary.right)
                 clauses.append(visitors.cloned_traverse(
                     join.onclause, {}, {'binary': visit_binary}))
             else:
index 3053590857cd6cfbab92716dedececf758ec114e..fc310f8f253ef37a90e9f33ee46b9f367d172b65 100644 (file)
@@ -4,6 +4,7 @@
 from sqlalchemy.testing import eq_
 from sqlalchemy import types as sqltypes, exc, schema
 from sqlalchemy.sql import table, column
+from sqlalchemy import and_
 from sqlalchemy.testing import (fixtures,
                                 AssertsExecutionResults,
                                 AssertsCompiledSQL)
@@ -600,6 +601,54 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
                             'mytable.name) AS bar FROM mytable',
                             dialect=oracle.dialect(use_ansi=False))
 
+    def test_nonansi_plusses_everthing_in_the_condition(self):
+        table1 = table('mytable',
+                       column('myid', Integer),
+                       column('name', String),
+                       column('description', String))
+
+        table2 = table(
+            'myothertable',
+            column('otherid', Integer),
+            column('othername', String),
+        )
+
+        stmt = select([table1]).select_from(
+            table1.outerjoin(
+                table2,
+                and_(
+                    table1.c.myid == table2.c.otherid,
+                    table2.c.othername > 5,
+                    table1.c.name == 'foo'
+                )
+            )
+        )
+        self.assert_compile(
+            stmt,
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable, myothertable WHERE mytable.myid = "
+            "myothertable.otherid(+) AND myothertable.othername(+) > "
+            ":othername_1 AND mytable.name = :name_1",
+            dialect=oracle.dialect(use_ansi=False))
+
+        stmt = select([table1]).select_from(
+            table1.outerjoin(
+                table2,
+                and_(
+                    table1.c.myid == table2.c.otherid,
+                    table2.c.othername == None,
+                    table1.c.name == None
+                )
+            )
+        )
+        self.assert_compile(
+            stmt,
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable, myothertable WHERE mytable.myid = "
+            "myothertable.otherid(+) AND myothertable.othername(+) IS NULL "
+            "AND mytable.name IS NULL",
+            dialect=oracle.dialect(use_ansi=False))
+
     def test_nonansi_nested_right_join(self):
         a = table('a', column('a'))
         b = table('b', column('b'))