From b9d44057b82af9f68fe9e06ea7bd52c4543181f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=98=E5=85=B8?= Date: Tue, 8 Sep 2026 12:43:50 +0800 Subject: [PATCH] fix(parser): parse Oracle outer join operator (+) on BETWEEN operands (#672) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 付典 --- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 18 +++++ .../operators/relational/BetweenTest.java | 75 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index ebb26f4929..f84aa4b637 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -8105,6 +8105,15 @@ Expression Between(Expression leftExpression) : LOOKAHEAD({ isComparisonOperatorAhead() }) betweenExpressionStart = RegularConditionRHS(betweenExpressionStart, EqualsTo.NO_ORACLE_JOIN) ] + [ + LOOKAHEAD("(" "+" ")") + "(" "+" ")" + { + if (betweenExpressionStart instanceof Column) { + ((Column) betweenExpressionStart).setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); + } + } + ] ) @@ -8116,6 +8125,15 @@ Expression Between(Expression leftExpression) : LOOKAHEAD({ isComparisonOperatorAhead() }) betweenExpressionEnd = RegularConditionRHS(betweenExpressionEnd, EqualsTo.NO_ORACLE_JOIN) ] + [ + LOOKAHEAD("(" "+" ")") + "(" "+" ")" + { + if (betweenExpressionEnd instanceof Column) { + ((Column) betweenExpressionEnd).setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); + } + } + ] ) { diff --git a/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java b/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java index 4fd8fd706a..ee04111bc4 100644 --- a/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/operators/relational/BetweenTest.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.expression.operators.relational; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Assertions; @@ -50,4 +51,78 @@ void testBetweenASymmetricIssue2250() throws JSQLParserException { Assertions.assertFalse(between.isUsingSymmetric()); Assertions.assertTrue(between.isUsingAsymmetric()); } + + @Test + void testBetweenWithOldOracleJoinSyntaxOnBothOperandsIssue672() throws JSQLParserException { + String sqlStr = + "SELECT * FROM table1 t1, table2 t2 WHERE t1.col1 BETWEEN t2.col2(+) AND t2.col3(+)"; + PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Between between = (Between) select.getWhere(); + + assertEquals(EqualsTo.ORACLE_JOIN_RIGHT, + ((Column) between.getBetweenExpressionStart()).getOldOracleJoinSyntax()); + assertEquals(EqualsTo.ORACLE_JOIN_RIGHT, + ((Column) between.getBetweenExpressionEnd()).getOldOracleJoinSyntax()); + } + + @Test + void testBetweenWithOldOracleJoinSyntaxOnStartOperandIssue672() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM table1 t1, table2 t2 WHERE t1.col1 BETWEEN t2.col2(+) AND 5", true); + } + + @Test + void testBetweenWithOldOracleJoinSyntaxOnEndOperandIssue672() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM table1 t1, table2 t2 WHERE t1.col1 BETWEEN 1 AND t2.col3(+)", true); + } + + @Test + void testNotBetweenWithOldOracleJoinSyntaxIssue672() throws JSQLParserException { + String sqlStr = + "SELECT * FROM table1 t1, table2 t2 WHERE t1.col1 NOT BETWEEN t2.col2(+) AND t2.col3(+)"; + PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Between between = (Between) select.getWhere(); + + assertTrue(between.isNot()); + assertEquals(EqualsTo.ORACLE_JOIN_RIGHT, + ((Column) between.getBetweenExpressionStart()).getOldOracleJoinSyntax()); + assertEquals(EqualsTo.ORACLE_JOIN_RIGHT, + ((Column) between.getBetweenExpressionEnd()).getOldOracleJoinSyntax()); + } + + @Test + void testBetweenSymmetricWithOldOracleJoinSyntaxIssue672() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t1, t2 WHERE t1.c BETWEEN SYMMETRIC t2.c(+) AND t2.d(+)", true); + } + + @Test + void testBetweenWithOldOracleJoinSyntaxInJoinOnClauseIssue672() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t1 JOIN t2 ON t1.c BETWEEN t2.c(+) AND t2.d(+)", true); + } + + @Test + void testBetweenWithOldOracleJoinSyntaxOnLeftOperand() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM table1 t1, table2 t2 WHERE t2.col2(+) BETWEEN t1.col1 AND t1.col3", + true); + } + + @Test + void testBetweenWithOldOracleJoinSyntaxBeforeComparisonSuffixOnOperand() + throws JSQLParserException { + // (+) directly followed by a comparison operator keeps the pre-existing + // RegularConditionRHS path, where the marker sits on the comparison itself + String sqlStr = + "SELECT * FROM t1, t2 WHERE t1.c BETWEEN t2.c(+) = 5 AND 1"; + PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Between between = (Between) select.getWhere(); + EqualsTo comparison = (EqualsTo) between.getBetweenExpressionStart(); + + assertEquals(EqualsTo.ORACLE_JOIN_RIGHT, comparison.getOldOracleJoinSyntax()); + assertEquals(EqualsTo.NO_ORACLE_JOIN, + ((Column) comparison.getLeftExpression()).getOldOracleJoinSyntax()); + } }