diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index 5ef4c6ad6..a9a4632a6 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -48,6 +48,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.Plus; +import net.sf.jsqlparser.expression.operators.relational.PriorTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; @@ -633,4 +635,34 @@ default void visit(StructType structType) { default void visit(LambdaExpression lambdaExpression) { this.visit(lambdaExpression, null); } + + T visit(HighExpression highExpression, S context); + + default void visit(HighExpression highExpression) { + this.visit(highExpression, null); + } + + T visit(LowExpression lowExpression, S context); + + default void visit(LowExpression lowExpression) { + this.visit(lowExpression, null); + } + + T visit(Plus plus, S context); + + default void visit(Plus plus) { + this.visit(plus, null); + } + + T visit(PriorTo priorTo, S context); + + default void visit(PriorTo priorTo) { + this.visit(priorTo, null); + } + + T visit(Inverse inverse, S context); + + default void visit(Inverse inverse) { + this.visit(inverse, null); + } } diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 9e1ee104d..714d76dba 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -29,6 +29,7 @@ import net.sf.jsqlparser.expression.operators.relational.Contains; import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExcludesExpression; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; @@ -36,6 +37,7 @@ import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.expression.operators.relational.IncludesExpression; import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; @@ -46,9 +48,12 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.Plus; +import net.sf.jsqlparser.expression.operators.relational.PriorTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; -import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; @@ -762,4 +767,29 @@ public T visit(LambdaExpression lambdaExpression, S context) { return lambdaExpression.getExpression().accept(this, context); } + @Override + public T visit(HighExpression highExpression, S context) { + return highExpression.getExpression().accept(this, context); + } + + @Override + public T visit(LowExpression lowExpression, S context) { + return lowExpression.getExpression().accept(this, context); + } + + @Override + public T visit(Plus plus, S context) { + return visitBinaryExpression(plus, context); + } + + @Override + public T visit(PriorTo priorTo, S context) { + return visitBinaryExpression(priorTo, context); + } + + @Override + public T visit(Inverse inverse, S context) { + return inverse.getExpression().accept(this, context); + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/HighExpression.java b/src/main/java/net/sf/jsqlparser/expression/HighExpression.java new file mode 100644 index 000000000..4d3a9cd63 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/HighExpression.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class HighExpression extends ASTNodeAccessImpl implements Expression { + private Expression expression; + + public HighExpression() { + // empty constructor + } + + public HighExpression(Expression expression) { + this.expression = expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return "HIGH " + expression.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/Inverse.java b/src/main/java/net/sf/jsqlparser/expression/Inverse.java new file mode 100644 index 000000000..b50d8be8f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/Inverse.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class Inverse extends ASTNodeAccessImpl implements Expression { + private Expression expression; + + public Inverse() { + // empty constructor + } + + public Inverse(Expression expression) { + this.expression = expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return "INVERSE (" + expression.toString() + ")"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/LowExpression.java b/src/main/java/net/sf/jsqlparser/expression/LowExpression.java new file mode 100644 index 000000000..2d2882a53 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/LowExpression.java @@ -0,0 +1,42 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class LowExpression extends ASTNodeAccessImpl implements Expression { + private Expression expression; + + public LowExpression() { + // empty constructor + } + + public LowExpression(Expression expression) { + this.expression = expression; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } + + public Expression getExpression() { + return expression; + } + + public void setExpression(Expression expression) { + this.expression = expression; + } + + @Override + public String toString() { + return "LOW " + expression.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/PreferringClause.java b/src/main/java/net/sf/jsqlparser/expression/PreferringClause.java new file mode 100644 index 000000000..00cf086fd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/PreferringClause.java @@ -0,0 +1,48 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; + +import java.io.Serializable; + +public class PreferringClause implements Serializable { + private Expression preferring; + private PartitionByClause partitionBy; + + public PreferringClause(Expression preferring) { + this.preferring = preferring; + } + + public void setPartitionExpressionList(ExpressionList expressionList, + boolean brackets) { + if (this.partitionBy == null) { + this.partitionBy = new PartitionByClause(); + } + partitionBy.setPartitionExpressionList(expressionList, brackets); + } + + public void toStringPreferring(StringBuilder b) { + b.append("PREFERRING "); + b.append(preferring.toString()); + + if (partitionBy != null) { + b.append(" "); + partitionBy.toStringPartitionBy(b); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + toStringPreferring(sb); + return sb.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Plus.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Plus.java new file mode 100644 index 000000000..e3362d950 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Plus.java @@ -0,0 +1,30 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Plus extends BinaryExpression { + public Plus(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public String getStringExpression() { + return "PLUS"; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/PriorTo.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/PriorTo.java new file mode 100644 index 000000000..267f158fd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/PriorTo.java @@ -0,0 +1,30 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class PriorTo extends BinaryExpression { + public PriorTo(Expression leftExpression, Expression rightExpression) { + super(leftExpression, rightExpression); + } + + @Override + public String getStringExpression() { + return "PRIOR TO"; + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index 7491a9c36..f6ff7e7aa 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -79,6 +79,7 @@ public class ParserKeywordsUtils { {"GROUPING", RESTRICTED_ALIAS}, {"QUALIFY", RESTRICTED_ALIAS}, {"HAVING", RESTRICTED_SQL2016}, + {"HIGH", RESTRICTED_JSQLPARSER}, {"IF", RESTRICTED_SQL2016}, {"IIF", RESTRICTED_ALIAS}, {"IGNORE", RESTRICTED_ALIAS}, @@ -89,12 +90,14 @@ public class ParserKeywordsUtils { {"INTERSECT", RESTRICTED_SQL2016}, {"INTERVAL", RESTRICTED_SQL2016}, {"INTO", RESTRICTED_JSQLPARSER}, + {"INVERSE", RESTRICTED_JSQLPARSER}, {"IS", RESTRICTED_SQL2016}, {"JOIN", RESTRICTED_JSQLPARSER}, {"LATERAL", RESTRICTED_SQL2016}, {"LEFT", RESTRICTED_SQL2016}, {"LIKE", RESTRICTED_SQL2016}, {"LIMIT", RESTRICTED_SQL2016}, + {"LOW", RESTRICTED_JSQLPARSER}, {"MINUS", RESTRICTED_SQL2016}, {"NATURAL", RESTRICTED_SQL2016}, {"NOCYCLE", RESTRICTED_JSQLPARSER}, @@ -110,6 +113,8 @@ public class ParserKeywordsUtils { {"OUTPUT", RESTRICTED_JSQLPARSER}, {"OPTIMIZE ", RESTRICTED_JSQLPARSER}, {"PIVOT", RESTRICTED_JSQLPARSER}, + {"PLUS", RESTRICTED_JSQLPARSER}, + {"PREFERRING", RESTRICTED_JSQLPARSER}, {"PROCEDURE", RESTRICTED_ALIAS}, {"PUBLIC", RESTRICTED_ALIAS}, {"RETURNING", RESTRICTED_JSQLPARSER}, @@ -232,14 +237,15 @@ public static TreeSet getAllKeywordsUsingRegex(File file) throws IOExcep // remove single and multiline comments tokenBlock = tokenBlock.replaceAll("(?sm)((\\/\\*.*?\\*\\/)|(\\/\\/.*?$))", ""); for (String tokenDefinition : getTokenDefinitions(tokenBlock)) { - // check if token definition is private - if (tokenDefinition.matches("(?sm)^<\\s*[^#].*")) { - Matcher tokenStringValueMatcher = tokenStringValuePattern.matcher(tokenDefinition); + // check if token definition is private + if (tokenDefinition.matches("(?sm)^<\\s*[^#].*")) { + Matcher tokenStringValueMatcher = + tokenStringValuePattern.matcher(tokenDefinition); while (tokenStringValueMatcher.find()) { String tokenValue = tokenStringValueMatcher.group(1); // test if pure US-ASCII if (CHARSET_ENCODER.canEncode(tokenValue) && tokenValue.matches("\\w+")) { - allKeywords.add(tokenValue); + allKeywords.add(tokenValue); } } } @@ -260,7 +266,7 @@ private static List getTokenDefinitions(String tokenBlock) { if (isQuotationMark(i, tokenBlockChars)) { // skip everything inside quotation marks while (!isQuotationMark(++i, tokenBlockChars)) { - // skip until quotation ends + // skip until quotation ends } } diff --git a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java index 0ff9b8868..3072d9872 100644 --- a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java +++ b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.PreferringClause; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.OutputClause; import net.sf.jsqlparser.statement.ReturningClause; @@ -40,6 +41,7 @@ public class Delete implements Statement { private List usingList; private List joins; private Expression where; + private PreferringClause preferringClause; private Limit limit; private List orderByElements; private boolean hasFrom = true; @@ -123,6 +125,14 @@ public void setWhere(Expression expression) { where = expression; } + public PreferringClause getPreferringClause() { + return preferringClause; + } + + public void setPreferringClause(PreferringClause preferringClause) { + this.preferringClause = preferringClause; + } + public OracleHint getOracleHint() { return oracleHint; } @@ -239,6 +249,10 @@ public String toString() { b.append(" WHERE ").append(where); } + if (preferringClause != null) { + b.append(" ").append(preferringClause); + } + if (orderByElements != null) { b.append(PlainSelect.orderByToString(orderByElements)); } @@ -289,6 +303,11 @@ public Delete withWhere(Expression where) { return this; } + public Delete withPreferringClause(PreferringClause preferringClause) { + this.setPreferringClause(preferringClause); + return this; + } + public Delete withHasFrom(boolean hasFrom) { this.setHasFrom(hasFrom); return this; diff --git a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java index 414a60945..1e84123c4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -13,6 +13,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.OracleHierarchicalExpression; import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.PreferringClause; import net.sf.jsqlparser.expression.WindowDefinition; import net.sf.jsqlparser.schema.Table; @@ -46,6 +47,7 @@ public class PlainSelect extends Select { private First first; private Top top; private OracleHierarchicalExpression oracleHierarchical = null; + private PreferringClause preferringClause = null; private OracleHint oracleHint = null; private boolean mySqlSqlCalcFoundRows = false; private MySqlSqlCacheFlags mySqlCacheFlag = null; @@ -424,6 +426,14 @@ public void setOracleHierarchical(OracleHierarchicalExpression oracleHierarchica this.oracleHierarchical = oracleHierarchical; } + public PreferringClause getPreferringClause() { + return preferringClause; + } + + public void setPreferringClause(PreferringClause preferringClause) { + this.preferringClause = preferringClause; + } + public OracleHint getOracleHint() { return oracleHint; } @@ -555,6 +565,9 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) { if (oracleHierarchical != null) { builder.append(oracleHierarchical); } + if (preferringClause != null) { + builder.append(" ").append(preferringClause); + } if (groupBy != null) { builder.append(" ").append(groupBy); } @@ -676,6 +689,11 @@ public PlainSelect withOracleHierarchical(OracleHierarchicalExpression oracleHie return this; } + public PlainSelect withPreferringClause(PreferringClause preferringClause) { + this.setPreferringClause(preferringClause); + return this; + } + public PlainSelect withOracleHint(OracleHint oracleHint) { this.setOracleHint(oracleHint); return this; diff --git a/src/main/java/net/sf/jsqlparser/statement/update/Update.java b/src/main/java/net/sf/jsqlparser/statement/update/Update.java index 2d948afda..b09854fe6 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/Update.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/Update.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.PreferringClause; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.OutputClause; @@ -39,6 +40,7 @@ public class Update implements Statement { private List> withItemsList; private Table table; private Expression where; + private PreferringClause preferringClause; private List updateSets; private FromItem fromItem; private List joins; @@ -125,6 +127,14 @@ public void setWhere(Expression expression) { where = expression; } + public PreferringClause getPreferringClause() { + return preferringClause; + } + + public void setPreferringClause(PreferringClause preferringClause) { + this.preferringClause = preferringClause; + } + public OracleHint getOracleHint() { return oracleHint; } @@ -336,6 +346,9 @@ public String toString() { b.append(" WHERE "); b.append(where); } + if (preferringClause != null) { + b.append(" ").append(preferringClause); + } if (orderByElements != null) { b.append(PlainSelect.orderByToString(orderByElements)); } @@ -400,6 +413,11 @@ public Update withWhere(Expression where) { return this; } + public Update withPreferringClause(PreferringClause preferringClause) { + this.setPreferringClause(preferringClause); + return this; + } + public Update withColumns(List columns) { this.setColumns(columns); return this; diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 37746e42d..60335a9d9 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -28,7 +28,9 @@ import net.sf.jsqlparser.expression.ExtractExpression; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.HexValue; +import net.sf.jsqlparser.expression.HighExpression; import net.sf.jsqlparser.expression.IntervalExpression; +import net.sf.jsqlparser.expression.Inverse; import net.sf.jsqlparser.expression.JdbcNamedParameter; import net.sf.jsqlparser.expression.JdbcParameter; import net.sf.jsqlparser.expression.JsonAggregateFunction; @@ -38,6 +40,7 @@ import net.sf.jsqlparser.expression.KeepExpression; import net.sf.jsqlparser.expression.LambdaExpression; import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.LowExpression; import net.sf.jsqlparser.expression.MySQLGroupConcat; import net.sf.jsqlparser.expression.NextValExpression; import net.sf.jsqlparser.expression.NotExpression; @@ -102,6 +105,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.Plus; +import net.sf.jsqlparser.expression.operators.relational.PriorTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; @@ -1589,6 +1594,36 @@ public Void visit(LambdaExpression lambdaExpression, S context) { return null; } + @Override + public Void visit(HighExpression highExpression, S context) { + highExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(LowExpression lowExpression, S context) { + lowExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(Plus plus, S context) { + visitBinaryExpression(plus); + return null; + } + + @Override + public Void visit(PriorTo priorTo, S context) { + visitBinaryExpression(priorTo); + return null; + } + + @Override + public Void visit(Inverse inverse, S context) { + inverse.getExpression().accept(this, context); + return null; + } + @Override public Void visit(VariableAssignment variableAssignment, S context) { variableAssignment.getVariable().accept(this, context); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java index 36fe3c0c8..11e6eb001 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java @@ -95,6 +95,9 @@ public void deParse(Delete delete) { deparseWhereClause(delete); + if (delete.getPreferringClause() != null) { + buffer.append(" ").append(delete.getPreferringClause()); + } if (delete.getOrderByElements() != null) { new OrderByDeParser(expressionVisitor, buffer).deParse(delete.getOrderByElements()); } 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 bb6b6a146..17f38d86b 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -28,7 +28,9 @@ import net.sf.jsqlparser.expression.ExtractExpression; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.HexValue; +import net.sf.jsqlparser.expression.HighExpression; import net.sf.jsqlparser.expression.IntervalExpression; +import net.sf.jsqlparser.expression.Inverse; import net.sf.jsqlparser.expression.JdbcNamedParameter; import net.sf.jsqlparser.expression.JdbcParameter; import net.sf.jsqlparser.expression.JsonAggregateFunction; @@ -37,6 +39,7 @@ import net.sf.jsqlparser.expression.KeepExpression; import net.sf.jsqlparser.expression.LambdaExpression; import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.LowExpression; import net.sf.jsqlparser.expression.MySQLGroupConcat; import net.sf.jsqlparser.expression.NextValExpression; import net.sf.jsqlparser.expression.NotExpression; @@ -103,6 +106,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; +import net.sf.jsqlparser.expression.operators.relational.Plus; +import net.sf.jsqlparser.expression.operators.relational.PriorTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; @@ -1712,4 +1717,29 @@ public StringBuilder visit(LambdaExpression lambdaExpression, S context) { lambdaExpression.getExpression().accept(this, context); return buffer; } + + @Override + public StringBuilder visit(HighExpression highExpression, S context) { + return buffer.append(highExpression.toString()); + } + + @Override + public StringBuilder visit(LowExpression lowExpression, S context) { + return buffer.append(lowExpression.toString()); + } + + @Override + public StringBuilder visit(Plus plus, S context) { + return buffer.append(plus.toString()); + } + + @Override + public StringBuilder visit(PriorTo priorTo, S context) { + return buffer.append(priorTo.toString()); + } + + @Override + public StringBuilder visit(Inverse inverse, S context) { + return buffer.append(inverse.toString()); + } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index 81a265d8b..9fb1673e9 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -260,6 +260,10 @@ public StringBuilder visit(PlainSelect plainSelect, S context) { plainSelect.getOracleHierarchical().accept(expressionVisitor, context); } + if (plainSelect.getPreferringClause() != null) { + buffer.append(" ").append(plainSelect.getPreferringClause().toString()); + } + if (plainSelect.getGroupBy() != null) { buffer.append(" "); new GroupByDeParser(expressionVisitor, buffer).deParse(plainSelect.getGroupBy()); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java index 071ceeca5..a16deec83 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java @@ -93,6 +93,9 @@ public void deParse(Update update) { deparseWhereClause(update); + if (update.getPreferringClause() != null) { + buffer.append(" ").append(update.getPreferringClause()); + } if (update.getOrderByElements() != null) { new OrderByDeParser(expressionVisitor, buffer).deParse(update.getOrderByElements()); } diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java index ae82e703a..6c662d26e 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java @@ -27,7 +27,9 @@ import net.sf.jsqlparser.expression.ExtractExpression; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.HexValue; +import net.sf.jsqlparser.expression.HighExpression; import net.sf.jsqlparser.expression.IntervalExpression; +import net.sf.jsqlparser.expression.Inverse; import net.sf.jsqlparser.expression.JdbcNamedParameter; import net.sf.jsqlparser.expression.JdbcParameter; import net.sf.jsqlparser.expression.JsonAggregateFunction; @@ -36,6 +38,7 @@ import net.sf.jsqlparser.expression.KeepExpression; import net.sf.jsqlparser.expression.LambdaExpression; import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.LowExpression; import net.sf.jsqlparser.expression.MySQLGroupConcat; import net.sf.jsqlparser.expression.NextValExpression; import net.sf.jsqlparser.expression.NotExpression; @@ -104,6 +107,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; +import net.sf.jsqlparser.expression.operators.relational.Plus; +import net.sf.jsqlparser.expression.operators.relational.PriorTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; @@ -1101,6 +1106,36 @@ public Void visit(LambdaExpression lambdaExpression, S context) { return null; } + @Override + public Void visit(HighExpression highExpression, S context) { + highExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(LowExpression lowExpression, S context) { + lowExpression.getExpression().accept(this, context); + return null; + } + + @Override + public Void visit(Plus plus, S context) { + visitBinaryExpression(plus, " PLUS "); + return null; + } + + @Override + public Void visit(PriorTo priorTo, S context) { + visitBinaryExpression(priorTo, " PLUS "); + return null; + } + + @Override + public Void visit(Inverse inverse, S context) { + inverse.getExpression().accept(this, context); + return null; + } + public void visit(TimeKeyExpression timeKeyExpression) { visit(timeKeyExpression, null); } @@ -1210,4 +1245,24 @@ public void visit(LambdaExpression lambdaExpression) { visit(lambdaExpression, null); } + public void visit(HighExpression highExpression) { + visit(highExpression, null); + } + + public void visit(LowExpression lowExpression) { + visit(lowExpression, null); + } + + public void visit(Plus plus) { + visit(plus, null); + } + + public void visit(PriorTo priorTo) { + visit(priorTo, null); + } + + public void visit(Inverse inverse) { + visit(inverse, null); + } + } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 7dd9706bd..984f4c10c 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -279,6 +279,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -301,6 +302,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -324,6 +326,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -377,6 +380,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| +| | | | @@ -1486,6 +1491,7 @@ Update Update(): List> with = null; List updateSets; Expression where = null; + PreferringClause preferringClause = null; FromItem fromItem = null; List joins = null; Limit limit = null; @@ -1512,6 +1518,7 @@ Update Update(): [ LOOKAHEAD(2) joins=JoinsList() ] ] [ where=WhereClause() { update.setWhere(where); } ] + [ preferringClause=PreferringClause() { update.setPreferringClause(preferringClause); } ] [ orderByElements = OrderByElements() { update.setOrderByElements(orderByElements); } ] [ limit = PlainLimit() { update.setLimit(limit); } ] @@ -1816,6 +1823,7 @@ Delete Delete(): List
usingList = new ArrayList
(); List joins = null; Expression where = null; + PreferringClause preferringClause = null; Limit limit = null; List orderByElements; boolean hasFrom = false; @@ -1841,6 +1849,7 @@ Delete Delete(): [ usingTable=TableWithAlias() { usingList.add(usingTable); } ("," usingTable=TableWithAlias() { usingList.add(usingTable); } )*] [where=WhereClause() { delete.setWhere(where); } ] + [preferringClause=PreferringClause() { delete.setPreferringClause(preferringClause);} ] [orderByElements = OrderByElements() { delete.setOrderByElements(orderByElements); } ] [limit=PlainLimit() {delete.setLimit(limit); } ] @@ -2439,6 +2448,9 @@ PlainSelect PlainSelect() #PlainSelect: Skip skip = null; First first = null; OracleHierarchicalExpression oracleHierarchicalQueryClause = null; + PreferringClause preferringClause = null; + ExpressionList expressionList = null; + boolean partitionByBrackets = false; List
intoTables = null; Table updateTable = null; Wait wait = null; @@ -2514,6 +2526,17 @@ PlainSelect PlainSelect() #PlainSelect: [ LOOKAHEAD(2) ksqlWindow=KSQLWindowClause() { plainSelect.setKsqlWindow(ksqlWindow); } ] [ LOOKAHEAD(2) where=WhereClause() { plainSelect.setWhere(where); }] [ LOOKAHEAD(2) oracleHierarchicalQueryClause=OracleHierarchicalQueryClause() { plainSelect.setOracleHierarchical(oracleHierarchicalQueryClause); } ] + [ LOOKAHEAD(2) preferringClause=PreferringClause() { plainSelect.setPreferringClause(preferringClause); } + [LOOKAHEAD(2) + ( + (LOOKAHEAD(ComplexExpressionList()) expressionList=ComplexExpressionList() + | "(" {partitionByBrackets = true;} expressionList=ComplexExpressionList() ")" ) + ) + { + preferringClause.setPartitionExpressionList(expressionList, partitionByBrackets); + } + ] + ] // Oracle supports "HAVING" before "GROUP BY", we will simply parse that but won't pay special attention to the order [ LOOKAHEAD(2) having=Having() { plainSelect.setHaving(having); }] [ LOOKAHEAD(2) groupBy=GroupByColumnReferences() { plainSelect.setGroupByElement(groupBy); }] @@ -3301,6 +3324,145 @@ OracleHierarchicalExpression OracleHierarchicalQueryClause(): } } +PreferringClause PreferringClause(): +{ + Expression result = null; +} +{ + + result=PreferenceTerm() + + { + PreferringClause preferring = new PreferringClause(result); + return preferring; + } +} + +Expression PreferenceTerm(): +{ + Expression retval = null; +} +{ + // recursively build preferenceterm inside Plus + // like Expression -> XorExpression -> OrExpression -> ... + + retval=Plus() + + { + return retval; + } +} + +Expression Plus(): +{ + Expression left, right, result; +} +{ + left=PriorTo() { result = left; } + ( + LOOKAHEAD(2) + + right=PriorTo() + { + result = new Plus(left, right); + left = result; + } + )* + { + return result; + } +} + +Expression PriorTo(): +{ + Expression left, right, result; +} +{ + ( + LOOKAHEAD(PreferenceTermTerminal(), {!interrupted}) left=PreferenceTermTerminal() + | "(" left=PreferenceTerm() ")" { left = new ParenthesedExpressionList(left); } + ) + { result = left; } + + ( + LOOKAHEAD(2) + + ( + LOOKAHEAD(PreferenceTermTerminal(), {!interrupted}) right=PreferenceTermTerminal() + | "(" right=PreferenceTerm() ")" { left = new ParenthesedExpressionList(right); } + ) + { + result = new PriorTo(left, right); + left = result; + } + )* + { + return result; + } +} + +Expression PreferenceTermTerminal(): +{ + Expression retval; +} +{ + ( + retval=HighExpression() + | retval=LowExpression() + | retval=Inverse() + | retval=Condition() + ) + + { + return retval; + } +} + +Expression HighExpression() #HighExpression: +{ + Expression result; +} +{ + + result=Expression() + + { + HighExpression high = new HighExpression(result); + linkAST(high,jjtThis); + return high; + } +} + +Expression LowExpression() #LowExpression: +{ + Expression result; +} +{ + + result=Expression() + + { + LowExpression low = new LowExpression(result); + linkAST(low,jjtThis); + return low; + } +} + +Expression Inverse() #Inverse: +{ + Expression result; +} +{ + + "(" result=PreferenceTerm() ")" + + { + Inverse inverse = new Inverse(result); + linkAST(inverse,jjtThis); + return inverse; + } +} + GroupByElement GroupByColumnReferences(): { Expression columnReference; diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst index a8500b393..133ce39cd 100644 --- a/src/site/sphinx/keywords.rst +++ b/src/site/sphinx/keywords.rst @@ -75,6 +75,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | HAVING | Yes | Yes | +----------------------+-------------+-----------+ +| HIGH | Yes | Yes | ++----------------------+-------------+-----------+ | IF | Yes | Yes | +----------------------+-------------+-----------+ | IIF | Yes | | @@ -95,6 +97,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | INTO | Yes | Yes | +----------------------+-------------+-----------+ +| INVERSE | Yes | Yes | ++----------------------+-------------+-----------+ | IS | Yes | Yes | +----------------------+-------------+-----------+ | JOIN | Yes | Yes | @@ -107,6 +111,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | LIMIT | Yes | Yes | +----------------------+-------------+-----------+ +| LOW | Yes | Yes | ++----------------------+-------------+-----------+ | MINUS | Yes | Yes | +----------------------+-------------+-----------+ | NATURAL | Yes | Yes | @@ -137,6 +143,10 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | PIVOT | Yes | Yes | +----------------------+-------------+-----------+ +| PLUS | Yes | Yes | ++----------------------+-------------+-----------+ +| PREFERRING | Yes | Yes | ++----------------------+-------------+-----------+ | PROCEDURE | Yes | | +----------------------+-------------+-----------+ | PUBLIC | Yes | | diff --git a/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java b/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java index a02137a19..aaa7e57cc 100644 --- a/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/delete/DeleteTest.java @@ -31,6 +31,8 @@ import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.update.Update; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; public class DeleteTest { @@ -371,4 +373,18 @@ void testSelectAndInsertWithin2Ctes() throws JSQLParserException { assertEquals(" inserted", withItems.get(1).getAlias().toString()); } + @ParameterizedTest + @ValueSource(strings = { + "DELETE FROM mytable PREFERRING HIGH mycolumn", + "DELETE FROM mytable PREFERRING LOW mycolumn", + "DELETE FROM mytable PREFERRING 1 = 1", + "DELETE FROM mytable PREFERRING (HIGH mycolumn)", + "DELETE FROM mytable PREFERRING INVERSE (HIGH mycolumn)", + "DELETE FROM mytable PREFERRING HIGH mycolumn1 PRIOR TO LOW mycolumn2", + "DELETE FROM mytable PREFERRING HIGH mycolumn1 PLUS LOW mycolumn2" + }) + public void testPreferringClause(String sqlStr) throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed(sqlStr); + } + } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index ca8039843..fd407ee47 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -5983,6 +5983,21 @@ public void execute() throws Throwable { }); } + @ParameterizedTest + @ValueSource(strings = { + "SELECT * FROM mytable PREFERRING HIGH mycolumn", + "SELECT * FROM mytable PREFERRING LOW mycolumn", + "SELECT * FROM mytable PREFERRING 1 = 1", + "SELECT * FROM mytable PREFERRING (HIGH mycolumn)", + "SELECT * FROM mytable PREFERRING INVERSE (HIGH mycolumn)", + "SELECT * FROM mytable PREFERRING HIGH mycolumn1 PRIOR TO LOW mycolumn2", + "SELECT * FROM mytable PREFERRING HIGH mycolumn1 PLUS LOW mycolumn2", + "SELECT * FROM mytable PREFERRING HIGH mycolumn PARTITION BY mycolumn" + }) + public void testPreferringClause(String sqlStr) throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr); + } + @Test void testInsertWithinCte() throws JSQLParserException { String sqlStr = "WITH inserted AS ( " + diff --git a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java index 90d04c358..75ff452b0 100644 --- a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java @@ -25,6 +25,8 @@ import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.io.StringReader; import java.util.List; @@ -534,4 +536,18 @@ void testSelectAndInsertWithin2Ctes() throws JSQLParserException { assertEquals(" inserted", withItems.get(1).getAlias().toString()); } + @ParameterizedTest + @ValueSource(strings = { + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING HIGH mycolumn", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING LOW mycolumn", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING 1 = 1", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING (HIGH mycolumn)", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING INVERSE (HIGH mycolumn)", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING HIGH mycolumn1 PRIOR TO LOW mycolumn2", + "UPDATE mytable SET mycolumn1 = mycolumn2 PREFERRING HIGH mycolumn1 PLUS LOW mycolumn2" + }) + public void testPreferringClause(String sqlStr) throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed(sqlStr); + } + }