diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 000000000..d47168171 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,34 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + - name: Build with Gradle + uses: gradle/gradle-build-action@v2.4.2 + with: + arguments: check diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 8aa61db7b..7317ab2f9 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -9,7 +9,7 @@ jobs: - name: Install XSLT Processor run: sudo apt-get install xsltproc sphinx-common - name: Install dependencies - run: pip install furo sphinx_rtd_theme sphinx-book-theme myst_parser sphinx-prompt sphinx_substitution_extensions sphinx_issues sphinx_inline_tabs pygments + run: pip install furo myst_parser sphinx-prompt sphinx_substitution_extensions sphinx_issues sphinx_inline_tabs pygments - name: Checkout project sources uses: actions/checkout@v2 with: diff --git a/README.md b/README.md index 643f6301a..61316bea2 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ SELECT 1 FROM dual WHERE a = b ``` ```text - SQL Text - └─Statements: net.sf.jsqlparser.statement.select.Select - ├─selectItems -> Collection - │ └─LongValue: 1 - ├─Table: dual - └─where: net.sf.jsqlparser.expression.operators.relational.EqualsTo - ├─Column: a - └─Column: b +SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─LongValue: 1 + ├─Table: dual + └─where: expression.operators.relational.EqualsTo + ├─Column: a + └─Column: b ``` ```java diff --git a/build.gradle b/build.gradle index a0003d29a..e1919b9a5 100644 --- a/build.gradle +++ b/build.gradle @@ -7,6 +7,7 @@ plugins { id "ca.coglinc2.javacc" version "latest.release" id 'jacoco' + id 'com.github.kt3k.coveralls' version "latest.release" id "com.github.spotbugs" version "latest.release" id "com.diffplug.spotless" version "latest.release" id 'pmd' @@ -179,6 +180,10 @@ test { maxHeapSize = "1G" } +coveralls { + jacocoReportPath 'build/reports/jacoco/test/jacocoTestReport.xml' +} + jacocoTestReport { dependsOn test // tests are required to run before generating the report reports { @@ -272,9 +277,7 @@ spotbugs { } pmd { - consoleOutput = false - //toolVersion = "6.46.0" - + consoleOutput = true sourceSets = [sourceSets.main] // clear the ruleset in order to use configured rules only @@ -436,23 +439,6 @@ xslt { tasks.register('sphinx', Exec) { dependsOn(gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc) -// doFirst() { -// exec { -// args = [ -// "install" -// , "sphinx_rtd_theme" -// , "sphinx-book-theme" -// , "myst_parser" -// , "sphinx-prompt" -// , "sphinx_substitution_extensions" -// , "sphinx_issues" -// , "sphinx_inline_tabs" -// , "pygments" -// ] -// executable "pip" -// } -// } - String PROLOG = """ .. |_| unicode:: U+00A0 :trim: @@ -555,7 +541,7 @@ publishing { maven { name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/manticore-projects/jsqlparser") + url = uri("https://maven.pkg.github.com/JSQLParser/jsqlparser") credentials(PasswordCredentials) } } diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index b611f89c2..b1bc3ffe5 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -44,7 +44,6 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.AllColumns; @@ -168,8 +167,6 @@ public interface ExpressionVisitor { void visit(JsonOperator jsonExpr); - void visit(RegExpMySQLOperator regExpMySQLOperator); - void visit(UserVariable var); void visit(NumericBind bind); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 965d767fc..867cd5fa4 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -44,7 +44,6 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.AllColumns; @@ -433,11 +432,6 @@ public void visit(JsonOperator expr) { visitBinaryExpression(expr); } - @Override - public void visit(RegExpMySQLOperator expr) { - visitBinaryExpression(expr); - } - @Override public void visit(UserVariable var) { diff --git a/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java index a374785f9..26584c63d 100644 --- a/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/IntervalExpression.java @@ -9,9 +9,10 @@ */ package net.sf.jsqlparser.expression; -import java.util.Objects; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import java.util.Objects; + public class IntervalExpression extends ASTNodeAccessImpl implements Expression { private String parameter = null; @@ -27,6 +28,10 @@ public IntervalExpression(boolean intervalKeyword) { this.intervalKeyword = intervalKeyword; } + public boolean isUsingIntervalKeyword() { + return intervalKeyword; + } + public String getParameter() { return parameter; } diff --git a/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java index 40c4678af..def6ec3d1 100644 --- a/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java +++ b/src/main/java/net/sf/jsqlparser/expression/TrimFunction.java @@ -96,7 +96,7 @@ public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); } - StringBuilder appendTo(StringBuilder builder) { + public StringBuilder appendTo(StringBuilder builder) { builder.append("Trim("); if (trimSpecification != null) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java index 9f08215b8..090d2707b 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java @@ -74,7 +74,7 @@ public ExpressionList withExpressions(T... expressions) { return addExpressions(expressions); } - public ExpressionList withExpressions(Collection expressions) { + public ExpressionList withExpressions(Collection expressions) { this.clear(); return addExpressions(expressions); } 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 eac3f2904..b292b827a 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 @@ -18,6 +18,7 @@ public class IsNullExpression extends ASTNodeAccessImpl implements Expression { private Expression leftExpression; private boolean not = false; private boolean useIsNull = false; + private boolean useNotNull = false; public Expression getLeftExpression() { return leftExpression; @@ -43,6 +44,15 @@ public void setUseIsNull(boolean useIsNull) { this.useIsNull = useIsNull; } + public boolean isUseNotNull() { + return useNotNull; + } + + public IsNullExpression setUseNotNull(boolean useNotNull) { + this.useNotNull = useNotNull; + return this; + } + @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); @@ -50,7 +60,9 @@ public void accept(ExpressionVisitor expressionVisitor) { @Override public String toString() { - if (isUseIsNull()) { + if (useNotNull) { + return leftExpression + " NOTNULL"; + } else if (useIsNull) { return leftExpression + (not ? " NOT" : "") + " ISNULL"; } else { return leftExpression + " IS " + (not ? "NOT " : "") + "NULL"; 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 fa094dca6..13bb3a223 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,10 +14,18 @@ import net.sf.jsqlparser.expression.ExpressionVisitor; public class LikeExpression extends BinaryExpression { + public enum KeyWord { + LIKE, ILIKE, RLIKE, REGEXP; + + public static KeyWord from(String keyword) { + return Enum.valueOf(KeyWord.class, keyword.toUpperCase()); + } + } private boolean not = false; + private boolean useBinary = false; private Expression escapeExpression = null; - private boolean caseInsensitive = false; + private KeyWord likeKeyWord = KeyWord.LIKE; public boolean isNot() { return not; @@ -27,23 +35,33 @@ public void setNot(boolean b) { not = b; } + public boolean isUseBinary() { + return useBinary; + } + + public LikeExpression setUseBinary(boolean useBinary) { + this.useBinary = useBinary; + return this; + } + @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); } + @Deprecated @Override public String getStringExpression() { - return caseInsensitive ? "ILIKE" : "LIKE"; + return likeKeyWord.toString(); } @Override public String toString() { - String retval = getLeftExpression() + " " + (not ? "NOT " : "") + getStringExpression() + " " + getRightExpression(); + String retval = getLeftExpression() + " " + (not ? "NOT " : "") + + likeKeyWord + " " + (useBinary ? "BINARY " : "") + getRightExpression(); if (escapeExpression != null) { - retval += " ESCAPE " + escapeExpression ; + retval += " ESCAPE " + escapeExpression; } - return retval; } @@ -55,12 +73,28 @@ public void setEscape(Expression escapeExpression) { this.escapeExpression = escapeExpression; } + @Deprecated public boolean isCaseInsensitive() { - return caseInsensitive; + return likeKeyWord == KeyWord.ILIKE; } + @Deprecated public void setCaseInsensitive(boolean caseInsensitive) { - this.caseInsensitive = caseInsensitive; + this.likeKeyWord = KeyWord.ILIKE; + } + + public KeyWord getLikeKeyWord() { + return likeKeyWord; + } + + public LikeExpression setLikeKeyWord(KeyWord likeKeyWord) { + this.likeKeyWord = likeKeyWord; + return this; + } + + public LikeExpression setLikeKeyWord(String likeKeyWord) { + this.likeKeyWord = KeyWord.from(likeKeyWord); + return this; } public LikeExpression withEscape(Expression escape) { @@ -68,6 +102,7 @@ public LikeExpression withEscape(Expression escape) { return this; } + @Deprecated public LikeExpression withCaseInsensitive(boolean caseInsensitive) { this.setCaseInsensitive(caseInsensitive); return this; diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java deleted file mode 100644 index 82059b1d5..000000000 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/RegExpMySQLOperator.java +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * #%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 java.util.Objects; -import net.sf.jsqlparser.expression.BinaryExpression; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.ExpressionVisitor; - -public class RegExpMySQLOperator extends BinaryExpression { - - private RegExpMatchOperatorType operatorType; - private boolean useRLike = false; - private boolean not = false; - - public RegExpMySQLOperator(RegExpMatchOperatorType operatorType) { - this(false, operatorType); - } - - public RegExpMySQLOperator(boolean not, RegExpMatchOperatorType operatorType) { - this.operatorType = Objects.requireNonNull(operatorType, "The provided RegExpMatchOperatorType must not be NULL."); - this.not = not; - } - - public boolean isNot() { - return not; - } - - public void setNot(boolean not) { - this.not = not; - } - - public RegExpMatchOperatorType getOperatorType() { - return operatorType; - } - - public boolean isUseRLike() { - return useRLike; - } - - public RegExpMySQLOperator useRLike() { - useRLike = true; - return this; - } - - @Override - public void accept(ExpressionVisitor expressionVisitor) { - expressionVisitor.visit(this); - } - - @Override - public String getStringExpression() { - return (not?"NOT ":"") - + (useRLike ? "RLIKE" : "REGEXP") - + (operatorType == RegExpMatchOperatorType.MATCH_CASESENSITIVE ? " BINARY" : ""); - } - - @Override - public RegExpMySQLOperator withLeftExpression(Expression arg0) { - return (RegExpMySQLOperator) super.withLeftExpression(arg0); - } - - @Override - public RegExpMySQLOperator withRightExpression(Expression arg0) { - return (RegExpMySQLOperator) super.withRightExpression(arg0); - } -} diff --git a/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java index b03858982..e13ec6ff6 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java +++ b/src/main/java/net/sf/jsqlparser/parser/ASTNodeAccessImpl.java @@ -23,4 +23,15 @@ public void setASTNode(SimpleNode node) { this.node = node; } + public StringBuilder appendTo(StringBuilder builder) { + SimpleNode simpleNode = getASTNode(); + Token token = simpleNode.jjtGetFirstToken(); + Token lastToken = simpleNode.jjtGetLastToken(); + while (token.next != null && token.absoluteEnd <= lastToken.absoluteEnd) { + builder.append(" ").append(token.image); + token = token.next; + } + return builder; + } + } diff --git a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java index c366cb80e..f99c7a8eb 100644 --- a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java +++ b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java @@ -299,7 +299,7 @@ public static Statement parseStatement(CCJSqlParser parser, ExecutorService exec Statement statement = null; Future future = executorService.submit(new Callable() { @Override - public Statement call() throws Exception { + public Statement call() throws ParseException { return parser.Statement(); } }); @@ -380,7 +380,7 @@ public static Statements parseStatements(CCJSqlParser parser, ExecutorService ex Statements statements = null; Future future = executorService.submit(new Callable() { @Override - public Statements call() throws Exception { + public Statements call() throws ParseException { return parser.Statements(); } }); diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index 8d7be17a1..53e49d93a 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -59,6 +59,7 @@ public class ParserKeywordsUtils { {"FORCE", RESTRICTED_SQL2016}, {"FOREIGN", RESTRICTED_SQL2016}, {"FROM", RESTRICTED_SQL2016}, {"FULL", RESTRICTED_SQL2016}, {"GROUP", RESTRICTED_SQL2016}, {"GROUPING", RESTRICTED_ALIAS}, + {"QUALIFY", RESTRICTED_ALIAS}, {"HAVING", RESTRICTED_SQL2016}, {"IF", RESTRICTED_SQL2016}, {"IIF", RESTRICTED_ALIAS}, {"IGNORE", RESTRICTED_ALIAS}, {"ILIKE", RESTRICTED_SQL2016}, {"IN", RESTRICTED_SQL2016}, {"INNER", RESTRICTED_SQL2016}, {"INTERSECT", RESTRICTED_SQL2016}, diff --git a/src/main/java/net/sf/jsqlparser/schema/Column.java b/src/main/java/net/sf/jsqlparser/schema/Column.java index 2432676dd..684e0faf5 100644 --- a/src/main/java/net/sf/jsqlparser/schema/Column.java +++ b/src/main/java/net/sf/jsqlparser/schema/Column.java @@ -94,10 +94,10 @@ public void setColumnName(String string) { @Override public String getFullyQualifiedName() { - return getName(false); + return getFullyQualifiedName(false); } - public String getName(boolean aliases) { + public String getFullyQualifiedName(boolean aliases) { StringBuilder fqn = new StringBuilder(); if (table != null) { @@ -121,6 +121,12 @@ public String getName(boolean aliases) { return fqn.toString(); } + // old and confusing, don't use it! + @Deprecated + public String getName(boolean aliases) { + return columnName; + } + @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); @@ -128,7 +134,7 @@ public void accept(ExpressionVisitor expressionVisitor) { @Override public String toString() { - return getName(true); + return getFullyQualifiedName(true); } public Column withTable(Table table) { diff --git a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java index 398c7c138..ea3809ad6 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -226,12 +226,21 @@ public String toString() { } sql.append("INTO "); sql.append(table).append(" "); + if (columns != null) { - sql.append(PlainSelect.getStringList(columns, true, true)).append(" "); + sql.append("("); + for (int i = 0; i < columns.size(); i++) { + if (i > 0) { + sql.append(", "); + } + // only plain names, but not fully qualified names allowed + sql.append(columns.get(i).getColumnName()); + } + sql.append(") "); } if (outputClause != null) { - sql.append(outputClause.toString()); + sql.append(outputClause); } if (select != null) { diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java b/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java new file mode 100644 index 000000000..e06ff248b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ForClause.java @@ -0,0 +1,29 @@ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +public class ForClause extends ASTNodeAccessImpl { + public enum ForOption { + BROWSE, XML, JSON; + + public static ForOption from(String option) { + return Enum.valueOf(ForOption.class, option.toUpperCase()); + } + } + + private ForOption forOption; + + public ForOption getForOption() { + return forOption; + } + + public ForClause setForOption(String forOption) { + this.forOption = ForOption.from(forOption); + return this; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Join.java b/src/main/java/net/sf/jsqlparser/statement/select/Join.java index 96f908c6a..dd34e9c06 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Join.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -34,7 +34,7 @@ public class Join extends ASTNodeAccessImpl { private boolean semi = false; private boolean straight = false; private boolean apply = false; - private FromItem rightItem; + private FromItem fromItem; private final LinkedList onExpressions = new LinkedList<>(); private final LinkedList usingColumns = new LinkedList<>(); private KSQLJoinWindow joinWindow; @@ -279,18 +279,29 @@ public void setCross(boolean cross) { /** * Returns the right item of the join + * */ public FromItem getRightItem() { - return rightItem; + return fromItem; } + @Deprecated public Join withRightItem(FromItem item) { - this.setRightItem(item); + this.setFromItem(item); return this; } public void setRightItem(FromItem item) { - rightItem = item; + fromItem = item; + } + + public FromItem getFromItem() { + return fromItem; + } + + public Join setFromItem(FromItem fromItem) { + this.fromItem = fromItem; + return this; } /** @@ -376,9 +387,9 @@ public String toString() { } if (isSimple() && isOuter()) { - builder.append("OUTER ").append(rightItem); + builder.append("OUTER ").append(fromItem); } else if (isSimple()) { - builder.append(rightItem); + builder.append(fromItem); } else { if (isNatural()) { builder.append("NATURAL "); @@ -410,7 +421,7 @@ public String toString() { builder.append("JOIN "); } - builder.append(rightItem).append((joinWindow != null) ? " WITHIN " + joinWindow : ""); + builder.append(fromItem).append((joinWindow != null) ? " WITHIN " + joinWindow : ""); } for (Expression onExpression : onExpressions) { diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java index a96abee15..ae4312319 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedFromItem.java @@ -41,6 +41,10 @@ public List getJoins() { return joins; } + public Join getJoin(int index) { + return joins.get(index); + } + public FromItem addJoins(Join... joins) { List list = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); Collections.addAll(list, joins); diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java index e9ae400b6..5bbf8391d 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/ParenthesedSelect.java @@ -54,6 +54,18 @@ public Select getSelect() { return select; } + public Values getValues() { + return (Values) select; + } + + public PlainSelect getPlainSelect() { + return (PlainSelect) select; + } + + public SetOperationList getSetOperationList() { + return (SetOperationList) select; + } + public void setSelect(Select select) { this.select = select; } 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 87b755754..5e322c52f 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -38,6 +38,7 @@ public class PlainSelect extends Select { private Expression where; private GroupByElement groupBy; private Expression having; + private Expression qualify; private OptimizeFor optimizeFor; private Skip skip; private boolean mySqlHintStraightJoin; @@ -82,6 +83,10 @@ public List> getSelectItems() { return selectItems; } + public SelectItem getSelectItem(int index) { + return selectItems.get(index); + } + public Expression getWhere() { return where; } @@ -124,7 +129,7 @@ public PlainSelect addSelectItems(Expression... expressions) { public PlainSelect addSelectItem(Expression expression, Alias alias) { selectItems = Optional.ofNullable(selectItems).orElseGet(ArrayList::new); - selectItems.add(new SelectItem(expression, alias)); + selectItems.add(new SelectItem<>(expression, alias)); return this; } @@ -177,6 +182,10 @@ public List getJoins() { return joins; } + public Join getJoin(int index) { + return joins.get(index); + } + public PlainSelect addJoins(Join... joins) { List list = Optional.ofNullable(getJoins()).orElseGet(ArrayList::new); Collections.addAll(list, joins); @@ -266,6 +275,15 @@ public void setHaving(Expression expression) { having = expression; } + public Expression getQualify() { + return qualify; + } + + public PlainSelect setQualify(Expression qualify) { + this.qualify = qualify; + return this; + } + /** * A list of {@link Expression}s of the GROUP BY clause. It is null in case there is no GROUP BY * clause @@ -457,6 +475,9 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) { if (having != null) { builder.append(" HAVING ").append(having); } + if (qualify != null) { + builder.append(" QUALIFY ").append(qualify); + } if (windowDefinitions != null) { builder.append(" WINDOW "); builder.append(windowDefinitions.stream().map(WindowDefinition::toString) diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Select.java b/src/main/java/net/sf/jsqlparser/statement/select/Select.java index 757adb5b4..2216fada9 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Select.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Select.java @@ -30,6 +30,9 @@ public abstract class Select extends ASTNodeAccessImpl implements Statement, Exp Fetch fetch; WithIsolation isolation; boolean oracleSiblings = false; + + ForClause forClause = null; + List orderByElements; public static String orderByToString(List orderByElements) { @@ -154,6 +157,15 @@ public Select withOracleSiblings(boolean oracleSiblings) { return this; } + public ForClause getForClause() { + return forClause; + } + + public Select setForClause(ForClause forClause) { + this.forClause = forClause; + return this; + } + public List getOrderByElements() { return orderByElements; } @@ -261,6 +273,10 @@ public StringBuilder appendTo(StringBuilder builder) { appendSelectBodyTo(builder); + if (forClause != null) { + forClause.appendTo(builder); + } + builder.append(orderByToString(oracleSiblings, orderByElements)); if (limitBy != null) { diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java b/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java index 79a03b802..48a609240 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/SetOperationList.java @@ -34,6 +34,10 @@ public List selects) { this.selects = selects; } @@ -46,7 +50,9 @@ public List getOperations() { return operations; } - + public SetOperation getOperation(int index) { + return operations.get(index); + } public void setOrderByElements(List orderByElements) { this.orderByElements = orderByElements; 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 0ba506ea6..c4975f503 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/Update.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/Update.java @@ -64,6 +64,10 @@ public List getUpdateSets() { return updateSets; } + public UpdateSet getUpdateSet(int index) { + return updateSets.get(index); + } + public void setUpdateSets(List updateSets) { this.updateSets = updateSets; } diff --git a/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java b/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java index 564a1559d..7c6fca540 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java @@ -21,7 +21,7 @@ public class UpdateSet implements Serializable { protected ExpressionList columns = new ExpressionList<>(); - protected ExpressionList values = new ExpressionList(); + protected ExpressionList values = new ExpressionList<>(); public UpdateSet() { @@ -40,6 +40,10 @@ public ExpressionList getColumns() { return columns; } + public Column getColumn(int index) { + return columns.get(index); + } + public void setColumns(ExpressionList columns) { this.columns = Objects.requireNonNull(columns); } @@ -48,6 +52,10 @@ public ExpressionList getValues() { return values; } + public Expression getValue(int index) { + return values.get(index); + } + public void setValues(ExpressionList values) { this.values = Objects.requireNonNull(values); } @@ -83,8 +91,8 @@ public void add(Expression expression) { values.add(expression); } - public void add(ExpressionList expressionList) { - values.addAll(expressionList.getExpressions()); + public void add(ExpressionList expressionList) { + values.addAll(expressionList); } public final static StringBuilder appendUpdateSetsTo(StringBuilder builder, diff --git a/src/main/java/net/sf/jsqlparser/util/PerformanceTest.java b/src/main/java/net/sf/jsqlparser/util/PerformanceTest.java new file mode 100644 index 000000000..3ed724938 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/PerformanceTest.java @@ -0,0 +1,125 @@ +package net.sf.jsqlparser.util; + +import net.sf.jsqlparser.parser.CCJSqlParser; + +public class PerformanceTest { + @SuppressWarnings("PMD.ExcessiveMethodLength") + public static void main(String[] args) throws Exception { + String sqlStr = "SELECT e.id\n" + + " , e.code\n" + + " , e.review_type\n" + + " , e.review_object\n" + + " , e.review_first_datetime AS reviewfirsttime\n" + + " , e.review_latest_datetime AS reviewnewtime\n" + + " , e.risk_event\n" + + " , e.risk_detail\n" + + " , e.risk_grade\n" + + " , e.risk_status\n" + + " , If( e.deal_type IS NULL\n" + + " OR e.deal_type = '', '--', e.deal_type ) AS dealtype\n" + + " , e.deal_result\n" + + " , If( e.deal_remark IS NULL\n" + + " OR e.deal_remark = '', '--', e.deal_remark ) AS dealremark\n" + + " , e.is_deleted\n" + + " , e.review_object_id\n" + + " , e.archive_id\n" + + " , e.feature AS featurename\n" + + " , Ifnull( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.review_first_user ), ( SELECT DISTINCT\n" + + " real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth\n" + + + " WHERE uniapp_user_id = e.review_first_user\n" + + + " AND is_disable = 0 ) ) AS reviewfirstuser\n" + + + " , Ifnull( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.review_latest_user ), ( SELECT DISTINCT\n" + + " real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth\n" + + + " WHERE uniapp_user_id = e.review_latest_user\n" + + + " AND is_disable = 0 ) ) AS reviewnewuser\n" + + + " , If( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.deal_user ) IS NOT NULL\n" + + " AND e.deal_user != - 9999, ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.deal_user ), '--' ) AS dealuser\n" + + + " , CASE\n" + + " WHEN 'COMPANY'\n" + + " THEN Concat( ( SELECT ar.customer_name\n" + + " FROM mtp_cs.mtp_rsk_cust_archive ar\n" + + " WHERE ar.is_deleted = 0\n" + + " AND ar.id = e.archive_id ), If( ( SELECT alias\n" + + + " FROM web_crm.wcrm_customer\n" + + + " WHERE id = e.customer_id ) = ''\n" + + + " OR ( SELECT alias\n" + + " FROM web_crm.wcrm_customer\n" + + " WHERE id = e.customer_id ) IS NULL, ' ', Concat( '(', ( SELECT alias\n" + + + " FROM web_crm.wcrm_customer\n" + + + " WHERE id = e.customer_id ), ')' ) ) )\n" + + + " WHEN 'EMPLOYEE'\n" + + " THEN ( SELECT Concat( auth.real_name, ' ', auth.phone )\n" + + " FROM app_sys.asys_uniapp_rn_auth auth\n" + + " WHERE auth.is_disable = 0\n" + + " AND auth.uniapp_user_id = e.uniapp_user_id )\n" + + " WHEN 'DEAL'\n" + + " THEN ( SELECT DISTINCT\n" + + " Concat( batch.code, '-', detail.line_seq\n" + + " , ' ', Ifnull( ( SELECT DISTINCT\n" + + " auth.real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth auth\n" + + + " WHERE auth.uniapp_user_id = e.uniapp_user_id\n" + + + " AND auth.is_disable = 0 ), ' ' ) )\n" + + + " FROM web_pym.wpym_payment_batch_detail detail\n" + + " LEFT JOIN web_pym.wpym_payment_batch batch\n" + + " ON detail.payment_batch_id = batch.id\n" + + " WHERE detail.id = e.review_object_id )\n" + + " WHEN 'TASK'\n" + + " THEN ( SELECT code\n" + + " FROM web_tm.wtm_task task\n" + + " WHERE e.review_object_id = task.id )\n" + + " ELSE NULL\n" + + " END AS reviewobjectname\n" + + " , CASE\n" + + " WHEN 4\n" + + " THEN 'HIGH_LEVEL'\n" + + " WHEN 3\n" + + " THEN 'MEDIUM_LEVEL'\n" + + " WHEN 2\n" + + " THEN 'LOW_LEVEL'\n" + + " ELSE 'HEALTHY'\n" + + " END AS risklevel\n" + + "FROM mtp_cs.mtp_rsk_event e\n" + + "WHERE e.is_deleted = 0\n" + + "ORDER BY e.review_latest_datetime DESC\n" + + "LIMIT 30\n" + + ";"; + + long startMillis = System.currentTimeMillis(); + for (int i = 1; i < 1000; i++) { + final CCJSqlParser parser = new CCJSqlParser(sqlStr) + .withSquareBracketQuotation(false) + .withAllowComplexParsing(true) + .withBackslashEscapeCharacter(false); + parser.Statements(); + long endMillis = System.currentTimeMillis(); + System.out.println("Duration [ms]: " + (endMillis - startMillis) / i); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 3c77176a0..625c2d779 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -96,7 +96,6 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; @@ -266,7 +265,7 @@ public void visit(PlainSelect plainSelect) { if (plainSelect.getJoins() != null) { for (Join join : plainSelect.getJoins()) { - join.getRightItem().accept(this); + join.getFromItem().accept(this); } } if (plainSelect.getWhere() != null) { @@ -659,11 +658,6 @@ public void visit(RegExpMatchOperator rexpr) { visitBinaryExpression(rexpr); } - @Override - public void visit(RegExpMySQLOperator rexpr) { - visitBinaryExpression(rexpr); - } - @Override public void visit(JsonExpression jsonExpr) { @@ -732,7 +726,7 @@ public void visit(Delete delete) { if (delete.getJoins() != null) { for (Join join : delete.getJoins()) { - join.getRightItem().accept(this); + join.getFromItem().accept(this); } } @@ -746,7 +740,7 @@ public void visit(Update update) { visit(update.getTable()); if (update.getStartJoins() != null) { for (Join join : update.getStartJoins()) { - join.getRightItem().accept(this); + join.getFromItem().accept(this); } } if (update.getExpressions() != null) { @@ -761,7 +755,7 @@ public void visit(Update update) { if (update.getJoins() != null) { for (Join join : update.getJoins()) { - join.getRightItem().accept(this); + join.getFromItem().accept(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 2b9818130..383c8802a 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -98,7 +98,6 @@ import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; import net.sf.jsqlparser.schema.Column; @@ -284,7 +283,9 @@ public void visit(SignedExpression signedExpression) { @Override public void visit(IsNullExpression isNullExpression) { isNullExpression.getLeftExpression().accept(this); - if (isNullExpression.isUseIsNull()) { + if (isNullExpression.isUseNotNull()) { + buffer.append(" NOTNULL"); + } else if (isNullExpression.isUseIsNull()) { if (isNullExpression.isNot()) { buffer.append(" NOT ISNULL"); } else { @@ -328,8 +329,17 @@ public void visit(JdbcParameter jdbcParameter) { @Override public void visit(LikeExpression likeExpression) { - visitBinaryExpression(likeExpression, (likeExpression.isNot() ? " NOT" : "") - + (likeExpression.isCaseInsensitive() ? " ILIKE " : " LIKE ")); + likeExpression.getLeftExpression().accept(this); + buffer.append(" "); + if (likeExpression.isNot()) { + buffer.append("NOT "); + } + buffer.append(likeExpression.getLikeKeyWord()).append(" "); + if (likeExpression.isUseBinary()) { + buffer.append("BINARY "); + } + likeExpression.getRightExpression().accept(this); + Expression escape = likeExpression.getEscape(); if (escape != null) { buffer.append(" ESCAPE "); @@ -816,8 +826,18 @@ public void visit(ExtractExpression eexpr) { } @Override - public void visit(IntervalExpression iexpr) { - buffer.append(iexpr.toString()); + public void visit(IntervalExpression intervalExpression) { + if (intervalExpression.isUsingIntervalKeyword()) { + buffer.append("INTERVAL "); + } + if (intervalExpression.getExpression() != null) { + intervalExpression.getExpression().accept(this); + } else { + buffer.append(intervalExpression.getParameter()); + } + if (intervalExpression.getIntervalType() != null) { + buffer.append(" ").append(intervalExpression.getIntervalType()); + } } @Override @@ -835,10 +855,6 @@ public void visit(RegExpMatchOperator rexpr) { visitBinaryExpression(rexpr, " " + rexpr.getStringExpression() + " "); } - @Override - public void visit(RegExpMySQLOperator rexpr) { - visitBinaryExpression(rexpr, " " + rexpr.getStringExpression() + " "); - } @Override public void visit(JsonExpression jsonExpr) { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java index a0614540e..84d473f76 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -60,7 +60,6 @@ public void deParse(Insert insert) { buffer.append("INTO "); buffer.append(insert.getTable().toString()); - if (insert.getColumns() != null) { buffer.append(" ("); for (Iterator iter = insert.getColumns().iterator(); iter.hasNext();) { 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 09f46dfdc..c5f6636e3 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -252,6 +252,10 @@ public void visit(PlainSelect plainSelect) { buffer.append(" HAVING "); plainSelect.getHaving().accept(expressionVisitor); } + if (plainSelect.getQualify() != null) { + buffer.append(" QUALIFY "); + plainSelect.getQualify().accept(expressionVisitor); + } if (plainSelect.getWindowDefinitions() != null) { buffer.append(" WINDOW "); buffer.append(plainSelect.getWindowDefinitions().stream() @@ -272,6 +276,10 @@ public void visit(PlainSelect plainSelect) { buffer.append(" SKIP LOCKED"); } } + if (plainSelect.getForClause() != null) { + plainSelect.getForClause().appendTo(buffer); + } + if (plainSelect.getOrderByElements() != null) { new OrderByDeParser(expressionVisitor, buffer).deParse(plainSelect.isOracleSiblings(), plainSelect.getOrderByElements()); @@ -468,7 +476,7 @@ public void deparseJoin(Join join) { } - FromItem fromItem = join.getRightItem(); + FromItem fromItem = join.getFromItem(); fromItem.accept(this); if (join.isWindowJoin()) { buffer.append(" WITHIN "); 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 fb03eeba3..fcc554dd6 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 @@ -99,7 +99,6 @@ import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; import net.sf.jsqlparser.parser.feature.Feature; @@ -481,11 +480,6 @@ public void visit(RegExpMatchOperator rexpr) { visitBinaryExpression(rexpr, " " + rexpr.getStringExpression() + " "); } - @Override - public void visit(RegExpMySQLOperator rexpr) { - visitBinaryExpression(rexpr, " " + rexpr.getStringExpression() + " "); - } - @Override public void visit(JsonExpression jsonExpr) { validateOptionalExpression(jsonExpr.getExpression()); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java index 31349aa4f..88166c1b5 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java @@ -233,7 +233,7 @@ public void validateOptionalJoin(Join join) { validateOptionalFeature(c, join.getUsingColumns(), Feature.joinUsingColumns); } - validateOptionalFromItem(join.getRightItem()); + validateOptionalFromItem(join.getFromItem()); for (Expression onExpression : join.getOnExpressions()) { validateOptionalExpression(onExpression); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index a346a0604..c5c5e3ced 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -79,11 +79,11 @@ public class CCJSqlParser extends AbstractJSqlParser { token_source.configuration = configuration; return this; } - + public FeatureConfiguration getConfiguration() { return token_source.configuration; } - + public CCJSqlParser me () { return this; } @@ -148,11 +148,13 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | | | +| | | | @@ -209,6 +211,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -220,6 +223,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -254,6 +258,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -309,6 +314,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -341,10 +347,12 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | | +| | | | @@ -364,6 +372,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -443,12 +452,16 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | | | +| +| | +| | | | @@ -1718,7 +1731,7 @@ String RelObjectNameWithoutValue() : { Token tk = null; } { ( tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BEGIN" | tk="BINARY" | tk="BIT" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONFLICT" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GLOBAL" | tk="GRANT" | tk="GUARD" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGISTER" | tk="RENAME" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RLIKE" | tk="ROLLBACK" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLTEXT" | tk="YAML" | tk="YES" | tk="ZONE" ) + | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="ALGORITHM" | tk="ALTER" | tk="ANALYZE" | tk="APPLY" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="BEGIN" | tk="BINARY" | tk="BIT" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMIT" | tk="CONFLICT" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATABASE" | tk="DDL" | tk="DECLARE" | tk="DEFAULT" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="END" | tk="ESCAPE" | tk="EXCLUDE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXTENDED" | tk="EXTRACT" | tk="FALSE" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GLOBAL" | tk="GRANT" | tk="GUARD" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="OVER" | tk="OVERLAPS" | tk="PARALLEL" | tk="PARENT" | tk="PARTITION" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PRECEDING" | tk="PRECISION" | tk="PRIMARY" | tk="PRIOR" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGISTER" | tk="RENAME" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="STORED" | tk="STRING" | tk="SUSPEND" | tk="SWITCH" | tk="SYNONYM" | tk="SYSTEM" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TO" | tk="TRIGGER" | tk="TRUE" | tk="TRUNCATE" | tk="TUMBLING" | tk="TYPE" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VERBOSE" | tk="VIEW" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" ) { return tk.image; } } @@ -1731,7 +1744,9 @@ String RelObjectName() : { (result = RelObjectNameWithoutValue() | tk= | tk= | tk= | tk= | tk= | tk= - | tk= | tk= | tk= | tk= | tk= ) + | tk= | tk= | tk= | tk= | tk= + | tk= + ) { return tk!=null ? tk.image : result; } } @@ -1826,24 +1841,22 @@ Select Select() #Select: WithIsolation withIsolation = null; } { + [ with=WithList() ] ( - [ with=WithList() ] - ( - LOOKAHEAD(3) select = PlainSelect() - | - LOOKAHEAD(3) select = Values() - | - LOOKAHEAD(3) select = ParenthesedSelect() - ) - [ LOOKAHEAD(2) select = SetOperationList(select) ] + LOOKAHEAD(3) select = PlainSelect() + | + LOOKAHEAD(3) select = Values() + | + LOOKAHEAD(3) select = ParenthesedSelect() + ) + [ LOOKAHEAD(2) select = SetOperationList(select) ] - [ LOOKAHEAD( ) orderByElements = OrderByElements() { select.setOrderByElements(orderByElements); } ] + [ LOOKAHEAD( ) orderByElements = OrderByElements() { select.setOrderByElements(orderByElements); } ] - [ LOOKAHEAD() limit=LimitWithOffset() {select.setLimit(limit);} ] - [ LOOKAHEAD() offset = Offset() { select.setOffset(offset);} ] - [ LOOKAHEAD() fetch = Fetch() { select.setFetch(fetch);} ] - [ LOOKAHEAD( ) withIsolation = WithIsolation() { select.setIsolation(withIsolation);} ] - ) + [ LOOKAHEAD() limit=LimitWithOffset() {select.setLimit(limit);} ] + [ LOOKAHEAD() offset = Offset() { select.setOffset(offset);} ] + [ LOOKAHEAD() fetch = Fetch() { select.setFetch(fetch);} ] + [ LOOKAHEAD( ) withIsolation = WithIsolation() { select.setIsolation(withIsolation);} ] { linkAST(select, jjtThis); @@ -1896,6 +1909,80 @@ LateralView LateralView() #LateralView: } } +ForClause ForClause() #ForClause: +{ + Token token = null; + ForClause forClause = new ForClause(); +} +{ + + ( + token = + | + token = + ( + ( + ( [ LOOKAHEAD(2) "(" ")" ] | ) + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | + | [ LOOKAHEAD(2) "(" ")" ] + | [ LOOKAHEAD(2) ( | ) ] + ) + )* + ) + | + ( + + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | + ) + )* + ) + | + ( + [ LOOKAHEAD(2) "(" ")" ] + ( + LOOKAHEAD(2) "," + ( + + | + | [ LOOKAHEAD(2) "(" ")" ] + | [ LOOKAHEAD(2) ( | ) ] + ) + )* + ) + ) + | + ( + token = ( | ) + ( + LOOKAHEAD(2) "," + ( + [ LOOKAHEAD(2) "(" ")" ] + | + | + ) + )* + ) + ) + { + forClause.setForOption(token.image); + linkAST(forClause,jjtThis); + return forClause; + } +} + + List LateralViews(): { ArrayList lateralViews = new ArrayList(); @@ -1932,9 +2019,11 @@ PlainSelect PlainSelect() #PlainSelect: List joins = null; List> distinctOn = null; Expression where = null; + ForClause forClause = null; List orderByElements; GroupByElement groupBy = null; Expression having = null; + Expression qualify; Limit limitBy = null; Limit limit = null; Offset offset = null; @@ -2005,6 +2094,8 @@ PlainSelect PlainSelect() #PlainSelect: [ LOOKAHEAD(2) having=Having() { plainSelect.setHaving(having); }] [ LOOKAHEAD(2) groupBy=GroupByColumnReferences() { plainSelect.setGroupByElement(groupBy); }] [ LOOKAHEAD(2) having=Having() { plainSelect.setHaving(having); }] + [ LOOKAHEAD(2) qualify=Qualify() {plainSelect.setQualify(qualify); }] + [ LOOKAHEAD(2) forClause = ForClause() {plainSelect.setForClause(forClause);} ] [ LOOKAHEAD( ) orderByElements = OrderByElements() { plainSelect.setOracleSiblings(true); plainSelect.setOrderByElements(orderByElements); } ] [ LOOKAHEAD(2) windowName = RelObjectName() winDef = windowDefinition() { List winDefs = new ArrayList(); winDefs.add(winDef.withWindowName(windowName)); } @@ -2027,9 +2118,6 @@ PlainSelect PlainSelect() #PlainSelect: ] [ LOOKAHEAD() optimize = OptimizeFor() { plainSelect.setOptimizeFor(optimize); } ] - - [ LOOKAHEAD(3) "(" token = ")" { plainSelect.setForXmlPath(token.image); } ] - { plainSelect.setSelectItems(selectItems); plainSelect.setFromItem(fromItem); @@ -2570,10 +2658,10 @@ Join JoinerExpression() #JoinerExpression: ) ] { - linkAST(join,jjtThis); - join.setRightItem(right); - return join; - } + linkAST(join,jjtThis); + join.setFromItem(right); + return join; +} } @@ -2587,8 +2675,8 @@ KSQLJoinWindow JoinWindow(): Token afterTimeUnitToken = null; } { - (beforeDurationToken= (beforeTimeUnitToken= | beforeTimeUnitToken=) - [ "," afterDurationToken= (afterTimeUnitToken= | afterTimeUnitToken=) ] + beforeDurationToken= (beforeTimeUnitToken= | beforeTimeUnitToken=) + [ "," afterDurationToken= (afterTimeUnitToken= | afterTimeUnitToken=) ] { if (afterDurationToken == null) { retval.setDuration(Long.parseLong(beforeDurationToken.image)); @@ -2602,7 +2690,7 @@ KSQLJoinWindow JoinWindow(): retval.setAfterTimeUnit(KSQLWindow.TimeUnit.from(afterTimeUnitToken.image)); retval.setBeforeAfterWindow(true); return retval; - }) + } } KSQLWindow KSQLWindowClause(): @@ -2746,6 +2834,17 @@ Expression Having(): } } +Expression Qualify(): +{ + Expression qualify = null; +} +{ + qualify=Expression() + { + return qualify; + } +} + List OrderByElements(): { List orderByList = new ArrayList(); @@ -2858,8 +2957,9 @@ Limit LimitBy(): } { limit = LimitWithOffset() - byExpressions = ExpressionList() { limit.setByExpressions(byExpressions); } + byExpressions = ExpressionList() { + limit.setByExpressions(byExpressions); return limit; } } @@ -2926,8 +3026,9 @@ OptimizeFor OptimizeFor(): LongValue value; } { - token= { value = new LongValue(token.image); } + token= { + value = new LongValue(token.image); return new OptimizeFor(value.getValue()); } } @@ -2951,9 +3052,12 @@ Top Top(): ":" { top.setExpression(new JdbcNamedParameter()); } [ LOOKAHEAD(2) token = { ((JdbcNamedParameter)top.getExpression()).setName(token.image); } ] | "(" - expr=AdditiveExpression() { top.setExpression(expr); } - { top.setParenthesis(true);} - ")" + expr=AdditiveExpression() + { + top.setExpression(expr); + top.setParenthesis(true); + } + ")" ) [ LOOKAHEAD(2) { top.setPercentage(true); } ] [ LOOKAHEAD(2) { top.setWithTies(true); }] { @@ -3166,8 +3270,6 @@ Expression RegularCondition() #RegularCondition: | token= { result = new NotEqualsTo(token.image); } | "@@" { result = new Matches(); } | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } - | [ { not=true; } ] [ LOOKAHEAD(2) { binary=true; } ] { result = new RegExpMySQLOperator(not, binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } - | [ LOOKAHEAD(2) { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE).useRLike(); } | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } @@ -3296,7 +3398,15 @@ Expression LikeExpression(Expression leftExpression) #LikeExpression: Token token; } { - [ { result.setNot(true); } ] ( | { result.setCaseInsensitive(true); } ) rightExpression=SimpleExpression() + [ { result.setNot(true); } ] + ( + token = + | token = + | token = + | token = + ) { result.setLikeKeyWord( LikeExpression.KeyWord.from(token.image)); } + [ {result.setUseBinary(true); } ] + rightExpression=SimpleExpression() [ LOOKAHEAD(2) ( LOOKAHEAD(2) token = { result.setEscape( new StringValue( token.image ) ); } @@ -3351,8 +3461,11 @@ Expression IsNullExpression(Expression leftExpression): IsNullExpression result = new IsNullExpression(); } { - ( { result.setUseIsNull(true); } | [ { result.setNot(true); } ] ) - + ( + [ { result.setNot(true); } ] { result.setUseIsNull(true); } + | { result.setUseIsNull(true); result.setUseNotNull(true); } + | [ { result.setNot(true); } ] + ) { result.setLeftExpression(leftExpression); return result; @@ -4398,8 +4511,9 @@ IntervalExpression IntervalExpressionWithoutInterval(Expression expr) : { interval = new IntervalExpression(false); interval.setExpression(expr); } - token = { interval.setIntervalType(token.image); } + token = { + interval.setIntervalType(token.image); return interval; } } @@ -5396,8 +5510,11 @@ AlterView AlterView(boolean useReplace): view=Table() { alterView.setView(view); alterView.setUseReplace(useReplace); } [ columnNames = ColumnsNamesList() { alterView.setColumnNames(columnNames); } ] - select=Select() { alterView.setSelect(select); } - { return alterView; } + select=Select() + { + alterView.setSelect(select); + return alterView; + } } List CreateParameter(): @@ -6287,9 +6404,9 @@ Wait Wait(): { // sqlserver-oracle-> WAIT (TIMEOUT) // https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10002.htm#i2126016 - token= { wait.setTimeout(Long.parseLong(token.image)); } - + token= { + wait.setTimeout(Long.parseLong(token.image)); return wait; } } @@ -6299,10 +6416,11 @@ SavepointStatement SavepointStatement(): SavepointStatement savepointStatement; } { - token= { savepointStatement = new SavepointStatement(token.image); } - { - return savepointStatement; - } + token= + { + savepointStatement = new SavepointStatement(token.image); + return savepointStatement; + } } RollbackStatement RollbackStatement(): @@ -6364,8 +6482,9 @@ Comment Comment(): view = Table() { result.setView(view); } ) ) - comment= { result.setComment(new StringValue(comment.image)); } + comment= { + result.setComment(new StringValue(comment.image)); return result; } } @@ -6544,10 +6663,11 @@ CreateSequence CreateSequence(): } { sequence=Sequence() { createSequence.setSequence(sequence); } - sequenceParameters = SequenceParameters() { sequence.setParameters(sequenceParameters); } - { - return createSequence; - } + sequenceParameters = SequenceParameters() + { + sequence.setParameters(sequenceParameters); + return createSequence; + } } AlterSequence AlterSequence(): @@ -6557,11 +6677,12 @@ AlterSequence AlterSequence(): List sequenceParameters; } { - sequence=Sequence() { alterSequence.setSequence(sequence); } - sequenceParameters = SequenceParameters() { sequence.setParameters(sequenceParameters); } - { - return alterSequence; - } + sequence=Sequence() { alterSequence.setSequence(sequence); } + sequenceParameters = SequenceParameters() + { + sequence.setParameters(sequenceParameters); + return alterSequence; + } } Statement Create(): diff --git a/src/site/sphinx/changelog.rst b/src/site/sphinx/changelog.rst index 427db83c8..9ce78242a 100644 --- a/src/site/sphinx/changelog.rst +++ b/src/site/sphinx/changelog.rst @@ -8,12 +8,33 @@ Latest Changes since |JSQLPARSER_VERSION| ============================================================= + * **doc: migration guide** + + Andreas Reichel, 2023-06-02 + * **fix: expose IntervalExpression attributes and use DeParser** + + Andreas Reichel, 2023-06-01 + * **doc: write migration guide** + + Andreas Reichel, 2023-05-29 + * **fix: throw the specific exception** + + Andreas Reichel, 2023-05-29 + * **doc: Website, fix tabs** + + Andreas Reichel, 2023-05-24 + * **doc: Website improvements** + + Andreas Reichel, 2023-05-22 * **build: improve Upload task** Andreas Reichel, 2023-05-19 * **feat: Quoted Identifiers can contain double-quotes (PostgreSQL)** Andreas Reichel, 2023-05-18 + * **Create gradle.yml** + + manticore-projects, 2023-05-18 * **feat: functions blocks, parenthesed JSON Expressions** Andreas Reichel, 2023-05-18 @@ -164,6 +185,9 @@ Latest Changes since |JSQLPARSER_VERSION| * **feat: Consolidate the `ExpressionList`, removing many redundant List alike Classes and Productions** Andreas Reichel, 2023-05-03 + * **Revert "fix: assign Enum case insensitive"** + + Andreas Reichel, 2023-05-02 * **fix: assign Enum case insensitive** Andreas Reichel, 2023-05-02 diff --git a/src/site/sphinx/contribution.rst b/src/site/sphinx/contribution.rst index e9c93827f..c001f8eb5 100644 --- a/src/site/sphinx/contribution.rst +++ b/src/site/sphinx/contribution.rst @@ -94,12 +94,14 @@ There is a task ``updateKeywords`` for Gradle and Maven, which will: .. tab:: Gradle + .. code-block:: shell :caption: Gradle `updateKeywords` Task gradle updateKeywords .. tab:: Maven + .. code-block:: shell :caption: Maven `updateKeywords` Task diff --git a/src/site/sphinx/index.rst b/src/site/sphinx/index.rst index c2294c131..c01f3e1ab 100644 --- a/src/site/sphinx/index.rst +++ b/src/site/sphinx/index.rst @@ -12,6 +12,7 @@ Java SQL Parser Library usage contribution + migration syntax_stable syntax_snapshot javadoc_stable diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst index 54b4d34c9..feb1fcf52 100644 --- a/src/site/sphinx/keywords.rst +++ b/src/site/sphinx/keywords.rst @@ -65,6 +65,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | GROUPING | Yes | | +----------------------+-------------+-----------+ +| QUALIFY | Yes | | ++----------------------+-------------+-----------+ | HAVING | Yes | Yes | +----------------------+-------------+-----------+ | IF | Yes | Yes | diff --git a/src/site/sphinx/migration.rst b/src/site/sphinx/migration.rst new file mode 100644 index 000000000..6a1fbc9e4 --- /dev/null +++ b/src/site/sphinx/migration.rst @@ -0,0 +1,387 @@ +********************************* +Migration to 5.0 +********************************* + +The new version of JSQLParser 5.0 is a rewrite in order to simply accessing the SQL's Abstract Syntax Tree (AST). Quite a few redundant classes have been removed or merged. + +As always, such a major improvement comes at a certain cost, which is breaking the previous API. Following the guidance below, the new API can be adopted easily although you are welcome to lodge a support request when any questions or concerns arise. + +`Values` Clause +--------------------------------- +The ``ValueListExpression`` has been replaced by ``Values``, which implements ``Select`` `Statement` and `Expression`. + +The ``ValuesStatement`` has been replaced by ``Values``, which implements ``Select`` `Statement` and `Expression`. + +.. tab:: Statement + + .. code-block:: SQL + + VALUES ( 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.Values + └─ParenthesedExpressionList: (1, 2, 3) + + + .. code-block:: JAVA + + Values values = (Values) CCJSqlParserUtil.parse(sqlStr); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Sub-query + + .. code-block:: SQL + + SELECT * + FROM ( VALUES 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─AllColumns: * + └─fromItem: statement.select.ParenthesedSelect + └─select: statement.select.Values + └─ExpressionList: 1, 2, 3 + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedSelect subSelect = (ParenthesedSelect) select.getFromItem(); + Values values = (Values) subSelect.getSelect(); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Expression + + .. code-block:: SQL + + UPDATE test + SET ( a + , b + , c ) = ( VALUES 1, 2, 3 ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.update.Update + ├─Table: test + └─updateSets: statement.update.UpdateSet + ├─ParenthesedExpressionList: (a, b, c) + └─ExpressionList: (VALUES 1, 2, 3) + + + .. code-block:: JAVA + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet = update.getUpdateSets().get(0); + ParenthesedSelect subSelect = (ParenthesedSelect) updateSet.getValues().get(0); + Values values = (Values) subSelect.getSelect(); + assertEquals( 3, values.getExpressions().size() ); + + +.. tab:: Clause + + .. code-block:: SQL + + INSERT INTO test + VALUES ( 1, 2, 3 ) + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.insert.Insert + ├─Table: test + └─select: statement.select.Values + └─ParenthesedExpressionList: (1, 2, 3) + + + .. code-block:: JAVA + + Insert insert = (Insert) CCJSqlParserUtil.parse(sqlStr); + Values values = (Values) insert.getSelect(); + Assertions.assertEquals(3, values.getExpressions().size()); + + +`Expression` Lists +--------------------------------- + +The class ``ExpressionList`` directly extends ``List`` directly and so ``ExpressionList.getExpressions()`` is obsolete. + +Any instance of `List` is considered an Anti Pattern and the class ``ExpressionList`` shall be used instead. + +``ItemsList`` has been removed and ``ExpressionList`` is used instead. + +``MultiExpressionList`` has been removed and ``ExpressionList`` is used instead (with ``ExpressionList`` elements). + +.. tab:: ExpressionList + + .. code-block:: SQL + + SELECT Function( a, b, c ) + FROM dual + GROUP BY a + , b + , c + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─expression: expression.Function + │ └─ExpressionList: a, b, c + ├─Table: dual + └─groupBy: statement.select.GroupByElement + └─ExpressionList: a, b, c + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Function function = (Function) select.getSelectItem(0).getExpression(); + assertEquals(3, function.getParameters().size()); + + ExpressionList groupByExpressions=select.getGroupBy().getGroupByExpressionList(); + assertEquals(3, groupByExpressions.size()); + + +.. tab:: Wrapped ExpressionList + + .. code-block:: SQL + + SELECT ( ( 1, 2, 3 ), ( 4, 5, 6 ), ( 7, 8, 9 ) ) + ; + + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + └─selectItems: statement.select.SelectItem + └─ParenthesedExpressionList: ((1, 2, 3), (4, 5, 6), (7, 8, 9)) + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedExpressionList expressionList = (ParenthesedExpressionList) select.getSelectItem(0).getExpression(); + + ParenthesedExpressionList expressionList1 = (ParenthesedExpressionList) expressionList.get(0); + assertEquals(3, expressionList1.size()); + + +Generic `SelectItem` +--------------------------------- + +The class ``SelectItem`` is now generic and various derivatives (e. |_| g. ``SelectExpressionItem``, ``FunctionItem``, ``ExpressionListItem``) have been removed. + + +`Select` Statement +--------------------------------- + +``SelectBody`` has been removed and `PlainSelect` can be used directly + +``SubJoin`` has been replaced by `ParenthesedFromItem`` (implementing a ``FromItem`` with a regular list of ``Join``) + +``SubSelect`` has been removed and any instance of ``Select`` (`PlainSelect`, `Values` or `SetOperationList`) can be used instead + +.. tab:: Select + + .. code-block:: SQL + + ( + SELECT * + FROM ( SELECT 1 ) + UNION ALL + SELECT * + FROM ( VALUES 1, 2, 3 ) + UNION ALL + VALUES ( 1, 2, 3 ) ) + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.ParenthesedSelect + └─select: statement.select.SetOperationList + ├─selects: statement.select.PlainSelect + │ ├─selectItems: statement.select.SelectItem + │ │ └─AllColumns: * + │ └─fromItem: statement.select.ParenthesedSelect + │ └─select: statement.select.PlainSelect + │ └─selectItems: statement.select.SelectItem + │ └─LongValue: 1 + ├─selects: statement.select.PlainSelect + │ ├─selectItems: statement.select.SelectItem + │ │ └─AllColumns: * + │ └─fromItem: statement.select.ParenthesedSelect + │ └─select: statement.select.Values + │ └─ExpressionList: 1, 2, 3 + ├─selects: statement.select.Values + │ └─ParenthesedExpressionList: (1, 2, 3) + ├─UnionOp: UNION ALL + └─UnionOp: UNION ALL + + + .. code-block:: JAVA + + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); + SetOperationList setOperationList = parenthesedSelect.getSetOperationList(); + + PlainSelect select1 = (PlainSelect) setOperationList.getSelect(0); + PlainSelect subSelect1 = ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); + Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); + + Values values = (Values) setOperationList.getSelect(2); + Assertions.assertEquals( 3, values.getExpressions().size()); + + + +.. tab:: Join + + .. code-block:: SQL + + SELECT * + FROM a + INNER JOIN ( b + LEFT JOIN c + ON b.d = c.d ) + ON a.e = b.e + ; + + .. code-block:: TEXT + + SQL Text + └─Statements: statement.select.PlainSelect + ├─selectItems: statement.select.SelectItem + │ └─AllColumns: * + ├─Table: a + └─joins: statement.select.Join + ├─rightItem: statement.select.ParenthesedFromItem + │ ├─Table: b + │ └─joins: statement.select.Join + │ ├─Table: c + │ └─onExpressions: expression.operators.relational.EqualsTo + │ ├─Column: b.d + │ └─Column: c.d + └─onExpressions: expression.operators.relational.EqualsTo + ├─Column: a.e + └─Column: b.e + + + .. code-block:: JAVA + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Table aTable = (Table) select.getFromItem(); + + ParenthesedFromItem fromItem = (ParenthesedFromItem) select.getJoin(0).getFromItem(); + Table bTable = (Table) fromItem.getFromItem(); + + Join join = fromItem.getJoin(0); + Table cTable = (Table) join.getFromItem(); + + assertEquals("c", cTable.getName()); + + +Brackets +--------------------------------- + +Any `hasBrackets()`, `isUsingBrackets()` and similar methods have been removed; instead the Parser will return a ``ParenthesedExpressionList`` or ``ParenthesedSelect`` or ``ParenthesedFromItem`` or ``Parenthesis`` wrapping the object within brackets. + +This allows for much better bracket handling. + +.. code-block:: SQL + :caption: `Parenthesis` and Brackets example + + ( SELECT ( 1 ) ) + ; + + +.. code-block:: TEXT + + SQL Text + └─Statements: statement.select.ParenthesedSelect + └─select: statement.select.PlainSelect + └─selectItems: statement.select.SelectItem + └─expression: expression.Parenthesis + └─LongValue: 1 + + +.. code-block:: JAVA + + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); + SetOperationList setOperationList = parenthesedSelect.getSetOperationList(); + + PlainSelect select1 = (PlainSelect) setOperationList.getSelect(0); + PlainSelect subSelect1 = ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); + Assertions.assertEquals( 1L, subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); + + Values values = (Values) setOperationList.getSelect(2); + Assertions.assertEquals( 3, values.getExpressions().size()); + + + +`UpdateSet` clause +--------------------------------- + +A ``List`` is used for any `Set` clause within `Insert`, `Update`, `Upsert` or `Merge` statements. + + +.. code-block:: SQL + :caption: `UpdateSet` example + + UPDATE a + SET ( a + , b + , c ) = ( 1 + , 2 + , 3 ) + , d = 4 + ; + + +.. code-block:: TEXT + + SQL Text + └─Statements: statement.update.Update + ├─Table: a + ├─updateSets: statement.update.UpdateSet + │ ├─ParenthesedExpressionList: (a, b, c) + │ └─ParenthesedExpressionList: (1, 2, 3) + └─updateSets: statement.update.UpdateSet + ├─ExpressionList: d + └─ExpressionList: 4 + + +.. code-block:: JAVA + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet1 = update.getUpdateSet(0); + Assertions.assertEquals( 3, updateSet1.getColumns().size()); + Assertions.assertEquals( 3, updateSet1.getValues().size()); + + UpdateSet updateSet2 = update.getUpdateSet(1); + Assertions.assertEquals( "d", updateSet2.getColumn(0).getColumnName()); + Assertions.assertEquals( 4L, ((LongValue) updateSet2.getValue(0)).getValue() ); + + +`Statements` collection +--------------------------------- + +The ``Statements`` class extends `List` directly and so ``Statements.getStatements()`` is obsolete. + diff --git a/src/test/java/net/sf/jsqlparser/expression/operators/relational/LikeExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/operators/relational/LikeExpressionTest.java index bafbbf7fb..073a2cef2 100644 --- a/src/test/java/net/sf/jsqlparser/expression/operators/relational/LikeExpressionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/operators/relational/LikeExpressionTest.java @@ -13,6 +13,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -32,12 +33,20 @@ public void testLikeNotIssue660() { @Test public void testSetEscapeAndGetStringExpression() throws JSQLParserException { - LikeExpression instance = (LikeExpression) CCJSqlParserUtil.parseExpression("name LIKE 'J%$_%'"); + LikeExpression instance = + (LikeExpression) CCJSqlParserUtil.parseExpression("name LIKE 'J%$_%'"); // escape character should be $ Expression instance2 = new StringValue("$"); instance.setEscape(instance2); - // match all records with names that start with letter ’J’ and have the ’_’ character in them + // match all records with names that start with letter ’J’ and have the ’_’ character in + // them assertEquals("name LIKE 'J%$_%' ESCAPE '$'", instance.toString()); } + + @Test + void testNotRLikeIssue1553() throws JSQLParserException { + String sqlStr = "select * from test where id not rlike '111'"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java b/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java index 55726a9f6..497be3371 100644 --- a/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java @@ -118,8 +118,6 @@ public class ReflectionModelTest { new net.sf.jsqlparser.expression.operators.relational.NotEqualsTo(), new net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator( RegExpMatchOperatorType.MATCH_CASEINSENSITIVE), - new net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator( - RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE), new net.sf.jsqlparser.expression.operators.relational.SimilarToExpression(), new net.sf.jsqlparser.schema.Column(), new net.sf.jsqlparser.schema.Database("db"), new net.sf.jsqlparser.schema.Sequence(), diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ForClauseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ForClauseTest.java new file mode 100644 index 000000000..c4b59d797 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/select/ForClauseTest.java @@ -0,0 +1,93 @@ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + +class ForClauseTest { + + @Test + void testForBrowse() throws JSQLParserException { + String sqlStr = "SELECT * FROM table FOR BROWSE"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForXMLPath() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR XML PATH('something'), ROOT('trkseg'), TYPE, BINARY BASE64, ELEMENTS ABSENT "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForXMLRaw() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR XML RAW('something'), ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForXMLAuto() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR XML AUTO, ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForXMLExplicit() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR XML EXPLICIT, ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForXML() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR XML EXPLICIT, ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA " + + "UNION ALL " + + "SELECT * " + + " FROM table " + + " FOR XML EXPLICIT, ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA " + + "UNION ALL " + + "SELECT * " + + " FROM table " + + " FOR XML AUTO, ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA " + + "UNION ALL " + + "SELECT * " + + " FROM table " + + " FOR XML RAW('something'), ROOT('trkseg'), TYPE, BINARY BASE64, XMLDATA "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testForJSON() throws JSQLParserException { + String sqlStr = + "SELECT * " + + " FROM table " + + " FOR JSON AUTO, ROOT('trkseg'), WITHOUT_ARRAY_WRAPPER, INCLUDE_NULL_VALUES " + + + "UNION ALL " + + "SELECT * " + + " FROM table " + + " FOR JSON PATH, ROOT('trkseg'), INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER "; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testIssue1800() throws JSQLParserException { + String sqlStr = + "SELECT (SELECT '1.0' AS '@Version', (SELECT 'Test' AS 'name', (SELECT (SELECT DISTINCT 51.64315 AS '@lat', 14.31709 AS '@lon' FOR XML PATH('trkpt'), TYPE) FOR XML PATH(''), ROOT('trkseg'), TYPE) FOR XML PATH('trk'), TYPE) FOR XML PATH('gpx'), TYPE) FOR XML PATH('')"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + +} diff --git a/src/test/java/net/sf/jsqlparser/statement/select/HiveTest.java b/src/test/java/net/sf/jsqlparser/statement/select/HiveTest.java index 2b1b1bab1..98140fc71 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/HiveTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/HiveTest.java @@ -30,7 +30,7 @@ public void testLeftSemiJoin() throws Exception { PlainSelect plainSelect = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sql, true); assertEquals(1, plainSelect.getJoins().size()); assertEquals("Othertable", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); assertTrue(plainSelect.getJoins().get(0).isLeft()); assertTrue(plainSelect.getJoins().get(0).isSemi()); } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/KSQLTest.java b/src/test/java/net/sf/jsqlparser/statement/select/KSQLTest.java index 2faf334a6..93e474394 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/KSQLTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/KSQLTest.java @@ -31,7 +31,7 @@ public void testKSQLWindowedJoin() throws Exception { assertEquals(1, plainSelect.getJoins().size()); assertEquals("table2", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); assertTrue(plainSelect.getJoins().get(0).isWindowJoin()); assertEquals(5L, plainSelect.getJoins().get(0).getJoinWindow().getDuration()); assertEquals("HOURS", @@ -51,7 +51,7 @@ public void testKSQLBeforeAfterWindowedJoin() throws Exception { assertEquals(1, plainSelect.getJoins().size()); assertEquals("table2", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); assertTrue(plainSelect.getJoins().get(0).isWindowJoin()); assertEquals(1L, plainSelect.getJoins().get(0).getJoinWindow().getBeforeDuration()); assertEquals("MINUTE", 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 0bea90afa..6e912e88f 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -31,6 +31,7 @@ import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.expression.operators.relational.InExpression; import net.sf.jsqlparser.expression.operators.relational.LikeExpression; @@ -1100,10 +1101,10 @@ public void testFrom() throws JSQLParserException { assertEquals(3, plainSelect.getJoins().size()); assertEquals("mytable0", plainSelect.getFromItem().getAlias().getName()); assertEquals("alias_tab1", - plainSelect.getJoins().get(0).getRightItem().getAlias().getName()); + plainSelect.getJoins().get(0).getFromItem().getAlias().getName()); assertEquals("alias_tab2", - plainSelect.getJoins().get(1).getRightItem().getAlias().getName()); - assertEquals("mytable4", plainSelect.getJoins().get(2).getRightItem().getAlias().getName()); + plainSelect.getJoins().get(1).getFromItem().getAlias().getName()); + assertEquals("mytable4", plainSelect.getJoins().get(2).getFromItem().getAlias().getName()); } @Test @@ -1113,7 +1114,7 @@ public void testJoin() throws JSQLParserException { PlainSelect plainSelect = (PlainSelect) select; assertEquals(1, plainSelect.getJoins().size()); assertEquals("tab2", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); assertEquals("tab1.id", ((Column) ((EqualsTo) plainSelect.getJoins().get(0).getOnExpression()) .getLeftExpression()).getFullyQualifiedName()); @@ -1124,7 +1125,7 @@ public void testJoin() throws JSQLParserException { plainSelect = (PlainSelect) select; assertEquals(2, plainSelect.getJoins().size()); assertEquals("tab3", - ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(1).getFromItem()).getFullyQualifiedName()); assertFalse(plainSelect.getJoins().get(1).isOuter()); statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id JOIN tab3"; @@ -1132,7 +1133,7 @@ public void testJoin() throws JSQLParserException { plainSelect = (PlainSelect) select; assertEquals(2, plainSelect.getJoins().size()); assertEquals("tab3", - ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(1).getFromItem()).getFullyQualifiedName()); assertFalse(plainSelect.getJoins().get(1).isOuter()); // implicit INNER @@ -1148,7 +1149,7 @@ public void testJoin() throws JSQLParserException { plainSelect = (PlainSelect) select; assertEquals(1, plainSelect.getJoins().size()); assertEquals("tab2", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); assertFalse(plainSelect.getJoins().get(0).isOuter()); assertEquals(2, plainSelect.getJoins().get(0).getUsingColumns().size()); assertEquals("id2", @@ -1168,8 +1169,8 @@ public void testJoin() throws JSQLParserException { assertTrue(plainSelect.getJoins().get(0).isOuter()); assertTrue(plainSelect.getJoins().get(0).isSimple()); assertEquals("bar", - ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); - assertEquals("b", plainSelect.getJoins().get(0).getRightItem().getAlias().getName()); + ((Table) plainSelect.getJoins().get(0).getFromItem()).getFullyQualifiedName()); + assertEquals("b", plainSelect.getJoins().get(0).getFromItem().getAlias().getName()); } @Test @@ -3370,7 +3371,7 @@ public void testTableFunctionWithParams() throws Exception { // verify params assertNotNull(function.getParameters()); - List expressions = function.getParameters().getExpressions(); + ExpressionList expressions = function.getParameters(); assertEquals(2, expressions.size()); Expression firstParam = expressions.get(0); @@ -4213,7 +4214,8 @@ public void testSqlContainIsNullFunctionShouldBeParsed3() throws JSQLParserExcep @Test public void testForXmlPath() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( - "SELECT '|' + person_name FROM person JOIN person_group ON person.person_id = person_group.person_id WHERE person_group.group_id = 1 FOR XML PATH('')"); + "SELECT '|' + person_name FROM person JOIN person_group ON person.person_id = person_group.person_id WHERE person_group.group_id = 1 FOR XML PATH('')", + true); } // @Test @@ -5699,4 +5701,25 @@ void testArrayColumnsIssue1757() throws JSQLParserException { sqlStr = "SELECT cast(my_map['my_key'] as int) FROM my_table WHERE id = 123"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + void testQualifyClauseIssue1805() throws JSQLParserException { + String sqlStr = "SELECT i, p, o\n" + + " FROM qt\n" + + " QUALIFY ROW_NUMBER() OVER (PARTITION BY p ORDER BY o) = 1"; + + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + public void testNotNullInFilter() throws JSQLParserException { + String stmt = "SELECT count(*) FILTER (WHERE i NOTNULL) AS filtered FROM tasks"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + @Test + public void testNotIsNullInFilter() throws JSQLParserException { + String stmt = "SELECT count(*) FILTER (WHERE i NOT ISNULL) AS filtered FROM tasks"; + assertSqlCanBeParsedAndDeparsed(stmt); + } } diff --git a/src/test/java/net/sf/jsqlparser/test/HowToUseSample.java b/src/test/java/net/sf/jsqlparser/test/HowToUseSample.java index 27de73ecb..91cd3aa8f 100644 --- a/src/test/java/net/sf/jsqlparser/test/HowToUseSample.java +++ b/src/test/java/net/sf/jsqlparser/test/HowToUseSample.java @@ -13,21 +13,44 @@ import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitorAdapter; +import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.parser.CCJSqlParser; +import net.sf.jsqlparser.parser.ParseException; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitorAdapter; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.select.AllColumns; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.ParenthesedFromItem; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectItem; import net.sf.jsqlparser.statement.select.SelectVisitorAdapter; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.update.Update; +import net.sf.jsqlparser.statement.update.UpdateSet; import net.sf.jsqlparser.util.deparser.StatementDeParser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + @SuppressWarnings("PMD") public class HowToUseSample { //@formatter:off @@ -175,4 +198,313 @@ public void showBracketHandling() throws JSQLParserException { System.out.println(reflectionString); } + + @Test + void migrationTest1() throws JSQLParserException { + String sqlStr = "VALUES ( 1, 2, 3 )"; + + Values values = (Values) CCJSqlParserUtil.parse(sqlStr); + Assertions.assertEquals(3, values.getExpressions().size()); + } + + @Test + void migrationTest2() throws JSQLParserException { + String sqlStr = "SELECT *\n" + + " FROM ( VALUES 1, 2, 3 )"; + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedSelect subSelect = (ParenthesedSelect) select.getFromItem(); + Values values = (Values) subSelect.getSelect(); + Assertions.assertEquals(3, values.getExpressions().size()); + } + + @Test + void migrationTest3() throws JSQLParserException { + String sqlStr = "UPDATE test\n" + + " SET ( a\n" + + " , b\n" + + " , c ) = ( VALUES 1, 2, 3 )"; + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet = update.getUpdateSets().get(0); + ParenthesedSelect subSelect = (ParenthesedSelect) updateSet.getValues().get(0); + Values values = (Values) subSelect.getSelect(); + Assertions.assertEquals(3, values.getExpressions().size()); + } + + @Test + void migrationTest4() throws JSQLParserException { + String sqlStr = "INSERT INTO test\n" + + " VALUES ( 1, 2, 3 )\n" + + " ;"; + + Insert insert = (Insert) CCJSqlParserUtil.parse(sqlStr); + Values values = (Values) insert.getSelect(); + Assertions.assertEquals(3, values.getExpressions().size()); + } + + @Test + void migrationTest5() throws JSQLParserException { + String sqlStr = "SELECT Function( a, b, c )\n" + + " FROM dual\n" + + " GROUP BY a\n" + + " , b\n" + + " , c"; + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Function function = (Function) select.getSelectItem(0).getExpression(); + Assertions.assertEquals(3, function.getParameters().size()); + + ExpressionList groupByExpressions = select.getGroupBy().getGroupByExpressionList(); + Assertions.assertEquals(3, groupByExpressions.size()); + } + + @Test + void migrationTest6() throws JSQLParserException { + String sqlStr = "(\n" + + " SELECT *\n" + + " FROM ( SELECT 1 )\n" + + " UNION ALL\n" + + " SELECT *\n" + + " FROM ( VALUES 1, 2, 3 )\n" + + " UNION ALL\n" + + " VALUES ( 1, 2, 3 ) )"; + + ParenthesedSelect parenthesedSelect = (ParenthesedSelect) CCJSqlParserUtil.parse(sqlStr); + SetOperationList setOperationList = parenthesedSelect.getSetOperationList(); + + PlainSelect select1 = (PlainSelect) setOperationList.getSelect(0); + PlainSelect subSelect1 = ((ParenthesedSelect) select1.getFromItem()).getPlainSelect(); + Assertions.assertEquals(1L, + subSelect1.getSelectItem(0).getExpression(LongValue.class).getValue()); + + Values values = (Values) setOperationList.getSelect(2); + Assertions.assertEquals(3, values.getExpressions().size()); + } + + @Test + void migrationTest7() throws JSQLParserException { + String sqlStr = "SELECT *\n" + + "FROM a\n" + + " INNER JOIN ( b\n" + + " LEFT JOIN c\n" + + " ON b.d = c.d )\n" + + " ON a.e = b.e"; + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + Table aTable = (Table) select.getFromItem(); + + ParenthesedFromItem fromItem = (ParenthesedFromItem) select.getJoin(0).getFromItem(); + Table bTable = (Table) fromItem.getFromItem(); + + Join join = fromItem.getJoin(0); + Table cTable = (Table) join.getFromItem(); + + Assertions.assertEquals("c", cTable.getName()); + } + + @Test + void migrationTest8() throws JSQLParserException { + String sqlStr = "SELECT ( ( 1, 2, 3 ), ( 4, 5, 6 ), ( 7, 8, 9 ) )"; + + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sqlStr); + ParenthesedExpressionList expressionList = + (ParenthesedExpressionList) select.getSelectItem(0).getExpression(); + + ParenthesedExpressionList expressionList1 = + (ParenthesedExpressionList) expressionList.get(0); + Assertions.assertEquals(3, expressionList1.size()); + } + + @Test + void migrationTest9() throws JSQLParserException { + String sqlStr = "UPDATE a\n" + + "SET ( a\n" + + " , b\n" + + " , c ) = ( 1\n" + + " , 2\n" + + " , 3 )\n" + + " , d = 4"; + + Update update = (Update) CCJSqlParserUtil.parse(sqlStr); + UpdateSet updateSet1 = update.getUpdateSet(0); + Assertions.assertEquals(3, updateSet1.getColumns().size()); + Assertions.assertEquals(3, updateSet1.getValues().size()); + + UpdateSet updateSet2 = update.getUpdateSet(1); + Assertions.assertEquals("d", updateSet2.getColumn(0).getColumnName()); + Assertions.assertEquals(4L, ((LongValue) updateSet2.getValue(0)).getValue()); + } + + @Test + void migrationTest10() throws JSQLParserException { + String sqlStr = "INSERT INTO target SELECT * FROM source"; + + PlainSelect select = new PlainSelect() + .addSelectItem(new AllColumns()) + .withFromItem(new Table("source")); + + Insert insert = new Insert() + .withTable(new Table("target")) + .withSelect(select); + + TestUtils.assertStatementCanBeDeparsedAs(insert, sqlStr); + } + + @Test + void migrationTest11() throws JSQLParserException { + String sqlStr = "INSERT INTO target VALUES (1, 2, 3)"; + + Values values = new Values() + .addExpressions( + new LongValue(1), new LongValue(2), new LongValue(3)); + + Insert insert = new Insert() + .withTable(new Table("target")) + .withSelect(values); + + TestUtils.assertStatementCanBeDeparsedAs(insert, sqlStr); + } + + @Test + void testComplexParsingOnly() throws Exception { + String sqlStr = "SELECT e.id\n" + + " , e.code\n" + + " , e.review_type\n" + + " , e.review_object\n" + + " , e.review_first_datetime AS reviewfirsttime\n" + + " , e.review_latest_datetime AS reviewnewtime\n" + + " , e.risk_event\n" + + " , e.risk_detail\n" + + " , e.risk_grade\n" + + " , e.risk_status\n" + + " , If( e.deal_type IS NULL\n" + + " OR e.deal_type = '', '--', e.deal_type ) AS dealtype\n" + + " , e.deal_result\n" + + " , If( e.deal_remark IS NULL\n" + + " OR e.deal_remark = '', '--', e.deal_remark ) AS dealremark\n" + + " , e.is_deleted\n" + + " , e.review_object_id\n" + + " , e.archive_id\n" + + " , e.feature AS featurename\n" + + " , Ifnull( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.review_first_user ), ( SELECT DISTINCT\n" + + " real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth\n" + + + " WHERE uniapp_user_id = e.review_first_user\n" + + + " AND is_disable = 0 ) ) AS reviewfirstuser\n" + + + " , Ifnull( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.review_latest_user ), ( SELECT DISTINCT\n" + + " real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth\n" + + + " WHERE uniapp_user_id = e.review_latest_user\n" + + + " AND is_disable = 0 ) ) AS reviewnewuser\n" + + + " , If( ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.deal_user ) IS NOT NULL\n" + + " AND e.deal_user != - 9999, ( SELECT real_name\n" + + " FROM bladex.blade_user\n" + + " WHERE id = e.deal_user ), '--' ) AS dealuser\n" + + + " , CASE\n" + + " WHEN 'COMPANY'\n" + + " THEN Concat( ( SELECT ar.customer_name\n" + + " FROM mtp_cs.mtp_rsk_cust_archive ar\n" + + " WHERE ar.is_deleted = 0\n" + + " AND ar.id = e.archive_id ), If( ( SELECT alias\n" + + + " FROM web_crm.wcrm_customer\n" + + + " WHERE id = e.customer_id ) = ''\n" + + + " OR ( SELECT alias\n" + + " FROM web_crm.wcrm_customer\n" + + " WHERE id = e.customer_id ) IS NULL, ' ', Concat( '(', ( SELECT alias\n" + + + " FROM web_crm.wcrm_customer\n" + + + " WHERE id = e.customer_id ), ')' ) ) )\n" + + + " WHEN 'EMPLOYEE'\n" + + " THEN ( SELECT Concat( auth.real_name, ' ', auth.phone )\n" + + " FROM app_sys.asys_uniapp_rn_auth auth\n" + + " WHERE auth.is_disable = 0\n" + + " AND auth.uniapp_user_id = e.uniapp_user_id )\n" + + " WHEN 'DEAL'\n" + + " THEN ( SELECT DISTINCT\n" + + " Concat( batch.code, '-', detail.line_seq\n" + + " , ' ', Ifnull( ( SELECT DISTINCT\n" + + " auth.real_name\n" + + " FROM app_sys.asys_uniapp_rn_auth auth\n" + + + " WHERE auth.uniapp_user_id = e.uniapp_user_id\n" + + + " AND auth.is_disable = 0 ), ' ' ) )\n" + + + " FROM web_pym.wpym_payment_batch_detail detail\n" + + " LEFT JOIN web_pym.wpym_payment_batch batch\n" + + " ON detail.payment_batch_id = batch.id\n" + + " WHERE detail.id = e.review_object_id )\n" + + " WHEN 'TASK'\n" + + " THEN ( SELECT code\n" + + " FROM web_tm.wtm_task task\n" + + " WHERE e.review_object_id = task.id )\n" + + " ELSE NULL\n" + + " END AS reviewobjectname\n" + + " , CASE\n" + + " WHEN 4\n" + + " THEN 'HIGH_LEVEL'\n" + + " WHEN 3\n" + + " THEN 'MEDIUM_LEVEL'\n" + + " WHEN 2\n" + + " THEN 'LOW_LEVEL'\n" + + " ELSE 'HEALTHY'\n" + + " END AS risklevel\n" + + "FROM mtp_cs.mtp_rsk_event e\n" + + "WHERE e.is_deleted = 0\n" + + "ORDER BY e.review_latest_datetime DESC\n" + + "LIMIT 30\n" + + ";"; + + long startMillis = System.currentTimeMillis(); + ExecutorService executorService = Executors.newSingleThreadExecutor(); + for (int i = 1; i < 1; i++) { + final CCJSqlParser parser = new CCJSqlParser(sqlStr) + .withSquareBracketQuotation(false) + .withAllowComplexParsing(true) + .withBackslashEscapeCharacter(false); + Future future = executorService.submit(new Callable() { + @Override + public Statements call() throws ParseException { + return parser.Statements(); + } + }); + try { + future.get(6000, TimeUnit.MILLISECONDS); + long endMillis = System.currentTimeMillis(); + + System.out.println("Time to parse [ms]: " + (endMillis - startMillis) / i); + } catch (TimeoutException | InterruptedException ex2) { + parser.interrupted = true; + future.cancel(true); + throw new JSQLParserException("Failed to within reasonable time ", ex2); + } catch (ExecutionException e) { + if (e.getCause() instanceof ParseException) { + ParseException parseException = (ParseException) e.getCause(); + net.sf.jsqlparser.parser.Token token = parseException.currentToken.next; + throw new JSQLParserException( + "Failed to parse statement at Token " + token.image); + } + } + } + executorService.shutdown(); + } }