From: Mike Bayer Date: Wed, 13 Sep 2017 18:53:29 +0000 (-0400) Subject: Ensure (+) is rendered for all right-hand members X-Git-Tag: origin~8^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=01ed5c4009ce77876345107b3f8385caa93edf84;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Ensure (+) is rendered for all right-hand members 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 --- diff --git a/doc/build/changelog/unreleased_12/4076.rst b/doc/build/changelog/unreleased_12/4076.rst new file mode 100644 index 0000000000..78cd6bcee0 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4076.rst @@ -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 diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index b2eb44b5b1..dbef1200a9 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -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: diff --git a/test/dialect/oracle/test_compiler.py b/test/dialect/oracle/test_compiler.py index 3053590857..fc310f8f25 100644 --- a/test/dialect/oracle/test_compiler.py +++ b/test/dialect/oracle/test_compiler.py @@ -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'))