From e3fd449e854b12636f6d534c7e4cb7fbe5e8494f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=98=E5=85=B8?= Date: Fri, 11 Sep 2026 22:59:07 +0800 Subject: [PATCH] fix(parser): preserve Oracle PRIOR position on all condition expressions (#2601) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Condition() drops the consumed PRIOR keyword unless the parsed condition implements SupportsOldOracleJoinSyntax: BETWEEN / LIKE / SIMILAR TO / IS NULL / IS TRUE / IS UNKNOWN / IS DISTINCT FROM / MEMBER OF silently lose it, InExpression throws IllegalArgumentException, and comparisons keep it in toString() but lose it in the deparser. The eight classes now implement the interface (real prior field, explicit reject for the join operator), InExpression stores the position instead of rejecting it, and ExpressionDeParser renders PRIOR for all of them including both positions of OldOracleJoinBinaryExpression. Signed-off-by: 付典 --- .../operators/relational/Between.java | 40 +++- .../operators/relational/InExpression.java | 11 +- .../relational/IsBooleanExpression.java | 43 +++- .../relational/IsDistinctExpression.java | 40 +++- .../relational/IsNullExpression.java | 45 +++- .../relational/IsUnknownExpression.java | 41 +++- .../operators/relational/LikeExpression.java | 40 +++- .../relational/MemberOfExpression.java | 41 +++- .../relational/SimilarToExpression.java | 40 +++- .../util/deparser/ExpressionDeParser.java | 44 +++- .../relational/OraclePriorPositionTest.java | 211 ++++++++++++++++++ 11 files changed, 572 insertions(+), 24 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/expression/operators/relational/OraclePriorPositionTest.java diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java index 8998863ef2..ff24e0eba3 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Between.java @@ -16,7 +16,7 @@ /** * A "BETWEEN" expr1 expr2 statement */ -public class Between extends ASTNodeAccessImpl implements Expression { +public class Between extends ASTNodeAccessImpl implements Expression, SupportsOldOracleJoinSyntax { private Expression leftExpression; private boolean not = false; @@ -82,7 +82,8 @@ public T accept(ExpressionVisitor expressionVisitor, S context) { @Override public String toString() { - return leftExpression + " " + (not ? "NOT " : "") + "BETWEEN " + return (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + leftExpression + " " + + (not ? "NOT " : "") + "BETWEEN " + (usingSymmetric ? "SYMMETRIC " : "") + (usingAsymmetric ? "ASYMMETRIC " : "") + betweenExpressionStart + " AND " @@ -120,4 +121,39 @@ public E getBetweenExpressionStart(Class type) { public E getLeftExpression(Class type) { return type.cast(getLeftExpression()); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public Between withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public Between withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java index 743d8a0aa7..c920c2b872 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java @@ -22,6 +22,8 @@ public class InExpression extends ASTNodeAccessImpl private Expression rightExpression; private int oldOracleJoinSyntax = NO_ORACLE_JOIN; + private int oraclePriorPosition = NO_ORACLE_PRIOR; + public InExpression() {} public InExpression(Expression leftExpression, Expression rightExpression) { @@ -94,6 +96,9 @@ private String getLeftExpressionString() { @Override public String toString() { StringBuilder statementBuilder = new StringBuilder(); + if (oraclePriorPosition == ORACLE_PRIOR_START) { + statementBuilder.append("PRIOR "); + } statementBuilder.append(getLeftExpressionString()); statementBuilder.append(" "); @@ -110,14 +115,12 @@ public String toString() { @Override public int getOraclePriorPosition() { - return SupportsOldOracleJoinSyntax.NO_ORACLE_PRIOR; + return oraclePriorPosition; } @Override public void setOraclePriorPosition(int priorPosition) { - if (priorPosition != SupportsOldOracleJoinSyntax.NO_ORACLE_PRIOR) { - throw new IllegalArgumentException("unexpected prior for oracle found"); - } + this.oraclePriorPosition = priorPosition; } public InExpression withRightExpression(Expression rightExpression) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java index 3c83365336..6cf101bd9a 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsBooleanExpression.java @@ -13,7 +13,8 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; -public class IsBooleanExpression extends ASTNodeAccessImpl implements Expression { +public class IsBooleanExpression extends ASTNodeAccessImpl + implements Expression, SupportsOldOracleJoinSyntax { private Expression leftExpression; private boolean not = false; @@ -50,10 +51,11 @@ public T accept(ExpressionVisitor expressionVisitor, S context) { @Override public String toString() { + String prior = oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : ""; if (isTrue()) { - return leftExpression + " IS" + (not ? " NOT" : "") + " TRUE"; + return prior + leftExpression + " IS" + (not ? " NOT" : "") + " TRUE"; } else { - return leftExpression + " IS" + (not ? " NOT" : "") + " FALSE"; + return prior + leftExpression + " IS" + (not ? " NOT" : "") + " FALSE"; } } @@ -75,4 +77,39 @@ public IsBooleanExpression withNot(boolean not) { public E getLeftExpression(Class type) { return type.cast(getLeftExpression()); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public IsBooleanExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public IsBooleanExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java index 60add6b6ed..12bc3027cd 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsDistinctExpression.java @@ -12,7 +12,7 @@ import net.sf.jsqlparser.expression.BinaryExpression; import net.sf.jsqlparser.expression.ExpressionVisitor; -public class IsDistinctExpression extends BinaryExpression { +public class IsDistinctExpression extends BinaryExpression implements SupportsOldOracleJoinSyntax { private boolean not = false; @@ -36,7 +36,43 @@ public String getStringExpression() { @Override public String toString() { - String retval = getLeftExpression() + getStringExpression() + getRightExpression(); + String retval = (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + + getLeftExpression() + getStringExpression() + getRightExpression(); return retval; } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public IsDistinctExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public IsDistinctExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java index 393f9f1944..f044c436ae 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsNullExpression.java @@ -14,7 +14,8 @@ import net.sf.jsqlparser.parser.ASTNodeAccessImpl; import net.sf.jsqlparser.schema.Column; -public class IsNullExpression extends ASTNodeAccessImpl implements Expression { +public class IsNullExpression extends ASTNodeAccessImpl + implements Expression, SupportsOldOracleJoinSyntax { private Expression leftExpression; private boolean not = false; @@ -72,12 +73,13 @@ public T accept(ExpressionVisitor expressionVisitor, S context) { @Override public String toString() { + String prior = oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : ""; if (useNotNull) { - return leftExpression + " NOTNULL"; + return prior + leftExpression + " NOTNULL"; } else if (useIsNull) { - return leftExpression + (not ? " NOT" : "") + " ISNULL"; + return prior + leftExpression + (not ? " NOT" : "") + " ISNULL"; } else { - return leftExpression + " IS " + (not ? "NOT " : "") + "NULL"; + return prior + leftExpression + " IS " + (not ? "NOT " : "") + "NULL"; } } @@ -99,4 +101,39 @@ public IsNullExpression withNot(boolean not) { public E getLeftExpression(Class type) { return type.cast(getLeftExpression()); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public IsNullExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public IsNullExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsUnknownExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsUnknownExpression.java index 506d6e2466..06abbbcb55 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsUnknownExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/IsUnknownExpression.java @@ -13,7 +13,8 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; -public class IsUnknownExpression extends ASTNodeAccessImpl implements Expression { +public class IsUnknownExpression extends ASTNodeAccessImpl + implements Expression, SupportsOldOracleJoinSyntax { private Expression leftExpression; private boolean isNot = false; @@ -41,7 +42,8 @@ public T accept(ExpressionVisitor expressionVisitor, S context) { @Override public String toString() { - return leftExpression + " IS" + (isNot ? " NOT" : "") + " UNKNOWN"; + return (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + leftExpression + " IS" + + (isNot ? " NOT" : "") + " UNKNOWN"; } public IsUnknownExpression withLeftExpression(Expression leftExpression) { @@ -57,4 +59,39 @@ public IsUnknownExpression withNot(boolean isNot) { public E getLeftExpression(Class type) { return type.cast(getLeftExpression()); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public IsUnknownExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public IsUnknownExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java index 770db3de39..4d9201e5e4 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java @@ -14,7 +14,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; -public class LikeExpression extends BinaryExpression { +public class LikeExpression extends BinaryExpression implements SupportsOldOracleJoinSyntax { private boolean not = false; private boolean useBinary = false; private Expression escapeExpression = null; @@ -50,7 +50,8 @@ public String getStringExpression() { @Override public String toString() { - String retval = getLeftExpression() + " " + (not ? "NOT " : "") + String retval = (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + + getLeftExpression() + " " + (not ? "NOT " : "") + (likeKeyWord == KeyWord.SIMILAR_TO ? "SIMILAR TO" : likeKeyWord) + " " + (useBinary ? "BINARY " : "") + getRightExpression(); if (escapeExpression != null) { @@ -125,4 +126,39 @@ public static KeyWord from(String keyword) { keyword.toUpperCase(Locale.ROOT).replaceAll("\\s+", "_")); } } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public LikeExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public LikeExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java index d602e62788..87a54dd072 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MemberOfExpression.java @@ -13,7 +13,8 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; -public class MemberOfExpression extends ASTNodeAccessImpl implements Expression { +public class MemberOfExpression extends ASTNodeAccessImpl + implements Expression, SupportsOldOracleJoinSyntax { Expression leftExpression; Expression rightExpression; @@ -53,11 +54,47 @@ public MemberOfExpression setNot(boolean not) { @Override public String toString() { - return leftExpression + " MEMBER OF " + rightExpression; + return (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + leftExpression + + " MEMBER OF " + rightExpression; } @Override public T accept(ExpressionVisitor expressionVisitor, S context) { return expressionVisitor.visit(this, context); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public MemberOfExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public MemberOfExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java index 0818d75c0a..fbb50f25d5 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/SimilarToExpression.java @@ -13,7 +13,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; -public class SimilarToExpression extends BinaryExpression { +public class SimilarToExpression extends BinaryExpression implements SupportsOldOracleJoinSyntax { private boolean not = false; private String escape = null; @@ -38,7 +38,8 @@ public String getStringExpression() { @Override public String toString() { - String retval = getLeftExpression() + " " + (not ? "NOT " : "") + getStringExpression() + String retval = (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "") + + getLeftExpression() + " " + (not ? "NOT " : "") + getStringExpression() + " " + getRightExpression(); if (escape != null) { retval += " ESCAPE " + "'" + escape + "'"; @@ -74,4 +75,39 @@ public SimilarToExpression withLeftExpression(Expression arg0) { public SimilarToExpression withRightExpression(Expression arg0) { return (SimilarToExpression) super.withRightExpression(arg0); } + + private int oraclePriorPosition = NO_ORACLE_PRIOR; + + @Override + public int getOldOracleJoinSyntax() { + return NO_ORACLE_JOIN; + } + + @Override + public void setOldOracleJoinSyntax(int oldOracleJoinSyntax) { + throw new IllegalArgumentException( + "oracle join operator (+) is not supported on this condition"); + } + + @Override + public SimilarToExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) { + setOldOracleJoinSyntax(oldOracleJoinSyntax); + return this; + } + + @Override + public int getOraclePriorPosition() { + return oraclePriorPosition; + } + + @Override + public void setOraclePriorPosition(int oraclePriorPosition) { + this.oraclePriorPosition = oraclePriorPosition; + } + + public SimilarToExpression withOraclePriorPosition(int oraclePriorPosition) { + setOraclePriorPosition(oraclePriorPosition); + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index 4a17272cc5..38fde71bb8 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -183,6 +183,9 @@ public StringBuilder visit(AndExpression andExpression, S context) { @Override public StringBuilder visit(Between between, S context) { + if (between.getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } between.getLeftExpression().accept(this, context); if (between.isNot()) { builder.append(" NOT"); @@ -268,11 +271,17 @@ public StringBuilder deparse( // if (expression.isNot()) { // buffer.append(NOT); // } + if (expression.getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } expression.getLeftExpression().accept(this, context); if (expression.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_RIGHT) { builder.append("(+)"); } builder.append(operator); + if (expression.getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_END) { + builder.append("PRIOR "); + } expression.getRightExpression().accept(this, context); if (expression.getOldOracleJoinSyntax() == EqualsTo.ORACLE_JOIN_LEFT) { builder.append("(+)"); @@ -345,6 +354,10 @@ public void visit(BitwiseLeftShift expr) { @Override public StringBuilder visit(InExpression inExpression, S context) { + if (inExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } inExpression.getLeftExpression().accept(this, context); if (inExpression .getOldOracleJoinSyntax() == SupportsOldOracleJoinSyntax.ORACLE_JOIN_RIGHT) { @@ -407,6 +420,10 @@ public StringBuilder visit(SignedExpression signedExpression, S context) { @Override public StringBuilder visit(IsNullExpression isNullExpression, S context) { + if (isNullExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } isNullExpression.getLeftExpression().accept(this, context); if (isNullExpression.isUseNotNull()) { builder.append(" NOTNULL"); @@ -428,6 +445,10 @@ public StringBuilder visit(IsNullExpression isNullExpression, S context) { @Override public StringBuilder visit(IsBooleanExpression isBooleanExpression, S context) { + if (isBooleanExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } isBooleanExpression.getLeftExpression().accept(this, context); if (isBooleanExpression.isTrue()) { if (isBooleanExpression.isNot()) { @@ -447,6 +468,10 @@ public StringBuilder visit(IsBooleanExpression isBooleanExpression, S contex @Override public StringBuilder visit(IsUnknownExpression isUnknownExpression, S context) { + if (isUnknownExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } isUnknownExpression.getLeftExpression().accept(this, context); if (isUnknownExpression.isNot()) { builder.append(" IS NOT UNKNOWN"); @@ -472,6 +497,10 @@ public StringBuilder visit(LikeExpression likeExpression, S context) { ? " SIMILAR TO" : likeExpression.getLikeKeyWord().toString(); + if (likeExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } likeExpression.getLeftExpression().accept(this, context); builder.append(" "); if (likeExpression.isNot()) { @@ -504,6 +533,10 @@ public StringBuilder visit(ExistsExpression existsExpression, S context) { @Override public StringBuilder visit(MemberOfExpression memberOfExpression, S context) { + if (memberOfExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } memberOfExpression.getLeftExpression().accept(this, context); if (memberOfExpression.isNot()) { builder.append(" NOT MEMBER OF "); @@ -1505,7 +1538,12 @@ public StringBuilder visit(CollateExpression col, S context) { @Override public StringBuilder visit(SimilarToExpression expr, S context) { - deparse(expr, (expr.isNot() ? " NOT" : "") + " SIMILAR TO ", null); + if (expr.getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } + expr.getLeftExpression().accept(this, context); + builder.append(expr.isNot() ? " NOT SIMILAR TO " : " SIMILAR TO "); + expr.getRightExpression().accept(this, context); return builder; } @@ -1756,6 +1794,10 @@ public StringBuilder visit(AllValue allValue, S context) { @Override public StringBuilder visit(IsDistinctExpression isDistinctExpression, S context) { + if (isDistinctExpression + .getOraclePriorPosition() == SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START) { + builder.append("PRIOR "); + } builder.append(isDistinctExpression.getLeftExpression()) .append(isDistinctExpression.getStringExpression()) .append(isDistinctExpression.getRightExpression()); diff --git a/src/test/java/net/sf/jsqlparser/expression/operators/relational/OraclePriorPositionTest.java b/src/test/java/net/sf/jsqlparser/expression/operators/relational/OraclePriorPositionTest.java new file mode 100644 index 0000000000..643342b71b --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/expression/operators/relational/OraclePriorPositionTest.java @@ -0,0 +1,211 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + +public class OraclePriorPositionTest { + + private static Expression whereOf(String sqlStr) throws JSQLParserException { + PlainSelect select = + (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + return select.getWhere(); + } + + @Test + public void testPriorBetween() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a BETWEEN 1 AND 2"); + Between between = (Between) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + between.getOraclePriorPosition()); + } + + @Test + public void testPriorLike() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a LIKE 'x%'"); + LikeExpression like = (LikeExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + like.getOraclePriorPosition()); + } + + @Test + public void testPriorNotLike() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a NOT LIKE 'x%'"); + LikeExpression like = (LikeExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + like.getOraclePriorPosition()); + } + + @Test + public void testPriorSimilarTo() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a SIMILAR TO 'x'"); + LikeExpression similarTo = (LikeExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + similarTo.getOraclePriorPosition()); + } + + @Test + public void testPriorSimilarToOnSeparateTokens() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a SIMILAR\nTO 'x'"); + SimilarToExpression similarTo = (SimilarToExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + similarTo.getOraclePriorPosition()); + } + + @Test + public void testPriorIsNull() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS NULL"); + IsNullExpression isNull = (IsNullExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isNull.getOraclePriorPosition()); + } + + @Test + public void testPriorIsNotNull() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS NOT NULL"); + IsNullExpression isNull = (IsNullExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isNull.getOraclePriorPosition()); + } + + @Test + public void testPriorIsTrue() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS TRUE"); + IsBooleanExpression isBoolean = (IsBooleanExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isBoolean.getOraclePriorPosition()); + } + + @Test + public void testPriorIsUnknown() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS UNKNOWN"); + IsUnknownExpression isUnknown = (IsUnknownExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isUnknown.getOraclePriorPosition()); + } + + @Test + public void testPriorIsDistinctFrom() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS DISTINCT FROM b"); + IsDistinctExpression isDistinct = (IsDistinctExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isDistinct.getOraclePriorPosition()); + } + + @Test + public void testPriorMemberOf() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a MEMBER OF (1)"); + MemberOfExpression memberOf = (MemberOfExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + memberOf.getOraclePriorPosition()); + } + + @Test + public void testPriorIn() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IN (1, 2)"); + InExpression in = (InExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + in.getOraclePriorPosition()); + } + + @Test + public void testPriorNotIn() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE PRIOR a NOT IN (1, 2)", + true); + } + + @Test + public void testPriorOnComparisonStartIsDeparsed() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE PRIOR a = b", true); + } + + @Test + public void testPriorOnComparisonEndIsDeparsed() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a = PRIOR b", true); + } + + @Test + public void testPriorIsNullShorthand() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a ISNULL"); + IsNullExpression isNull = (IsNullExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isNull.getOraclePriorPosition()); + } + + @Test + public void testPriorNotNullShorthand() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE PRIOR a NOTNULL", true); + } + + @Test + public void testPriorIsFalse() throws JSQLParserException { + Expression where = whereOf("SELECT * FROM t WHERE PRIOR a IS FALSE"); + IsBooleanExpression isBoolean = (IsBooleanExpression) where; + assertEquals(SupportsOldOracleJoinSyntax.ORACLE_PRIOR_START, + isBoolean.getOraclePriorPosition()); + } + + @Test + public void testPriorIsNotUnknown() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE PRIOR a IS NOT UNKNOWN", + true); + } + + @Test + public void testPriorNotSimilarTo() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t WHERE PRIOR a NOT SIMILAR TO 'x'", + true); + } + + @Test + public void testPriorInJoinOnClause() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t JOIN t2 ON PRIOR t.c = t2.c", true); + } + + @Test + public void testPriorBetweenEndOperandStaysIntact() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a BETWEEN 1 AND PRIOR 2", + true); + } + + @Test + public void testPlainConditionsWithoutPriorAreUnchanged() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a = b", true); + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a BETWEEN 1 AND 2", true); + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a LIKE 'x%'", true); + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a IS NULL", true); + } + + @Test + public void testNotPriorBetween() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t WHERE NOT PRIOR a BETWEEN 1 AND 2", true); + } + + @Test + public void testPriorOnSeveralConditions() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t WHERE PRIOR a = b AND PRIOR c BETWEEN 1 AND 2", true); + } + + @Test + public void testPriorCombinedWithOracleJoinOnBetween() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT * FROM t1, t2 WHERE PRIOR t2.c(+) BETWEEN 1 AND 2", true); + } +}