From 786c8fc65858ff6b94ee8295c80155f110013a4a Mon Sep 17 00:00:00 2001 From: Nico Date: Tue, 29 Jan 2019 12:11:07 +0100 Subject: [PATCH 01/77] added support for T-SQL left and right joins (*= and =*) --- .../expression/ExpressionVisitor.java | 6 ++++ .../expression/ExpressionVisitorAdapter.java | 11 +++++- .../operators/relational/TSQLLeftJoin.java | 36 +++++++++++++++++++ .../operators/relational/TSQLRightJoin.java | 36 +++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 12 +++++++ .../util/deparser/ExpressionDeParser.java | 12 +++++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 ++ .../statement/select/SelectTest.java | 14 +++++++- 8 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index b1e4e7463..d75fbb76c 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -48,6 +48,8 @@ import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; +import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; import net.sf.jsqlparser.expression.operators.relational.JsonOperator; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.SubSelect; @@ -180,4 +182,8 @@ public interface ExpressionVisitor { public void visit(NotExpression aThis); + public void visit(TSQLLeftJoin tsqlLeftJoin); + + public void visit(TSQLRightJoin tsqlRightJoin); + } diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index c5730c9a1..8988ac43b 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -24,7 +24,6 @@ import net.sf.jsqlparser.expression.operators.arithmetic.*; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.conditional.OrExpression; -import net.sf.jsqlparser.expression.operators.relational.JsonOperator; import net.sf.jsqlparser.expression.operators.relational.*; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.select.AllColumns; @@ -508,4 +507,14 @@ public void visit(DateTimeLiteralExpression literal) { } + @Override + public void visit(TSQLLeftJoin tsqlLeftJoin) { + visitBinaryExpression(tsqlLeftJoin); + } + + @Override + public void visit(TSQLRightJoin tsqlRightJoin) { + visitBinaryExpression(tsqlRightJoin); + } + } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java new file mode 100644 index 000000000..c8d9e2112 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java @@ -0,0 +1,36 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class TSQLLeftJoin extends ComparisonOperator { + + public TSQLLeftJoin() { + super("*="); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java new file mode 100644 index 000000000..0430a87d5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java @@ -0,0 +1,36 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class TSQLRightJoin extends ComparisonOperator { + + public TSQLRightJoin() { + super("=*"); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index f62880b6f..cedd973d8 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -91,6 +91,8 @@ 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.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Block; @@ -859,4 +861,14 @@ public void visit(DescribeStatement describe) { public void visit(ExplainStatement explain) { explain.getStatement().accept(this); } + + @Override + public void visit(TSQLLeftJoin tsqlLeftJoin) { + visitBinaryExpression(tsqlLeftJoin); + } + + @Override + public void visit(TSQLRightJoin tsqlRightJoin) { + visitBinaryExpression(tsqlRightJoin); + } } 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 9336bfa0d..4bd4498c7 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -94,6 +94,8 @@ import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax; +import net.sf.jsqlparser.expression.operators.relational.TSQLLeftJoin; +import net.sf.jsqlparser.expression.operators.relational.TSQLRightJoin; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.OrderByElement; @@ -812,4 +814,14 @@ public void visit(DateTimeLiteralExpression literal) { buffer.append(literal.toString()); } + @Override + public void visit(TSQLLeftJoin tsqlLeftJoin) { + visitBinaryExpression(tsqlLeftJoin, " *= "); + } + + @Override + public void visit(TSQLRightJoin tsqlRightJoin) { + visitBinaryExpression(tsqlRightJoin, " =* "); + } + } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 3369d06fb..ef943c2e3 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -2182,6 +2182,8 @@ Expression RegularCondition() #RegularCondition: ">" { result = new GreaterThan(); } | "<" { result = new MinorThan(); } | "=" { result = new EqualsTo(); } + | "*=" { result = new TSQLLeftJoin(); } + | "=*" { result = new TSQLRightJoin(); } | token= { result = new GreaterThanEquals(token.image); } | token= { result = new MinorThanEquals(token.image); } | token= { result = new NotEqualsTo(token.image); } 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 0c670beee..3b003599c 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -1810,12 +1810,24 @@ public void testFunctionRight() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed(statement); } + @Test + public void testTSQLJoin() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a *= tabelle2.b"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + @Test + public void testTSQLJoin2() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a =* tabelle2.b"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + @Test public void testOracleJoin() throws JSQLParserException { String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a = tabelle2.b(+)"; assertSqlCanBeParsedAndDeparsed(stmt); } - + @Test public void testOracleJoin2() throws JSQLParserException { String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) = tabelle2.b"; From 9a67d1277a0bf801e3222fad93d0a9e0a345f2f9 Mon Sep 17 00:00:00 2001 From: joeqiao Date: Tue, 8 Nov 2022 09:27:25 +0800 Subject: [PATCH 02/77] add support for index hints in Update statement for MySQL --- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 13 ++++++++++- .../statement/update/UpdateTest.java | 5 +++++ .../net/sf/jsqlparser/test/TestUtils.java | 22 ++++++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index e446a5caf..e57ac525b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1203,7 +1203,7 @@ Update Update( List with ): { update.setOracleHint(getOracleHint()); } [ LOOKAHEAD(2) { modifierPriority = UpdateModifierPriority.LOW_PRIORITY; }] [ LOOKAHEAD(2) { modifierIgnore = true; }] - table=TableWithAlias() startJoins=JoinsList() + table=TableWithAliasAndMysqlIndexHint() startJoins=JoinsList() ( LOOKAHEAD(3) tableColumn=Column() "=" valueExpression=SimpleExpression() { update.addUpdateSet(tableColumn, valueExpression); } @@ -1914,6 +1914,17 @@ Table TableWithAlias(): { return table; } } +Table TableWithAliasAndMysqlIndexHint(): +{ + Table table = null; + Alias alias = null; + MySQLIndexHint indexHint = null; +} +{ + table=Table() [alias=Alias() { table.setAlias(alias); }] [indexHint=MySQLIndexHint() {table.setHint(indexHint);}] + { return table; } +} + Select SelectWithWithItems( ): { Select select; diff --git a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java index f06de1401..da543d50f 100644 --- a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java @@ -177,6 +177,11 @@ public void testOracleHint() throws JSQLParserException { // assertOracleHintExists("UPDATE mytable /*+ SOMEHINT */ set col1='as', col2=?, col3=565 Where o >= 3", true, "SOMEHINT"); } + @Test + public void testMysqlHint() throws JSQLParserException { + assertUpdateMysqlHintExists("UPDATE demo FORCE INDEX (idx_demo) SET col1 = NULL WHERE col2 = 1", true, "FORCE", "INDEX", "idx_demo"); + } + @Test public void testWith() throws JSQLParserException { String statement diff --git a/src/test/java/net/sf/jsqlparser/test/TestUtils.java b/src/test/java/net/sf/jsqlparser/test/TestUtils.java index 322250559..7ace9d91b 100644 --- a/src/test/java/net/sf/jsqlparser/test/TestUtils.java +++ b/src/test/java/net/sf/jsqlparser/test/TestUtils.java @@ -20,6 +20,7 @@ import java.util.stream.Stream; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.MySQLIndexHint; import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.parser.CCJSqlParser; import net.sf.jsqlparser.parser.CCJSqlParserUtil; @@ -37,11 +38,11 @@ import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + /** * * @author toben @@ -342,4 +343,19 @@ public static void assertOracleHintExists(String sql, boolean assertDeparser, St assertEquals(hints[0], hint.getValue()); } } + + public static void assertUpdateMysqlHintExists(String sql, boolean assertDeparser, String action, String qualifier, String... indexNames) + throws JSQLParserException { + if (assertDeparser) { + assertSqlCanBeParsedAndDeparsed(sql, true); + } + Statement statement = CCJSqlParserUtil.parse(sql); + assertInstanceOf(Update.class, statement); + Update updateStmt = (Update) statement; + final MySQLIndexHint indexHint = updateStmt.getTable().getIndexHint(); + assertNotNull(indexHint); + assertEquals(indexHint.getAction(), action); + assertEquals(indexHint.getIndexQualifier(), qualifier); + assertArrayEquals(indexHint.getIndexNames().toArray(), indexNames); + } } From a9ed79825110df7d619209355d7b2528c6e5e397 Mon Sep 17 00:00:00 2001 From: hezw <562991351@qq.com> Date: Wed, 24 May 2023 11:09:19 +0800 Subject: [PATCH 03/77] feat:support clickhouse global keyword in IN Expression Add support for clickhouse global keyword in IN Expression example: SELECT lo_linenumber,lo_orderkey from lo_linenumber where lo_linenumber global in (1,2,3) --- .../operators/relational/InExpression.java | 16 ++++++++++++++++ .../util/deparser/ExpressionDeParser.java | 3 +++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../statement/select/ClickHouseTest.java | 7 +++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java index 89f410870..3c37c19b7 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java @@ -17,6 +17,7 @@ public class InExpression extends ASTNodeAccessImpl implements Expression, Suppo private Expression leftExpression; private ItemsList rightItemsList; + private boolean global = false; private boolean not = false; private Expression rightExpression; private int oldOracleJoinSyntax = NO_ORACLE_JOIN; @@ -69,6 +70,13 @@ public final void setLeftExpression(Expression expression) { leftExpression = expression; } + public boolean isGlobal() { + return global; + } + + public void setGlobal(boolean b) { + global = b; + } public boolean isNot() { return not; } @@ -100,6 +108,9 @@ public String toString() { statementBuilder.append(getLeftExpressionString()); statementBuilder.append(" "); + if (global) { + statementBuilder.append("GLOBAL "); + } if (not) { statementBuilder.append("NOT "); } @@ -141,6 +152,11 @@ public InExpression withOraclePriorPosition(int priorPosition) { return this; } + public InExpression withGlobal(boolean global) { + this.setGlobal(global); + return this; + } + public InExpression withNot(boolean not) { this.setNot(not); return this; diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index f1e75ae61..5c2d9771e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -252,6 +252,9 @@ public void visit(InExpression inExpression) { .getOldOracleJoinSyntax() == SupportsOldOracleJoinSyntax.ORACLE_JOIN_RIGHT) { buffer.append("(+)"); } + if (inExpression.isGlobal()) { + buffer.append(" GLOBAL"); + } if (inExpression.isNot()) { buffer.append(" NOT"); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 472a0c2aa..30bd223aa 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3314,7 +3314,7 @@ Expression InExpression() #InExpression : leftExpression=SimpleExpression() { result.setLeftExpression(leftExpression); } [ "(" "+" ")" { result.setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); } ] - [ { result.setNot(true); } ] + [ { result.setGlobal(true); } ][ { result.setNot(true); } ] ( LOOKAHEAD(2) token= { result.setRightExpression(new StringValue(token.image)); } | LOOKAHEAD(3) rightExpression = Function() { result.setRightExpression(rightExpression); } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java index fa52af6a9..26f2ec33c 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -34,4 +34,11 @@ public void testFunctionWithAttributesIssue1742() throws JSQLParserException { sql = "SELECT schemaName.f1(arguments).f2(arguments).f3.f4 from dual"; assertSqlCanBeParsedAndDeparsed(sql, true); } + + @Test + public void testGlobalIn() throws JSQLParserException { + String sql = + "SELECT lo_linenumber,lo_orderkey from lo_linenumber where lo_linenumber global in (1,2,3)"; + assertSqlCanBeParsedAndDeparsed(sql, true); + } } From 9857d021a790e8b6539d1cfc09c10e33dd8a711c Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Sat, 2 Sep 2023 00:16:30 +0200 Subject: [PATCH 04/77] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 60b36c8a7..9067f410c 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.7 + 4.8-SNAPSHOT JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - jsqlparser-4.7 + HEAD From ac40baa83358c82666d3fa0b0c36c52e8c5c5464 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 13:04:08 +0700 Subject: [PATCH 05/77] doc: Migration to 4.7 --- build.gradle | 3 ++- src/site/sphinx/migration.rst | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d4cc92e13..f37a3f652 100644 --- a/build.gradle +++ b/build.gradle @@ -478,7 +478,7 @@ tasks.register('sphinx', Exec) { } publish { - dependsOn(check) + dependsOn(check, sphinx) } publishing { @@ -571,6 +571,7 @@ remotes { } tasks.register('upload') { + dependsOn(check, sphinx) doFirst { if (findProperty("${project.name}.host") == null) { println( diff --git a/src/site/sphinx/migration.rst b/src/site/sphinx/migration.rst index 6a1fbc9e4..94f299fab 100644 --- a/src/site/sphinx/migration.rst +++ b/src/site/sphinx/migration.rst @@ -1,8 +1,8 @@ ********************************* -Migration to 5.0 +Migration to 4.7 ********************************* -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. +The new version of JSQLParser 4.7 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. From eb3a6cb69e07dce2ad93d6dccfb570b5509ec603 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 13:28:17 +0700 Subject: [PATCH 06/77] build: add tag, update changelog --- build.gradle | 4 ++-- src/site/sphinx/changelog.rst | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index f37a3f652..4bca6b872 100644 --- a/build.gradle +++ b/build.gradle @@ -478,7 +478,7 @@ tasks.register('sphinx', Exec) { } publish { - dependsOn(check, sphinx) + dependsOn(check, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc) } publishing { @@ -571,7 +571,7 @@ remotes { } tasks.register('upload') { - dependsOn(check, sphinx) + dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc) doFirst { if (findProperty("${project.name}.host") == null) { println( diff --git a/src/site/sphinx/changelog.rst b/src/site/sphinx/changelog.rst index 9d98f0d37..7e1b34f5d 100644 --- a/src/site/sphinx/changelog.rst +++ b/src/site/sphinx/changelog.rst @@ -8,6 +8,21 @@ Latest Changes since |JSQLPARSER_VERSION| ============================================================= + * **doc: Migration to 4.7** + + Andreas Reichel, 2023-09-02 + * **[maven-release-plugin] prepare for next development iteration** + + Tobias Warneke, 2023-09-01 + * **[maven-release-plugin] prepare release jsqlparser-4.7** + + Tobias Warneke, 2023-09-01 + * **feat: H2 BYTEA Values `X'01' '02'`** + + Andreas Reichel, 2023-09-01 + * **doc: update changelog** + + Andreas Reichel, 2023-09-01 * **feat: BigQuery Except(..) Replace(..) syntax** Andreas Reichel, 2023-09-01 From e428c7fa0072133ea365087f58822721b28121d9 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 16:26:17 +0700 Subject: [PATCH 07/77] doc: many website cosmetics about Release vs. Snapshot --- .github/workflows/sphinx.yml | 2 +- .gitignore | 4 +- build.gradle | 12 +- src/main/resources/rr/xhtml2rst.xsl | 21 +- src/site/sphinx/changelog.rst | 1232 --- src/site/sphinx/index.rst | 26 +- src/site/sphinx/syntax_stable.rst | 13867 -------------------------- 7 files changed, 49 insertions(+), 15115 deletions(-) delete mode 100644 src/site/sphinx/changelog.rst delete mode 100644 src/site/sphinx/syntax_stable.rst diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 8ff3932aa..e35f98b59 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2 - name: Run build with Gradle Wrapper - run: gradle --no-build-cache clean xmldoc sphinx + run: FLOATING_TOC=false gradle --no-build-cache clean xmldoc sphinx - name: Deploy uses: actions/configure-pages@v2 - name: Upload artifact diff --git a/.gitignore b/.gitignore index 76f3c99dd..20db77d7c 100755 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ /src/site/sphinx/changelog.rst /src/site/sphinx/javadoc_stable.rst /src/site/sphinx/syntax_stable.rst +/src/site/sphinx/javadoc_snapshot.rst +/src/site/sphinx/syntax_snapshot.rst # Generated by javacc-maven-plugin /bin @@ -29,5 +31,3 @@ /nbproject/ /.gradle -/src/site/sphinx/javadoc_snapshot.rst -/src/site/sphinx/syntax_snapshot.rst diff --git a/build.gradle b/build.gradle index 4bca6b872..32d5ed8c6 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,10 @@ def getVersion = { boolean considerSnapshot -> ? "${major}.${minor}.${patch}${snapshot}" : "${major}.${minor}${snapshot}" } -version = getVersion(true) + +// for publishing a release, call Gradle with Environment Variable RELEASE: +// RELEASE=true gradle JSQLParser:publish +version = getVersion( !System.getenv("RELEASE") ) group = 'com.github.jsqlparser' description = 'JSQLParser library' archivesBaseName = "JSQLParser" @@ -431,6 +434,11 @@ xslt { dependsOn(renderRR) stylesheet 'src/main/resources/rr/xhtml2rst.xsl' + parameters ( + "withFloatingToc": System.getenv().getOrDefault("FLOATING_TOC", "true"), + "isSnapshot": Boolean.toString(version.endsWith("-SNAPSHOT")) + ) + // Transform every .xml file in the "input" directory. input "$buildDir/rr/JSqlParserCC.xhtml" output outFile @@ -571,7 +579,6 @@ remotes { } tasks.register('upload') { - dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc) doFirst { if (findProperty("${project.name}.host") == null) { println( @@ -594,4 +601,5 @@ tasks.register('upload') { } } } +upload.dependsOn(check, assemble, gitChangelogTask, renderRR, xslt, updateKeywords, xmldoc) diff --git a/src/main/resources/rr/xhtml2rst.xsl b/src/main/resources/rr/xhtml2rst.xsl index 5d0132bad..666113762 100644 --- a/src/main/resources/rr/xhtml2rst.xsl +++ b/src/main/resources/rr/xhtml2rst.xsl @@ -21,11 +21,14 @@ omit-xml-declaration="yes" indent="no" /> - + + - + + + .. raw:: html <div id="floating-toc"> @@ -36,18 +39,26 @@ <ul id="toc-list"></ul> </div> - + + + ********************************************************************* -SQL Syntax |JSQLPARSER_SNAPSHOT_VERSION| +SQL Syntax JSQLParser- + |JSQLPARSER_SNAPSHOT_VERSION| + |JSQLPARSER_VERSION| + ********************************************************************* -The EBNF and Railroad Diagrams for JSQLParser-|JSQLPARSER_VERSION|. +The EBNF and Railroad Diagrams for JSQLParser- + |JSQLPARSER_SNAPSHOT_VERSION| + |JSQLPARSER_VERSION| + . diff --git a/src/site/sphinx/changelog.rst b/src/site/sphinx/changelog.rst deleted file mode 100644 index 7e1b34f5d..000000000 --- a/src/site/sphinx/changelog.rst +++ /dev/null @@ -1,1232 +0,0 @@ - -************************ -Changelog -************************ - - -Latest Changes since |JSQLPARSER_VERSION| -============================================================= - - - * **doc: Migration to 4.7** - - Andreas Reichel, 2023-09-02 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2023-09-01 - * **[maven-release-plugin] prepare release jsqlparser-4.7** - - Tobias Warneke, 2023-09-01 - * **feat: H2 BYTEA Values `X'01' '02'`** - - Andreas Reichel, 2023-09-01 - * **doc: update changelog** - - Andreas Reichel, 2023-09-01 - * **feat: BigQuery Except(..) Replace(..) syntax** - - Andreas Reichel, 2023-09-01 - * **test: add test for Postgres `NOTNULL` expression** - - Andreas Reichel, 2023-08-31 - * **build: Disable Signing with in Memory keys temporarily** - - Andreas Reichel, 2023-08-29 - * **fix: ExpressionList of Expressions in `Values`** - - Andreas Reichel, 2023-08-26 - * **style: add license headers** - - Andreas Reichel, 2023-08-23 - * **feat: implement a few missing expressions** - - Andreas Reichel, 2023-08-23 - * **fix: check for NULL before iterating** - - Andreas Reichel, 2023-08-23 - * **doc: explain the TablesNamesFinder, fix the example** - - Andreas Reichel, 2023-08-20 - * **doc: explain the TablesNamesFinder** - - Andreas Reichel, 2023-08-20 - * **Fixing a problem with an OP_CONCAT in WhenExpression (#1837)** - - amigalev, 2023-08-20 - * **refactor: TablesNamesFinder** - - Andreas Reichel, 2023-08-20 - * **build: disable Gradle publish** - - Andreas Reichel, 2023-08-20 - * **refactor: TableFunction extends Function, supports `LATERAL` prefix** - - Andreas Reichel, 2023-08-20 - * **Update Gradle JavaCC parser to latest version (3.0.0) (#1843)** - - Zbynek Konecny, 2023-08-05 - * **Update sql-parser-error.md** - - manticore-projects, 2023-07-26 - * **Update sql-parser-error.md** - - manticore-projects, 2023-07-26 - * **Update sql-parser-error.md** - - manticore-projects, 2023-07-25 - * **feat: SQL:2016 TABLESAMPLE clause** - - Andreas Reichel, 2023-07-12 - * **feat: add a method checking balanced brackets** - - Andreas Reichel, 2023-07-12 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-12 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-03 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-02 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-02 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-02 - * **build: publish Snapshots on commit** - - Andreas Reichel, 2023-07-02 - * **feat: add support for INTERPRET function parsing (#1816)** - - Matteo Sist, 2023-06-29 - * **style: Licenses from Maven plugin** - - Andreas Reichel, 2023-06-27 - * **fix: Backslash escaped single quote `'\''`** - - Andreas Reichel, 2023-06-27 - * **Assorted Fixes #8 (#1807)** - - manticore-projects, 2023-06-15 - * **fix: `INSERT` must use simple Column Names only** - - Andreas Reichel, 2023-06-15 - * **feat: MySQL `NOT RLIKE`, `NOT REGEXP` expressions** - - Andreas Reichel, 2023-06-15 - * **feat: Postgres `NOTNULL` support** - - manticore-projects, 2023-06-15 - * **feat: `QUALIFY` clause** - - Andreas Reichel, 2023-06-15 - * **docs: write migration guide** - - Andreas Reichel, 2023-06-11 - * **fix: SPHINX modules and themes** - - Andreas Reichel, 2023-06-02 - * **feat: T-SQL `FOR ...` clause** - - Andreas Reichel, 2023-06-02 - * **doc: migration guide** - - Andreas Reichel, 2023-06-02 - * **fix: expose IntervalExpression attributes and use DeParser** - - Andreas Reichel, 2023-06-01 - * **Update sphinx.yml** - - manticore-projects, 2023-06-01 - * **JSQLParser 5.0 (#1778)** - - manticore-projects, 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 - * **feat: functions blocks, parenthesed JSON Expressions** - - Andreas Reichel, 2023-05-18 - * **feat: parse CREATE TRIGGER as UnsupportedStatement** - - Andreas Reichel, 2023-05-17 - * **build: try to work around the Maven/JDK8 issue on GitHub** - - Andreas Reichel, 2023-05-17 - * **refact: Statements extends List** - - Andreas Reichel, 2023-05-17 - * **style: remove unused imports** - - Andreas Reichel, 2023-05-17 - * **feat: chaining JSON Expressions** - - Andreas Reichel, 2023-05-17 - * **style: Cosmetic improvements** - - Andreas Reichel, 2023-05-17 - * **style: Quieten the logger** - - Andreas Reichel, 2023-05-17 - * **fix: Complex Parsing Approach** - - Andreas Reichel, 2023-05-17 - * **refactor: CREATE and ALTER productions** - - Andreas Reichel, 2023-05-16 - * **refactor: RETURNING clause** - - Andreas Reichel, 2023-05-16 - * **refactor: SHOW statement, supporting any RDBMS specific implementation** - - Andreas Reichel, 2023-05-16 - * **refactor: simplify production `CreateParameter()`** - - Andreas Reichel, 2023-05-16 - * **fix: issue #1789** - - Andreas Reichel, 2023-05-16 - * **fix: issue #1789** - - Andreas Reichel, 2023-05-16 - * **fix: issue #1791** - - Andreas Reichel, 2023-05-15 - * **build: improve the GIT Snapshot detection** - - Andreas Reichel, 2023-05-15 - * **build: Sphinx build fixes** - - Andreas Reichel, 2023-05-14 - * **build: Sphinx build fixes** - - Andreas Reichel, 2023-05-14 - * **build: Sphinx build fixes** - - Andreas Reichel, 2023-05-14 - * **Update sphinx.yml** - - manticore-projects, 2023-05-14 - * **feat: Write API documentation to the WebSite via XMLDoclet** - - Andreas Reichel, 2023-05-14 - * **test: add unit test for issue #1778** - - Andreas Reichel, 2023-05-11 - * **style: appease PMD/Codacy** - - Andreas Reichel, 2023-05-11 - * **style: appease PMD/Codacy** - - Andreas Reichel, 2023-05-11 - * **feat: `MEMBER OF` condition as shown at https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_member-of** - - Andreas Reichel, 2023-05-11 - * **feat: access Elements of Array Columns** - - Andreas Reichel, 2023-05-11 - * **feat: JdbcNamedParameter allows "&" (instead of ":")** - - Andreas Reichel, 2023-05-11 - * **fix: Java Version 8** - - Andreas Reichel, 2023-05-09 - * **refactor: generify `SelectItem` and remove `FunctionItem` and `ExpressionListItem`** - - Andreas Reichel, 2023-05-09 - * **style: replace all List with ExpressionList<> and enforce policy via Acceptance Test** - - Andreas Reichel, 2023-05-09 - * **fix: find the correct position when field belongs to an internal class** - - Andreas Reichel, 2023-05-09 - * **style: Appease PMD** - - Andreas Reichel, 2023-05-07 - * **style: Appease Checkstyle** - - Andreas Reichel, 2023-05-07 - * **test: Disable API Sanitation for the moment** - - Andreas Reichel, 2023-05-07 - * **refactor: `Insert` uses `ExpressionList` and `UpdateSet`** - - Andreas Reichel, 2023-05-07 - * **build: improve Gradle Build** - - Andreas Reichel, 2023-05-07 - * **refactor: remove SimpleFunction** - - Andreas Reichel, 2023-05-06 - * **doc: RR chart colors cater for Dark Mode** - - Andreas Reichel, 2023-05-06 - * **doc: Better Sphinx Tabs** - - Andreas Reichel, 2023-05-06 - * **style: Rework all the ENUMs** - - Andreas Reichel, 2023-05-05 - * **style: Appease Codacy** - - Andreas Reichel, 2023-05-04 - * **refactor: Remove `ItemsList`, `MultiExpressionList`, `Replace`** - - Andreas Reichel, 2023-05-04 - * **style: Checkstyle** - - Andreas Reichel, 2023-05-03 - * **style: Appease Codacy** - - Andreas Reichel, 2023-05-03 - * **build: Increase TimeOut for the GitHub CI** - - Andreas Reichel, 2023-05-03 - * **refactor: UpdateSets for `Update` and `InsertConflictTarget`** - - Andreas Reichel, 2023-05-03 - * **fix: Remove tests for `()`, since `ParenthesedExpressionList` will catch those too** - - Andreas Reichel, 2023-05-03 - * **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 - * **doc: Update the README.md** - - Andreas Reichel, 2023-05-01 - * **build: Add missing import** - - Andreas Reichel, 2023-04-30 - * **doc: Update examples** - - Andreas Reichel, 2023-04-30 - * **refactor: remove `SelectExpressionItem` in favor of `SelectItem`** - - Andreas Reichel, 2023-04-30 - * **test: add specific tests for closed issues** - - Andreas Reichel, 2023-04-30 - * **test: add specific tests for closed issues** - - Andreas Reichel, 2023-04-30 - * **feat: ClickHouse `LIMIT ... BY ...` clause** - - Andreas Reichel, 2023-04-30 - * **feat: implement SQL:2016 Convert() and Trim()** - - Andreas Reichel, 2023-04-30 - * **feat: Switch off contradicting `JOIN` qualifiers, when setting a qualifier** - - Andreas Reichel, 2023-04-30 - * **feat: Test if a JOIN is an INNER JOIN according to the SQL:2016** - - Andreas Reichel, 2023-04-30 - * **feat: ClickHouse `Select...` ``FINAL` modifier** - - Andreas Reichel, 2023-04-29 - * **feat: Multi-Part Names for Variables and Parameters** - - Andreas Reichel, 2023-04-29 - * **feat: Oracle `HAVING` before `GROUP BY`** - - Andreas Reichel, 2023-04-29 - * **feat: Lateral View** - - Andreas Reichel, 2023-04-29 - * **Fix #1758: Use long for Feature.timeOut (#1759)** - - Tomasz Zarna, 2023-04-27 - * **Ignoring unnecessarily generated jacoco report (#1762)** - - optimizing-ci-builds, 2023-04-27 - * **Ignoring unnecessarily generated by pmd plugin (#1763)** - - optimizing-ci-builds, 2023-04-27 - * **Refactor Parenthesed SelectBody and FromItem (#1754)** - - manticore-projects, 2023-04-27 - * **Assorted Fixes #7 (#1745)** - - manticore-projects, 2023-03-21 - * **disable xml report (#1748)** - - optimizing-ci-builds, 2023-03-21 - * **Assorted Fixes #6 (#1740)** - - manticore-projects, 2023-03-09 - * **test: commit missing test** - - Andreas Reichel, 2023-03-07 - * **style: apply Spotless** - - Andreas Reichel, 2023-03-07 - * **feat: FETCH uses EXPRESSION** - - Andreas Reichel, 2023-03-07 - * **version 4.7-SNAPSHOT** - - Tobias Warneke, 2023-02-23 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2023-02-23 - * **feat: Support more Statement Separators** - - Andreas Reichel, 2023-02-02 - * **Update issue templates** - - manticore-projects, 2023-02-01 - * **Update issue templates** - - manticore-projects, 2023-02-01 - * **doc: fix the issue template** - - Andreas Reichel, 2023-02-01 - * **feat: CREATE VIEW ... REFRESH AUTO...** - - Andreas Reichel, 2023-01-30 - * **style: Appease PMD/Codacy** - - Andreas Reichel, 2023-01-29 - * **feat: Oracle Alternative Quoting** - - Andreas Reichel, 2023-01-29 - * **doc: Better integration of the RR diagrams** - - Andreas Reichel, 2023-01-21 - * **feat: make important Classes Serializable** - - Andreas Reichel, 2023-01-21 - * **chore: Make Serializable** - - Andreas Reichel, 2023-01-21 - * **doc: request for `Conventional Commit` messages** - - Andreas Reichel, 2023-01-21 - * **Sphinx Documentation** - - Andreas Reichel, 2023-01-21 - * **Define Reserved Keywords explicitly** - - Andreas Reichel, 2023-01-21 - * **Adjust Gradle to JUnit 5** - - Andreas Reichel, 2023-01-21 - * **Enhanced Keywords** - - Andreas Reichel, 2023-01-21 - * **Remove unused imports** - - Andreas Reichel, 2023-01-21 - * **Fix test resources** - - Andreas Reichel, 2023-01-21 - * **Do not mark SpeedTest for concurrent execution** - - Andreas Reichel, 2023-01-21 - * **Fix incorrect tests** - - Andreas Reichel, 2023-01-21 - * **Remove unused imports** - - Andreas Reichel, 2023-01-21 - * **Adjust Gradle to JUnit 5** - - Andreas Reichel, 2023-01-21 - * **Do not mark SpeedTest for concurrent execution** - - Andreas Reichel, 2023-01-21 - * **Reduce cyclomatic complexity in CreateView.toString** - - zaza, 2023-01-08 - * **Fixes #1684: Support CREATE MATERIALIZED VIEW with AUTO REFRESH** - - zaza, 2022-12-11 - -Version jsqlparser-4.6 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.6** - - Tobias Warneke, 2023-02-23 - * **actualized release plugin** - - Tobias Warneke, 2023-02-23 - * **actualized release plugin** - - Tobias Warneke, 2023-02-23 - * **Update build.gradle** - - Tobias, 2023-02-17 - * **Update README.md** - - Tobias, 2023-02-17 - * **Oracle Alternative Quoting (#1722)** - - manticore-projects, 2023-02-07 - * **Issue1673 case within brackets (#1675)** - - manticore-projects, 2023-01-31 - * **Added support for SHOW INDEX from table (#1704)** - - Jayant Kumar Yadav, 2023-01-31 - * **Sphinx Website (#1624)** - - manticore-projects, 2023-01-20 - * **Assorted Fixes #5 (#1715)** - - manticore-projects, 2023-01-20 - * **Support DROP MATERIALIZED VIEW statements (#1711)** - - Tomasz Zarna, 2023-01-12 - * **corrected readme** - - Tobias Warneke, 2023-01-04 - * **Update README.md** - - Tobias, 2022-12-27 - * **Fix #1686: add support for creating views with "IF NOT EXISTS" clause (#1690)** - - Tomasz Zarna, 2022-12-22 - * **Assorted Fixes #4 (#1676)** - - manticore-projects, 2022-12-22 - * **Fixed download war script in the renderRR task (#1659)** - - haha1903, 2022-12-10 - * **Assorted fixes (#1666)** - - manticore-projects, 2022-11-20 - * **Fix parsing statements with multidimensional array PR2 (#1665)** - - manticore-projects, 2022-11-20 - * **removed disabled from Keyword tests and imports** - - Tobias Warneke, 2022-11-02 - * **removed disabled from Keyword tests** - - Tobias Warneke, 2022-11-02 - * **Keywords2: Update whitelisted Keywords (#1653)** - - manticore-projects, 2022-11-02 - * **Enhanced Keywords (#1382)** - - manticore-projects, 2022-10-25 - * **#1610 Support for SKIP LOCKED tokens on SELECT statements (#1649)** - - Lucas Dillmann, 2022-10-25 - * **Assorted fixes (#1646)** - - manticore-projects, 2022-10-16 - * **actualized multiple dependencies** - - Tobias Warneke, 2022-09-28 - * **Bump h2 from 1.4.200 to 2.1.210 (#1639)** - - dependabot[bot], 2022-09-28 - * **Support BigQuery SAFE_CAST (#1622) (#1634)** - - dequn, 2022-09-20 - * **fix: add missing public Getter (#1632)** - - manticore-projects, 2022-09-20 - * **Support timestamptz dateliteral (#1621)** - - Todd Pollak, 2022-08-31 - * **fixes #1617** - - Tobias Warneke, 2022-08-31 - * **fixes #419** - - Tobias Warneke, 2022-08-31 - * **Closes #1604, added simple OVERLAPS support (#1611)** - - Rob Audenaerde, 2022-08-16 - * **Fixes PR #1524 support hive alter sql (#1609)** - - manticore-projects, 2022-08-14 - * **#1524 support hive alter sql : ALTER TABLE name ADD COLUMNS (col_spec[, col_spec ...]) (#1605)** - - Zhumin-lv-wn, 2022-08-03 - * **fixes #1581** - - Tobias Warneke, 2022-07-25 - * **Using own Feature - constant for "delete with returning" #1597 (#1598)** - - gitmotte, 2022-07-25 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2022-07-22 - -Version jsqlparser-4.5 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.5** - - Tobias Warneke, 2022-07-22 - * **introduced changelog generator** - - Tobias Warneke, 2022-07-22 - * **fixes #1596** - - Tobias Warneke, 2022-07-22 - * **integrated test for #1595** - - Tobias Warneke, 2022-07-19 - * **reduced time to parse exception to minimize impact on building time** - - Tobias Warneke, 2022-07-19 - * **add support for drop column if exists (#1594)** - - rrrship, 2022-07-19 - * **PostgreSQL INSERT ... ON CONFLICT Issue #1551 (#1552)** - - manticore-projects, 2022-07-19 - * **Configurable Parser Timeout via Feature (#1592)** - - manticore-projects, 2022-07-19 - * **fixes #1590** - - Tobias Warneke, 2022-07-19 - * **fixes #1590** - - Tobias Warneke, 2022-07-19 - * **extended support Postgres' `Extract( field FROM source)` where `field` is a String instead of a Keyword (#1591)** - - manticore-projects, 2022-07-19 - * **Closes #1579. Added ANALYZE support. (#1587)** - - Rob Audenaerde, 2022-07-14 - * **Closes #1583:: Implement Postgresql optional TABLE in TRUNCATE (#1585)** - - Rob Audenaerde, 2022-07-14 - * **Support table option character set and index options (#1586)** - - luofei, 2022-07-14 - * **corrected a last minute bug** - - Tobias Warneke, 2022-07-09 - * **corrected a last minute bug** - - Tobias Warneke, 2022-07-09 - * **corrected a last minute bug** - - Tobias Warneke, 2022-07-09 - * **fixes #1576** - - Tobias Warneke, 2022-07-09 - * **added simple test for #1580** - - Tobias Warneke, 2022-07-07 - * **disabled test for large cnf expansion and stack overflow problem** - - Tobias Warneke, 2022-07-07 - * **Add test for LikeExpression.setEscape and LikeExpression.getStringExpression (#1568)** - - Caro, 2022-07-07 - * **add support for postgres drop function statement (#1557)** - - rrrship, 2022-07-06 - * **Add support for Hive dialect GROUPING SETS. (#1539)** - - chenwl, 2022-07-06 - * **fixes #1566** - - Tobias Warneke, 2022-06-28 - * **Postgres NATURAL LEFT/RIGHT joins (#1560)** - - manticore-projects, 2022-06-28 - * **compound statement tests (#1545)** - - Matthew Rathbone, 2022-06-08 - * **Allow isolation keywords as column name and aliases (#1534)** - - Tomer Shay (Shimshi), 2022-05-19 - * **added github action badge** - - Tobias, 2022-05-16 - * **Create maven.yml** - - Tobias, 2022-05-16 - * **introduced deparser and toString correction for insert output clause** - - Tobias Warneke, 2022-05-15 - * **revived compilable status after merge** - - Tobias Warneke, 2022-05-15 - * **INSERT with SetOperations (#1531)** - - manticore-projects, 2022-05-15 - * **#1516 rename without column keyword (#1533)** - - manticore-projects, 2022-05-11 - * **Add support for `... ALTER COLUMN ... DROP DEFAULT` (#1532)** - - manticore-projects, 2022-05-11 - * **#1527 DELETE ... RETURNING ... (#1528)** - - manticore-projects, 2022-05-11 - * **fixs #1520 (#1521)** - - chiangcho, 2022-05-11 - * **Unsupported statement (#1519)** - - manticore-projects, 2022-05-11 - * **fixes #1518** - - Tobias Warneke, 2022-04-26 - * **Update bug_report.md (#1512)** - - manticore-projects, 2022-04-22 - * **changed to allow #1481** - - Tobias Warneke, 2022-04-22 - * **Performance Improvements (#1439)** - - manticore-projects, 2022-04-14 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2022-04-10 - -Version jsqlparser-4.4 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.4** - - Tobias Warneke, 2022-04-10 - * **Json function Improvements (#1506)** - - manticore-projects, 2022-04-09 - * **fixes #1505** - - Tobias Warneke, 2022-04-09 - * **fixes #1502** - - Tobias Warneke, 2022-04-09 - * **Issue1500 - Circular References in `AllColumns` and `AllTableColumns` (#1501)** - - manticore-projects, 2022-04-03 - * **Optimize assertCanBeParsedAndDeparsed (#1389)** - - manticore-projects, 2022-04-02 - * **Add geometry distance operator (#1493)** - - Thomas Powell, 2022-04-02 - * **Support WITH TIES option in TOP #1435 (#1479)** - - Olivier Cavadenti, 2022-04-02 - * **https://github.com/JSQLParser/JSqlParser/issues/1483 (#1485)** - - gitmotte, 2022-04-02 - * **fixes #1482** - - Tobias Warneke, 2022-03-15 - * **fixes #1482** - - Tobias Warneke, 2022-03-15 - * **Extending CaseExpression, covering #1458 (#1459)** - - Mathieu Goeminne, 2022-03-15 - * **fixes #1471** - - Tobias Warneke, 2022-02-18 - * **fixes #1471** - - Tobias Warneke, 2022-02-18 - * **fixes #1470** - - Tobias Warneke, 2022-02-06 - * **Add support for IS DISTINCT FROM clause (#1457)** - - Tomer Shay (Shimshi), 2022-01-18 - * **fix fetch present in the end of union query (#1456)** - - chiangcho, 2022-01-18 - * **added SQL_CACHE implementation and changed** - - Tobias Warneke, 2022-01-09 - * **support for db2 with ru (#1446)** - - chiangcho, 2021-12-20 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2021-12-12 - -Version jsqlparser-4.3 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.3** - - Tobias Warneke, 2021-12-12 - * **updated readme.md to show all changes for version 4.3** - - Tobias Warneke, 2021-12-12 - * **Adjust Gradle to JUnit 5 (#1428)** - - manticore-projects, 2021-11-28 - * **corrected some maven plugin versions** - - Tobias Warneke, 2021-11-28 - * **fixes #1429** - - Tobias Warneke, 2021-11-23 - * **closes #1427** - - Tobias Warneke, 2021-11-21 - * **CreateTableTest** - - Tobias Warneke, 2021-11-21 - * **Support EMIT CHANGES for KSQL (#1426)** - - Olivier Cavadenti, 2021-11-21 - * **SelectTest.testMultiPartColumnNameWithDatabaseNameAndSchemaName** - - Tobias Warneke, 2021-11-21 - * **reformatted test source code** - - Tobias Warneke, 2021-11-21 - * **organize imports** - - Tobias Warneke, 2021-11-21 - * **replaced all junit 3 and 4 with junit 5 stuff** - - Tobias Warneke, 2021-11-21 - * **Support RESTART without value (#1425)** - - Olivier Cavadenti, 2021-11-20 - * **Add support for oracle UnPivot when use multi columns at once. (#1419)** - - LeiJun, 2021-11-19 - * **Fix issue in parsing TRY_CAST() function (#1391)** - - Prashant Sutar, 2021-11-19 - * **fixes #1414** - - Tobias Warneke, 2021-11-19 - * **Add support for expressions (such as columns) in AT TIME ZONE expressions (#1413)** - - Tomer Shay (Shimshi), 2021-11-19 - * **Add supported for quoted cast expressions for PostgreSQL (#1411)** - - Tomer Shay (Shimshi), 2021-11-19 - * **added USE SCHEMA and CREATE OR REPLACE
support; things that are allowed in Snowflake SQL (#1409)** - - Richard Kooijman, 2021-11-19 - * **Issue #420 Like Expression with Escape Expression (#1406)** - - manticore-projects, 2021-11-19 - * **fixes #1405 and some junit.jupiter stuff** - - Tobias Warneke, 2021-11-19 - * **#1401 add junit-jupiter-api (#1403)** - - gitmotte, 2021-11-19 - * **Support Postgres Dollar Quotes #1372 (#1395)** - - Olivier Cavadenti, 2021-11-19 - * **Add Delete / Update modifiers for MySQL #1254 (#1396)** - - Olivier Cavadenti, 2021-11-19 - * **Fixes #1381 (#1383)** - - manticore-projects, 2021-11-19 - * **Allows CASE ... ELSE ComplexExpression (#1388)** - - manticore-projects, 2021-11-02 - * **IN() with complex expressions (#1384)** - - manticore-projects, 2021-11-01 - * **Fixes #1385 and PR#1380 (#1386)** - - manticore-projects, 2021-10-22 - * **Fixes #1369 (#1370)** - - Ben Grabham, 2021-10-20 - * **Fixes #1371 (#1377)** - - manticore-projects, 2021-10-20 - * **LIMIT OFFSET with Expressions (#1378)** - - manticore-projects, 2021-10-20 - * **Oracle Multi Column Drop (#1379)** - - manticore-projects, 2021-10-20 - * **Support alias for UnPivot statement (see discussion #1374) (#1380)** - - fabriziodelfranco, 2021-10-20 - * **Issue1352 (#1353)** - - manticore-projects, 2021-10-09 - * **Enhance ALTER TABLE ... DROP CONSTRAINTS ... (#1351)** - - manticore-projects, 2021-10-08 - * **Function to use AllColumns or AllTableColumns Expression (#1350)** - - manticore-projects, 2021-10-08 - * **Postgres compliant ALTER TABLE ... RENAME TO ... (#1334)** - - manticore-projects, 2021-09-18 - * **Postgres compliant ALTER TABLE ... RENAME TO ... (#1334)** - - manticore-projects, 2021-09-18 - * **corrected readme to the new snapshot version** - - Tobias Warneke, 2021-09-08 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2021-09-08 - -Version jsqlparser-4.2 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.2** - - Tobias Warneke, 2021-09-08 - * **introducing test for issue #1328** - - Tobias Warneke, 2021-09-07 - * **included some distinct check** - - Tobias Warneke, 2021-09-07 - * **corrected a merge bug** - - Tobias Warneke, 2021-09-07 - * **Prepare4.2 (#1329)** - - manticore-projects, 2021-09-07 - * **CREATE TABLE AS (...) UNION (...) fails (#1309)** - - François Sécherre, 2021-09-07 - * **Fixes #1325 (#1327)** - - manticore-projects, 2021-09-06 - * **Implement Joins with multiple trailing ON Expressions (#1303)** - - manticore-projects, 2021-09-06 - * **Fix Gradle PMD and Checkstyle (#1318)** - - manticore-projects, 2021-09-01 - * **Fixes #1306 (#1311)** - - manticore-projects, 2021-08-28 - * **Update sets (#1317)** - - manticore-projects, 2021-08-27 - * **Special oracle tests (#1279)** - - manticore-projects, 2021-08-09 - * **Implements Hierarchical CONNECT_BY_ROOT Operator (#1282)** - - manticore-projects, 2021-08-09 - * **Implement Transact-SQL IF ELSE Statement Control Flows. (#1275)** - - manticore-projects, 2021-08-09 - * **Add some flexibility to the Alter Statement (#1293)** - - manticore-projects, 2021-08-02 - * **Implement Oracle's Alter System (#1288)** - - manticore-projects, 2021-08-02 - * **Implement Oracle Named Function Parameters Func( param1 => arg1, ...) (#1283)** - - manticore-projects, 2021-08-02 - * **Implement Gradle Buildsystem (#1271)** - - manticore-projects, 2021-08-02 - * **fixes #1272** - - Tobias Warneke, 2021-07-26 - * **Allowes JdbcParameter or JdbcNamedParameter for MySQL FullTextSearch (#1278)** - - manticore-projects, 2021-07-26 - * **Fixes #1267 Cast into RowConstructor (#1274)** - - manticore-projects, 2021-07-26 - * **Separate MySQL Special String Functions accepting Named Argument Separation as this could collide with ComplexExpressionList when InExpression is involved (#1285)** - - manticore-projects, 2021-07-26 - * **Implements Oracle RENAME oldTable TO newTable Statement (#1286)** - - manticore-projects, 2021-07-26 - * **Implement Oracle Purge Statement (#1287)** - - manticore-projects, 2021-07-26 - * **included jacoco to allow code coverage for netbeans** - - Tobias Warneke, 2021-07-18 - * **corrected a Lookahead problem** - - Tobias Warneke, 2021-07-16 - * **Json functions (#1263)** - - manticore-projects, 2021-07-16 - * **fixes #1255** - - Tobias Warneke, 2021-07-16 - * **Active JJDoc and let it create the Grammar BNF documentation (#1256)** - - manticore-projects, 2021-07-16 - * **Bump commons-io from 2.6 to 2.7 (#1265)** - - dependabot[bot], 2021-07-14 - * **Update README.md** - - Tobias, 2021-07-13 - * **Implement DB2 Special Register Date Time CURRENT DATE and CURRENT TIME (#1252)** - - manticore-projects, 2021-07-13 - * **Rename the PMD ruleset configuration file hoping for automatic synchronization with Codacy (#1251)** - - manticore-projects, 2021-07-13 - * **corrected .travis.yml** - - Tobias Warneke, 2021-07-05 - * **corrected .travis.yml** - - Tobias Warneke, 2021-07-05 - * **Update README.md** - - Tobias, 2021-07-05 - * **fixes #1250** - - Tobias Warneke, 2021-07-01 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2021-06-30 - -Version jsqlparser-4.1 -============================================================= - - - * **[maven-release-plugin] prepare release jsqlparser-4.1** - - Tobias Warneke, 2021-06-30 - * **fixes #1140** - - Tobias Warneke, 2021-06-30 - * **introduced #1248 halfway** - - Tobias Warneke, 2021-06-30 - * **Savepoint rollback (#1236)** - - manticore-projects, 2021-06-30 - * **Fixes Function Parameter List Brackets issue #1239 (#1240)** - - manticore-projects, 2021-06-30 - * **corrected javadoc problem** - - Tobias Warneke, 2021-06-27 - * **corrected some lookahead problem** - - Tobias Warneke, 2021-06-26 - * **RESET statement, SET PostgreSQL compatibility (#1104)** - - Роман Зотов, 2021-06-26 - * **corrected some lookahead problem** - - Tobias Warneke, 2021-06-26 - * **Implement Oracle Alter Session Statements (#1234)** - - manticore-projects, 2021-06-26 - * **fixes #1230** - - Tobias Warneke, 2021-06-26 - * **Support DELETE FROM T1 USING T2 WHERE ... (#1228)** - - francois-secherre, 2021-06-16 - * **Row access support (#1181)** - - Роман Зотов, 2021-06-16 - * **corrected lookahead problem of PR #1225** - - Tobias Warneke, 2021-06-14 - * **Delete queries without from, with a schema identifier fails (#1224)** - - François Sécherre, 2021-06-14 - * **Create temporary table t(c1, c2) as select ... (#1225)** - - francois-secherre, 2021-06-14 - * **Nested with items (#1221)** - - manticore-projects, 2021-06-10 - * **Implement GROUP BY () without columns (#1218)** - - manticore-projects, 2021-06-03 - * **TSQL Compliant NEXT VALUE FOR sequence_id (but keeping the spurious NEXTVAL FOR expression) (#1216)** - - manticore-projects, 2021-06-02 - * **Pmd clean up (#1215)** - - manticore-projects, 2021-06-02 - * **Add support for boolean 'XOR' operator (#1193)** - - Adaptive Recognition, 2021-06-02 - * **Update README.md** - - Tobias, 2021-05-31 - * **Implement WITH for DELETE, UPDATE and MERGE statements (#1217)** - - manticore-projects, 2021-05-31 - * **increases complex scanning range** - - Tobias Warneke, 2021-05-26 - * **Allow Complex Parsing of Functions (#1200)** - - manticore-projects, 2021-05-26 - * **Add support for AT TIME ZONE expressions (#1196)** - - Tomer Shay (Shimshi), 2021-05-25 - * **fixes #1211** - - Tobias Warneke, 2021-05-25 - * **fixes #1212** - - Tobias Warneke, 2021-05-25 - * **Fix Nested CASE WHEN performance, fixes issue #1162 (#1208)** - - manticore-projects, 2021-05-25 - * **Add support for casts in json expressions (#1189)** - - Tomer Shay (Shimshi), 2021-05-10 - * **fixes #1185** - - Tobias Warneke, 2021-05-04 - * **supporting/fixing unique inside sql function such as count eg - SELECT count(UNIQUE col2) FROM mytable (#1184)** - - RajaSudharsan Adhikesavan, 2021-05-01 - * **Oracle compliant ALTER TABLE ADD/MODIFY deparser (#1163)** - - manticore-projects, 2021-04-21 - * **Pmd (#1165)** - - manticore-projects, 2021-04-20 - * **function order by support (#1108)** - - Роман Зотов, 2021-04-20 - * **fixes #1159** - - Tobias Warneke, 2021-04-16 - * **added improvements of pr to readme** - - Tobias Warneke, 2021-04-16 - * **Assorted fixes to the Java CC Parser definition (#1153)** - - manticore-projects, 2021-04-16 - * **fixes #1138** - - Tobias Warneke, 2021-04-10 - * **fixes #1138** - - Tobias Warneke, 2021-04-10 - * **fixes #1137** - - Tobias Warneke, 2021-04-10 - * **fixes #1136** - - Tobias Warneke, 2021-04-10 - * **issue #1134 adressed** - - Tobias Warneke, 2021-03-20 - * **Add support for union_with_brackets_and_orderby (#1131)** - - Tomer Shay (Shimshi), 2021-03-14 - * **Add support for union without brackets and with limit (#1132)** - - Tomer Shay (Shimshi), 2021-03-14 - * **Add support for functions in an interval expression (#1099)** - - Tomer Shay (Shimshi), 2021-03-14 - * **subArray support arr[1:3] (#1109)** - - Роман Зотов, 2021-02-05 - * **bug fix (#769)** - - Kunal jha, 2021-02-05 - * **Array contructor support (#1105)** - - Роман Зотов, 2021-02-04 - * **Partial support construct tuple as simple expression (#1107)** - - Роман Зотов, 2021-01-31 - * **support create table parameters without columns, parameter values any names (#1106)** - - Роман Зотов, 2021-01-31 - * **fixes #995** - - Tobias Warneke, 2021-01-13 - * **fixes #1100** - - Tobias Warneke, 2021-01-13 - * **next correction of parenthesis around unions** - - Tobias Warneke, 2021-01-11 - * **fixes #992** - - Tobias Warneke, 2021-01-07 - * **corrected patch for case as table name** - - Tobias Warneke, 2021-01-07 - * **Added support for the Case keyword in table names (#1093)** - - Tomer Shay (Shimshi), 2021-01-07 - * **corrected some javadoc parameter** - - Tobias Warneke, 2021-01-03 - * **added missing pivot test files** - - Tobias Warneke, 2021-01-03 - * **fixes #282 - first refactoring to allow with clause as a start in insert and update** - - Tobias Warneke, 2021-01-02 - * **fixes #282 - first refactoring to allow with clause as a start in insert and update** - - Tobias Warneke, 2021-01-02 - * **Update README.md** - - Tobias, 2021-01-02 - * **fixes #887** - - Tobias Warneke, 2021-01-02 - * **fixes #1091 - added H2 casewhen function with conditional parameters** - - Tobias Warneke, 2021-01-01 - * **fixes #1091 - added H2 casewhen function with conditional parameters** - - Tobias Warneke, 2021-01-01 - * **[maven-release-plugin] prepare for next development iteration** - - Tobias Warneke, 2021-01-01 - diff --git a/src/site/sphinx/index.rst b/src/site/sphinx/index.rst index c01f3e1ab..18569eaab 100644 --- a/src/site/sphinx/index.rst +++ b/src/site/sphinx/index.rst @@ -13,27 +13,41 @@ Java SQL Parser Library usage contribution migration - syntax_stable - syntax_snapshot - javadoc_stable - javadoc_snapshot + SQL Grammar Stable + SQL Grammar Snapshot + Java API Stable + Java API Snapshot keywords changelog -.. image:: https://maven-badges.herokuapp.com/maven-central/com.github.jsqlparser/jsqlparser/badge.svg - :alt: Maven Repo +.. image:: https://img.shields.io/github/release/JSQLParser/JSqlParser?include_prereleases=&sort=semver&color=blue + :alt: GitGub Release Badge + :target: https://github.com/JSQLParser/JSqlParser/releases + +.. image:: https://img.shields.io/github/issues/JSQLParser/JSqlParser + :alt: GitGub Issues Badge + :target: https://github.com/JSQLParser/JSqlParser/issues + +.. image:: https://badgen.net/maven/v/maven-central/com.github.jsqlparser/jsqlparser + :alt: Maven Badge + :target: https://mvnrepository.com/artifact/com.github.jsqlparser/jsqlparser .. image:: https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml/badge.svg :alt: Maven Build Status + :target: https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml .. image:: https://coveralls.io/repos/JSQLParser/JSqlParser/badge.svg?branch=master :alt: Coverage Status + :target: https://coveralls.io/github/com.github.jsqlparser/jsqlparser?branch=master .. image:: https://app.codacy.com/project/badge/Grade/6f9a2d7eb98f45969749e101322634a1 :alt: Codacy Status + :target: https://app.codacy.com/gh/com.github.jsqlparser/jsqlparser/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade .. image:: https://www.javadoc.io/badge/com.github.jsqlparser/jsqlparser.svg :alt: Java Docs + :target: https://javadoc.io/doc/com.github.jsqlparser/jsqlparser/latest/index.html + **JSQLParser** is a SQL statement parser built from JavaCC. It translates SQLs in a traversable hierarchy of Java classes. diff --git a/src/site/sphinx/syntax_stable.rst b/src/site/sphinx/syntax_stable.rst deleted file mode 100644 index 8ea321548..000000000 --- a/src/site/sphinx/syntax_stable.rst +++ /dev/null @@ -1,13867 +0,0 @@ - -********************************************************************* -SQL Syntax |JSQLPARSER_VERSION| Release -********************************************************************* - -The EBNF and Railroad Diagrams for JSQLParser-|JSQLPARSER_VERSION|. - - -====================================================================================================================== -Statement -====================================================================================================================== - - -.. raw:: html - - - - - - IF - - Condition - - SingleStatement - - Block - - ST_SEMICOLON - ELSE - - SingleStatement - - Block - - ST_SEMICOLON - - EOF - - UnsupportedStatement - -
- - -
         ::= ( 'IF' Condition ( ( SingleStatement | Block ) ST_SEMICOLON? 'ELSE' )? )? ( SingleStatement | Block ) ST_SEMICOLON? EOF
-
           | UnsupportedStatement
-
- Not referenced by any. -
- - -====================================================================================================================== -SingleStatement -====================================================================================================================== - - -.. raw:: html - - - - - - WithList - - SelectWithWithItems - - Insert - - Update - - Delete - - Merge - - Select - - Upsert - - Alter - - RenameTableStatement - - Create - - Drop - - Analyze - - Truncate - - Execute - - Set - - Reset - - Show - - Use - - SavepointStatement - - RollbackStatement - COMMIT - - Comment - - Describe - - Explain - - Declare - - Grant - - PurgeStatement - -
- - -
         ::= WithList? ( SelectWithWithItems | Insert | Update | Delete | Merge )
-
           | Select
-
           | Upsert
-
           | Alter
-
           | RenameTableStatement
-
           | Create
-
           | Drop
-
           | Analyze
-
           | Truncate
-
           | Execute
-
           | Set
-
           | Reset
-
           | Show
-
           | Use
-
           | SavepointStatement
-
           | RollbackStatement
-
           | 'COMMIT'
-
           | Comment
-
           | Describe
-
           | Explain
-
           | Declare
-
           | Grant
-
           | PurgeStatement
-
- Referenced by: -
- - -====================================================================================================================== -Block -====================================================================================================================== - - -.. raw:: html - - - - - - BEGIN - - ST_SEMICOLON - - SingleStatement - - Block - - ST_SEMICOLON - END - - ST_SEMICOLON - -
- -
Block    ::= 'BEGIN' ST_SEMICOLON* ( ( SingleStatement | Block ) ST_SEMICOLON )+ 'END' ST_SEMICOLON?
-
- Referenced by: -
- - -====================================================================================================================== -Statements -====================================================================================================================== - - -.. raw:: html - - - - - - ST_SEMICOLON - IF - - Condition - - SingleStatement - - Block - - ST_SEMICOLON - ELSE - - SingleStatement - - Block - - SingleStatement - - Block - - ST_SEMICOLON - - UnsupportedStatement - - ST_SEMICOLON - IF - - Condition - - SingleStatement - - Block - - ST_SEMICOLON - ELSE - - SingleStatement - - Block - - SingleStatement - - Block - - ST_SEMICOLON - - UnsupportedStatement - - EOF - -
- - - -
- Not referenced by any. -
- - -====================================================================================================================== -Declare -====================================================================================================================== - - -.. raw:: html - - - - - - DECLARE - - UserVariable - TABLE - - ( - - ColumnDefinition - , - - ) - - AS - - RelObjectName - - ColDataType - = - - Expression - - UserVariable - , - - -
- -
Declare  ::= 'DECLARE' UserVariable ( 'TABLE' '(' ColumnDefinition ( ',' ColumnDefinition )* ')' | 'AS' RelObjectName | ColDataType ( '=' Expression )? ( ',' UserVariable ColDataType ( '=' Expression )? )* )
-
- Referenced by: -
- - -====================================================================================================================== -Set -====================================================================================================================== - - -.. raw:: html - - - - - - SET - - LOCAL - - SESSION - - K_DATETIMELITERAL - ZONE - - UserVariable - - IdentifierChain - = - - Expression - ZONE - - K_DATETIMELITERAL - = - - RelObjectNameExt - , - - -
- -
Set      ::= 'SET' ( 'LOCAL' | 'SESSION' )? ( K_DATETIMELITERAL 'ZONE' | ( UserVariable | IdentifierChain ) '='? ) Expression ( ',' ( K_DATETIMELITERAL 'ZONE' | RelObjectNameExt '='? )? Expression )*
-
- Referenced by: -
- - -====================================================================================================================== -Reset -====================================================================================================================== - - -.. raw:: html - - - - - - RESET - - K_DATETIMELITERAL - ZONE - - RelObjectName - ALL - - -
- -
Reset    ::= 'RESET' ( K_DATETIMELITERAL 'ZONE' | RelObjectName | 'ALL' )
-
- Referenced by: -
- - -====================================================================================================================== -RenameTableStatement -====================================================================================================================== - - -.. raw:: html - - - - - - RENAME - - TABLE - - IF - - EXISTS - - Table - WAIT - - S_LONG - NOWAIT - - TO - - Table - - Table - , - - -
- - -
         ::= 'RENAME' 'TABLE'? ( 'IF' 'EXISTS' )? Table ( 'WAIT' S_LONG | 'NOWAIT' )? 'TO' Table ( ',' Table 'TO' Table )*
-
- Referenced by: -
- - -====================================================================================================================== -PurgeStatement -====================================================================================================================== - - -.. raw:: html - - - - - - PURGE - - TABLE - - Table - INDEX - - Index - RECYCLEBIN - - DBA_RECYCLEBIN - - TABLESPACE - - S_IDENTIFIER - USER - - S_IDENTIFIER - -
- - -
         ::= 'PURGE' ( 'TABLE' Table | 'INDEX' Index | 'RECYCLEBIN' | 'DBA_RECYCLEBIN' | 'TABLESPACE' S_IDENTIFIER ( 'USER' S_IDENTIFIER )? )
-
- Referenced by: -
- - -====================================================================================================================== -Describe -====================================================================================================================== - - -.. raw:: html - - - - - - DESCRIBE - - Table - -
- -
Describe ::= 'DESCRIBE' Table
-
- Referenced by: -
- - -====================================================================================================================== -Explain -====================================================================================================================== - - -.. raw:: html - - - - - - EXPLAIN - - ExplainStatementOptions - - Select - -
- - -
- Referenced by: -
- - -====================================================================================================================== -ExplainOptionBoolean -====================================================================================================================== - - -.. raw:: html - - - - - - TRUE - - FALSE - - ON - - OFF - - -
- - -
         ::= ( 'TRUE' | 'FALSE' | 'ON' | 'OFF' )?
-
- Referenced by: -
- - -====================================================================================================================== -ExplainFormatOption -====================================================================================================================== - - -.. raw:: html - - - - - - XML - - JSON - - YAML - - -
- - -
         ::= ( 'XML' | 'JSON' | 'YAML' )?
-
- Referenced by: -
- - -====================================================================================================================== -ExplainStatementOptions -====================================================================================================================== - - -.. raw:: html - - - - - - ANALYZE - - BUFFERS - - COSTS - - VERBOSE - - ExplainOptionBoolean - FORMAT - - ExplainFormatOption - -
- - -
         ::= ( ( 'ANALYZE' | 'BUFFERS' | 'COSTS' | 'VERBOSE' ) ExplainOptionBoolean | 'FORMAT' ExplainFormatOption )*
-
- Referenced by: -
- - -====================================================================================================================== -Use -====================================================================================================================== - - -.. raw:: html - - - - - - USE - - SCHEMA - - RelObjectNameExt - -
- -
Use      ::= 'USE' 'SCHEMA'? RelObjectNameExt
-
- Referenced by: -
- - -====================================================================================================================== -Show -====================================================================================================================== - - -.. raw:: html - - - - - - SHOW - - ShowColumns - - ShowIndex - - ShowTables - - captureRest - -
- -
Show     ::= 'SHOW' ( ShowColumns | ShowIndex | ShowTables | captureRest )
-
- Referenced by: -
- - -====================================================================================================================== -ShowColumns -====================================================================================================================== - - -.. raw:: html - - - - - - COLUMNS - - FROM - - RelObjectNameExt - -
- - -
         ::= 'COLUMNS' 'FROM' RelObjectNameExt
-
- Referenced by: -
- - -====================================================================================================================== -ShowIndex -====================================================================================================================== - - -.. raw:: html - - - - - - INDEX - - FROM - - RelObjectNameExt - -
- - -
         ::= 'INDEX' 'FROM' RelObjectNameExt
-
- Referenced by: -
- - -====================================================================================================================== -ShowTables -====================================================================================================================== - - -.. raw:: html - - - - - - EXTENDED - - FULL - - TABLES - - FROM - - IN - - RelObjectNameExt - LIKE - - SimpleExpression - WHERE - - Expression - -
- - -
         ::= 'EXTENDED'? 'FULL'? 'TABLES' ( ( 'FROM' | 'IN' ) RelObjectNameExt )? ( 'LIKE' SimpleExpression | 'WHERE' Expression )?
-
- Referenced by: -
- - -====================================================================================================================== -Values -====================================================================================================================== - - -.. raw:: html - - - - - - VALUES - - VALUE - - ExpressionList - -
- -
Values   ::= ( 'VALUES' | 'VALUE' ) ExpressionList
-
- Referenced by: -
- - -====================================================================================================================== -ReturningClause -====================================================================================================================== - - -.. raw:: html - - - - - - RETURNING - - RETURN - - SelectItemsList - INTO - - Table - - UserVariable - , - - -
- - -
         ::= ( 'RETURNING' | 'RETURN' ) SelectItemsList ( 'INTO' ( Table | UserVariable ) ( ',' ( Table | UserVariable ) )* )?
-
- Referenced by: -
- - -====================================================================================================================== -Update -====================================================================================================================== - - -.. raw:: html - - - - - - UPDATE - - LOW_PRIORITY - - IGNORE - - TableWithAlias - - JoinsList - SET - - UpdateSets - - OutputClause - FROM - - FromItem - - JoinsList - - WhereClause - - OrderByElements - - PlainLimit - - ReturningClause - -
- - -
- Referenced by: -
- - -====================================================================================================================== -UpdateSets -====================================================================================================================== - - -.. raw:: html - - - - - - Column - = - - Expression - - ParenthesedExpressionList - = - - ParenthesedSelect - - ParenthesedExpressionList - , - - Column - = - - Expression - - ParenthesedExpressionList - = - - ParenthesedSelect - - ParenthesedExpressionList - -
- - - -
- - -====================================================================================================================== -Insert -====================================================================================================================== - - -.. raw:: html - - - - - - INSERT - - LOW_PRIORITY - - DELAYED - - HIGH_PRIORITY - - IGNORE - - INTO - - Table - AS - - RelObjectNameWithoutValue - ( - - ColumnList - ) - - OutputClause - SET - - UpdateSets - - Select - ON - - DUPLICATE - - KEY - - UPDATE - - UpdateSets - ON - - CONFLICT - - InsertConflictTarget - - InsertConflictAction - - ReturningClause - -
- -
Insert   ::= 'INSERT' ( 'LOW_PRIORITY' | 'DELAYED' | 'HIGH_PRIORITY' )? 'IGNORE'? 'INTO'? - Table ( 'AS'? RelObjectNameWithoutValue )? ( '(' ColumnList ')' )? OutputClause? ( 'SET' UpdateSets | Select ) ( 'ON' 'DUPLICATE' 'KEY' 'UPDATE' UpdateSets )? ( 'ON' 'CONFLICT' InsertConflictTarget? InsertConflictAction )? ReturningClause?
-
- Referenced by: -
- - -====================================================================================================================== -InsertConflictTarget -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - RelObjectNameExt2 - , - - ) - - WhereClause - ON - - CONSTRAINT - - RelObjectNameExt2 - -
- - -
         ::= '(' RelObjectNameExt2 ( ',' RelObjectNameExt2 )* ')' WhereClause?
-
           | 'ON' 'CONSTRAINT' RelObjectNameExt2
-
- Referenced by: -
- - -====================================================================================================================== -InsertConflictAction -====================================================================================================================== - - -.. raw:: html - - - - - - DO - - NOTHING - - UPDATE - - SET - - UpdateSets - - WhereClause - -
- - -
         ::= 'DO' ( 'NOTHING' | 'UPDATE' 'SET' UpdateSets WhereClause? )
-
- Referenced by: -
- - -====================================================================================================================== -OutputClause -====================================================================================================================== - - -.. raw:: html - - - - - - OUTPUT - - SelectItemsList - INTO - - UserVariable - - Table - - ColumnsNamesList - -
- - -
         ::= 'OUTPUT' SelectItemsList ( 'INTO' ( UserVariable | Table ) ColumnsNamesList? )?
-
- Referenced by: -
- - -====================================================================================================================== -Upsert -====================================================================================================================== - - -.. raw:: html - - - - - - UPSERT - - INSERT - - OR - - REPLACE - - INTO - - Table - - ParenthesedColumnList - SET - - UpdateSets - - Select - ON - - DUPLICATE - - KEY - - UPDATE - - UpdateSets - -
- -
Upsert   ::= ( 'UPSERT' | ( 'INSERT' 'OR' )? 'REPLACE' ) 'INTO'? Table ParenthesedColumnList? ( 'SET' UpdateSets | Select ) ( 'ON' 'DUPLICATE' 'KEY' 'UPDATE' UpdateSets )?
-
- Referenced by: -
- - -====================================================================================================================== -Delete -====================================================================================================================== - - -.. raw:: html - - - - - - DELETE - - LOW_PRIORITY - - QUICK - - IGNORE - - TableWithAlias - , - - OutputClause - FROM - - TableWithAlias - - JoinsList - USING - - TableWithAlias - , - - WhereClause - - OrderByElements - - PlainLimit - - ReturningClause - -
- -
Delete   ::= 'DELETE' 'LOW_PRIORITY'? 'QUICK'? 'IGNORE'? ( ( TableWithAlias ( ',' TableWithAlias )* OutputClause? )? 'FROM' )? ( TableWithAlias JoinsList? )? ( 'USING' TableWithAlias ( ',' TableWithAlias )* )? WhereClause? OrderByElements? PlainLimit? ReturningClause?
-
- Referenced by: -
- - -====================================================================================================================== -Merge -====================================================================================================================== - - -.. raw:: html - - - - - - MERGE - - INTO - - TableWithAlias - USING - - FromItem - ON - - ( - - Expression - ) - - MergeUpdateClause - - MergeInsertClause - - MergeInsertClause - - MergeUpdateClause - -
- - -
- Referenced by: -
- - -====================================================================================================================== -MergeUpdateClause -====================================================================================================================== - - -.. raw:: html - - - - - - WHEN - - MATCHED - - THEN - - UPDATE - - SET - - UpdateSets - WHERE - - Expression - DELETE - - WHERE - - Expression - -
- - -
         ::= 'WHEN' 'MATCHED' 'THEN' 'UPDATE' 'SET' UpdateSets ( 'WHERE' Expression )? ( 'DELETE' 'WHERE' Expression )?
-
- Referenced by: -
- - -====================================================================================================================== -MergeInsertClause -====================================================================================================================== - - -.. raw:: html - - - - - - WHEN - - NOT - - MATCHED - - THEN - - INSERT - - ( - - ColumnList - ) - - VALUES - - ( - - SimpleExpressionList - ) - - WHERE - - Expression - -
- - -
         ::= 'WHEN' 'NOT' 'MATCHED' 'THEN' 'INSERT' ( '(' ColumnList ')' )? 'VALUES' '(' SimpleExpressionList ')' ( 'WHERE' Expression )?
-
- Referenced by: -
- - -====================================================================================================================== -RelObjectNameList -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameExt - . - - : - - . - - RelObjectNameExt2 - -
- - -
         ::= RelObjectNameExt ( ( '.' | ':' ) '.'* RelObjectNameExt2 )*
-
- - -====================================================================================================================== -Column -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - . - - K_NEXTVAL - - ArrayConstructor - -
- - -
- - -====================================================================================================================== -RelObjectNameWithoutValue -====================================================================================================================== - - -.. raw:: html - - - - - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - - K_DATE_LITERAL - - K_DATETIMELITERAL - - K_STRING_FUNCTION_NAME - - K_ISOLATION - - K_TIME_KEY_EXPR - ACTION - - ACTIVE - - ADD - - ADVANCE - - ADVISE - - AGAINST - - ALGORITHM - - ALTER - - ANALYZE - - APPLY - - ARCHIVE - - ARRAY - - ASC - - AT - - AUTHORIZATION - - AUTO - - BEGIN - - BINARY - - BIT - - BUFFERS - - BY - - BYTE - - BYTES - - CACHE - - CALL - - CASCADE - - CASE - - CAST - - CHANGE - - CHANGES - - CHAR - - CHARACTER - - CHECKPOINT - - CLOSE - - COLLATE - - COLUMN - - COLUMNS - - COMMENT - - COMMIT - - CONFLICT - - CONVERT - - COSTS - - CS - - CYCLE - - DATABASE - - DDL - - DECLARE - - DEFAULT - - DEFERRABLE - - DELAYED - - DELETE - - DESC - - DESCRIBE - - DISABLE - - DISCONNECT - - DIV - - DML - - DO - - DOMAIN - - DROP - - DUMP - - DUPLICATE - - EMIT - - ENABLE - - END - - ESCAPE - - EXCLUDE - - EXEC - - EXECUTE - - EXPLAIN - - EXTENDED - - EXTRACT - - FALSE - - FILTER - - FIRST - - FLUSH - - FN - - FOLLOWING - - FORMAT - - FULLTEXT - - FUNCTION - - GLOBAL - - GRANT - - GUARD - - HISTORY - - HOPPING - - INCLUDE - - INCREMENT - - INDEX - - INSERT - - INTERLEAVE - - ISNULL - - JSON - - KEEP - - KEY - - KEYS - - LAST - - LEADING - - LINK - - LOCAL - - LOCKED - - LOG - - MATCH - - MATCHED - - MATERIALIZED - - MAXVALUE - - MEMBER - - MERGE - - MINVALUE - - MODIFY - - MOVEMENT - - NEXT - - NO - - NOCACHE - - NOKEEP - - NOLOCK - - NOMAXVALUE - - NOMINVALUE - - NOORDER - - NOTHING - - NOVALIDATE - - NOWAIT - - NULLS - - OF - - OFF - - OPEN - - OVER - - OVERLAPS - - PARALLEL - - PARENT - - PARTITION - - PATH - - PERCENT - - PLACING - - PRECEDING - - PRECISION - - PRIMARY - - PRIOR - - PURGE - - QUERY - - QUICK - - QUIESCE - - RANGE - - READ - - RECYCLEBIN - - REFERENCES - - REFRESH - - REGISTER - - RENAME - - REPLACE - - RESET - - RESTART - - RESTRICT - - RESTRICTED - - RESUMABLE - - RESUME - - RLIKE - - ROLLBACK - - ROW - - ROWS - - RR - - RS - - SAVEPOINT - - SCHEMA - - SEPARATOR - - SEQUENCE - - SESSION - - SETS - - SHOW - - SHUTDOWN - - SIBLINGS - - SIGNED - - SIMILAR - - SIZE - - SKIP - - STORED - - STRING - - SUSPEND - - SWITCH - - SYNONYM - - SYSTEM - - TABLE - - TABLESPACE - - TEMP - - TEMPORARY - - THEN - - TIMEOUT - - TIMESTAMPTZ - - TO - - TRIGGER - - TRUE - - TRUNCATE - - TUMBLING - - TYPE - - UNLOGGED - - UNQIESCE - - UNSIGNED - - UPDATE - - UPSERT - - UR - - USER - - VALIDATE - - VERBOSE - - VIEW - - WAIT - - WITHIN - - WITHOUT - - WORK - - XML - - XMLAGG - - XMLTEXT - - YAML - - YES - - ZONE - - -
- - -
         ::= S_IDENTIFIER
-
           | S_QUOTED_IDENTIFIER
-
           | K_DATE_LITERAL
-
           | K_DATETIMELITERAL
-
           | K_STRING_FUNCTION_NAME
-
           | K_ISOLATION
-
           | K_TIME_KEY_EXPR
-
           | 'ACTION'
-
           | 'ACTIVE'
-
           | 'ADD'
-
           | 'ADVANCE'
-
           | 'ADVISE'
-
           | 'AGAINST'
-
           | 'ALGORITHM'
-
           | 'ALTER'
-
           | 'ANALYZE'
-
           | 'APPLY'
-
           | 'ARCHIVE'
-
           | 'ARRAY'
-
           | 'ASC'
-
           | 'AT'
-
           | 'AUTHORIZATION'
-
           | 'AUTO'
-
           | 'BEGIN'
-
           | 'BINARY'
-
           | 'BIT'
-
           | 'BUFFERS'
-
           | 'BY'
-
           | 'BYTE'
-
           | 'BYTES'
-
           | 'CACHE'
-
           | 'CALL'
-
           | 'CASCADE'
-
           | 'CASE'
-
           | 'CAST'
-
           | 'CHANGE'
-
           | 'CHANGES'
-
           | 'CHAR'
-
           | 'CHARACTER'
-
           | 'CHECKPOINT'
-
           | 'CLOSE'
-
           | 'COLLATE'
-
           | 'COLUMN'
-
           | 'COLUMNS'
-
           | 'COMMENT'
-
           | 'COMMIT'
-
           | 'CONFLICT'
-
           | 'CONVERT'
-
           | 'COSTS'
-
           | 'CS'
-
           | 'CYCLE'
-
           | 'DATABASE'
-
           | 'DDL'
-
           | 'DECLARE'
-
           | 'DEFAULT'
-
           | 'DEFERRABLE'
-
           | 'DELAYED'
-
           | 'DELETE'
-
           | 'DESC'
-
           | 'DESCRIBE'
-
           | 'DISABLE'
-
           | 'DISCONNECT'
-
           | 'DIV'
-
           | 'DML'
-
           | 'DO'
-
           | 'DOMAIN'
-
           | 'DROP'
-
           | 'DUMP'
-
           | 'DUPLICATE'
-
           | 'EMIT'
-
           | 'ENABLE'
-
           | 'END'
-
           | 'ESCAPE'
-
           | 'EXCLUDE'
-
           | 'EXEC'
-
           | 'EXECUTE'
-
           | 'EXPLAIN'
-
           | 'EXTENDED'
-
           | 'EXTRACT'
-
           | 'FALSE'
-
           | 'FILTER'
-
           | 'FIRST'
-
           | 'FLUSH'
-
           | 'FN'
-
           | 'FOLLOWING'
-
           | 'FORMAT'
-
           | 'FULLTEXT'
-
           | 'FUNCTION'
-
           | 'GLOBAL'
-
           | 'GRANT'
-
           | 'GUARD'
-
           | 'HISTORY'
-
           | 'HOPPING'
-
           | 'INCLUDE'
-
           | 'INCREMENT'
-
           | 'INDEX'
-
           | 'INSERT'
-
           | 'INTERLEAVE'
-
           | 'ISNULL'
-
           | 'JSON'
-
           | 'KEEP'
-
           | 'KEY'
-
           | 'KEYS'
-
           | 'LAST'
-
           | 'LEADING'
-
           | 'LINK'
-
           | 'LOCAL'
-
           | 'LOCKED'
-
           | 'LOG'
-
           | 'MATCH'
-
           | 'MATCHED'
-
           | 'MATERIALIZED'
-
           | 'MAXVALUE'
-
           | 'MEMBER'
-
           | 'MERGE'
-
           | 'MINVALUE'
-
           | 'MODIFY'
-
           | 'MOVEMENT'
-
           | 'NEXT'
-
           | 'NO'
-
           | 'NOCACHE'
-
           | 'NOKEEP'
-
           | 'NOLOCK'
-
           | 'NOMAXVALUE'
-
           | 'NOMINVALUE'
-
           | 'NOORDER'
-
           | 'NOTHING'
-
           | 'NOVALIDATE'
-
           | 'NOWAIT'
-
           | 'NULLS'
-
           | 'OF'
-
           | 'OFF'
-
           | 'OPEN'
-
           | 'OVER'
-
           | 'OVERLAPS'
-
           | 'PARALLEL'
-
           | 'PARENT'
-
           | 'PARTITION'
-
           | 'PATH'
-
           | 'PERCENT'
-
           | 'PLACING'
-
           | 'PRECEDING'
-
           | 'PRECISION'
-
           | 'PRIMARY'
-
           | 'PRIOR'
-
           | 'PURGE'
-
           | 'QUERY'
-
           | 'QUICK'
-
           | 'QUIESCE'
-
           | 'RANGE'
-
           | 'READ'
-
           | 'RECYCLEBIN'
-
           | 'REFERENCES'
-
           | 'REFRESH'
-
           | 'REGISTER'
-
           | 'RENAME'
-
           | 'REPLACE'
-
           | 'RESET'
-
           | 'RESTART'
-
           | 'RESTRICT'
-
           | 'RESTRICTED'
-
           | 'RESUMABLE'
-
           | 'RESUME'
-
           | 'RLIKE'
-
           | 'ROLLBACK'
-
           | 'ROW'
-
           | 'ROWS'
-
           | 'RR'
-
           | 'RS'
-
           | 'SAVEPOINT'
-
           | 'SCHEMA'
-
           | 'SEPARATOR'
-
           | 'SEQUENCE'
-
           | 'SESSION'
-
           | 'SETS'
-
           | 'SHOW'
-
           | 'SHUTDOWN'
-
           | 'SIBLINGS'
-
           | 'SIGNED'
-
           | 'SIMILAR'
-
           | 'SIZE'
-
           | 'SKIP'
-
           | 'STORED'
-
           | 'STRING'
-
           | 'SUSPEND'
-
           | 'SWITCH'
-
           | 'SYNONYM'
-
           | 'SYSTEM'
-
           | 'TABLE'
-
           | 'TABLESPACE'
-
           | 'TEMP'
-
           | 'TEMPORARY'
-
           | 'THEN'
-
           | 'TIMEOUT'
-
           | 'TIMESTAMPTZ'
-
           | 'TO'
-
           | 'TRIGGER'
-
           | 'TRUE'
-
           | 'TRUNCATE'
-
           | 'TUMBLING'
-
           | 'TYPE'
-
           | 'UNLOGGED'
-
           | 'UNQIESCE'
-
           | 'UNSIGNED'
-
           | 'UPDATE'
-
           | 'UPSERT'
-
           | 'UR'
-
           | 'USER'
-
           | 'VALIDATE'
-
           | 'VERBOSE'
-
           | 'VIEW'
-
           | 'WAIT'
-
           | 'WITHIN'
-
           | 'WITHOUT'
-
           | 'WORK'
-
           | 'XML'
-
           | 'XMLAGG'
-
           | 'XMLTEXT'
-
           | 'YAML'
-
           | 'YES'
-
           | 'ZONE'
-
- - -====================================================================================================================== -RelObjectName -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameWithoutValue - GROUP - - INTERVAL - - ON - - START - - TOP - - VALUE - - VALUES - - CREATE - - TABLES - - CONNECT - - IGNORE - - -
- - -
         ::= RelObjectNameWithoutValue
-
           | 'GROUP'
-
           | 'INTERVAL'
-
           | 'ON'
-
           | 'START'
-
           | 'TOP'
-
           | 'VALUE'
-
           | 'VALUES'
-
           | 'CREATE'
-
           | 'TABLES'
-
           | 'CONNECT'
-
           | 'IGNORE'
-
- - -====================================================================================================================== -RelObjectNameWithoutStart -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameWithoutValue - TOP - - VALUE - - VALUES - - INTERVAL - - -
- - -
         ::= RelObjectNameWithoutValue
-
           | 'TOP'
-
           | 'VALUE'
-
           | 'VALUES'
-
           | 'INTERVAL'
-
- Referenced by: -
- - -====================================================================================================================== -RelObjectNameExt -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - ALL - - ANY - - SOME - - LEFT - - RIGHT - - SET - - DOUBLE - - IF - - IIF - - OPTIMIZE - - LIMIT - - OFFSET - - PROCEDURE - - PUBLIC - - CASEWHEN - - IN - - GROUPING - - ORDER - - -
- - -
         ::= RelObjectName
-
           | 'ALL'
-
           | 'ANY'
-
           | 'SOME'
-
           | 'LEFT'
-
           | 'RIGHT'
-
           | 'SET'
-
           | 'DOUBLE'
-
           | 'IF'
-
           | 'IIF'
-
           | 'OPTIMIZE'
-
           | 'LIMIT'
-
           | 'OFFSET'
-
           | 'PROCEDURE'
-
           | 'PUBLIC'
-
           | 'CASEWHEN'
-
           | 'IN'
-
           | 'GROUPING'
-
           | 'ORDER'
-
- - -====================================================================================================================== -RelObjectNameExt2 -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameExt - FROM - - K_SELECT - CURRENT - - -
- - -
         ::= RelObjectNameExt
-
           | 'FROM'
-
           | K_SELECT
-
           | 'CURRENT'
-
- - -====================================================================================================================== -Table -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - -
- - -
- - -====================================================================================================================== -TableWithAlias -====================================================================================================================== - - -.. raw:: html - - - - - - Table - - Alias - -
- - -
         ::= Table Alias?
-
- Referenced by: -
- - -====================================================================================================================== -SelectWithWithItems -====================================================================================================================== - - -.. raw:: html - - - - - - Select - -
- - -
         ::= Select
-
- Referenced by: -
- - -====================================================================================================================== -Select -====================================================================================================================== - - -.. raw:: html - - - - - - WithList - - PlainSelect - - Values - - ParenthesedSelect - - SetOperationList - - OrderByElements - - LimitWithOffset - - Offset - - Fetch - - WithIsolation - -
- - -
- - -====================================================================================================================== -ParenthesedSelect -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - Select - ) - - -
- - -
         ::= '(' Select ')'
-
- - -====================================================================================================================== -LateralView -====================================================================================================================== - - -.. raw:: html - - - - - - LATERAL - - VIEW - - OUTER - - Function - - RelObjectNameWithoutStart - AS - - RelObjectNameWithoutStart - -
- - -
         ::= 'LATERAL' 'VIEW' 'OUTER'? Function RelObjectNameWithoutStart? 'AS' RelObjectNameWithoutStart
-
- Referenced by: -
- - -====================================================================================================================== -LateralViews -====================================================================================================================== - - -.. raw:: html - - - - - - LateralView - -
- - -
         ::= LateralView+
-
- Referenced by: -
- - -====================================================================================================================== -LateralSubSelect -====================================================================================================================== - - -.. raw:: html - - - - - - LATERAL - - ( - - Select - ) - - -
- - -
         ::= 'LATERAL' '(' Select ')'
-
- Referenced by: -
- - -====================================================================================================================== -PlainSelect -====================================================================================================================== - - -.. raw:: html - - - - - - K_SELECT - STRAIGHT_JOIN - - Skip - - First - ALL - - DISTINCT - - ON - - ( - - SelectItemsList - ) - - UNIQUE - - SQL_CALC_FOUND_ROWS - - SQL_NO_CACHE - - SQL_CACHE - - Top - - SelectItemsList - - IntoClause - FROM - - FromItem - - LateralViews - - JoinsList - FINAL - - KSQLWindowClause - - WhereClause - - OracleHierarchicalQueryClause - - Having - - GroupByColumnReferences - - Having - - OrderByElements - WINDOW - - RelObjectName - AS - - windowDefinition - , - - OrderByElements - EMIT - - CHANGES - - LimitBy - - LimitWithOffset - - Offset - - LimitWithOffset - - Fetch - - WithIsolation - FOR - - UPDATE - - OF - - Table - - Wait - NOWAIT - - SKIP - - LOCKED - - OptimizeFor - FOR - - XML - - PATH - - ( - - S_CHAR_LITERAL - ) - - -
- - -
         ::= K_SELECT 'STRAIGHT_JOIN'? Skip? First? ( 'ALL' | 'DISTINCT' ( 'ON' '(' SelectItemsList ')' )? | 'UNIQUE' | 'SQL_CALC_FOUND_ROWS' | 'SQL_NO_CACHE' | 'SQL_CACHE' )? Top? SelectItemsList IntoClause? ( 'FROM' FromItem LateralViews? JoinsList? )? 'FINAL'? KSQLWindowClause? WhereClause? OracleHierarchicalQueryClause? Having? GroupByColumnReferences? Having? OrderByElements? ( 'WINDOW' RelObjectName 'AS' windowDefinition ( ',' RelObjectName 'AS' windowDefinition )* )? OrderByElements? ( 'EMIT' 'CHANGES' )? LimitBy? LimitWithOffset? Offset? LimitWithOffset? Fetch? WithIsolation? ( 'FOR' 'UPDATE' ( 'OF' Table )? Wait? ( 'NOWAIT' | 'SKIP' 'LOCKED' )? )? OptimizeFor? ( 'FOR' 'XML' 'PATH' '(' S_CHAR_LITERAL ')' )?
-
- Referenced by: -
- - -====================================================================================================================== -SetOperationList -====================================================================================================================== - - -.. raw:: html - - - - - - UNION - - ALL - - DISTINCT - - INTERSECT - - MINUS - - EXCEPT - - PlainSelect - - Values - - ParenthesedSelect - - OrderByElements - - LimitWithOffset - - Offset - - LimitWithOffset - - Fetch - - WithIsolation - -
- - -
         ::= ( ( 'UNION' ( 'ALL' | 'DISTINCT' )? | 'INTERSECT' | 'MINUS' | 'EXCEPT' - ) ( PlainSelect | Values | ParenthesedSelect ) )+ OrderByElements? LimitWithOffset? Offset? LimitWithOffset? Fetch? WithIsolation?
-
- Referenced by: -
- - -====================================================================================================================== -WithList -====================================================================================================================== - - -.. raw:: html - - - - - - WITH - - WithItem - , - - -
- -
WithList ::= 'WITH' WithItem ( ',' WithItem )*
-
- Referenced by: -
- - -====================================================================================================================== -WithItem -====================================================================================================================== - - -.. raw:: html - - - - - - RECURSIVE - - RelObjectName - ( - - SelectItemsList - ) - - AS - - ParenthesedSelect - -
- -
WithItem ::= 'RECURSIVE'? RelObjectName ( '(' SelectItemsList ')' )? 'AS' ParenthesedSelect
-
- Referenced by: -
- - -====================================================================================================================== -SelectItemsList -====================================================================================================================== - - -.. raw:: html - - - - - - SelectItem - , - - -
- - -
         ::= SelectItem ( ',' SelectItem )*
-
- - -====================================================================================================================== -SelectItem -====================================================================================================================== - - -.. raw:: html - - - - - - * - - AllTableColumns - - Condition - - ConcatExpression - - Expression - - Alias - -
- - -
         ::= ( '*' | AllTableColumns | Condition | ConcatExpression | Expression ) Alias?
-
- Referenced by: -
- - -====================================================================================================================== -AllTableColumns -====================================================================================================================== - - -.. raw:: html - - - - - - Table - . - - * - - -
- - -
         ::= Table '.' '*'
-
- Referenced by: -
- - -====================================================================================================================== -Alias -====================================================================================================================== - - -.. raw:: html - - - - - - AS - - RelObjectNameWithoutStart - - S_CHAR_LITERAL - ( - - RelObjectName - - ColDataType - , - - ) - - -
- - -
- - -====================================================================================================================== -SQLServerHint -====================================================================================================================== - - -.. raw:: html - - - - - - INDEX - - ( - - RelObjectName - ) - - NOLOCK - - -
- - -
         ::= 'INDEX' '(' RelObjectName ')'
-
           | 'NOLOCK'
-
- Referenced by: -
- - -====================================================================================================================== -SQLServerHints -====================================================================================================================== - - -.. raw:: html - - - - - - WITH - - ( - - SQLServerHint - , - - ) - - -
- - -
         ::= 'WITH' '(' SQLServerHint ( ',' SQLServerHint )* ')'
-
- Referenced by: -
- - -====================================================================================================================== -MySQLIndexHint -====================================================================================================================== - - -.. raw:: html - - - - - - USE - - SHOW - - IGNORE - - FORCE - - INDEX - - KEY - - ( - - RelObjectNameWithoutValue - , - - ) - - -
- - -
         ::= ( 'USE' | 'SHOW' | 'IGNORE' | 'FORCE' ) ( 'INDEX' | 'KEY' ) '(' RelObjectNameWithoutValue ( ',' RelObjectNameWithoutValue )* ')'
-
- Referenced by: -
- - -====================================================================================================================== -FunctionItem -====================================================================================================================== - - -.. raw:: html - - - - - - Function - - Alias - -
- - -
         ::= Function Alias?
-
- Referenced by: -
- - -====================================================================================================================== -PivotForColumns -====================================================================================================================== - - -.. raw:: html - - - - - - ParenthesedColumnList - - Column - -
- - -
         ::= ParenthesedColumnList
-
           | Column
-
- Referenced by: -
- - -====================================================================================================================== -PivotFunctionItems -====================================================================================================================== - - -.. raw:: html - - - - - - FunctionItem - , - - -
- - -
         ::= FunctionItem ( ',' FunctionItem )*
-
- Referenced by: -
- - -====================================================================================================================== -ExpressionListItem -====================================================================================================================== - - -.. raw:: html - - - - - - ParenthesedExpressionList - - Alias - -
- - -
         ::= ParenthesedExpressionList Alias?
-
- Referenced by: -
- - -====================================================================================================================== -PivotMultiInItems -====================================================================================================================== - - -.. raw:: html - - - - - - ExpressionListItem - , - - -
- - -
         ::= ExpressionListItem ( ',' ExpressionListItem )*
-
- Referenced by: -
- - -====================================================================================================================== -Pivot -====================================================================================================================== - - -.. raw:: html - - - - - - PIVOT - - ( - - PivotFunctionItems - FOR - - PivotForColumns - IN - - ( - - SelectItemsList - - PivotMultiInItems - ) - - ) - - Alias - -
- -
Pivot    ::= 'PIVOT' '(' PivotFunctionItems 'FOR' PivotForColumns 'IN' '(' ( SelectItemsList | PivotMultiInItems ) ')' ')' Alias?
-
- Referenced by: -
- - -====================================================================================================================== -PivotXml -====================================================================================================================== - - -.. raw:: html - - - - - - PIVOT - - XML - - ( - - PivotFunctionItems - FOR - - PivotForColumns - IN - - ( - - ANY - - Select - - SelectItemsList - - PivotMultiInItems - ) - - ) - - -
- -
PivotXml ::= 'PIVOT' 'XML' '(' PivotFunctionItems 'FOR' PivotForColumns 'IN' '(' ( 'ANY' | Select | SelectItemsList | PivotMultiInItems ) ')' ')'
-
- Referenced by: -
- - -====================================================================================================================== -UnPivot -====================================================================================================================== - - -.. raw:: html - - - - - - UNPIVOT - - INCLUDE - - EXCLUDE - - NULLS - - ( - - PivotForColumns - FOR - - PivotForColumns - IN - - ( - - SelectItemsList - ) - - ) - - Alias - -
- -
UnPivot  ::= 'UNPIVOT' ( ( 'INCLUDE' | 'EXCLUDE' ) 'NULLS' )? '(' PivotForColumns 'FOR' PivotForColumns 'IN' '(' SelectItemsList ')' ')' Alias?
-
- Referenced by: -
- - -====================================================================================================================== -IntoClause -====================================================================================================================== - - -.. raw:: html - - - - - - INTO - - Table - , - - -
- - -
         ::= 'INTO' Table ( ',' Table )*
-
- Referenced by: -
- - -====================================================================================================================== -ParenthesedFromItem -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - FromItem - - JoinsList - ) - - -
- - -
         ::= '(' FromItem JoinsList? ')'
-
- Referenced by: -
- - -====================================================================================================================== -FromItem -====================================================================================================================== - - -.. raw:: html - - - - - - TableFunction - - Table - - ParenthesedFromItem - - ParenthesedSelect - - Pivot - - UnPivot - - LateralSubSelect - - Alias - - UnPivot - - PivotXml - - Pivot - - MySQLIndexHint - - SQLServerHints - -
- - -
- - -====================================================================================================================== -JoinsList -====================================================================================================================== - - -.. raw:: html - - - - - - JoinerExpression - -
- - -
         ::= JoinerExpression+
-
- - -====================================================================================================================== -JoinerExpression -====================================================================================================================== - - -.. raw:: html - - - - - - GLOBAL - - NATURAL - - RIGHT - - FULL - - OUTER - - LEFT - - SEMI - - OUTER - - INNER - - CROSS - - JOIN - - , - - OUTER - - STRAIGHT_JOIN - - APPLY - - FromItem - WITHIN - - ( - - JoinWindow - ) - - ON - - Expression - USING - - ( - - Column - , - - ) - - -
- - -
         ::= 'GLOBAL'? 'NATURAL'? ( ( 'RIGHT' | 'FULL' )? 'OUTER'? | 'LEFT' ( 'SEMI' - | 'OUTER' )? | 'INNER' | 'CROSS' ) ( 'JOIN' | ',' 'OUTER'? | 'STRAIGHT_JOIN' | 'APPLY' - ) FromItem ( ( 'WITHIN' '(' JoinWindow ')' )? ( 'ON' Expression )+ | 'USING' '(' Column ( ',' Column )* ')' )?
-
- Referenced by: -
- - -====================================================================================================================== -JoinWindow -====================================================================================================================== - - -.. raw:: html - - - - - - S_LONG - - S_IDENTIFIER - - K_DATE_LITERAL - , - - S_LONG - - S_IDENTIFIER - - K_DATE_LITERAL - -
- - -
         ::= S_LONG ( S_IDENTIFIER | K_DATE_LITERAL ) ( ',' S_LONG ( S_IDENTIFIER | K_DATE_LITERAL ) )?
-
- Referenced by: -
- - -====================================================================================================================== -KSQLWindowClause -====================================================================================================================== - - -.. raw:: html - - - - - - WINDOW - - HOPPING - - ( - - SIZE - - S_LONG - - S_IDENTIFIER - , - - ADVANCE - - BY - - SESSION - - ( - - TUMBLING - - ( - - SIZE - - S_LONG - - S_IDENTIFIER - ) - - -
- - -
         ::= 'WINDOW' ( 'HOPPING' '(' 'SIZE' S_LONG S_IDENTIFIER ',' 'ADVANCE' 'BY' | 'SESSION' '(' | 'TUMBLING' '(' 'SIZE' ) S_LONG S_IDENTIFIER ')'
-
- Referenced by: -
- - -====================================================================================================================== -WhereClause -====================================================================================================================== - - -.. raw:: html - - - - - - WHERE - - Expression - -
- - -
         ::= 'WHERE' Expression
-
- - -====================================================================================================================== -OracleHierarchicalQueryClause -====================================================================================================================== - - -.. raw:: html - - - - - - START - - WITH - - AndExpression - CONNECT - - BY - - NOCYCLE - - CONNECT - - BY - - NOCYCLE - - AndExpression - START - - WITH - - AndExpression - -
- - -
         ::= ( 'START' 'WITH' AndExpression 'CONNECT' 'BY' 'NOCYCLE'? | 'CONNECT' 'BY' 'NOCYCLE'? ( AndExpression 'START' 'WITH' )? ) AndExpression
-
- Referenced by: -
- - -====================================================================================================================== -GroupByColumnReferences -====================================================================================================================== - - -.. raw:: html - - - - - - GROUP - - BY - - GROUPING - - SETS - - ( - - GroupingSet - , - - ) - - ExpressionList - GROUPING - - SETS - - ( - - GroupingSet - , - - ) - - -
- - -
         ::= 'GROUP' 'BY' ( 'GROUPING' 'SETS' '(' GroupingSet ( ',' GroupingSet )* ')' | ExpressionList ( 'GROUPING' 'SETS' '(' GroupingSet ( ',' GroupingSet )* ')' )? )
-
- Referenced by: -
- - -====================================================================================================================== -GroupingSet -====================================================================================================================== - - -.. raw:: html - - - - - - ParenthesedExpressionList - - SimpleExpression - -
- - -
         ::= ParenthesedExpressionList
-
           | SimpleExpression
-
- Referenced by: -
- - -====================================================================================================================== -Having -====================================================================================================================== - - -.. raw:: html - - - - - - HAVING - - Expression - -
- -
Having   ::= 'HAVING' Expression
-
- Referenced by: -
- - -====================================================================================================================== -OrderByElements -====================================================================================================================== - - -.. raw:: html - - - - - - ORDER - - SIBLINGS - - BY - - OrderByElement - , - - -
- - -
         ::= 'ORDER' 'SIBLINGS'? 'BY' OrderByElement ( ',' OrderByElement )*
-
- - -====================================================================================================================== -OrderByElement -====================================================================================================================== - - -.. raw:: html - - - - - - Expression - ASC - - DESC - - NULLS - - FIRST - - LAST - - -
- - -
         ::= Expression ( 'ASC' | 'DESC' )? ( 'NULLS' ( 'FIRST' | 'LAST' )? )?
-
- Referenced by: -
- - -====================================================================================================================== -SimpleJdbcParameter -====================================================================================================================== - - -.. raw:: html - - - - - - ? - - S_LONG - -
- - -
         ::= '?' S_LONG?
-
- - -====================================================================================================================== -SimpleJdbcNamedParameter -====================================================================================================================== - - -.. raw:: html - - - - - - : - - RelObjectNameExt - -
- - -
         ::= ':' RelObjectNameExt
-
- Referenced by: -
- - -====================================================================================================================== -LimitWithOffset -====================================================================================================================== - - -.. raw:: html - - - - - - LIMIT - - Expression - , - - Expression - - PlainLimit - -
- - -
         ::= 'LIMIT' Expression ',' Expression
-
           | PlainLimit
-
- - -====================================================================================================================== -PlainLimit -====================================================================================================================== - - -.. raw:: html - - - - - - LIMIT - - ParenthesedSelect - - Expression - -
- - -
         ::= 'LIMIT' ( ParenthesedSelect | Expression )
-
- Referenced by: -
- - -====================================================================================================================== -LimitBy -====================================================================================================================== - - -.. raw:: html - - - - - - LimitWithOffset - BY - - ExpressionList - -
- - -
- Referenced by: -
- - -====================================================================================================================== -Offset -====================================================================================================================== - - -.. raw:: html - - - - - - OFFSET - - Expression - ROWS - - ROW - - -
- -
Offset   ::= 'OFFSET' Expression ( 'ROWS' | 'ROW' )?
-
- - -====================================================================================================================== -Fetch -====================================================================================================================== - - -.. raw:: html - - - - - - FETCH - - FIRST - - NEXT - - Expression - ROWS - - ROW - - ONLY - - -
- -
Fetch    ::= 'FETCH' ( 'FIRST' | 'NEXT' ) Expression ( 'ROWS' | 'ROW' ) 'ONLY'
-
- - -====================================================================================================================== -WithIsolation -====================================================================================================================== - - -.. raw:: html - - - - - - WITH - - K_ISOLATION - -
- - -
         ::= 'WITH' K_ISOLATION
-
- - -====================================================================================================================== -OptimizeFor -====================================================================================================================== - - -.. raw:: html - - - - - - OPTIMIZE - - FOR - - S_LONG - ROWS - - -
- - -
         ::= 'OPTIMIZE' 'FOR' S_LONG 'ROWS'
-
- Referenced by: -
- - -====================================================================================================================== -Top -====================================================================================================================== - - -.. raw:: html - - - - - - TOP - - S_LONG - - SimpleJdbcParameter - : - - S_IDENTIFIER - ( - - AdditiveExpression - ) - - PERCENT - - WITH TIES - - -
- -
Top      ::= 'TOP' ( S_LONG | SimpleJdbcParameter | ':' S_IDENTIFIER? | '(' AdditiveExpression ')' ) 'PERCENT'? 'WITH TIES'?
-
- Referenced by: -
- - -====================================================================================================================== -Skip -====================================================================================================================== - - -.. raw:: html - - - - - - SKIP - - S_LONG - - S_IDENTIFIER - - SimpleJdbcParameter - -
- -
Skip     ::= 'SKIP' ( S_LONG | S_IDENTIFIER | SimpleJdbcParameter )
-
- Referenced by: -
- - -====================================================================================================================== -First -====================================================================================================================== - - -.. raw:: html - - - - - - FIRST - - LIMIT - - S_LONG - - S_IDENTIFIER - - SimpleJdbcParameter - -
- -
First    ::= ( 'FIRST' | 'LIMIT' ) ( S_LONG | S_IDENTIFIER | SimpleJdbcParameter )
-
- Referenced by: -
- - -====================================================================================================================== -Expression -====================================================================================================================== - - -.. raw:: html - - - - - - XorExpression - -
- - -
         ::= XorExpression
-
- - -====================================================================================================================== -XorExpression -====================================================================================================================== - - -.. raw:: html - - - - - - OrExpression - XOR - - -
- - -
         ::= OrExpression ( 'XOR' OrExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -OrExpression -====================================================================================================================== - - -.. raw:: html - - - - - - AndExpression - OR - - -
- - -
         ::= AndExpression ( 'OR' AndExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -AndExpression -====================================================================================================================== - - -.. raw:: html - - - - - - Condition - NOT - - ! - - ( - - XorExpression - ) - - AND - - && - - -
- - -
         ::= ( Condition | ( 'NOT' | '!' )? '(' XorExpression ')' ) ( ( 'AND' | '&&' ) ( Condition | ( 'NOT' | '!' )? '(' XorExpression ')' ) )*
-
- - -====================================================================================================================== -Condition -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - ! - - RegularCondition - - SQLCondition - -
- - -
         ::= ( 'NOT' | '!' )? ( RegularCondition | SQLCondition )
-
- - -====================================================================================================================== -OverlapsCondition -====================================================================================================================== - - -.. raw:: html - - - - - - ParenthesedExpressionList - OVERLAPS - - ParenthesedExpressionList - -
- - -
         ::= ParenthesedExpressionList 'OVERLAPS' ParenthesedExpressionList
-
- Referenced by: -
- - -====================================================================================================================== -RegularCondition -====================================================================================================================== - - -.. raw:: html - - - - - - PRIOR - - ComparisonItem - ( - - + - - ) - - > - - < - - = - - OP_GREATERTHANEQUALS - - OP_MINORTHANEQUALS - - OP_NOTEQUALSSTANDARD - - OP_NOTEQUALSBANG - @@ - - ~ - - NOT - - REGEXP - - RLIKE - - BINARY - - ~* - - !~ - - !~* - - @> - - <@ - - ? - - ?| - - ?& - - OP_CONCAT - - - - -# - - <-> - - <#> - - PRIOR - - ComparisonItem - ( - - + - - ) - - -
- - -
         ::= 'PRIOR'? ComparisonItem ( '(' '+' ')' )? ( '>' | '<' | '=' | OP_GREATERTHANEQUALS | OP_MINORTHANEQUALS | OP_NOTEQUALSSTANDARD | OP_NOTEQUALSBANG | '@@' | '~' | ( 'NOT'? 'REGEXP' | 'RLIKE' ) 'BINARY'? | '~*' | '!~' | '!~*' | '@>' - | '<@' | '?' | '?|' | '?&' | OP_CONCAT | '-' | '-#' | '<->' | '<#>' ) 'PRIOR'? ComparisonItem ( '(' '+' ')' )?
-
- Referenced by: -
- - -====================================================================================================================== -SQLCondition -====================================================================================================================== - - -.. raw:: html - - - - - - ExistsExpression - - InExpression - - OverlapsCondition - - SimpleExpression - - Between - - MemberOfExpression - - IsNullExpression - - IsBooleanExpression - - LikeExpression - - IsDistinctExpression - - SimilarToExpression - -
- - -
         ::= ExistsExpression
-
           | InExpression
-
           | OverlapsCondition
-
-
- Referenced by: -
- - -====================================================================================================================== -InExpression -====================================================================================================================== - - -.. raw:: html - - - - - - SimpleExpression - ( - - + - - ) - - NOT - - IN - - S_CHAR_LITERAL - - Function - - ParenthesedSelect - - ParenthesedExpressionList - - SimpleExpression - -
- - -
         ::= SimpleExpression ( '(' '+' ')' )? 'NOT'? 'IN' ( S_CHAR_LITERAL | Function | ParenthesedSelect | ParenthesedExpressionList | SimpleExpression )
-
- Referenced by: -
- - -====================================================================================================================== -Between -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - BETWEEN - - SimpleExpression - AND - - SimpleExpression - -
- -
Between  ::= 'NOT'? 'BETWEEN' SimpleExpression 'AND' SimpleExpression
-
- Referenced by: -
- - -====================================================================================================================== -LikeExpression -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - LIKE - - ILIKE - - SimpleExpression - ESCAPE - - S_CHAR_LITERAL - - Expression - -
- - -
         ::= 'NOT'? ( 'LIKE' | 'ILIKE' ) SimpleExpression ( 'ESCAPE' ( S_CHAR_LITERAL | Expression ) )?
-
- Referenced by: -
- - -====================================================================================================================== -SimilarToExpression -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - SIMILAR - - TO - - SimpleExpression - ESCAPE - - S_CHAR_LITERAL - -
- - -
         ::= 'NOT'? 'SIMILAR' 'TO' SimpleExpression ( 'ESCAPE' S_CHAR_LITERAL )?
-
- Referenced by: -
- - -====================================================================================================================== -IsDistinctExpression -====================================================================================================================== - - -.. raw:: html - - - - - - IS - - NOT - - DISTINCT - - FROM - - SimpleExpression - -
- - -
         ::= 'IS' 'NOT'? 'DISTINCT' 'FROM' SimpleExpression
-
- Referenced by: -
- - -====================================================================================================================== -IsNullExpression -====================================================================================================================== - - -.. raw:: html - - - - - - ISNULL - - IS - - NOT - - NULL - - -
- - -
         ::= 'ISNULL'
-
           | 'IS' 'NOT'? 'NULL'
-
- Referenced by: -
- - -====================================================================================================================== -IsBooleanExpression -====================================================================================================================== - - -.. raw:: html - - - - - - IS - - NOT - - TRUE - - FALSE - - -
- - -
         ::= 'IS' 'NOT'? ( 'TRUE' | 'FALSE' )
-
- Referenced by: -
- - -====================================================================================================================== -ExistsExpression -====================================================================================================================== - - -.. raw:: html - - - - - - EXISTS - - SimpleExpression - -
- - -
         ::= 'EXISTS' SimpleExpression
-
- Referenced by: -
- - -====================================================================================================================== -MemberOfExpression -====================================================================================================================== - - -.. raw:: html - - - - - - MEMBER - - OF - - Expression - -
- - -
         ::= 'MEMBER' 'OF' Expression
-
- Referenced by: -
- - -====================================================================================================================== -ExpressionList -====================================================================================================================== - - -.. raw:: html - - - - - - ComplexExpressionList - - SimpleExpressionList - - ParenthesedExpressionList - -
- - -
         ::= ComplexExpressionList
-
           | SimpleExpressionList
-
           | ParenthesedExpressionList
-
- - -====================================================================================================================== -ParenthesedExpressionList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - ComplexExpressionList - - SimpleExpressionList - ) - - -
- - -
         ::= '(' ( ComplexExpressionList | SimpleExpressionList )? ')'
-
- - -====================================================================================================================== -SimpleExpressionList -====================================================================================================================== - - -.. raw:: html - - - - - - SimpleExpression - , - - -
- - -
         ::= SimpleExpression ( ',' SimpleExpression )*
-
- - -====================================================================================================================== -ColumnList -====================================================================================================================== - - -.. raw:: html - - - - - - Column - , - - -
- - -
         ::= Column ( ',' Column )*
-
- - -====================================================================================================================== -ParenthesedColumnList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - ColumnList - ) - - -
- - -
         ::= '(' ColumnList ')'
-
- Referenced by: -
- - -====================================================================================================================== -ComplexExpressionList -====================================================================================================================== - - -.. raw:: html - - - - - - OracleNamedFunctionParameter - - Expression - , - - -
- - - -
- - -====================================================================================================================== -NamedExpressionListExprFirst -====================================================================================================================== - - -.. raw:: html - - - - - - SimpleExpression - FROM - - IN - - PLACING - - SimpleExpression - FOR - - FROM - - SimpleExpression - FOR - - SimpleExpression - -
- - -
         ::= SimpleExpression ( 'FROM' | 'IN' | 'PLACING' ) SimpleExpression ( ( 'FOR' | 'FROM' ) SimpleExpression ( 'FOR' SimpleExpression )? )?
-
- - -====================================================================================================================== -ComparisonItem -====================================================================================================================== - - -.. raw:: html - - - - - - AnyComparisonExpression - - SimpleExpression - - ParenthesedExpressionList - - RowConstructor - - PrimaryExpression - -
- - -
         ::= AnyComparisonExpression
-
           | SimpleExpression
-
           | ParenthesedExpressionList
-
           | RowConstructor
-
           | PrimaryExpression
-
- Referenced by: -
- - -====================================================================================================================== -AnyComparisonExpression -====================================================================================================================== - - -.. raw:: html - - - - - - ANY - - SOME - - ALL - - Select - -
- - -
         ::= ( 'ANY' | 'SOME' | 'ALL' ) Select
-
- Referenced by: -
- - -====================================================================================================================== -SimpleExpression -====================================================================================================================== - - -.. raw:: html - - - - - - UserVariable - = - - := - - ConcatExpression - -
- - -
         ::= ( UserVariable ( '=' | ':=' ) )? ConcatExpression
-
- - -====================================================================================================================== -ConcatExpression -====================================================================================================================== - - -.. raw:: html - - - - - - BitwiseAndOr - - OP_CONCAT - -
- - -
         ::= BitwiseAndOr ( OP_CONCAT BitwiseAndOr )*
-
- Referenced by: -
- - -====================================================================================================================== -BitwiseAndOr -====================================================================================================================== - - -.. raw:: html - - - - - - AdditiveExpression - | - - & - - << - - >> - - -
- - -
         ::= AdditiveExpression ( ( '|' | '&' | '<<' | '>>' ) AdditiveExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -AdditiveExpression -====================================================================================================================== - - -.. raw:: html - - - - - - MultiplicativeExpression - + - - - - - -
- - -
         ::= MultiplicativeExpression ( ( '+' | '-' ) MultiplicativeExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -MultiplicativeExpression -====================================================================================================================== - - -.. raw:: html - - - - - - BitwiseXor - * - - / - - DIV - - % - - -
- - -
         ::= BitwiseXor ( ( '*' | '/' | 'DIV' | '%' ) BitwiseXor )*
-
- Referenced by: -
- - -====================================================================================================================== -BitwiseXor -====================================================================================================================== - - -.. raw:: html - - - - - - PrimaryExpression - ^ - - -
- - -
         ::= PrimaryExpression ( '^' PrimaryExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -ArrayExpression -====================================================================================================================== - - -.. raw:: html - - - - - - [ - - SimpleExpression - : - - SimpleExpression - ] - - -
- - -
         ::= ( '[' SimpleExpression? ( ':' SimpleExpression? )? ']' )+
-
- Referenced by: -
- - -====================================================================================================================== -PrimaryExpression -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - ! - - + - - - - - ~ - - NULL - - CaseWhenExpression - - SimpleJdbcParameter - - JdbcNamedParameter - - UserVariable - - NumericBind - - ExtractExpression - - MySQLGroupConcat - - XMLSerializeExpr - - JsonExpression - - JsonFunction - - JsonAggregateFunction - - FullTextSearch - - Function - - AnalyticExpression - - IntervalExpression - - S_DOUBLE - - S_LONG - - S_HEX - - CastExpression - - CharacterPrimary - - K_TIME_KEY_EXPR - CURRENT - - DateTimeLiteralExpression - ARRAY - - ArrayConstructor - - NextValExpression - - ConnectByRootOperator - ALL - - Column - - S_CHAR_LITERAL - {d - - {t - - {ts - - S_CHAR_LITERAL - } - - ParenthesedSelect - - ParenthesedExpressionList - . - - RelObjectNameExt - COLLATE - - S_IDENTIFIER - - IntervalExpressionWithoutInterval - - ArrayExpression - :: - - ColDataType - AT - - K_DATETIMELITERAL - ZONE - - PrimaryExpression - -
- - -
         ::= ( 'NOT' | '!' )? ( '+' | '-' | '~' )? ( 'NULL' | CaseWhenExpression | SimpleJdbcParameter | JdbcNamedParameter | UserVariable | NumericBind | ExtractExpression | MySQLGroupConcat | XMLSerializeExpr | JsonExpression | JsonFunction | JsonAggregateFunction | FullTextSearch | Function AnalyticExpression? | IntervalExpression | S_DOUBLE | S_LONG | S_HEX | CastExpression | CharacterPrimary | K_TIME_KEY_EXPR | 'CURRENT' | DateTimeLiteralExpression | 'ARRAY' ArrayConstructor | NextValExpression | ConnectByRootOperator | 'ALL' | Column | S_CHAR_LITERAL | ( '{d' | '{t' | '{ts' ) S_CHAR_LITERAL '}' | ParenthesedSelect | ParenthesedExpressionList ( '.' RelObjectNameExt )? ) ( 'COLLATE' S_IDENTIFIER )? IntervalExpressionWithoutInterval? ArrayExpression? ( '::' ColDataType )* ( 'AT' K_DATETIMELITERAL 'ZONE' PrimaryExpression )*
-
- - -====================================================================================================================== -ConnectByRootOperator -====================================================================================================================== - - -.. raw:: html - - - - - - CONNECT_BY_ROOT - - Column - -
- - -
         ::= 'CONNECT_BY_ROOT' Column
-
- Referenced by: -
- - -====================================================================================================================== -NextValExpression -====================================================================================================================== - - -.. raw:: html - - - - - - K_NEXTVAL - - RelObjectNameList - -
- - -
         ::= K_NEXTVAL RelObjectNameList
-
- Referenced by: -
- - -====================================================================================================================== -JdbcNamedParameter -====================================================================================================================== - - -.. raw:: html - - - - - - : - - & - - IdentifierChain - -
- - -
         ::= ( ':' | '&' ) IdentifierChain
-
- - -====================================================================================================================== -OracleNamedFunctionParameter -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameExt2 - => - - Expression - -
- - -
         ::= RelObjectNameExt2 '=>' Expression
-
- Referenced by: -
- - -====================================================================================================================== -UserVariable -====================================================================================================================== - - -.. raw:: html - - - - - - @ - - @@ - - IdentifierChain - -
- - -
         ::= ( '@' | '@@' ) IdentifierChain
-
- - -====================================================================================================================== -NumericBind -====================================================================================================================== - - -.. raw:: html - - - - - - : - - S_LONG - -
- - -
         ::= ':' S_LONG
-
- Referenced by: -
- - -====================================================================================================================== -DateTimeLiteralExpression -====================================================================================================================== - - -.. raw:: html - - - - - - K_DATETIMELITERAL - - S_CHAR_LITERAL - -
- - -
         ::= K_DATETIMELITERAL S_CHAR_LITERAL
-
- Referenced by: -
- - -====================================================================================================================== -RangeExpression -====================================================================================================================== - - -.. raw:: html - - - - - - : - - Expression - -
- - -
         ::= ':' Expression
-
- Referenced by: -
- - -====================================================================================================================== -ArrayConstructor -====================================================================================================================== - - -.. raw:: html - - - - - - [ - - Expression - - RangeExpression - - ArrayConstructor - , - - ] - - -
- - -
         ::= '[' ( ( Expression RangeExpression? | ArrayConstructor ) ( ',' ( Expression RangeExpression? | ArrayConstructor ) )* )? ']'
-
- - -====================================================================================================================== -ParenthesedExpression -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - PrimaryExpression - ) - - -
- - -
         ::= '(' PrimaryExpression ')'
-
- Referenced by: -
- - -====================================================================================================================== -JsonExpression -====================================================================================================================== - - -.. raw:: html - - - - - - CaseWhenExpression - - SimpleJdbcParameter - - JdbcNamedParameter - - UserVariable - - JsonFunction - - JsonAggregateFunction - - FullTextSearch - - Function - - Column - - S_CHAR_LITERAL - - ParenthesedExpression - - ParenthesedSelect - :: - - ColDataType - -> - - ->> - - S_CHAR_LITERAL - - S_LONG - #> - - #>> - - S_CHAR_LITERAL - :: - - ColDataType - -> - - ->> - - S_CHAR_LITERAL - - S_LONG - #> - - #>> - - S_CHAR_LITERAL - -
- - -
         ::= ( CaseWhenExpression | SimpleJdbcParameter | JdbcNamedParameter | UserVariable | JsonFunction | JsonAggregateFunction | FullTextSearch | Function | Column | S_CHAR_LITERAL | ParenthesedExpression | ParenthesedSelect ) ( '::' ColDataType )* ( ( '->' | '->>' ) ( S_CHAR_LITERAL | S_LONG ) | ( '#>' | '#>>' ) S_CHAR_LITERAL )+ ( ( '::' ColDataType )+ ( ( '->' | '->>' ) ( S_CHAR_LITERAL | S_LONG ) | ( '#>' | '#>>' ) S_CHAR_LITERAL )* )*
-
- Referenced by: -
- - -====================================================================================================================== -JsonFunction -====================================================================================================================== - - -.. raw:: html - - - - - - JSON_OBJECT - - ( - - KEY - - S_CHAR_LITERAL - : - - , - - VALUE - - Expression - FORMAT - - JSON - - , - - KEY - - S_CHAR_LITERAL - : - - , - - VALUE - - Expression - FORMAT - - JSON - - NULL - - ABSENT - - ON - - NULL - - WITH - - WITHOUT - - UNIQUE - - KEYS - - JSON_ARRAY - - ( - - NULL - - ON - - NULL - - Expression - FORMAT - - JSON - - , - - ABSENT - - ON - - NULL - - ) - - -
- - -
         ::= ( 'JSON_OBJECT' '(' ( 'KEY'? S_CHAR_LITERAL ( ( ':' | ',' | 'VALUE' ) Expression ( 'FORMAT' 'JSON' )? )? ( ',' 'KEY'? S_CHAR_LITERAL ( ':' | ',' | 'VALUE' ) Expression ( 'FORMAT' 'JSON' )? )* )? ( ( 'NULL' | 'ABSENT' ) 'ON' 'NULL' )? ( ( 'WITH' | 'WITHOUT' - ) 'UNIQUE' 'KEYS' )? | 'JSON_ARRAY' '(' ( 'NULL' 'ON' 'NULL' | Expression ( 'FORMAT' 'JSON' )? ( ',' Expression ( 'FORMAT' 'JSON' )? )* )* ( 'ABSENT' 'ON' 'NULL' )? ) ')'
-
- - -====================================================================================================================== -JsonAggregateFunction -====================================================================================================================== - - -.. raw:: html - - - - - - JSON_OBJECTAGG - - ( - - KEY - - DT_ZONE - - S_DOUBLE - - S_LONG - - S_HEX - - S_CHAR_LITERAL - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - : - - VALUE - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - FORMAT - - JSON - - NULL - - ABSENT - - ON - - NULL - - WITH - - WITHOUT - - UNIQUE - - KEYS - - JSON_ARRAYAGG - - ( - - Expression - FORMAT - - JSON - - OrderByElements - NULL - - ABSENT - - ON - - NULL - - ) - - FILTER - - ( - - WHERE - - Expression - ) - - OVER - - ( - - PARTITION - - BY - - ComplexExpressionList - ( - - ComplexExpressionList - ) - - OrderByElements - - WindowElement - ) - - -
- - -
         ::= ( 'JSON_OBJECTAGG' '(' 'KEY'? ( DT_ZONE | S_DOUBLE | S_LONG | S_HEX | S_CHAR_LITERAL | S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ( ':' | 'VALUE' ) ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ( 'FORMAT' 'JSON' )? ( ( 'NULL' | 'ABSENT' ) 'ON' 'NULL' )? ( ( 'WITH' | 'WITHOUT' - ) 'UNIQUE' 'KEYS' )? | 'JSON_ARRAYAGG' '(' Expression ( 'FORMAT' 'JSON' )? OrderByElements? ( ( 'NULL' | 'ABSENT' ) 'ON' 'NULL' )? ) ')' ( 'FILTER' '(' 'WHERE' Expression ')' )? ( 'OVER' '(' ( 'PARTITION' 'BY' ( ComplexExpressionList | '(' ComplexExpressionList ')' ) )? OrderByElements? WindowElement? ')' )?
-
- - -====================================================================================================================== -IntervalExpression -====================================================================================================================== - - -.. raw:: html - - - - - - INTERVAL - - - - - S_LONG - - S_DOUBLE - - S_CHAR_LITERAL - - SimpleJdbcParameter - - JdbcNamedParameter - - Function - - Column - - S_IDENTIFIER - - K_DATE_LITERAL - -
- - - -
- Referenced by: -
- - -====================================================================================================================== -IntervalExpressionWithoutInterval -====================================================================================================================== - - -.. raw:: html - - - - - - K_DATE_LITERAL - -
- - -
         ::= K_DATE_LITERAL
-
- Referenced by: -
- - -====================================================================================================================== -KeepExpression -====================================================================================================================== - - -.. raw:: html - - - - - - KEEP - - ( - - S_IDENTIFIER - FIRST - - LAST - - OrderByElements - ) - - -
- - -
         ::= 'KEEP' '(' S_IDENTIFIER ( 'FIRST' | 'LAST' ) OrderByElements ')'
-
- Referenced by: -
- - -====================================================================================================================== -windowFun -====================================================================================================================== - - -.. raw:: html - - - - - - IGNORE - - NULLS - - OVER - - WITHIN - - GROUP - - RelObjectName - - windowDefinition - OVER - - ( - - PARTITION - - BY - - ComplexExpressionList - ( - - ComplexExpressionList - ) - - ) - - -
- - -
         ::= ( ( 'IGNORE' 'NULLS' )? 'OVER' | 'WITHIN' 'GROUP' ) ( RelObjectName | windowDefinition ( 'OVER' '(' ( 'PARTITION' 'BY' ( ComplexExpressionList | '(' ComplexExpressionList ')' ) )? ')' )? )
-
- Referenced by: -
- - -====================================================================================================================== -windowDefinition -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - PARTITION - - BY - - ComplexExpressionList - ( - - ComplexExpressionList - ) - - OrderByElements - - WindowElement - ) - - -
- - -
         ::= '(' ( 'PARTITION' 'BY' ( ComplexExpressionList | '(' ComplexExpressionList ')' ) )? OrderByElements? WindowElement? ')'
-
- Referenced by: -
- - -====================================================================================================================== -AnalyticExpression -====================================================================================================================== - - -.. raw:: html - - - - - - FILTER - - ( - - WHERE - - Expression - ) - - windowFun - - windowFun - -
- - -
         ::= 'FILTER' '(' 'WHERE' Expression ')' windowFun?
-
           | windowFun
-
- Referenced by: -
- - -====================================================================================================================== -WindowElement -====================================================================================================================== - - -.. raw:: html - - - - - - ROWS - - RANGE - - BETWEEN - - WindowOffset - AND - - WindowOffset - -
- - -
         ::= ( 'ROWS' | 'RANGE' ) ( 'BETWEEN' WindowOffset 'AND' )? WindowOffset
-
- - -====================================================================================================================== -WindowOffset -====================================================================================================================== - - -.. raw:: html - - - - - - UNBOUNDED - - SimpleExpression - PRECEDING - - FOLLOWING - - CURRENT - - ROW - - -
- - -
         ::= ( 'UNBOUNDED' | SimpleExpression ) ( 'PRECEDING' | 'FOLLOWING' )
-
           | 'CURRENT' 'ROW'
-
- Referenced by: -
- - -====================================================================================================================== -ExtractExpression -====================================================================================================================== - - -.. raw:: html - - - - - - EXTRACT - - ( - - RelObjectName - - S_CHAR_LITERAL - FROM - - SimpleExpression - ) - - -
- - -
         ::= 'EXTRACT' '(' ( RelObjectName | S_CHAR_LITERAL ) 'FROM' SimpleExpression ')'
-
- Referenced by: -
- - -====================================================================================================================== -CastExpression -====================================================================================================================== - - -.. raw:: html - - - - - - CAST - - SAFE_CAST - - TRY_CAST - - ( - - SimpleExpression - AS - - ROW - - ( - - ColumnDefinition - , - - ) - - ColDataType - ) - - -
- - -
         ::= ( 'CAST' | 'SAFE_CAST' | 'TRY_CAST' ) '(' SimpleExpression 'AS' ( 'ROW' '(' ColumnDefinition ( ',' ColumnDefinition )* ')' | ColDataType ) ')'
-
- Referenced by: -
- - -====================================================================================================================== -CaseWhenExpression -====================================================================================================================== - - -.. raw:: html - - - - - - CASE - - Expression - - WhenThenSearchCondition - ELSE - - ( - - CaseWhenExpression - ) - - CaseWhenExpression - - Expression - - SimpleExpression - END - - -
- - -
         ::= 'CASE' Expression? WhenThenSearchCondition+ ( 'ELSE' ( '(' CaseWhenExpression ')' | CaseWhenExpression | Expression | SimpleExpression ) )? 'END'
-
- - -====================================================================================================================== -WhenThenSearchCondition -====================================================================================================================== - - -.. raw:: html - - - - - - WHEN - - Expression - THEN - - Expression - - SimpleExpression - -
- - -
         ::= 'WHEN' Expression 'THEN' ( Expression | SimpleExpression )
-
- Referenced by: -
- - -====================================================================================================================== -RowConstructor -====================================================================================================================== - - -.. raw:: html - - - - - - ROW - - ParenthesedExpressionList - -
- - -
         ::= 'ROW' ParenthesedExpressionList
-
- Referenced by: -
- - -====================================================================================================================== -VariableExpression -====================================================================================================================== - - -.. raw:: html - - - - - - UserVariable - = - - SimpleExpression - -
- - -
         ::= UserVariable '=' SimpleExpression
-
- Not referenced by any. -
- - -====================================================================================================================== -Execute -====================================================================================================================== - - -.. raw:: html - - - - - - EXEC - - EXECUTE - - CALL - - RelObjectNameList - - ExpressionList - -
- -
Execute  ::= ( 'EXEC' | 'EXECUTE' | 'CALL' ) RelObjectNameList ExpressionList?
-
- Referenced by: -
- - -====================================================================================================================== -FullTextSearch -====================================================================================================================== - - -.. raw:: html - - - - - - MATCH - - ( - - ColumnList - ) - - AGAINST - - ( - - S_CHAR_LITERAL - - SimpleJdbcParameter - - SimpleJdbcNamedParameter - IN NATURAL LANGUAGE MODE - - IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION - - IN BOOLEAN MODE - - WITH QUERY EXPANSION - - ) - - -
- - -
         ::= 'MATCH' '(' ColumnList ')' 'AGAINST' '(' ( S_CHAR_LITERAL | SimpleJdbcParameter | SimpleJdbcNamedParameter ) ( 'IN NATURAL LANGUAGE MODE' | 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION' - | 'IN BOOLEAN MODE' | 'WITH QUERY EXPANSION' )? ')'
-
- - -====================================================================================================================== -Function -====================================================================================================================== - - -.. raw:: html - - - - - - { - - FN - - InternalFunction - } - - SpecialStringFunctionWithNamedParameters - - InternalFunction - -
- -
Function ::= '{' 'FN' InternalFunction '}'
- -
           | InternalFunction
-
- - -====================================================================================================================== -SpecialStringFunctionWithNamedParameters -====================================================================================================================== - - -.. raw:: html - - - - - - K_STRING_FUNCTION_NAME - ( - - NamedExpressionListExprFirst - - ExpressionList - ) - - -
- - - -
- Referenced by: -
- - -====================================================================================================================== -InternalFunction -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - ( - - DISTINCT - - ALL - - UNIQUE - - * - - AllTableColumns - - ExpressionList - - OrderByElements - - Select - IGNORE - - NULLS - - ) - - . - - Function - - Column - - KeepExpression - -
- - -
         ::= RelObjectNameList '(' ( ( 'DISTINCT' | 'ALL' | 'UNIQUE' )? ( '*' | AllTableColumns | ExpressionList OrderByElements? | Select ) )? ( 'IGNORE' 'NULLS' )? ')' ( '.' ( Function | Column ) )? KeepExpression?
-
- Referenced by: -
- - -====================================================================================================================== -XMLSerializeExpr -====================================================================================================================== - - -.. raw:: html - - - - - - XMLSERIALIZE - - ( - - XMLAGG - - ( - - XMLTEXT - - ( - - SimpleExpression - ) - - OrderByElements - ) - - AS - - ColDataType - ) - - -
- - -
         ::= 'XMLSERIALIZE' '(' 'XMLAGG' '(' 'XMLTEXT' '(' SimpleExpression ')' OrderByElements? ')' 'AS' ColDataType ')'
-
- Referenced by: -
- - -====================================================================================================================== -MySQLGroupConcat -====================================================================================================================== - - -.. raw:: html - - - - - - GROUP_CONCAT - - ( - - DISTINCT - - ExpressionList - - OrderByElements - SEPARATOR - - S_CHAR_LITERAL - ) - - -
- - -
         ::= 'GROUP_CONCAT' '(' 'DISTINCT'? ExpressionList OrderByElements? ( 'SEPARATOR' S_CHAR_LITERAL )? ')'
-
- Referenced by: -
- - -====================================================================================================================== -TableFunction -====================================================================================================================== - - -.. raw:: html - - - - - - Function - - Alias - -
- - -
         ::= Function Alias?
-
- Referenced by: -
- - -====================================================================================================================== -ColumnNamesWithParamsList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - RelObjectName - - CreateParameter - , - - ) - - -
- - -
         ::= '(' RelObjectName CreateParameter? ( ',' RelObjectName CreateParameter? )* ')'
-
- Referenced by: -
- - -====================================================================================================================== -Index -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - -
- - -
- Referenced by: -
- - -====================================================================================================================== -CreateIndex -====================================================================================================================== - - -.. raw:: html - - - - - - CreateParameter - INDEX - - Index - ON - - Table - USING - - S_IDENTIFIER - - ColumnNamesWithParamsList - - CreateParameter - -
- - -
         ::= CreateParameter? 'INDEX' Index 'ON' Table ( 'USING' S_IDENTIFIER )? ColumnNamesWithParamsList CreateParameter*
-
- Referenced by: -
- - -====================================================================================================================== -ColumnDefinition -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - - ColDataType - - CreateParameter - -
- - - -
- - -====================================================================================================================== -CreateSchema -====================================================================================================================== - - -.. raw:: html - - - - - - SCHEMA - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - AUTHORIZATION - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - - PathSpecification - CREATE - - CreateTable - - CreateView - -
- - -
         ::= 'SCHEMA' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER )? ( 'AUTHORIZATION' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )? PathSpecification? ( 'CREATE' CreateTable | CreateView )*
-
- Referenced by: -
- - -====================================================================================================================== -PathSpecification -====================================================================================================================== - - -.. raw:: html - - - - - - PATH - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - , - - -
- - -
         ::= 'PATH' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ( ',' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )*
-
- Referenced by: -
- - -====================================================================================================================== -CreateTable -====================================================================================================================== - - -.. raw:: html - - - - - - UNLOGGED - - GLOBAL - - CreateParameter - TABLE - - IF - - NOT - - EXISTS - - Table - ( - - ColumnDefinition - , - - INDEX - - UNIQUE - - FULLTEXT - - KEY - - RelObjectName - - ColumnNamesWithParamsList - - CreateParameter - CONSTRAINT - - RelObjectName - PRIMARY - - KEY - - UNIQUE - - KEY - - ColumnNamesWithParamsList - - CreateParameter - FOREIGN - - KEY - - ColumnNamesWithParamsList - REFERENCES - - Table - - ColumnsNamesList - ON - - DELETE - - UPDATE - - Action - ON - - DELETE - - UPDATE - - Action - CHECK - - ( - - Expression - ) - - EXCLUDE - - WHERE - - ( - - Expression - ) - - ColumnDefinition - - RelObjectName - , - - ) - - CreateParameter - - RowMovement - AS - - Select - LIKE - - ( - - Table - ) - - Table - , - - SpannerInterleaveIn - -
- - -
         ::= 'UNLOGGED'? 'GLOBAL'? CreateParameter* 'TABLE' ( 'IF' 'NOT' 'EXISTS' )? Table ( '(' ( RelObjectName ( ',' RelObjectName )* | ColumnDefinition ( ',' ( ( 'INDEX' | 'UNIQUE'? 'FULLTEXT'? 'KEY' ) RelObjectName ColumnNamesWithParamsList CreateParameter* | ( 'CONSTRAINT' RelObjectName )? ( ( 'PRIMARY' 'KEY' | 'UNIQUE' 'KEY'? ) ColumnNamesWithParamsList CreateParameter* | 'FOREIGN' 'KEY' ColumnNamesWithParamsList 'REFERENCES' Table ColumnsNamesList ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? | 'CHECK' ( '(' Expression ')' )* ) | 'EXCLUDE' 'WHERE' ( '(' Expression ')' )* | ColumnDefinition ) )* ) ')' )? CreateParameter* RowMovement? ( 'AS' Select )? ( 'LIKE' ( '(' Table ')' | Table ) )? ( ',' SpannerInterleaveIn )?
-
- Referenced by: -
- - -====================================================================================================================== -SpannerInterleaveIn -====================================================================================================================== - - -.. raw:: html - - - - - - INTERLEAVE - - IN - - PARENT - - Table - ON - - DELETE - - NO - - ACTION - - CASCADE - - -
- - -
         ::= 'INTERLEAVE' 'IN' 'PARENT' Table ( 'ON' 'DELETE' ( 'NO' 'ACTION' | 'CASCADE' ) )?
-
- Referenced by: -
- - -====================================================================================================================== -ColDataType -====================================================================================================================== - - -.. raw:: html - - - - - - ARRAY - - < - - ColDataType - > - - BYTES - - STRING - - JSON - - ( - - S_LONG - - S_IDENTIFIER - ) - - CHARACTER - - BIT - - VARYING - - DOUBLE - - PRECISION - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - - K_DATETIMELITERAL - - K_DATE_LITERAL - XML - - INTERVAL - - DT_ZONE - CHAR - - SET - - BINARY - - JSON - - STRING - - . - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - UNSIGNED - - SIGNED - - S_IDENTIFIER - ( - - S_LONG - BYTE - - CHAR - - S_CHAR_LITERAL - - S_IDENTIFIER - CHAR - - , - - ) - - [ - - S_LONG - ] - - CHARACTER - - SET - - S_IDENTIFIER - BINARY - - -
- - -
         ::= ( 'ARRAY' '<' ColDataType '>' | ( 'BYTES' | 'STRING' | 'JSON' ) '(' ( S_LONG | S_IDENTIFIER ) ')' | ( 'CHARACTER' | 'BIT' ) 'VARYING'? | 'DOUBLE' 'PRECISION'? | ( S_IDENTIFIER | S_QUOTED_IDENTIFIER | K_DATETIMELITERAL | K_DATE_LITERAL | 'XML' | 'INTERVAL' | DT_ZONE | 'CHAR' | 'SET' | 'BINARY' | 'JSON' | 'STRING' ) ( '.' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )? | ( 'UNSIGNED' | 'SIGNED' ) S_IDENTIFIER? ) ( '(' ( ( S_LONG ( 'BYTE' | 'CHAR' )? | S_CHAR_LITERAL | S_IDENTIFIER | 'CHAR' ) ','? )* ')' )? ( '[' S_LONG? ']' )* ( 'CHARACTER' 'SET' ( S_IDENTIFIER | 'BINARY' ) )?
-
- - -====================================================================================================================== -Analyze -====================================================================================================================== - - -.. raw:: html - - - - - - ANALYZE - - Table - -
- -
Analyze  ::= 'ANALYZE' Table
-
- Referenced by: -
- - -====================================================================================================================== -CreateView -====================================================================================================================== - - -.. raw:: html - - - - - - NO - - FORCE - - TEMP - - TEMPORARY - - MATERIALIZED - - VIEW - - Table - AUTO - - REFRESH - - YES - - NO - - IF - - NOT - - EXISTS - - ColumnsNamesList - AS - - Select - WITH - - READ - - ONLY - - -
- - -
         ::= ( 'NO'? 'FORCE' )? ( 'TEMP' | 'TEMPORARY' )? 'MATERIALIZED'? 'VIEW' Table ( 'AUTO' 'REFRESH' ( 'YES' | 'NO' ) )? ( 'IF' 'NOT' 'EXISTS' )? ColumnsNamesList? 'AS' Select ( 'WITH' 'READ' 'ONLY' )?
-
- Referenced by: -
- - -====================================================================================================================== -Action -====================================================================================================================== - - -.. raw:: html - - - - - - CASCADE - - RESTRICT - - NO - - ACTION - - SET - - NULL - - DEFAULT - - -
- -
Action   ::= 'CASCADE'
-
           | 'RESTRICT'
-
           | 'NO' 'ACTION'
-
           | 'SET' ( 'NULL' | 'DEFAULT' )
-
- Referenced by: -
- - -====================================================================================================================== -AlterView -====================================================================================================================== - - -.. raw:: html - - - - - - VIEW - - Table - - ColumnsNamesList - AS - - Select - -
- - -
         ::= 'VIEW' Table ColumnsNamesList? 'AS' Select
-
- Referenced by: -
- - -====================================================================================================================== -CreateParameter -====================================================================================================================== - - -.. raw:: html - - - - - - K_NEXTVAL - ( - - S_CHAR_LITERAL - :: - - ColDataType - ) - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - . - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - USING - - INDEX - - TABLESPACE - - RelObjectName - - S_CHAR_LITERAL - NULL - - NOT - - PRIMARY - - FOREIGN - - REFERENCES - - KEY - - STORED - - ON - - COMMIT - - DROP - - ROWS - - UNIQUE - - CASCADE - - DELETE - - UPDATE - - CONSTRAINT - - WITH - - EXCLUDE - - WHERE - - UNSIGNED - - TEMP - - TEMPORARY - - PARTITION - - BY - - IN - - TYPE - - COMMENT - - USING - - COLLATE - - ASC - - DESC - - TRUE - - FALSE - - PARALLEL - - BINARY - - START - - K_TIME_KEY_EXPR - = - - DEFAULT - - AS - - CHECK - - ( - - Expression - ) - - + - - - - - S_LONG - - S_DOUBLE - - AList - CHARACTER - - SET - - ARRAY - - ArrayConstructor - :: - - ColDataType - -
- - -
         ::= K_NEXTVAL '(' S_CHAR_LITERAL '::' ColDataType ')'
-
           | ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ( '.' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )?
-
           | ( 'USING' 'INDEX' )? 'TABLESPACE' RelObjectName
-
           | S_CHAR_LITERAL
-
           | 'NULL'
-
           | 'NOT'
-
           | 'PRIMARY'
-
           | 'FOREIGN'
-
           | 'REFERENCES'
-
           | 'KEY'
-
           | 'STORED'
-
           | 'ON'
-
           | 'COMMIT'
-
           | 'DROP'
-
           | 'ROWS'
-
           | 'UNIQUE'
-
           | 'CASCADE'
-
           | 'DELETE'
-
           | 'UPDATE'
-
           | 'CONSTRAINT'
-
           | 'WITH'
-
           | 'EXCLUDE'
-
           | 'WHERE'
-
           | 'UNSIGNED'
-
           | 'TEMP'
-
           | 'TEMPORARY'
-
           | 'PARTITION'
-
           | 'BY'
-
           | 'IN'
-
           | 'TYPE'
-
           | 'COMMENT'
-
           | 'USING'
-
           | 'COLLATE'
-
           | 'ASC'
-
           | 'DESC'
-
           | 'TRUE'
-
           | 'FALSE'
-
           | 'PARALLEL'
-
           | 'BINARY'
-
           | 'START'
-
           | K_TIME_KEY_EXPR
-
           | '='
-
           | ( 'DEFAULT' | 'AS' | 'CHECK' ) ( '(' Expression ')' )?
-
           | ( '+' | '-' )? S_LONG
-
           | S_DOUBLE
-
           | AList
-
           | 'CHARACTER' 'SET'
-
           | 'ARRAY' ArrayConstructor
-
           | '::' ColDataType
-
- - -====================================================================================================================== -RowMovement -====================================================================================================================== - - -.. raw:: html - - - - - - ENABLE - - DISABLE - - ROW - - MOVEMENT - - -
- - -
         ::= ( 'ENABLE' | 'DISABLE' ) 'ROW' 'MOVEMENT'
-
- Referenced by: -
- - -====================================================================================================================== -AList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - S_LONG - - S_DOUBLE - - S_CHAR_LITERAL - - RelObjectNameWithoutValue - , - - = - - ) - - -
- -
AList    ::= '(' ( ( S_LONG | S_DOUBLE | S_CHAR_LITERAL | RelObjectNameWithoutValue ) ( ',' | '=' )? )* ')'
-
- Referenced by: -
- - -====================================================================================================================== -ColumnsNamesListItem -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - ( - - S_LONG - ) - - -
- - -
         ::= RelObjectName ( '(' S_LONG ')' )?
-
- Referenced by: -
- - -====================================================================================================================== -ColumnsNamesList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - ColumnsNamesListItem - , - - ) - - -
- - -
         ::= '(' ColumnsNamesListItem ( ',' ColumnsNamesListItem )* ')'
-
- - -====================================================================================================================== -FuncArgsListItem -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - - RelObjectName - ( - - S_LONG - ) - - -
- - -
         ::= RelObjectName RelObjectName? ( '(' S_LONG ')' )?
-
- Referenced by: -
- - -====================================================================================================================== -FuncArgsList -====================================================================================================================== - - -.. raw:: html - - - - - - ( - - FuncArgsListItem - , - - ) - - -
- - -
         ::= '(' ( FuncArgsListItem ( ',' FuncArgsListItem )* )? ')'
-
- Referenced by: -
- - -====================================================================================================================== -Drop -====================================================================================================================== - - -.. raw:: html - - - - - - DROP - - MATERIALIZED - - S_IDENTIFIER - TEMPORARY - - TABLE - - INDEX - - VIEW - - SCHEMA - - SEQUENCE - - FUNCTION - - IF - - EXISTS - - Table - - FuncArgsList - - S_IDENTIFIER - CASCADE - - RESTRICT - - ON - - -
- -
Drop     ::= 'DROP' 'MATERIALIZED'? ( S_IDENTIFIER | 'TEMPORARY'? 'TABLE' | 'INDEX' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'FUNCTION' ) - ( 'IF' 'EXISTS' )? Table FuncArgsList? ( S_IDENTIFIER | 'CASCADE' | 'RESTRICT' | 'ON' )*
-
- Referenced by: -
- - -====================================================================================================================== -Truncate -====================================================================================================================== - - -.. raw:: html - - - - - - TRUNCATE - - TABLE - - ONLY - - Table - CASCADE - - -
- -
Truncate ::= 'TRUNCATE' 'TABLE'? 'ONLY'? Table 'CASCADE'?
-
- Referenced by: -
- - -====================================================================================================================== -AlterExpressionColumnDataType -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - TYPE - - ColDataType - - CreateParameter - -
- - -
         ::= RelObjectName 'TYPE'? ColDataType CreateParameter*
-
- Referenced by: -
- - -====================================================================================================================== -AlterExpressionColumnDropNotNull -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - DROP - - NOT - - NULL - - -
- - -
         ::= RelObjectName 'DROP' 'NOT'? 'NULL'
-
- Referenced by: -
- - -====================================================================================================================== -AlterExpressionColumnDropDefault -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - DROP - - DEFAULT - - -
- - -
         ::= RelObjectName 'DROP' 'DEFAULT'
-
- Referenced by: -
- - -====================================================================================================================== -AlterExpressionConstraintState -====================================================================================================================== - - -.. raw:: html - - - - - - NOT - - DEFERRABLE - - VALIDATE - - NOVALIDATE - - ENABLE - - DISABLE - - -
- - -
         ::= ( 'NOT'? 'DEFERRABLE' | 'VALIDATE' | 'NOVALIDATE' | 'ENABLE' | 'DISABLE' - )*
-
- Referenced by: -
- - -====================================================================================================================== -AlterExpression -====================================================================================================================== - - -.. raw:: html - - - - - - ADD - - ALTER - - MODIFY - - PRIMARY - - KEY - - KEY - - INDEX - - RelObjectName - - ColumnsNamesList - - AlterExpressionConstraintState - UNIQUE - - KEY - - INDEX - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - - ColumnsNamesList - USING - - RelObjectName - COLUMN - - ( - - AlterExpressionColumnDataType - , - - ) - - AlterExpressionColumnDataType - - AlterExpressionColumnDropNotNull - - AlterExpressionColumnDropDefault - ( - - AlterExpressionColumnDataType - , - - ) - - FOREIGN - - KEY - - ColumnsNamesList - REFERENCES - - Table - - ColumnsNamesList - ON - - DELETE - - UPDATE - - Action - ON - - DELETE - - UPDATE - - Action - CONSTRAINT - - RelObjectName - FOREIGN - - KEY - - ColumnsNamesList - REFERENCES - - Table - - ColumnsNamesList - ON - - DELETE - - UPDATE - - Action - ON - - DELETE - - UPDATE - - Action - KEY - - ColumnsNamesList - - AlterExpressionConstraintState - PRIMARY - - KEY - - UNIQUE - - KEY - - INDEX - - ColumnsNamesList - - AlterExpressionConstraintState - USING - - RelObjectName - CHECK - - ( - - Expression - ) - - RelObjectName - COMMENT - - S_CHAR_LITERAL - CHANGE - - COLUMN - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - - AlterExpressionColumnDataType - DROP - - ColumnsNamesList - COLUMN - - IF - - EXISTS - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - INVALIDATE - - CASCADE - - CONSTRAINTS - - INDEX - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - UNIQUE - - FOREIGN - - KEY - - ColumnsNamesList - PRIMARY - - KEY - - CONSTRAINT - - IF - - EXISTS - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - CASCADE - - RESTRICT - - ALGORITHM - - = - - RelObjectName - RENAME - - COLUMN - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - TO - - S_IDENTIFIER - - S_QUOTED_IDENTIFIER - COMMENT - - S_CHAR_LITERAL - - captureRest - -
- - -
         ::= ( 'ADD' | 'ALTER' | 'MODIFY' ) ( ( ( 'PRIMARY' 'KEY' | ( 'KEY' | 'INDEX' - ) RelObjectName ) ColumnsNamesList AlterExpressionConstraintState | 'UNIQUE' ( ( 'KEY' | 'INDEX' ) ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )? ColumnsNamesList ) ( 'USING' RelObjectName )? | 'COLUMN'? ( '(' AlterExpressionColumnDataType ( ',' AlterExpressionColumnDataType )* ')' | AlterExpressionColumnDataType | AlterExpressionColumnDropNotNull | AlterExpressionColumnDropDefault ) | '(' AlterExpressionColumnDataType ( ',' AlterExpressionColumnDataType )* ')' | 'FOREIGN' 'KEY' ColumnsNamesList 'REFERENCES' Table ColumnsNamesList? ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? | 'CONSTRAINT' RelObjectName ( ( 'FOREIGN' 'KEY' ColumnsNamesList 'REFERENCES' Table ColumnsNamesList? ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? ( 'ON' ( 'DELETE' | 'UPDATE' ) Action )? | 'KEY' ColumnsNamesList ) AlterExpressionConstraintState | ( 'PRIMARY' 'KEY' | 'UNIQUE' ( 'KEY' | 'INDEX' )? ) ColumnsNamesList AlterExpressionConstraintState ( 'USING' RelObjectName )? | 'CHECK' ( '(' Expression ')' )* ) | RelObjectName 'COMMENT' S_CHAR_LITERAL )
-
           | 'CHANGE' 'COLUMN'? ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) AlterExpressionColumnDataType
-
           | 'DROP' ( ( ColumnsNamesList | 'COLUMN'? ( 'IF' 'EXISTS' )? ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ) 'INVALIDATE'? ( 'CASCADE' 'CONSTRAINTS'? )? | 'INDEX' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) | ( ( 'UNIQUE' | 'FOREIGN' 'KEY' ) ColumnsNamesList | 'PRIMARY' 'KEY' | 'CONSTRAINT' ( 'IF' 'EXISTS' )? ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) ) ( 'CASCADE' | 'RESTRICT' )? )
-
           | 'ALGORITHM' '='? RelObjectName
-
           | 'RENAME' ( 'COLUMN'? ( S_IDENTIFIER | S_QUOTED_IDENTIFIER ) )? 'TO' ( S_IDENTIFIER | S_QUOTED_IDENTIFIER )
-
           | 'COMMENT' S_CHAR_LITERAL
-
           | captureRest
-
- Referenced by: -
- - -====================================================================================================================== -Alter -====================================================================================================================== - - -.. raw:: html - - - - - - ALTER - - AlterTable - - AlterSession - - AlterView - - AlterSystemStatement - - AlterSequence - - captureRest - REPLACE - - AlterView - - captureRest - -
- - -
           | 'REPLACE' ( AlterView | captureRest )
-
- Referenced by: -
- - -====================================================================================================================== -AlterTable -====================================================================================================================== - - -.. raw:: html - - - - - - TABLE - - ONLY - - IF - - EXISTS - - Table - - AlterExpression - , - - -
- - -
         ::= 'TABLE' 'ONLY'? ( 'IF' 'EXISTS' )? Table AlterExpression ( ',' AlterExpression )*
-
- Referenced by: -
- - -====================================================================================================================== -AlterSession -====================================================================================================================== - - -.. raw:: html - - - - - - SESSION - - ADVISE - - COMMIT - - ROLLBACK - - NOTHING - - CLOSE - - DATABASE - - LINK - - ENABLE - - DISABLE - - COMMIT - - IN - - PROCEDURE - - GUARD - - PARALLEL - - DML - - DDL - - QUERY - - RESUMABLE - - FORCE - - PARALLEL - - DML - - DDL - - QUERY - - SET - - S_CHAR_LITERAL - - S_IDENTIFIER - = - - S_LONG - PARALLEL - - -
- - -
         ::= 'SESSION' ( 'ADVISE' ( 'COMMIT' | 'ROLLBACK' | 'NOTHING' ) | 'CLOSE' - 'DATABASE' 'LINK' | ( 'ENABLE' | 'DISABLE' ) ( 'COMMIT' 'IN' 'PROCEDURE' | 'GUARD' - | 'PARALLEL' ( 'DML' | 'DDL' | 'QUERY' ) | 'RESUMABLE' ) | 'FORCE' 'PARALLEL' ( 'DML' - | 'DDL' | 'QUERY' ) | 'SET' ) ( S_CHAR_LITERAL | S_IDENTIFIER | '=' | S_LONG | 'PARALLEL' )*
-
- Referenced by: -
- - -====================================================================================================================== -AlterSystemStatement -====================================================================================================================== - - -.. raw:: html - - - - - - SYSTEM - - ARCHIVE - - LOG - - CHECKPOINT - - DUMP - - ACTIVE - - SESSION - - HISTORY - - ENABLE - - DISABLE - - DISTRIBUTED RECOVERY - - RESTRICTED SESSION - - FLUSH - - DISCONNECT - - SESSION - - KILL SESSION - - SWITCH - - SUSPEND - - RESUME - - QUIESCE - - RESTRICTED - - UNQUIESCE - - SHUTDOWN - - REGISTER - - SET - - RESET - - captureRest - -
- - -
         ::= 'SYSTEM' ( 'ARCHIVE' 'LOG' | 'CHECKPOINT' | 'DUMP' 'ACTIVE' 'SESSION' - 'HISTORY' | ( 'ENABLE' | 'DISABLE' ) ( 'DISTRIBUTED RECOVERY' | 'RESTRICTED SESSION' - ) | 'FLUSH' | 'DISCONNECT' 'SESSION' | 'KILL SESSION' | 'SWITCH' | 'SUSPEND' | 'RESUME' - | 'QUIESCE' 'RESTRICTED' | 'UNQUIESCE' | 'SHUTDOWN' | 'REGISTER' | 'SET' | 'RESET' - ) captureRest
-
- Referenced by: -
- - -====================================================================================================================== -Wait -====================================================================================================================== - - -.. raw:: html - - - - - - WAIT - - S_LONG - -
- -
Wait     ::= 'WAIT' S_LONG
-
- Referenced by: -
- - -====================================================================================================================== -SavepointStatement -====================================================================================================================== - - -.. raw:: html - - - - - - SAVEPOINT - - S_IDENTIFIER - -
- - -
         ::= 'SAVEPOINT' S_IDENTIFIER
-
- Referenced by: -
- - -====================================================================================================================== -RollbackStatement -====================================================================================================================== - - -.. raw:: html - - - - - - ROLLBACK - - WORK - - TO - - SAVEPOINT - - S_IDENTIFIER - FORCE - - S_CHAR_LITERAL - -
- - -
         ::= 'ROLLBACK' 'WORK'? ( 'TO' 'SAVEPOINT'? S_IDENTIFIER | 'FORCE' S_CHAR_LITERAL )?
-
- Referenced by: -
- - -====================================================================================================================== -Comment -====================================================================================================================== - - -.. raw:: html - - - - - - COMMENT - - ON - - TABLE - - VIEW - - Table - COLUMN - - Column - IS - - S_CHAR_LITERAL - -
- -
Comment  ::= 'COMMENT' 'ON' ( ( 'TABLE' | 'VIEW' ) Table | 'COLUMN' Column ) 'IS' S_CHAR_LITERAL
-
- Referenced by: -
- - -====================================================================================================================== -Grant -====================================================================================================================== - - -.. raw:: html - - - - - - GRANT - - readGrantTypes - , - - ON - - RelObjectNameList - - S_IDENTIFIER - TO - - UsersList - -
- -
Grant    ::= 'GRANT' ( ( readGrantTypes ( ',' readGrantTypes )* )? 'ON' RelObjectNameList | S_IDENTIFIER ) 'TO' UsersList
-
- Referenced by: -
- - -====================================================================================================================== -UsersList -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectName - , - - ColumnsNamesListItem - -
- - -
         ::= RelObjectName ( ',' ColumnsNamesListItem )*
-
- Referenced by: -
- - -====================================================================================================================== -readGrantTypes -====================================================================================================================== - - -.. raw:: html - - - - - - K_SELECT - INSERT - - UPDATE - - DELETE - - EXECUTE - - ALTER - - DROP - - -
- - -
         ::= K_SELECT
-
           | 'INSERT'
-
           | 'UPDATE'
-
           | 'DELETE'
-
           | 'EXECUTE'
-
           | 'ALTER'
-
           | 'DROP'
-
- Referenced by: -
- - -====================================================================================================================== -Sequence -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - -
- - -
- Referenced by: -
- - -====================================================================================================================== -SequenceParameters -====================================================================================================================== - - -.. raw:: html - - - - - - INCREMENT - - BY - - START - - WITH - - MAXVALUE - - MINVALUE - - CACHE - - S_LONG - RESTART - - WITH - - S_LONG - NOMAXVALUE - - NOMINVALUE - - NOCYCLE - - CYCLE - - NOCACHE - - ORDER - - NOORDER - - KEEP - - NOKEEP - - SESSION - - GLOBAL - - -
- - -
         ::= ( ( 'INCREMENT' 'BY' | 'START' 'WITH' | 'MAXVALUE' | 'MINVALUE' | 'CACHE' - ) S_LONG | 'RESTART' ( 'WITH' S_LONG )? | 'NOMAXVALUE' | 'NOMINVALUE' | 'NOCYCLE' | 'CYCLE' | 'NOCACHE' | 'ORDER' | 'NOORDER' - | 'KEEP' | 'NOKEEP' | 'SESSION' | 'GLOBAL' )*
-
- Referenced by: -
- - -====================================================================================================================== -CreateSequence -====================================================================================================================== - - -.. raw:: html - - - - - - SEQUENCE - - Sequence - - SequenceParameters - -
- - -
         ::= 'SEQUENCE' Sequence SequenceParameters
-
- Referenced by: -
- - -====================================================================================================================== -AlterSequence -====================================================================================================================== - - -.. raw:: html - - - - - - SEQUENCE - - Sequence - - SequenceParameters - -
- - -
         ::= 'SEQUENCE' Sequence SequenceParameters
-
- Referenced by: -
- - -====================================================================================================================== -Create -====================================================================================================================== - - -.. raw:: html - - - - - - CREATE - - OR - - REPLACE - - CreateFunctionStatement - - CreateSchema - - CreateSequence - - CreateSynonym - - CreateTable - - CreateView - TRIGGER - - DOMAIN - - captureRest - - CreateIndex - -
- -
Create   ::= 'CREATE' ( 'OR' 'REPLACE' )? ( CreateFunctionStatement | CreateSchema | CreateSequence | CreateSynonym | CreateTable | CreateView | ( 'TRIGGER' | 'DOMAIN' )? captureRest | CreateIndex )
-
- Referenced by: -
- - -====================================================================================================================== -CreateFunctionStatement -====================================================================================================================== - - -.. raw:: html - - - - - - FUNCTION - - PROCEDURE - - captureFunctionBody - -
- - -
         ::= ( 'FUNCTION' | 'PROCEDURE' ) captureFunctionBody
-
- Referenced by: -
- - -====================================================================================================================== -CreateSynonym -====================================================================================================================== - - -.. raw:: html - - - - - - PUBLIC - - SYNONYM - - Synonym - FOR - - RelObjectNameList - -
- - -
         ::= 'PUBLIC'? 'SYNONYM' Synonym 'FOR' RelObjectNameList
-
- Referenced by: -
- - -====================================================================================================================== -Synonym -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameList - -
- - -
- Referenced by: -
- - -====================================================================================================================== -UnsupportedStatement -====================================================================================================================== - - -.. raw:: html - - - - - - captureUnsupportedStatementDeclaration - -
- - - -
- Referenced by: -
- - -====================================================================================================================== -IdentifierChain -====================================================================================================================== - - -.. raw:: html - - - - - - RelObjectNameExt2 - . - - -
- - -
         ::= RelObjectNameExt2 ( '.' RelObjectNameExt2 )*
-
- - -====================================================================================================================== -CharacterPrimary -====================================================================================================================== - - -.. raw:: html - - - - - - TranscodingFunction - - TrimFunction - -
- - -
         ::= TranscodingFunction
-
           | TrimFunction
-
- Referenced by: -
- - -====================================================================================================================== -TranscodingFunction -====================================================================================================================== - - -.. raw:: html - - - - - - CONVERT - - ( - - Expression - USING - - IdentifierChain - ) - - -
- - -
         ::= 'CONVERT' '(' Expression 'USING' IdentifierChain ')'
-
- Referenced by: -
- - -====================================================================================================================== -TrimFunction -====================================================================================================================== - - -.. raw:: html - - - - - - TRIM - - ( - - LEADING - - TRAILING - - BOTH - - Expression - , - - FROM - - Expression - ) - - -
- - -
         ::= 'TRIM' '(' ( 'LEADING' | 'TRAILING' | 'BOTH' )? Expression? ( ( ',' | 'FROM' ) Expression )? ')'
-
- Referenced by: -
- - -====================================================================================================================== -WHITESPACE -====================================================================================================================== - - -.. raw:: html - - - - - - - - [#x9] - - [#xD] - - [#xA] - - -
- - -
         ::= [ #x9#xD#xA]
-
- - -====================================================================================================================== -K_DATETIMELITERAL -====================================================================================================================== - - -.. raw:: html - - - - - - DATE - - TIME - - TIMESTAMP - - TIMESTAMPTZ - - -
- - -
         ::= 'DATE'
-
           | 'TIME'
-
           | 'TIMESTAMP'
-
           | 'TIMESTAMPTZ'
-
- - -====================================================================================================================== -K_DATE_LITERAL -====================================================================================================================== - - -.. raw:: html - - - - - - YEAR - - MONTH - - DAY - - HOUR - - MINUTE - - SECOND - - -
- - -
         ::= 'YEAR'
-
           | 'MONTH'
-
           | 'DAY'
-
           | 'HOUR'
-
           | 'MINUTE'
-
           | 'SECOND'
-
- - -====================================================================================================================== -K_ISOLATION -====================================================================================================================== - - -.. raw:: html - - - - - - UR - - RS - - RR - - CS - - -
- - -
         ::= 'UR'
-
           | 'RS'
-
           | 'RR'
-
           | 'CS'
-
- - -====================================================================================================================== -K_NEXTVAL -====================================================================================================================== - - -.. raw:: html - - - - - - NEXTVAL - - - - FOR - - NEXT - - - - VALUE - - - - FOR - - -
- - -
         ::= 'NEXTVAL' ( ' '+ 'FOR' )?
-
           | 'NEXT' ' '+ 'VALUE' ' '+ 'FOR'
-
- - -====================================================================================================================== -K_SELECT -====================================================================================================================== - - -.. raw:: html - - - - - - SELECT - - SEL - - -
- -
K_SELECT ::= 'SELECT'
-
           | 'SEL'
-
- - -====================================================================================================================== -K_TIME_KEY_EXPR -====================================================================================================================== - - -.. raw:: html - - - - - - CURRENT - - _ - - - - TIMESTAMP - - TIME - - DATE - - () - - -
- - -
         ::= 'CURRENT' ( '_' | ' '+ ) ( 'TIMESTAMP' | 'TIME' | 'DATE' ) '()'?
-
- - -====================================================================================================================== -K_STRING_FUNCTION_NAME -====================================================================================================================== - - -.. raw:: html - - - - - - SUBSTR - - SUBSTRING - - TRIM - - POSITION - - OVERLAY - - -
- - -
         ::= 'SUBSTR'
-
           | 'SUBSTRING'
-
           | 'TRIM'
-
           | 'POSITION'
-
           | 'OVERLAY'
-
- - -====================================================================================================================== -ST_SEMICOLON -====================================================================================================================== - - -.. raw:: html - - - - - - ; - - [#xA] - - / - - [#xA] - - [#xA] - - go - - -
- - -
         ::= ';'
-
           | #xA ( [/#xA] #xA | 'go' )
-
- Referenced by: -
- - -====================================================================================================================== -OP_GREATERTHANEQUALS -====================================================================================================================== - - -.. raw:: html - - - - - - > - - WHITESPACE - = - - -
- - -
         ::= '>' WHITESPACE* '='
-
- Referenced by: -
- - -====================================================================================================================== -OP_MINORTHANEQUALS -====================================================================================================================== - - -.. raw:: html - - - - - - < - - WHITESPACE - = - - -
- - -
         ::= '<' WHITESPACE* '='
-
- Referenced by: -
- - -====================================================================================================================== -OP_NOTEQUALSSTANDARD -====================================================================================================================== - - -.. raw:: html - - - - - - < - - WHITESPACE - > - - -
- - -
         ::= '<' WHITESPACE* '>'
-
- Referenced by: -
- - -====================================================================================================================== -OP_NOTEQUALSBANG -====================================================================================================================== - - -.. raw:: html - - - - - - ! - - WHITESPACE - = - - -
- - -
         ::= '!' WHITESPACE* '='
-
- Referenced by: -
- - -====================================================================================================================== -OP_CONCAT -====================================================================================================================== - - -.. raw:: html - - - - - - | - - WHITESPACE - | - - -
- - -
         ::= '|' WHITESPACE* '|'
-
- - -====================================================================================================================== -DT_ZONE -====================================================================================================================== - - -.. raw:: html - - - - - - K_DATETIMELITERAL - - WHITESPACE - ( - - S_LONG - ) - - WHITESPACE - WITH - - WITHOUT - - WHITESPACE - LOCAL - - WHITESPACE - TIME - - WHITESPACE - ZONE - - -
- -
DT_ZONE  ::= K_DATETIMELITERAL WHITESPACE* ( '(' S_LONG ')' )? WHITESPACE* ( 'WITH' | 'WITHOUT' ) WHITESPACE+ ( 'LOCAL' WHITESPACE+ )? 'TIME' WHITESPACE+ 'ZONE'
-
- - -====================================================================================================================== -S_DOUBLE -====================================================================================================================== - - -.. raw:: html - - - - - - S_LONG - . - - S_LONG - e - - E - - + - - [#x2D] - - S_LONG - - S_LONG - . - - e - - E - - + - - [#x2D] - - S_LONG - e - - E - - + - - [#x2D] - - S_LONG - -
- -
S_DOUBLE ::= S_LONG? '.' S_LONG ( [eE] [+#x2D]? S_LONG )?
-
           | S_LONG ( '.' ( [eE] [+#x2D]? S_LONG )? | [eE] [+#x2D]? S_LONG )
-
- - -====================================================================================================================== -S_LONG -====================================================================================================================== - - -.. raw:: html - - - - - - DIGIT - -
- -
S_LONG   ::= DIGIT+
-
- - -====================================================================================================================== -DIGIT -====================================================================================================================== - - -.. raw:: html - - - - - - [0-9] - - -
- -
DIGIT    ::= [0-9]
-
- Referenced by: -
- - -====================================================================================================================== -S_HEX -====================================================================================================================== - - -.. raw:: html - - - - - - x' - - HEX_VALUE - ' - - 0x - - HEX_VALUE - -
- -
S_HEX    ::= "x'" HEX_VALUE+ "'"
-
           | '0x' HEX_VALUE+
-
- - -====================================================================================================================== -HEX_VALUE -====================================================================================================================== - - -.. raw:: html - - - - - - [0-9] - - [A-F] - - -
- - -
         ::= [0-9A-F]
-
- Referenced by: -
- - -====================================================================================================================== -LINE_COMMENT -====================================================================================================================== - - -.. raw:: html - - - - - - -- - - // - - [^#xD#xA] - - -
- - -
         ::= ( '--' | '//' ) [^#xD#xA]*
-
- Not referenced by any. -
- - -====================================================================================================================== -MULTI_LINE_COMMENT -====================================================================================================================== - - -.. raw:: html - - - - - - /* - - [^*] - - * - - [^*] - - [^*/] - - / - - -
- - -
         ::= '/*' [^*]* '*' ( ( [^*/] [^*]* )? '*' )* '/'
-
- Not referenced by any. -
- - -====================================================================================================================== -S_IDENTIFIER -====================================================================================================================== - - -.. raw:: html - - - - - - LETTER - - PART_LETTER - -
- - -
         ::= LETTER PART_LETTER*
-
- - -====================================================================================================================== -LETTER -====================================================================================================================== - - -.. raw:: html - - - - - - UnicodeIdentifierStart - - Nd - $ - - _ - - [#x23] - - -
- - -
           | Nd
-
           | [$_#x23]
-
- Referenced by: -
- - -====================================================================================================================== -PART_LETTER -====================================================================================================================== - - -.. raw:: html - - - - - - UnicodeIdentifierStart - - UnicodeIdentifierExtend - $ - - _ - - @ - - [#x23] - - -
- - -
         ::= UnicodeIdentifierStart
-
           | UnicodeIdentifierExtend
-
           | [$_@#x23]
-
- Referenced by: -
- - -====================================================================================================================== -UnicodeIdentifierStart -====================================================================================================================== - - -.. raw:: html - - - - - - [#xB7] - - Ll - - Lm - - Lo - - Lt - - Lu - - Nl - - CJK - -
- - -
         ::= #xB7
-
           | Ll
-
           | Lm
-
           | Lo
-
           | Lt
-
           | Lu
-
           | Nl
-
           | CJK
-
- Referenced by: -
- - -====================================================================================================================== -Ll -====================================================================================================================== - - -.. raw:: html - - - - - - [a-z] - - [#xB5] - - [#xDF-#xF6] - - [#xF8-#xFF] - - [#x101] - - [#x103] - - [#x105] - - [#x107] - - [#x109] - - [#x10B] - - [#x10D] - - [#x10F] - - [#x111] - - [#x113] - - [#x115] - - [#x117] - - [#x119] - - [#x11B] - - [#x11D] - - [#x11F] - - [#x121] - - [#x123] - - [#x125] - - [#x127] - - [#x129] - - [#x12B] - - [#x12D] - - [#x12F] - - [#x131] - - [#x133] - - [#x135] - - [#x137-#x138] - - [#x13A] - - [#x13C] - - [#x13E] - - [#x140] - - [#x142] - - [#x144] - - [#x146] - - [#x148-#x149] - - [#x14B] - - [#x14D] - - [#x14F] - - [#x151] - - [#x153] - - [#x155] - - [#x157] - - [#x159] - - [#x15B] - - [#x15D] - - [#x15F] - - [#x161] - - [#x163] - - [#x165] - - [#x167] - - [#x169] - - [#x16B] - - [#x16D] - - [#x16F] - - [#x171] - - [#x173] - - [#x175] - - [#x177] - - [#x17A] - - [#x17C] - - [#x17E-#x180] - - [#x183] - - [#x185] - - [#x188] - - [#x18C-#x18D] - - [#x192] - - [#x195] - - [#x199-#x19B] - - [#x19E] - - [#x1A1] - - [#x1A3] - - [#x1A5] - - [#x1A8] - - [#x1AA-#x1AB] - - [#x1AD] - - [#x1B0] - - [#x1B4] - - [#x1B6] - - [#x1B9-#x1BA] - - [#x1BD-#x1BF] - - [#x1C6] - - [#x1C9] - - [#x1CC] - - [#x1CE] - - [#x1D0] - - [#x1D2] - - [#x1D4] - - [#x1D6] - - [#x1D8] - - [#x1DA] - - [#x1DC-#x1DD] - - [#x1DF] - - [#x1E1] - - [#x1E3] - - [#x1E5] - - [#x1E7] - - [#x1E9] - - [#x1EB] - - [#x1ED] - - [#x1EF-#x1F0] - - [#x1F3] - - [#x1F5] - - [#x1F9] - - [#x1FB] - - [#x1FD] - - [#x1FF] - - [#x201] - - [#x203] - - [#x205] - - [#x207] - - [#x209] - - [#x20B] - - [#x20D] - - [#x20F] - - [#x211] - - [#x213] - - [#x215] - - [#x217] - - [#x219] - - [#x21B] - - [#x21D] - - [#x21F] - - [#x221] - - [#x223] - - [#x225] - - [#x227] - - [#x229] - - [#x22B] - - [#x22D] - - [#x22F] - - [#x231] - - [#x233-#x239] - - [#x23C] - - [#x23F-#x240] - - [#x242] - - [#x247] - - [#x249] - - [#x24B] - - [#x24D] - - [#x24F-#x293] - - [#x295-#x2AF] - - [#x371] - - [#x373] - - [#x377] - - [#x37B-#x37D] - - [#x390] - - [#x3AC-#x3CE] - - [#x3D0-#x3D1] - - [#x3D5-#x3D7] - - [#x3D9] - - [#x3DB] - - [#x3DD] - - [#x3DF] - - [#x3E1] - - [#x3E3] - - [#x3E5] - - [#x3E7] - - [#x3E9] - - [#x3EB] - - [#x3ED] - - [#x3EF-#x3F3] - - [#x3F5] - - [#x3F8] - - [#x3FB-#x3FC] - - [#x430-#x45F] - - [#x461] - - [#x463] - - [#x465] - - [#x467] - - [#x469] - - [#x46B] - - [#x46D] - - [#x46F] - - [#x471] - - [#x473] - - [#x475] - - [#x477] - - [#x479] - - [#x47B] - - [#x47D] - - [#x47F] - - [#x481] - - [#x48B] - - [#x48D] - - [#x48F] - - [#x491] - - [#x493] - - [#x495] - - [#x497] - - [#x499] - - [#x49B] - - [#x49D] - - [#x49F] - - [#x4A1] - - [#x4A3] - - [#x4A5] - - [#x4A7] - - [#x4A9] - - [#x4AB] - - [#x4AD] - - [#x4AF] - - [#x4B1] - - [#x4B3] - - [#x4B5] - - [#x4B7] - - [#x4B9] - - [#x4BB] - - [#x4BD] - - [#x4BF] - - [#x4C2] - - [#x4C4] - - [#x4C6] - - [#x4C8] - - [#x4CA] - - [#x4CC] - - [#x4CE-#x4CF] - - [#x4D1] - - [#x4D3] - - [#x4D5] - - [#x4D7] - - [#x4D9] - - [#x4DB] - - [#x4DD] - - [#x4DF] - - [#x4E1] - - [#x4E3] - - [#x4E5] - - [#x4E7] - - [#x4E9] - - [#x4EB] - - [#x4ED] - - [#x4EF] - - [#x4F1] - - [#x4F3] - - [#x4F5] - - [#x4F7] - - [#x4F9] - - [#x4FB] - - [#x4FD] - - [#x4FF] - - [#x501] - - [#x503] - - [#x505] - - [#x507] - - [#x509] - - [#x50B] - - [#x50D] - - [#x50F] - - [#x511] - - [#x513] - - [#x515] - - [#x517] - - [#x519] - - [#x51B] - - [#x51D] - - [#x51F] - - [#x521] - - [#x523] - - [#x525] - - [#x527] - - [#x529] - - [#x52B] - - [#x52D] - - [#x52F] - - [#x560-#x588] - - [#x10D0-#x10FA] - - [#x10FD-#x10FF] - - [#x13F8-#x13FD] - - [#x1C80-#x1C88] - - [#x1D00-#x1D2B] - - [#x1D6B-#x1D77] - - [#x1D79-#x1D9A] - - [#x1E01] - - [#x1E03] - - [#x1E05] - - [#x1E07] - - [#x1E09] - - [#x1E0B] - - [#x1E0D] - - [#x1E0F] - - [#x1E11] - - [#x1E13] - - [#x1E15] - - [#x1E17] - - [#x1E19] - - [#x1E1B] - - [#x1E1D] - - [#x1E1F] - - [#x1E21] - - [#x1E23] - - [#x1E25] - - [#x1E27] - - [#x1E29] - - [#x1E2B] - - [#x1E2D] - - [#x1E2F] - - [#x1E31] - - [#x1E33] - - [#x1E35] - - [#x1E37] - - [#x1E39] - - [#x1E3B] - - [#x1E3D] - - [#x1E3F] - - [#x1E41] - - [#x1E43] - - [#x1E45] - - [#x1E47] - - [#x1E49] - - [#x1E4B] - - [#x1E4D] - - [#x1E4F] - - [#x1E51] - - [#x1E53] - - [#x1E55] - - [#x1E57] - - [#x1E59] - - [#x1E5B] - - [#x1E5D] - - [#x1E5F] - - [#x1E61] - - [#x1E63] - - [#x1E65] - - [#x1E67] - - [#x1E69] - - [#x1E6B] - - [#x1E6D] - - [#x1E6F] - - [#x1E71] - - [#x1E73] - - [#x1E75] - - [#x1E77] - - [#x1E79] - - [#x1E7B] - - [#x1E7D] - - [#x1E7F] - - [#x1E81] - - [#x1E83] - - [#x1E85] - - [#x1E87] - - [#x1E89] - - [#x1E8B] - - [#x1E8D] - - [#x1E8F] - - [#x1E91] - - [#x1E93] - - [#x1E95-#x1E9D] - - [#x1E9F] - - [#x1EA1] - - [#x1EA3] - - [#x1EA5] - - [#x1EA7] - - [#x1EA9] - - [#x1EAB] - - [#x1EAD] - - [#x1EAF] - - [#x1EB1] - - [#x1EB3] - - [#x1EB5] - - [#x1EB7] - - [#x1EB9] - - [#x1EBB] - - [#x1EBD] - - [#x1EBF] - - [#x1EC1] - - [#x1EC3] - - [#x1EC5] - - [#x1EC7] - - [#x1EC9] - - [#x1ECB] - - [#x1ECD] - - [#x1ECF] - - [#x1ED1] - - [#x1ED3] - - [#x1ED5] - - [#x1ED7] - - [#x1ED9] - - [#x1EDB] - - [#x1EDD] - - [#x1EDF] - - [#x1EE1] - - [#x1EE3] - - [#x1EE5] - - [#x1EE7] - - [#x1EE9] - - [#x1EEB] - - [#x1EED] - - [#x1EEF] - - [#x1EF1] - - [#x1EF3] - - [#x1EF5] - - [#x1EF7] - - [#x1EF9] - - [#x1EFB] - - [#x1EFD] - - [#x1EFF-#x1F07] - - [#x1F10-#x1F15] - - [#x1F20-#x1F27] - - [#x1F30-#x1F37] - - [#x1F40-#x1F45] - - [#x1F50-#x1F57] - - [#x1F60-#x1F67] - - [#x1F70-#x1F7D] - - [#x1F80-#x1F87] - - [#x1F90-#x1F97] - - [#x1FA0-#x1FA7] - - [#x1FB0-#x1FB4] - - [#x1FB6-#x1FB7] - - [#x1FBE] - - [#x1FC2-#x1FC4] - - [#x1FC6-#x1FC7] - - [#x1FD0-#x1FD3] - - [#x1FD6-#x1FD7] - - [#x1FE0-#x1FE7] - - [#x1FF2-#x1FF4] - - [#x1FF6-#x1FF7] - - [#x210A] - - [#x210E-#x210F] - - [#x2113] - - [#x212F] - - [#x2134] - - [#x2139] - - [#x213C-#x213D] - - [#x2146-#x2149] - - [#x214E] - - [#x2184] - - [#x2C30-#x2C5F] - - [#x2C61] - - [#x2C65-#x2C66] - - [#x2C68] - - [#x2C6A] - - [#x2C6C] - - [#x2C71] - - [#x2C73-#x2C74] - - [#x2C76-#x2C7B] - - [#x2C81] - - [#x2C83] - - [#x2C85] - - [#x2C87] - - [#x2C89] - - [#x2C8B] - - [#x2C8D] - - [#x2C8F] - - [#x2C91] - - [#x2C93] - - [#x2C95] - - [#x2C97] - - [#x2C99] - - [#x2C9B] - - [#x2C9D] - - [#x2C9F] - - [#x2CA1] - - [#x2CA3] - - [#x2CA5] - - [#x2CA7] - - [#x2CA9] - - [#x2CAB] - - [#x2CAD] - - [#x2CAF] - - [#x2CB1] - - [#x2CB3] - - [#x2CB5] - - [#x2CB7] - - [#x2CB9] - - [#x2CBB] - - [#x2CBD] - - [#x2CBF] - - [#x2CC1] - - [#x2CC3] - - [#x2CC5] - - [#x2CC7] - - [#x2CC9] - - [#x2CCB] - - [#x2CCD] - - [#x2CCF] - - [#x2CD1] - - [#x2CD3] - - [#x2CD5] - - [#x2CD7] - - [#x2CD9] - - [#x2CDB] - - [#x2CDD] - - [#x2CDF] - - [#x2CE1] - - [#x2CE3-#x2CE4] - - [#x2CEC] - - [#x2CEE] - - [#x2CF3] - - [#x2D00-#x2D25] - - [#x2D27] - - [#x2D2D] - - [#xA641] - - [#xA643] - - [#xA645] - - [#xA647] - - [#xA649] - - [#xA64B] - - [#xA64D] - - [#xA64F] - - [#xA651] - - [#xA653] - - [#xA655] - - [#xA657] - - [#xA659] - - [#xA65B] - - [#xA65D] - - [#xA65F] - - [#xA661] - - [#xA663] - - [#xA665] - - [#xA667] - - [#xA669] - - [#xA66B] - - [#xA66D] - - [#xA681] - - [#xA683] - - [#xA685] - - [#xA687] - - [#xA689] - - [#xA68B] - - [#xA68D] - - [#xA68F] - - [#xA691] - - [#xA693] - - [#xA695] - - [#xA697] - - [#xA699] - - [#xA69B] - - [#xA723] - - [#xA725] - - [#xA727] - - [#xA729] - - [#xA72B] - - [#xA72D] - - [#xA72F-#xA731] - - [#xA733] - - [#xA735] - - [#xA737] - - [#xA739] - - [#xA73B] - - [#xA73D] - - [#xA73F] - - [#xA741] - - [#xA743] - - [#xA745] - - [#xA747] - - [#xA749] - - [#xA74B] - - [#xA74D] - - [#xA74F] - - [#xA751] - - [#xA753] - - [#xA755] - - [#xA757] - - [#xA759] - - [#xA75B] - - [#xA75D] - - [#xA75F] - - [#xA761] - - [#xA763] - - [#xA765] - - [#xA767] - - [#xA769] - - [#xA76B] - - [#xA76D] - - [#xA76F] - - [#xA771-#xA778] - - [#xA77A] - - [#xA77C] - - [#xA77F] - - [#xA781] - - [#xA783] - - [#xA785] - - [#xA787] - - [#xA78C] - - [#xA78E] - - [#xA791] - - [#xA793-#xA795] - - [#xA797] - - [#xA799] - - [#xA79B] - - [#xA79D] - - [#xA79F] - - [#xA7A1] - - [#xA7A3] - - [#xA7A5] - - [#xA7A7] - - [#xA7A9] - - [#xA7AF] - - [#xA7B5] - - [#xA7B7] - - [#xA7B9] - - [#xA7BB] - - [#xA7BD] - - [#xA7BF] - - [#xA7C1] - - [#xA7C3] - - [#xA7C8] - - [#xA7CA] - - [#xA7D1] - - [#xA7D3] - - [#xA7D5] - - [#xA7D7] - - [#xA7D9] - - [#xA7F6] - - [#xA7FA] - - [#xAB30-#xAB5A] - - [#xAB60-#xAB68] - - [#xAB70-#xABBF] - - [#xFB00-#xFB06] - - [#xFB13-#xFB17] - - [#xFF41-#xFF5A] - - -
- -
Ll       ::= [a-z#xB5#xDF-#xF6#xF8-#xFF#x101#x103#x105#x107#x109#x10B#x10D#x10F#x111#x113#x115#x117#x119#x11B#x11D#x11F#x121#x123#x125#x127#x129#x12B#x12D#x12F#x131#x133#x135#x137-#x138#x13A#x13C#x13E#x140#x142#x144#x146#x148-#x149#x14B#x14D#x14F#x151#x153#x155#x157#x159#x15B#x15D#x15F#x161#x163#x165#x167#x169#x16B#x16D#x16F#x171#x173#x175#x177#x17A#x17C#x17E-#x180#x183#x185#x188#x18C-#x18D#x192#x195#x199-#x19B#x19E#x1A1#x1A3#x1A5#x1A8#x1AA-#x1AB#x1AD#x1B0#x1B4#x1B6#x1B9-#x1BA#x1BD-#x1BF#x1C6#x1C9#x1CC#x1CE#x1D0#x1D2#x1D4#x1D6#x1D8#x1DA#x1DC-#x1DD#x1DF#x1E1#x1E3#x1E5#x1E7#x1E9#x1EB#x1ED#x1EF-#x1F0#x1F3#x1F5#x1F9#x1FB#x1FD#x1FF#x201#x203#x205#x207#x209#x20B#x20D#x20F#x211#x213#x215#x217#x219#x21B#x21D#x21F#x221#x223#x225#x227#x229#x22B#x22D#x22F#x231#x233-#x239#x23C#x23F-#x240#x242#x247#x249#x24B#x24D#x24F-#x293#x295-#x2AF#x371#x373#x377#x37B-#x37D#x390#x3AC-#x3CE#x3D0-#x3D1#x3D5-#x3D7#x3D9#x3DB#x3DD#x3DF#x3E1#x3E3#x3E5#x3E7#x3E9#x3EB#x3ED#x3EF-#x3F3#x3F5#x3F8#x3FB-#x3FC#x430-#x45F#x461#x463#x465#x467#x469#x46B#x46D#x46F#x471#x473#x475#x477#x479#x47B#x47D#x47F#x481#x48B#x48D#x48F#x491#x493#x495#x497#x499#x49B#x49D#x49F#x4A1#x4A3#x4A5#x4A7#x4A9#x4AB#x4AD#x4AF#x4B1#x4B3#x4B5#x4B7#x4B9#x4BB#x4BD#x4BF#x4C2#x4C4#x4C6#x4C8#x4CA#x4CC#x4CE-#x4CF#x4D1#x4D3#x4D5#x4D7#x4D9#x4DB#x4DD#x4DF#x4E1#x4E3#x4E5#x4E7#x4E9#x4EB#x4ED#x4EF#x4F1#x4F3#x4F5#x4F7#x4F9#x4FB#x4FD#x4FF#x501#x503#x505#x507#x509#x50B#x50D#x50F#x511#x513#x515#x517#x519#x51B#x51D#x51F#x521#x523#x525#x527#x529#x52B#x52D#x52F#x560-#x588#x10D0-#x10FA#x10FD-#x10FF#x13F8-#x13FD#x1C80-#x1C88#x1D00-#x1D2B#x1D6B-#x1D77#x1D79-#x1D9A#x1E01#x1E03#x1E05#x1E07#x1E09#x1E0B#x1E0D#x1E0F#x1E11#x1E13#x1E15#x1E17#x1E19#x1E1B#x1E1D#x1E1F#x1E21#x1E23#x1E25#x1E27#x1E29#x1E2B#x1E2D#x1E2F#x1E31#x1E33#x1E35#x1E37#x1E39#x1E3B#x1E3D#x1E3F#x1E41#x1E43#x1E45#x1E47#x1E49#x1E4B#x1E4D#x1E4F#x1E51#x1E53#x1E55#x1E57#x1E59#x1E5B#x1E5D#x1E5F#x1E61#x1E63#x1E65#x1E67#x1E69#x1E6B#x1E6D#x1E6F#x1E71#x1E73#x1E75#x1E77#x1E79#x1E7B#x1E7D#x1E7F#x1E81#x1E83#x1E85#x1E87#x1E89#x1E8B#x1E8D#x1E8F#x1E91#x1E93#x1E95-#x1E9D#x1E9F#x1EA1#x1EA3#x1EA5#x1EA7#x1EA9#x1EAB#x1EAD#x1EAF#x1EB1#x1EB3#x1EB5#x1EB7#x1EB9#x1EBB#x1EBD#x1EBF#x1EC1#x1EC3#x1EC5#x1EC7#x1EC9#x1ECB#x1ECD#x1ECF#x1ED1#x1ED3#x1ED5#x1ED7#x1ED9#x1EDB#x1EDD#x1EDF#x1EE1#x1EE3#x1EE5#x1EE7#x1EE9#x1EEB#x1EED#x1EEF#x1EF1#x1EF3#x1EF5#x1EF7#x1EF9#x1EFB#x1EFD#x1EFF-#x1F07#x1F10-#x1F15#x1F20-#x1F27#x1F30-#x1F37#x1F40-#x1F45#x1F50-#x1F57#x1F60-#x1F67#x1F70-#x1F7D#x1F80-#x1F87#x1F90-#x1F97#x1FA0-#x1FA7#x1FB0-#x1FB4#x1FB6-#x1FB7#x1FBE#x1FC2-#x1FC4#x1FC6-#x1FC7#x1FD0-#x1FD3#x1FD6-#x1FD7#x1FE0-#x1FE7#x1FF2-#x1FF4#x1FF6-#x1FF7#x210A#x210E-#x210F#x2113#x212F#x2134#x2139#x213C-#x213D#x2146-#x2149#x214E#x2184#x2C30-#x2C5F#x2C61#x2C65-#x2C66#x2C68#x2C6A#x2C6C#x2C71#x2C73-#x2C74#x2C76-#x2C7B#x2C81#x2C83#x2C85#x2C87#x2C89#x2C8B#x2C8D#x2C8F#x2C91#x2C93#x2C95#x2C97#x2C99#x2C9B#x2C9D#x2C9F#x2CA1#x2CA3#x2CA5#x2CA7#x2CA9#x2CAB#x2CAD#x2CAF#x2CB1#x2CB3#x2CB5#x2CB7#x2CB9#x2CBB#x2CBD#x2CBF#x2CC1#x2CC3#x2CC5#x2CC7#x2CC9#x2CCB#x2CCD#x2CCF#x2CD1#x2CD3#x2CD5#x2CD7#x2CD9#x2CDB#x2CDD#x2CDF#x2CE1#x2CE3-#x2CE4#x2CEC#x2CEE#x2CF3#x2D00-#x2D25#x2D27#x2D2D#xA641#xA643#xA645#xA647#xA649#xA64B#xA64D#xA64F#xA651#xA653#xA655#xA657#xA659#xA65B#xA65D#xA65F#xA661#xA663#xA665#xA667#xA669#xA66B#xA66D#xA681#xA683#xA685#xA687#xA689#xA68B#xA68D#xA68F#xA691#xA693#xA695#xA697#xA699#xA69B#xA723#xA725#xA727#xA729#xA72B#xA72D#xA72F-#xA731#xA733#xA735#xA737#xA739#xA73B#xA73D#xA73F#xA741#xA743#xA745#xA747#xA749#xA74B#xA74D#xA74F#xA751#xA753#xA755#xA757#xA759#xA75B#xA75D#xA75F#xA761#xA763#xA765#xA767#xA769#xA76B#xA76D#xA76F#xA771-#xA778#xA77A#xA77C#xA77F#xA781#xA783#xA785#xA787#xA78C#xA78E#xA791#xA793-#xA795#xA797#xA799#xA79B#xA79D#xA79F#xA7A1#xA7A3#xA7A5#xA7A7#xA7A9#xA7AF#xA7B5#xA7B7#xA7B9#xA7BB#xA7BD#xA7BF#xA7C1#xA7C3#xA7C8#xA7CA#xA7D1#xA7D3#xA7D5#xA7D7#xA7D9#xA7F6#xA7FA#xAB30-#xAB5A#xAB60-#xAB68#xAB70-#xABBF#xFB00-#xFB06#xFB13-#xFB17#xFF41-#xFF5A]
-
- Referenced by: -
- - -====================================================================================================================== -Lm -====================================================================================================================== - - -.. raw:: html - - - - - - [#x2B0-#x2C1] - - [#x2C6-#x2D1] - - [#x2E0-#x2E4] - - [#x2EC] - - [#x2EE] - - [#x374] - - [#x37A] - - [#x559] - - [#x640] - - [#x6E5-#x6E6] - - [#x7F4-#x7F5] - - [#x7FA] - - [#x81A] - - [#x824] - - [#x828] - - [#x8C9] - - [#x971] - - [#xE46] - - [#xEC6] - - [#x10FC] - - [#x17D7] - - [#x1843] - - [#x1AA7] - - [#x1C78-#x1C7D] - - [#x1D2C-#x1D6A] - - [#x1D78] - - [#x1D9B-#x1DBF] - - [#x2071] - - [#x207F] - - [#x2090-#x209C] - - [#x2C7C-#x2C7D] - - [#x2D6F] - - [#x2E2F] - - [#x3005] - - [#x3031-#x3035] - - [#x303B] - - [#x309D-#x309E] - - [#x30FC-#x30FE] - - [#xA015] - - [#xA4F8-#xA4FD] - - [#xA60C] - - [#xA67F] - - [#xA69C-#xA69D] - - [#xA717-#xA71F] - - [#xA770] - - [#xA788] - - [#xA7F2-#xA7F4] - - [#xA7F8-#xA7F9] - - [#xA9CF] - - [#xA9E6] - - [#xAA70] - - [#xAADD] - - [#xAAF3-#xAAF4] - - [#xAB5C-#xAB5F] - - [#xAB69] - - [#xFF70] - - [#xFF9E-#xFF9F] - - -
- -
Lm       ::= [#x2B0-#x2C1#x2C6-#x2D1#x2E0-#x2E4#x2EC#x2EE#x374#x37A#x559#x640#x6E5-#x6E6#x7F4-#x7F5#x7FA#x81A#x824#x828#x8C9#x971#xE46#xEC6#x10FC#x17D7#x1843#x1AA7#x1C78-#x1C7D#x1D2C-#x1D6A#x1D78#x1D9B-#x1DBF#x2071#x207F#x2090-#x209C#x2C7C-#x2C7D#x2D6F#x2E2F#x3005#x3031-#x3035#x303B#x309D-#x309E#x30FC-#x30FE#xA015#xA4F8-#xA4FD#xA60C#xA67F#xA69C-#xA69D#xA717-#xA71F#xA770#xA788#xA7F2-#xA7F4#xA7F8-#xA7F9#xA9CF#xA9E6#xAA70#xAADD#xAAF3-#xAAF4#xAB5C-#xAB5F#xAB69#xFF70#xFF9E-#xFF9F]
-
- Referenced by: -
- - -====================================================================================================================== -Lo -====================================================================================================================== - - -.. raw:: html - - - - - - [#xAA] - - [#xBA] - - [#x1BB] - - [#x1C0-#x1C3] - - [#x294] - - [#x5D0-#x5EA] - - [#x5EF-#x5F2] - - [#x620-#x63F] - - [#x641-#x64A] - - [#x66E-#x66F] - - [#x671-#x6D3] - - [#x6D5] - - [#x6EE-#x6EF] - - [#x6FA-#x6FC] - - [#x6FF] - - [#x710] - - [#x712-#x72F] - - [#x74D-#x7A5] - - [#x7B1] - - [#x7CA-#x7EA] - - [#x800-#x815] - - [#x840-#x858] - - [#x860-#x86A] - - [#x870-#x887] - - [#x889-#x88E] - - [#x8A0-#x8C8] - - [#x904-#x939] - - [#x93D] - - [#x950] - - [#x958-#x961] - - [#x972-#x980] - - [#x985-#x98C] - - [#x98F-#x990] - - [#x993-#x9A8] - - [#x9AA-#x9B0] - - [#x9B2] - - [#x9B6-#x9B9] - - [#x9BD] - - [#x9CE] - - [#x9DC-#x9DD] - - [#x9DF-#x9E1] - - [#x9F0-#x9F1] - - [#x9FC] - - [#xA05-#xA0A] - - [#xA0F-#xA10] - - [#xA13-#xA28] - - [#xA2A-#xA30] - - [#xA32-#xA33] - - [#xA35-#xA36] - - [#xA38-#xA39] - - [#xA59-#xA5C] - - [#xA5E] - - [#xA72-#xA74] - - [#xA85-#xA8D] - - [#xA8F-#xA91] - - [#xA93-#xAA8] - - [#xAAA-#xAB0] - - [#xAB2-#xAB3] - - [#xAB5-#xAB9] - - [#xABD] - - [#xAD0] - - [#xAE0-#xAE1] - - [#xAF9] - - [#xB05-#xB0C] - - [#xB0F-#xB10] - - [#xB13-#xB28] - - [#xB2A-#xB30] - - [#xB32-#xB33] - - [#xB35-#xB39] - - [#xB3D] - - [#xB5C-#xB5D] - - [#xB5F-#xB61] - - [#xB71] - - [#xB83] - - [#xB85-#xB8A] - - [#xB8E-#xB90] - - [#xB92-#xB95] - - [#xB99-#xB9A] - - [#xB9C] - - [#xB9E-#xB9F] - - [#xBA3-#xBA4] - - [#xBA8-#xBAA] - - [#xBAE-#xBB9] - - [#xBD0] - - [#xC05-#xC0C] - - [#xC0E-#xC10] - - [#xC12-#xC28] - - [#xC2A-#xC39] - - [#xC3D] - - [#xC58-#xC5A] - - [#xC5D] - - [#xC60-#xC61] - - [#xC80] - - [#xC85-#xC8C] - - [#xC8E-#xC90] - - [#xC92-#xCA8] - - [#xCAA-#xCB3] - - [#xCB5-#xCB9] - - [#xCBD] - - [#xCDD-#xCDE] - - [#xCE0-#xCE1] - - [#xCF1-#xCF2] - - [#xD04-#xD0C] - - [#xD0E-#xD10] - - [#xD12-#xD3A] - - [#xD3D] - - [#xD4E] - - [#xD54-#xD56] - - [#xD5F-#xD61] - - [#xD7A-#xD7F] - - [#xD85-#xD96] - - [#xD9A-#xDB1] - - [#xDB3-#xDBB] - - [#xDBD] - - [#xDC0-#xDC6] - - [#xE01-#xE30] - - [#xE32-#xE33] - - [#xE40-#xE45] - - [#xE81-#xE82] - - [#xE84] - - [#xE86-#xE8A] - - [#xE8C-#xEA3] - - [#xEA5] - - [#xEA7-#xEB0] - - [#xEB2-#xEB3] - - [#xEBD] - - [#xEC0-#xEC4] - - [#xEDC-#xEDF] - - [#xF00] - - [#xF40-#xF47] - - [#xF49-#xF6C] - - [#xF88-#xF8C] - - [#x1000-#x102A] - - [#x103F] - - [#x1050-#x1055] - - [#x105A-#x105D] - - [#x1061] - - [#x1065-#x1066] - - [#x106E-#x1070] - - [#x1075-#x1081] - - [#x108E] - - [#x1100-#x1248] - - [#x124A-#x124D] - - [#x1250-#x1256] - - [#x1258] - - [#x125A-#x125D] - - [#x1260-#x1288] - - [#x128A-#x128D] - - [#x1290-#x12B0] - - [#x12B2-#x12B5] - - [#x12B8-#x12BE] - - [#x12C0] - - [#x12C2-#x12C5] - - [#x12C8-#x12D6] - - [#x12D8-#x1310] - - [#x1312-#x1315] - - [#x1318-#x135A] - - [#x1380-#x138F] - - [#x1401-#x166C] - - [#x166F-#x167F] - - [#x1681-#x169A] - - [#x16A0-#x16EA] - - [#x16F1-#x16F8] - - [#x1700-#x1711] - - [#x171F-#x1731] - - [#x1740-#x1751] - - [#x1760-#x176C] - - [#x176E-#x1770] - - [#x1780-#x17B3] - - [#x17DC] - - [#x1820-#x1842] - - [#x1844-#x1878] - - [#x1880-#x1884] - - [#x1887-#x18A8] - - [#x18AA] - - [#x18B0-#x18F5] - - [#x1900-#x191E] - - [#x1950-#x196D] - - [#x1970-#x1974] - - [#x1980-#x19AB] - - [#x19B0-#x19C9] - - [#x1A00-#x1A16] - - [#x1A20-#x1A54] - - [#x1B05-#x1B33] - - [#x1B45-#x1B4C] - - [#x1B83-#x1BA0] - - [#x1BAE-#x1BAF] - - [#x1BBA-#x1BE5] - - [#x1C00-#x1C23] - - [#x1C4D-#x1C4F] - - [#x1C5A-#x1C77] - - [#x1CE9-#x1CEC] - - [#x1CEE-#x1CF3] - - [#x1CF5-#x1CF6] - - [#x1CFA] - - [#x2135-#x2138] - - [#x2D30-#x2D67] - - [#x2D80-#x2D96] - - [#x2DA0-#x2DA6] - - [#x2DA8-#x2DAE] - - [#x2DB0-#x2DB6] - - [#x2DB8-#x2DBE] - - [#x2DC0-#x2DC6] - - [#x2DC8-#x2DCE] - - [#x2DD0-#x2DD6] - - [#x2DD8-#x2DDE] - - [#x3006] - - [#x303C] - - [#x3041-#x3096] - - [#x309F] - - [#x30A1-#x30FA] - - [#x30FF] - - [#x3105-#x312F] - - [#x3131-#x318E] - - [#x31A0-#x31BF] - - [#x31F0-#x31FF] - - [#x4DBF] - - [#x9FFF-#xA014] - - [#xA016-#xA48C] - - [#xA4D0-#xA4F7] - - [#xA500-#xA60B] - - [#xA610-#xA61F] - - [#xA62A-#xA62B] - - [#xA66E] - - [#xA6A0-#xA6E5] - - [#xA78F] - - [#xA7F7] - - [#xA7FB-#xA801] - - [#xA803-#xA805] - - [#xA807-#xA80A] - - [#xA80C-#xA822] - - [#xA840-#xA873] - - [#xA882-#xA8B3] - - [#xA8F2-#xA8F7] - - [#xA8FB] - - [#xA8FD-#xA8FE] - - [#xA90A-#xA925] - - [#xA930-#xA946] - - [#xA960-#xA97C] - - [#xA984-#xA9B2] - - [#xA9E0-#xA9E4] - - [#xA9E7-#xA9EF] - - [#xA9FA-#xA9FE] - - [#xAA00-#xAA28] - - [#xAA40-#xAA42] - - [#xAA44-#xAA4B] - - [#xAA60-#xAA6F] - - [#xAA71-#xAA76] - - [#xAA7A] - - [#xAA7E-#xAAAF] - - [#xAAB1] - - [#xAAB5-#xAAB6] - - [#xAAB9-#xAABD] - - [#xAAC0] - - [#xAAC2] - - [#xAADB-#xAADC] - - [#xAAE0-#xAAEA] - - [#xAAF2] - - [#xAB01-#xAB06] - - [#xAB09-#xAB0E] - - [#xAB11-#xAB16] - - [#xAB20-#xAB26] - - [#xAB28-#xAB2E] - - [#xABC0-#xABE2] - - [#xD7A3] - - [#xD7B0-#xD7C6] - - [#xD7CB-#xD7FB] - - [#xF900-#xFA6D] - - [#xFA70-#xFAD9] - - [#xFB1D] - - [#xFB1F-#xFB28] - - [#xFB2A-#xFB36] - - [#xFB38-#xFB3C] - - [#xFB3E] - - [#xFB40-#xFB41] - - [#xFB43-#xFB44] - - [#xFB46-#xFBB1] - - [#xFBD3-#xFD3D] - - [#xFD50-#xFD8F] - - [#xFD92-#xFDC7] - - [#xFDF0-#xFDFB] - - [#xFE70-#xFE74] - - [#xFE76-#xFEFC] - - [#xFF66-#xFF6F] - - [#xFF71-#xFF9D] - - [#xFFA0-#xFFBE] - - [#xFFC2-#xFFC7] - - [#xFFCA-#xFFCF] - - [#xFFD2-#xFFD7] - - [#xFFDA-#xFFDC] - - -
- -
Lo       ::= [#xAA#xBA#x1BB#x1C0-#x1C3#x294#x5D0-#x5EA#x5EF-#x5F2#x620-#x63F#x641-#x64A#x66E-#x66F#x671-#x6D3#x6D5#x6EE-#x6EF#x6FA-#x6FC#x6FF#x710#x712-#x72F#x74D-#x7A5#x7B1#x7CA-#x7EA#x800-#x815#x840-#x858#x860-#x86A#x870-#x887#x889-#x88E#x8A0-#x8C8#x904-#x939#x93D#x950#x958-#x961#x972-#x980#x985-#x98C#x98F-#x990#x993-#x9A8#x9AA-#x9B0#x9B2#x9B6-#x9B9#x9BD#x9CE#x9DC-#x9DD#x9DF-#x9E1#x9F0-#x9F1#x9FC#xA05-#xA0A#xA0F-#xA10#xA13-#xA28#xA2A-#xA30#xA32-#xA33#xA35-#xA36#xA38-#xA39#xA59-#xA5C#xA5E#xA72-#xA74#xA85-#xA8D#xA8F-#xA91#xA93-#xAA8#xAAA-#xAB0#xAB2-#xAB3#xAB5-#xAB9#xABD#xAD0#xAE0-#xAE1#xAF9#xB05-#xB0C#xB0F-#xB10#xB13-#xB28#xB2A-#xB30#xB32-#xB33#xB35-#xB39#xB3D#xB5C-#xB5D#xB5F-#xB61#xB71#xB83#xB85-#xB8A#xB8E-#xB90#xB92-#xB95#xB99-#xB9A#xB9C#xB9E-#xB9F#xBA3-#xBA4#xBA8-#xBAA#xBAE-#xBB9#xBD0#xC05-#xC0C#xC0E-#xC10#xC12-#xC28#xC2A-#xC39#xC3D#xC58-#xC5A#xC5D#xC60-#xC61#xC80#xC85-#xC8C#xC8E-#xC90#xC92-#xCA8#xCAA-#xCB3#xCB5-#xCB9#xCBD#xCDD-#xCDE#xCE0-#xCE1#xCF1-#xCF2#xD04-#xD0C#xD0E-#xD10#xD12-#xD3A#xD3D#xD4E#xD54-#xD56#xD5F-#xD61#xD7A-#xD7F#xD85-#xD96#xD9A-#xDB1#xDB3-#xDBB#xDBD#xDC0-#xDC6#xE01-#xE30#xE32-#xE33#xE40-#xE45#xE81-#xE82#xE84#xE86-#xE8A#xE8C-#xEA3#xEA5#xEA7-#xEB0#xEB2-#xEB3#xEBD#xEC0-#xEC4#xEDC-#xEDF#xF00#xF40-#xF47#xF49-#xF6C#xF88-#xF8C#x1000-#x102A#x103F#x1050-#x1055#x105A-#x105D#x1061#x1065-#x1066#x106E-#x1070#x1075-#x1081#x108E#x1100-#x1248#x124A-#x124D#x1250-#x1256#x1258#x125A-#x125D#x1260-#x1288#x128A-#x128D#x1290-#x12B0#x12B2-#x12B5#x12B8-#x12BE#x12C0#x12C2-#x12C5#x12C8-#x12D6#x12D8-#x1310#x1312-#x1315#x1318-#x135A#x1380-#x138F#x1401-#x166C#x166F-#x167F#x1681-#x169A#x16A0-#x16EA#x16F1-#x16F8#x1700-#x1711#x171F-#x1731#x1740-#x1751#x1760-#x176C#x176E-#x1770#x1780-#x17B3#x17DC#x1820-#x1842#x1844-#x1878#x1880-#x1884#x1887-#x18A8#x18AA#x18B0-#x18F5#x1900-#x191E#x1950-#x196D#x1970-#x1974#x1980-#x19AB#x19B0-#x19C9#x1A00-#x1A16#x1A20-#x1A54#x1B05-#x1B33#x1B45-#x1B4C#x1B83-#x1BA0#x1BAE-#x1BAF#x1BBA-#x1BE5#x1C00-#x1C23#x1C4D-#x1C4F#x1C5A-#x1C77#x1CE9-#x1CEC#x1CEE-#x1CF3#x1CF5-#x1CF6#x1CFA#x2135-#x2138#x2D30-#x2D67#x2D80-#x2D96#x2DA0-#x2DA6#x2DA8-#x2DAE#x2DB0-#x2DB6#x2DB8-#x2DBE#x2DC0-#x2DC6#x2DC8-#x2DCE#x2DD0-#x2DD6#x2DD8-#x2DDE#x3006#x303C#x3041-#x3096#x309F#x30A1-#x30FA#x30FF#x3105-#x312F#x3131-#x318E#x31A0-#x31BF#x31F0-#x31FF#x4DBF#x9FFF-#xA014#xA016-#xA48C#xA4D0-#xA4F7#xA500-#xA60B#xA610-#xA61F#xA62A-#xA62B#xA66E#xA6A0-#xA6E5#xA78F#xA7F7#xA7FB-#xA801#xA803-#xA805#xA807-#xA80A#xA80C-#xA822#xA840-#xA873#xA882-#xA8B3#xA8F2-#xA8F7#xA8FB#xA8FD-#xA8FE#xA90A-#xA925#xA930-#xA946#xA960-#xA97C#xA984-#xA9B2#xA9E0-#xA9E4#xA9E7-#xA9EF#xA9FA-#xA9FE#xAA00-#xAA28#xAA40-#xAA42#xAA44-#xAA4B#xAA60-#xAA6F#xAA71-#xAA76#xAA7A#xAA7E-#xAAAF#xAAB1#xAAB5-#xAAB6#xAAB9-#xAABD#xAAC0#xAAC2#xAADB-#xAADC#xAAE0-#xAAEA#xAAF2#xAB01-#xAB06#xAB09-#xAB0E#xAB11-#xAB16#xAB20-#xAB26#xAB28-#xAB2E#xABC0-#xABE2#xD7A3#xD7B0-#xD7C6#xD7CB-#xD7FB#xF900-#xFA6D#xFA70-#xFAD9#xFB1D#xFB1F-#xFB28#xFB2A-#xFB36#xFB38-#xFB3C#xFB3E#xFB40-#xFB41#xFB43-#xFB44#xFB46-#xFBB1#xFBD3-#xFD3D#xFD50-#xFD8F#xFD92-#xFDC7#xFDF0-#xFDFB#xFE70-#xFE74#xFE76-#xFEFC#xFF66-#xFF6F#xFF71-#xFF9D#xFFA0-#xFFBE#xFFC2-#xFFC7#xFFCA-#xFFCF#xFFD2-#xFFD7#xFFDA-#xFFDC]
-
- Referenced by: -
- - -====================================================================================================================== -Lt -====================================================================================================================== - - -.. raw:: html - - - - - - [#x1C5] - - [#x1C8] - - [#x1CB] - - [#x1F2] - - [#x1F88-#x1F8F] - - [#x1F98-#x1F9F] - - [#x1FA8-#x1FAF] - - [#x1FBC] - - [#x1FCC] - - [#x1FFC] - - -
- -
Lt       ::= [#x1C5#x1C8#x1CB#x1F2#x1F88-#x1F8F#x1F98-#x1F9F#x1FA8-#x1FAF#x1FBC#x1FCC#x1FFC]
-
- Referenced by: -
- - -====================================================================================================================== -Lu -====================================================================================================================== - - -.. raw:: html - - - - - - [A-Z] - - [#xC0-#xD6] - - [#xD8-#xDE] - - [#x100] - - [#x102] - - [#x104] - - [#x106] - - [#x108] - - [#x10A] - - [#x10C] - - [#x10E] - - [#x110] - - [#x112] - - [#x114] - - [#x116] - - [#x118] - - [#x11A] - - [#x11C] - - [#x11E] - - [#x120] - - [#x122] - - [#x124] - - [#x126] - - [#x128] - - [#x12A] - - [#x12C] - - [#x12E] - - [#x130] - - [#x132] - - [#x134] - - [#x136] - - [#x139] - - [#x13B] - - [#x13D] - - [#x13F] - - [#x141] - - [#x143] - - [#x145] - - [#x147] - - [#x14A] - - [#x14C] - - [#x14E] - - [#x150] - - [#x152] - - [#x154] - - [#x156] - - [#x158] - - [#x15A] - - [#x15C] - - [#x15E] - - [#x160] - - [#x162] - - [#x164] - - [#x166] - - [#x168] - - [#x16A] - - [#x16C] - - [#x16E] - - [#x170] - - [#x172] - - [#x174] - - [#x176] - - [#x178-#x179] - - [#x17B] - - [#x17D] - - [#x181-#x182] - - [#x184] - - [#x186-#x187] - - [#x189-#x18B] - - [#x18E-#x191] - - [#x193-#x194] - - [#x196-#x198] - - [#x19C-#x19D] - - [#x19F-#x1A0] - - [#x1A2] - - [#x1A4] - - [#x1A6-#x1A7] - - [#x1A9] - - [#x1AC] - - [#x1AE-#x1AF] - - [#x1B1-#x1B3] - - [#x1B5] - - [#x1B7-#x1B8] - - [#x1BC] - - [#x1C4] - - [#x1C7] - - [#x1CA] - - [#x1CD] - - [#x1CF] - - [#x1D1] - - [#x1D3] - - [#x1D5] - - [#x1D7] - - [#x1D9] - - [#x1DB] - - [#x1DE] - - [#x1E0] - - [#x1E2] - - [#x1E4] - - [#x1E6] - - [#x1E8] - - [#x1EA] - - [#x1EC] - - [#x1EE] - - [#x1F1] - - [#x1F4] - - [#x1F6-#x1F8] - - [#x1FA] - - [#x1FC] - - [#x1FE] - - [#x200] - - [#x202] - - [#x204] - - [#x206] - - [#x208] - - [#x20A] - - [#x20C] - - [#x20E] - - [#x210] - - [#x212] - - [#x214] - - [#x216] - - [#x218] - - [#x21A] - - [#x21C] - - [#x21E] - - [#x220] - - [#x222] - - [#x224] - - [#x226] - - [#x228] - - [#x22A] - - [#x22C] - - [#x22E] - - [#x230] - - [#x232] - - [#x23A-#x23B] - - [#x23D-#x23E] - - [#x241] - - [#x243-#x246] - - [#x248] - - [#x24A] - - [#x24C] - - [#x24E] - - [#x370] - - [#x372] - - [#x376] - - [#x37F] - - [#x386] - - [#x388-#x38A] - - [#x38C] - - [#x38E-#x38F] - - [#x391-#x3A1] - - [#x3A3-#x3AB] - - [#x3CF] - - [#x3D2-#x3D4] - - [#x3D8] - - [#x3DA] - - [#x3DC] - - [#x3DE] - - [#x3E0] - - [#x3E2] - - [#x3E4] - - [#x3E6] - - [#x3E8] - - [#x3EA] - - [#x3EC] - - [#x3EE] - - [#x3F4] - - [#x3F7] - - [#x3F9-#x3FA] - - [#x3FD-#x42F] - - [#x460] - - [#x462] - - [#x464] - - [#x466] - - [#x468] - - [#x46A] - - [#x46C] - - [#x46E] - - [#x470] - - [#x472] - - [#x474] - - [#x476] - - [#x478] - - [#x47A] - - [#x47C] - - [#x47E] - - [#x480] - - [#x48A] - - [#x48C] - - [#x48E] - - [#x490] - - [#x492] - - [#x494] - - [#x496] - - [#x498] - - [#x49A] - - [#x49C] - - [#x49E] - - [#x4A0] - - [#x4A2] - - [#x4A4] - - [#x4A6] - - [#x4A8] - - [#x4AA] - - [#x4AC] - - [#x4AE] - - [#x4B0] - - [#x4B2] - - [#x4B4] - - [#x4B6] - - [#x4B8] - - [#x4BA] - - [#x4BC] - - [#x4BE] - - [#x4C0-#x4C1] - - [#x4C3] - - [#x4C5] - - [#x4C7] - - [#x4C9] - - [#x4CB] - - [#x4CD] - - [#x4D0] - - [#x4D2] - - [#x4D4] - - [#x4D6] - - [#x4D8] - - [#x4DA] - - [#x4DC] - - [#x4DE] - - [#x4E0] - - [#x4E2] - - [#x4E4] - - [#x4E6] - - [#x4E8] - - [#x4EA] - - [#x4EC] - - [#x4EE] - - [#x4F0] - - [#x4F2] - - [#x4F4] - - [#x4F6] - - [#x4F8] - - [#x4FA] - - [#x4FC] - - [#x4FE] - - [#x500] - - [#x502] - - [#x504] - - [#x506] - - [#x508] - - [#x50A] - - [#x50C] - - [#x50E] - - [#x510] - - [#x512] - - [#x514] - - [#x516] - - [#x518] - - [#x51A] - - [#x51C] - - [#x51E] - - [#x520] - - [#x522] - - [#x524] - - [#x526] - - [#x528] - - [#x52A] - - [#x52C] - - [#x52E] - - [#x531-#x556] - - [#x10A0-#x10C5] - - [#x10C7] - - [#x10CD] - - [#x13A0-#x13F5] - - [#x1C90-#x1CBA] - - [#x1CBD-#x1CBF] - - [#x1E00] - - [#x1E02] - - [#x1E04] - - [#x1E06] - - [#x1E08] - - [#x1E0A] - - [#x1E0C] - - [#x1E0E] - - [#x1E10] - - [#x1E12] - - [#x1E14] - - [#x1E16] - - [#x1E18] - - [#x1E1A] - - [#x1E1C] - - [#x1E1E] - - [#x1E20] - - [#x1E22] - - [#x1E24] - - [#x1E26] - - [#x1E28] - - [#x1E2A] - - [#x1E2C] - - [#x1E2E] - - [#x1E30] - - [#x1E32] - - [#x1E34] - - [#x1E36] - - [#x1E38] - - [#x1E3A] - - [#x1E3C] - - [#x1E3E] - - [#x1E40] - - [#x1E42] - - [#x1E44] - - [#x1E46] - - [#x1E48] - - [#x1E4A] - - [#x1E4C] - - [#x1E4E] - - [#x1E50] - - [#x1E52] - - [#x1E54] - - [#x1E56] - - [#x1E58] - - [#x1E5A] - - [#x1E5C] - - [#x1E5E] - - [#x1E60] - - [#x1E62] - - [#x1E64] - - [#x1E66] - - [#x1E68] - - [#x1E6A] - - [#x1E6C] - - [#x1E6E] - - [#x1E70] - - [#x1E72] - - [#x1E74] - - [#x1E76] - - [#x1E78] - - [#x1E7A] - - [#x1E7C] - - [#x1E7E] - - [#x1E80] - - [#x1E82] - - [#x1E84] - - [#x1E86] - - [#x1E88] - - [#x1E8A] - - [#x1E8C] - - [#x1E8E] - - [#x1E90] - - [#x1E92] - - [#x1E94] - - [#x1E9E] - - [#x1EA0] - - [#x1EA2] - - [#x1EA4] - - [#x1EA6] - - [#x1EA8] - - [#x1EAA] - - [#x1EAC] - - [#x1EAE] - - [#x1EB0] - - [#x1EB2] - - [#x1EB4] - - [#x1EB6] - - [#x1EB8] - - [#x1EBA] - - [#x1EBC] - - [#x1EBE] - - [#x1EC0] - - [#x1EC2] - - [#x1EC4] - - [#x1EC6] - - [#x1EC8] - - [#x1ECA] - - [#x1ECC] - - [#x1ECE] - - [#x1ED0] - - [#x1ED2] - - [#x1ED4] - - [#x1ED6] - - [#x1ED8] - - [#x1EDA] - - [#x1EDC] - - [#x1EDE] - - [#x1EE0] - - [#x1EE2] - - [#x1EE4] - - [#x1EE6] - - [#x1EE8] - - [#x1EEA] - - [#x1EEC] - - [#x1EEE] - - [#x1EF0] - - [#x1EF2] - - [#x1EF4] - - [#x1EF6] - - [#x1EF8] - - [#x1EFA] - - [#x1EFC] - - [#x1EFE] - - [#x1F08-#x1F0F] - - [#x1F18-#x1F1D] - - [#x1F28-#x1F2F] - - [#x1F38-#x1F3F] - - [#x1F48-#x1F4D] - - [#x1F59] - - [#x1F5B] - - [#x1F5D] - - [#x1F5F] - - [#x1F68-#x1F6F] - - [#x1FB8-#x1FBB] - - [#x1FC8-#x1FCB] - - [#x1FD8-#x1FDB] - - [#x1FE8-#x1FEC] - - [#x1FF8-#x1FFB] - - [#x2102] - - [#x2107] - - [#x210B-#x210D] - - [#x2110-#x2112] - - [#x2115] - - [#x2119-#x211D] - - [#x2124] - - [#x2126] - - [#x2128] - - [#x212A-#x212D] - - [#x2130-#x2133] - - [#x213E-#x213F] - - [#x2145] - - [#x2183] - - [#x2C00-#x2C2F] - - [#x2C60] - - [#x2C62-#x2C64] - - [#x2C67] - - [#x2C69] - - [#x2C6B] - - [#x2C6D-#x2C70] - - [#x2C72] - - [#x2C75] - - [#x2C7E-#x2C80] - - [#x2C82] - - [#x2C84] - - [#x2C86] - - [#x2C88] - - [#x2C8A] - - [#x2C8C] - - [#x2C8E] - - [#x2C90] - - [#x2C92] - - [#x2C94] - - [#x2C96] - - [#x2C98] - - [#x2C9A] - - [#x2C9C] - - [#x2C9E] - - [#x2CA0] - - [#x2CA2] - - [#x2CA4] - - [#x2CA6] - - [#x2CA8] - - [#x2CAA] - - [#x2CAC] - - [#x2CAE] - - [#x2CB0] - - [#x2CB2] - - [#x2CB4] - - [#x2CB6] - - [#x2CB8] - - [#x2CBA] - - [#x2CBC] - - [#x2CBE] - - [#x2CC0] - - [#x2CC2] - - [#x2CC4] - - [#x2CC6] - - [#x2CC8] - - [#x2CCA] - - [#x2CCC] - - [#x2CCE] - - [#x2CD0] - - [#x2CD2] - - [#x2CD4] - - [#x2CD6] - - [#x2CD8] - - [#x2CDA] - - [#x2CDC] - - [#x2CDE] - - [#x2CE0] - - [#x2CE2] - - [#x2CEB] - - [#x2CED] - - [#x2CF2] - - [#xA640] - - [#xA642] - - [#xA644] - - [#xA646] - - [#xA648] - - [#xA64A] - - [#xA64C] - - [#xA64E] - - [#xA650] - - [#xA652] - - [#xA654] - - [#xA656] - - [#xA658] - - [#xA65A] - - [#xA65C] - - [#xA65E] - - [#xA660] - - [#xA662] - - [#xA664] - - [#xA666] - - [#xA668] - - [#xA66A] - - [#xA66C] - - [#xA680] - - [#xA682] - - [#xA684] - - [#xA686] - - [#xA688] - - [#xA68A] - - [#xA68C] - - [#xA68E] - - [#xA690] - - [#xA692] - - [#xA694] - - [#xA696] - - [#xA698] - - [#xA69A] - - [#xA722] - - [#xA724] - - [#xA726] - - [#xA728] - - [#xA72A] - - [#xA72C] - - [#xA72E] - - [#xA732] - - [#xA734] - - [#xA736] - - [#xA738] - - [#xA73A] - - [#xA73C] - - [#xA73E] - - [#xA740] - - [#xA742] - - [#xA744] - - [#xA746] - - [#xA748] - - [#xA74A] - - [#xA74C] - - [#xA74E] - - [#xA750] - - [#xA752] - - [#xA754] - - [#xA756] - - [#xA758] - - [#xA75A] - - [#xA75C] - - [#xA75E] - - [#xA760] - - [#xA762] - - [#xA764] - - [#xA766] - - [#xA768] - - [#xA76A] - - [#xA76C] - - [#xA76E] - - [#xA779] - - [#xA77B] - - [#xA77D-#xA77E] - - [#xA780] - - [#xA782] - - [#xA784] - - [#xA786] - - [#xA78B] - - [#xA78D] - - [#xA790] - - [#xA792] - - [#xA796] - - [#xA798] - - [#xA79A] - - [#xA79C] - - [#xA79E] - - [#xA7A0] - - [#xA7A2] - - [#xA7A4] - - [#xA7A6] - - [#xA7A8] - - [#xA7AA-#xA7AE] - - [#xA7B0-#xA7B4] - - [#xA7B6] - - [#xA7B8] - - [#xA7BA] - - [#xA7BC] - - [#xA7BE] - - [#xA7C0] - - [#xA7C2] - - [#xA7C4-#xA7C7] - - [#xA7C9] - - [#xA7D0] - - [#xA7D6] - - [#xA7D8] - - [#xA7F5] - - [#xFF21-#xFF3A] - - -
- -
Lu       ::= [A-Z#xC0-#xD6#xD8-#xDE#x100#x102#x104#x106#x108#x10A#x10C#x10E#x110#x112#x114#x116#x118#x11A#x11C#x11E#x120#x122#x124#x126#x128#x12A#x12C#x12E#x130#x132#x134#x136#x139#x13B#x13D#x13F#x141#x143#x145#x147#x14A#x14C#x14E#x150#x152#x154#x156#x158#x15A#x15C#x15E#x160#x162#x164#x166#x168#x16A#x16C#x16E#x170#x172#x174#x176#x178-#x179#x17B#x17D#x181-#x182#x184#x186-#x187#x189-#x18B#x18E-#x191#x193-#x194#x196-#x198#x19C-#x19D#x19F-#x1A0#x1A2#x1A4#x1A6-#x1A7#x1A9#x1AC#x1AE-#x1AF#x1B1-#x1B3#x1B5#x1B7-#x1B8#x1BC#x1C4#x1C7#x1CA#x1CD#x1CF#x1D1#x1D3#x1D5#x1D7#x1D9#x1DB#x1DE#x1E0#x1E2#x1E4#x1E6#x1E8#x1EA#x1EC#x1EE#x1F1#x1F4#x1F6-#x1F8#x1FA#x1FC#x1FE#x200#x202#x204#x206#x208#x20A#x20C#x20E#x210#x212#x214#x216#x218#x21A#x21C#x21E#x220#x222#x224#x226#x228#x22A#x22C#x22E#x230#x232#x23A-#x23B#x23D-#x23E#x241#x243-#x246#x248#x24A#x24C#x24E#x370#x372#x376#x37F#x386#x388-#x38A#x38C#x38E-#x38F#x391-#x3A1#x3A3-#x3AB#x3CF#x3D2-#x3D4#x3D8#x3DA#x3DC#x3DE#x3E0#x3E2#x3E4#x3E6#x3E8#x3EA#x3EC#x3EE#x3F4#x3F7#x3F9-#x3FA#x3FD-#x42F#x460#x462#x464#x466#x468#x46A#x46C#x46E#x470#x472#x474#x476#x478#x47A#x47C#x47E#x480#x48A#x48C#x48E#x490#x492#x494#x496#x498#x49A#x49C#x49E#x4A0#x4A2#x4A4#x4A6#x4A8#x4AA#x4AC#x4AE#x4B0#x4B2#x4B4#x4B6#x4B8#x4BA#x4BC#x4BE#x4C0-#x4C1#x4C3#x4C5#x4C7#x4C9#x4CB#x4CD#x4D0#x4D2#x4D4#x4D6#x4D8#x4DA#x4DC#x4DE#x4E0#x4E2#x4E4#x4E6#x4E8#x4EA#x4EC#x4EE#x4F0#x4F2#x4F4#x4F6#x4F8#x4FA#x4FC#x4FE#x500#x502#x504#x506#x508#x50A#x50C#x50E#x510#x512#x514#x516#x518#x51A#x51C#x51E#x520#x522#x524#x526#x528#x52A#x52C#x52E#x531-#x556#x10A0-#x10C5#x10C7#x10CD#x13A0-#x13F5#x1C90-#x1CBA#x1CBD-#x1CBF#x1E00#x1E02#x1E04#x1E06#x1E08#x1E0A#x1E0C#x1E0E#x1E10#x1E12#x1E14#x1E16#x1E18#x1E1A#x1E1C#x1E1E#x1E20#x1E22#x1E24#x1E26#x1E28#x1E2A#x1E2C#x1E2E#x1E30#x1E32#x1E34#x1E36#x1E38#x1E3A#x1E3C#x1E3E#x1E40#x1E42#x1E44#x1E46#x1E48#x1E4A#x1E4C#x1E4E#x1E50#x1E52#x1E54#x1E56#x1E58#x1E5A#x1E5C#x1E5E#x1E60#x1E62#x1E64#x1E66#x1E68#x1E6A#x1E6C#x1E6E#x1E70#x1E72#x1E74#x1E76#x1E78#x1E7A#x1E7C#x1E7E#x1E80#x1E82#x1E84#x1E86#x1E88#x1E8A#x1E8C#x1E8E#x1E90#x1E92#x1E94#x1E9E#x1EA0#x1EA2#x1EA4#x1EA6#x1EA8#x1EAA#x1EAC#x1EAE#x1EB0#x1EB2#x1EB4#x1EB6#x1EB8#x1EBA#x1EBC#x1EBE#x1EC0#x1EC2#x1EC4#x1EC6#x1EC8#x1ECA#x1ECC#x1ECE#x1ED0#x1ED2#x1ED4#x1ED6#x1ED8#x1EDA#x1EDC#x1EDE#x1EE0#x1EE2#x1EE4#x1EE6#x1EE8#x1EEA#x1EEC#x1EEE#x1EF0#x1EF2#x1EF4#x1EF6#x1EF8#x1EFA#x1EFC#x1EFE#x1F08-#x1F0F#x1F18-#x1F1D#x1F28-#x1F2F#x1F38-#x1F3F#x1F48-#x1F4D#x1F59#x1F5B#x1F5D#x1F5F#x1F68-#x1F6F#x1FB8-#x1FBB#x1FC8-#x1FCB#x1FD8-#x1FDB#x1FE8-#x1FEC#x1FF8-#x1FFB#x2102#x2107#x210B-#x210D#x2110-#x2112#x2115#x2119-#x211D#x2124#x2126#x2128#x212A-#x212D#x2130-#x2133#x213E-#x213F#x2145#x2183#x2C00-#x2C2F#x2C60#x2C62-#x2C64#x2C67#x2C69#x2C6B#x2C6D-#x2C70#x2C72#x2C75#x2C7E-#x2C80#x2C82#x2C84#x2C86#x2C88#x2C8A#x2C8C#x2C8E#x2C90#x2C92#x2C94#x2C96#x2C98#x2C9A#x2C9C#x2C9E#x2CA0#x2CA2#x2CA4#x2CA6#x2CA8#x2CAA#x2CAC#x2CAE#x2CB0#x2CB2#x2CB4#x2CB6#x2CB8#x2CBA#x2CBC#x2CBE#x2CC0#x2CC2#x2CC4#x2CC6#x2CC8#x2CCA#x2CCC#x2CCE#x2CD0#x2CD2#x2CD4#x2CD6#x2CD8#x2CDA#x2CDC#x2CDE#x2CE0#x2CE2#x2CEB#x2CED#x2CF2#xA640#xA642#xA644#xA646#xA648#xA64A#xA64C#xA64E#xA650#xA652#xA654#xA656#xA658#xA65A#xA65C#xA65E#xA660#xA662#xA664#xA666#xA668#xA66A#xA66C#xA680#xA682#xA684#xA686#xA688#xA68A#xA68C#xA68E#xA690#xA692#xA694#xA696#xA698#xA69A#xA722#xA724#xA726#xA728#xA72A#xA72C#xA72E#xA732#xA734#xA736#xA738#xA73A#xA73C#xA73E#xA740#xA742#xA744#xA746#xA748#xA74A#xA74C#xA74E#xA750#xA752#xA754#xA756#xA758#xA75A#xA75C#xA75E#xA760#xA762#xA764#xA766#xA768#xA76A#xA76C#xA76E#xA779#xA77B#xA77D-#xA77E#xA780#xA782#xA784#xA786#xA78B#xA78D#xA790#xA792#xA796#xA798#xA79A#xA79C#xA79E#xA7A0#xA7A2#xA7A4#xA7A6#xA7A8#xA7AA-#xA7AE#xA7B0-#xA7B4#xA7B6#xA7B8#xA7BA#xA7BC#xA7BE#xA7C0#xA7C2#xA7C4-#xA7C7#xA7C9#xA7D0#xA7D6#xA7D8#xA7F5#xFF21-#xFF3A]
-
- Referenced by: -
- - -====================================================================================================================== -Nl -====================================================================================================================== - - -.. raw:: html - - - - - - [#x16EE-#x16F0] - - [#x2160-#x2182] - - [#x2185-#x2188] - - [#x3007] - - [#x3021-#x3029] - - [#x3038-#x303A] - - [#xA6E6-#xA6EF] - - -
- -
Nl       ::= [#x16EE-#x16F0#x2160-#x2182#x2185-#x2188#x3007#x3021-#x3029#x3038-#x303A#xA6E6-#xA6EF]
-
- Referenced by: -
- - -====================================================================================================================== -UnicodeIdentifierExtend -====================================================================================================================== - - -.. raw:: html - - - - - - Mn - - Mc - - Nd - - Pc - - Cf - - CJK - -
- - -
         ::= Mn
-
           | Mc
-
           | Nd
-
           | Pc
-
           | Cf
-
           | CJK
-
- Referenced by: -
- - -====================================================================================================================== -Cf -====================================================================================================================== - - -.. raw:: html - - - - - - [#xAD] - - [#x600-#x605] - - [#x61C] - - [#x6DD] - - [#x70F] - - [#x890-#x891] - - [#x8E2] - - [#x180E] - - [#x200B-#x200F] - - [#x202A-#x202E] - - [#x2060-#x2064] - - [#x2066-#x206F] - - [#xFEFF] - - [#xFFF9-#xFFFB] - - -
- -
Cf       ::= [#xAD#x600-#x605#x61C#x6DD#x70F#x890-#x891#x8E2#x180E#x200B-#x200F#x202A-#x202E#x2060-#x2064#x2066-#x206F#xFEFF#xFFF9-#xFFFB]
-
- Referenced by: -
- - -====================================================================================================================== -Mc -====================================================================================================================== - - -.. raw:: html - - - - - - [#x903] - - [#x93B] - - [#x93E-#x940] - - [#x949-#x94C] - - [#x94E-#x94F] - - [#x982-#x983] - - [#x9BE-#x9C0] - - [#x9C7-#x9C8] - - [#x9CB-#x9CC] - - [#x9D7] - - [#xA03] - - [#xA3E-#xA40] - - [#xA83] - - [#xABE-#xAC0] - - [#xAC9] - - [#xACB-#xACC] - - [#xB02-#xB03] - - [#xB3E] - - [#xB40] - - [#xB47-#xB48] - - [#xB4B-#xB4C] - - [#xB57] - - [#xBBE-#xBBF] - - [#xBC1-#xBC2] - - [#xBC6-#xBC8] - - [#xBCA-#xBCC] - - [#xBD7] - - [#xC01-#xC03] - - [#xC41-#xC44] - - [#xC82-#xC83] - - [#xCBE] - - [#xCC0-#xCC4] - - [#xCC7-#xCC8] - - [#xCCA-#xCCB] - - [#xCD5-#xCD6] - - [#xCF3] - - [#xD02-#xD03] - - [#xD3E-#xD40] - - [#xD46-#xD48] - - [#xD4A-#xD4C] - - [#xD57] - - [#xD82-#xD83] - - [#xDCF-#xDD1] - - [#xDD8-#xDDF] - - [#xDF2-#xDF3] - - [#xF3E-#xF3F] - - [#xF7F] - - [#x102B-#x102C] - - [#x1031] - - [#x1038] - - [#x103B-#x103C] - - [#x1056-#x1057] - - [#x1062-#x1064] - - [#x1067-#x106D] - - [#x1083-#x1084] - - [#x1087-#x108C] - - [#x108F] - - [#x109A-#x109C] - - [#x1715] - - [#x1734] - - [#x17B6] - - [#x17BE-#x17C5] - - [#x17C7-#x17C8] - - [#x1923-#x1926] - - [#x1929-#x192B] - - [#x1930-#x1931] - - [#x1933-#x1938] - - [#x1A19-#x1A1A] - - [#x1A55] - - [#x1A57] - - [#x1A61] - - [#x1A63-#x1A64] - - [#x1A6D-#x1A72] - - [#x1B04] - - [#x1B35] - - [#x1B3B] - - [#x1B3D-#x1B41] - - [#x1B43-#x1B44] - - [#x1B82] - - [#x1BA1] - - [#x1BA6-#x1BA7] - - [#x1BAA] - - [#x1BE7] - - [#x1BEA-#x1BEC] - - [#x1BEE] - - [#x1BF2-#x1BF3] - - [#x1C24-#x1C2B] - - [#x1C34-#x1C35] - - [#x1CE1] - - [#x1CF7] - - [#x302E-#x302F] - - [#xA823-#xA824] - - [#xA827] - - [#xA880-#xA881] - - [#xA8B4-#xA8C3] - - [#xA952-#xA953] - - [#xA983] - - [#xA9B4-#xA9B5] - - [#xA9BA-#xA9BB] - - [#xA9BE-#xA9C0] - - [#xAA2F-#xAA30] - - [#xAA33-#xAA34] - - [#xAA4D] - - [#xAA7B] - - [#xAA7D] - - [#xAAEB] - - [#xAAEE-#xAAEF] - - [#xAAF5] - - [#xABE3-#xABE4] - - [#xABE6-#xABE7] - - [#xABE9-#xABEA] - - [#xABEC] - - -
- -
Mc       ::= [#x903#x93B#x93E-#x940#x949-#x94C#x94E-#x94F#x982-#x983#x9BE-#x9C0#x9C7-#x9C8#x9CB-#x9CC#x9D7#xA03#xA3E-#xA40#xA83#xABE-#xAC0#xAC9#xACB-#xACC#xB02-#xB03#xB3E#xB40#xB47-#xB48#xB4B-#xB4C#xB57#xBBE-#xBBF#xBC1-#xBC2#xBC6-#xBC8#xBCA-#xBCC#xBD7#xC01-#xC03#xC41-#xC44#xC82-#xC83#xCBE#xCC0-#xCC4#xCC7-#xCC8#xCCA-#xCCB#xCD5-#xCD6#xCF3#xD02-#xD03#xD3E-#xD40#xD46-#xD48#xD4A-#xD4C#xD57#xD82-#xD83#xDCF-#xDD1#xDD8-#xDDF#xDF2-#xDF3#xF3E-#xF3F#xF7F#x102B-#x102C#x1031#x1038#x103B-#x103C#x1056-#x1057#x1062-#x1064#x1067-#x106D#x1083-#x1084#x1087-#x108C#x108F#x109A-#x109C#x1715#x1734#x17B6#x17BE-#x17C5#x17C7-#x17C8#x1923-#x1926#x1929-#x192B#x1930-#x1931#x1933-#x1938#x1A19-#x1A1A#x1A55#x1A57#x1A61#x1A63-#x1A64#x1A6D-#x1A72#x1B04#x1B35#x1B3B#x1B3D-#x1B41#x1B43-#x1B44#x1B82#x1BA1#x1BA6-#x1BA7#x1BAA#x1BE7#x1BEA-#x1BEC#x1BEE#x1BF2-#x1BF3#x1C24-#x1C2B#x1C34-#x1C35#x1CE1#x1CF7#x302E-#x302F#xA823-#xA824#xA827#xA880-#xA881#xA8B4-#xA8C3#xA952-#xA953#xA983#xA9B4-#xA9B5#xA9BA-#xA9BB#xA9BE-#xA9C0#xAA2F-#xAA30#xAA33-#xAA34#xAA4D#xAA7B#xAA7D#xAAEB#xAAEE-#xAAEF#xAAF5#xABE3-#xABE4#xABE6-#xABE7#xABE9-#xABEA#xABEC]
-
- Referenced by: -
- - -====================================================================================================================== -Mn -====================================================================================================================== - - -.. raw:: html - - - - - - [#x300-#x36F] - - [#x483-#x487] - - [#x591-#x5BD] - - [#x5BF] - - [#x5C1-#x5C2] - - [#x5C4-#x5C5] - - [#x5C7] - - [#x610-#x61A] - - [#x64B-#x65F] - - [#x670] - - [#x6D6-#x6DC] - - [#x6DF-#x6E4] - - [#x6E7-#x6E8] - - [#x6EA-#x6ED] - - [#x711] - - [#x730-#x74A] - - [#x7A6-#x7B0] - - [#x7EB-#x7F3] - - [#x7FD] - - [#x816-#x819] - - [#x81B-#x823] - - [#x825-#x827] - - [#x829-#x82D] - - [#x859-#x85B] - - [#x898-#x89F] - - [#x8CA-#x8E1] - - [#x8E3-#x902] - - [#x93A] - - [#x93C] - - [#x941-#x948] - - [#x94D] - - [#x951-#x957] - - [#x962-#x963] - - [#x981] - - [#x9BC] - - [#x9C1-#x9C4] - - [#x9CD] - - [#x9E2-#x9E3] - - [#x9FE] - - [#xA01-#xA02] - - [#xA3C] - - [#xA41-#xA42] - - [#xA47-#xA48] - - [#xA4B-#xA4D] - - [#xA51] - - [#xA70-#xA71] - - [#xA75] - - [#xA81-#xA82] - - [#xABC] - - [#xAC1-#xAC5] - - [#xAC7-#xAC8] - - [#xACD] - - [#xAE2-#xAE3] - - [#xAFA-#xAFF] - - [#xB01] - - [#xB3C] - - [#xB3F] - - [#xB41-#xB44] - - [#xB4D] - - [#xB55-#xB56] - - [#xB62-#xB63] - - [#xB82] - - [#xBC0] - - [#xBCD] - - [#xC00] - - [#xC04] - - [#xC3C] - - [#xC3E-#xC40] - - [#xC46-#xC48] - - [#xC4A-#xC4D] - - [#xC55-#xC56] - - [#xC62-#xC63] - - [#xC81] - - [#xCBC] - - [#xCBF] - - [#xCC6] - - [#xCCC-#xCCD] - - [#xCE2-#xCE3] - - [#xD00-#xD01] - - [#xD3B-#xD3C] - - [#xD41-#xD44] - - [#xD4D] - - [#xD62-#xD63] - - [#xD81] - - [#xDCA] - - [#xDD2-#xDD4] - - [#xDD6] - - [#xE31] - - [#xE34-#xE3A] - - [#xE47-#xE4E] - - [#xEB1] - - [#xEB4-#xEBC] - - [#xEC8-#xECE] - - [#xF18-#xF19] - - [#xF35] - - [#xF37] - - [#xF39] - - [#xF71-#xF7E] - - [#xF80-#xF84] - - [#xF86-#xF87] - - [#xF8D-#xF97] - - [#xF99-#xFBC] - - [#xFC6] - - [#x102D-#x1030] - - [#x1032-#x1037] - - [#x1039-#x103A] - - [#x103D-#x103E] - - [#x1058-#x1059] - - [#x105E-#x1060] - - [#x1071-#x1074] - - [#x1082] - - [#x1085-#x1086] - - [#x108D] - - [#x109D] - - [#x135D-#x135F] - - [#x1712-#x1714] - - [#x1732-#x1733] - - [#x1752-#x1753] - - [#x1772-#x1773] - - [#x17B4-#x17B5] - - [#x17B7-#x17BD] - - [#x17C6] - - [#x17C9-#x17D3] - - [#x17DD] - - [#x180B-#x180D] - - [#x180F] - - [#x1885-#x1886] - - [#x18A9] - - [#x1920-#x1922] - - [#x1927-#x1928] - - [#x1932] - - [#x1939-#x193B] - - [#x1A17-#x1A18] - - [#x1A1B] - - [#x1A56] - - [#x1A58-#x1A5E] - - [#x1A60] - - [#x1A62] - - [#x1A65-#x1A6C] - - [#x1A73-#x1A7C] - - [#x1A7F] - - [#x1AB0-#x1ABD] - - [#x1ABF-#x1ACE] - - [#x1B00-#x1B03] - - [#x1B34] - - [#x1B36-#x1B3A] - - [#x1B3C] - - [#x1B42] - - [#x1B6B-#x1B73] - - [#x1B80-#x1B81] - - [#x1BA2-#x1BA5] - - [#x1BA8-#x1BA9] - - [#x1BAB-#x1BAD] - - [#x1BE6] - - [#x1BE8-#x1BE9] - - [#x1BED] - - [#x1BEF-#x1BF1] - - [#x1C2C-#x1C33] - - [#x1C36-#x1C37] - - [#x1CD0-#x1CD2] - - [#x1CD4-#x1CE0] - - [#x1CE2-#x1CE8] - - [#x1CED] - - [#x1CF4] - - [#x1CF8-#x1CF9] - - [#x1DC0-#x1DFF] - - [#x20D0-#x20DC] - - [#x20E1] - - [#x20E5-#x20F0] - - [#x2CEF-#x2CF1] - - [#x2D7F] - - [#x2DE0-#x2DFF] - - [#x302A-#x302D] - - [#x3099-#x309A] - - [#xA66F] - - [#xA674-#xA67D] - - [#xA69E-#xA69F] - - [#xA6F0-#xA6F1] - - [#xA802] - - [#xA806] - - [#xA80B] - - [#xA825-#xA826] - - [#xA82C] - - [#xA8C4-#xA8C5] - - [#xA8E0-#xA8F1] - - [#xA8FF] - - [#xA926-#xA92D] - - [#xA947-#xA951] - - [#xA980-#xA982] - - [#xA9B3] - - [#xA9B6-#xA9B9] - - [#xA9BC-#xA9BD] - - [#xA9E5] - - [#xAA29-#xAA2E] - - [#xAA31-#xAA32] - - [#xAA35-#xAA36] - - [#xAA43] - - [#xAA4C] - - [#xAA7C] - - [#xAAB0] - - [#xAAB2-#xAAB4] - - [#xAAB7-#xAAB8] - - [#xAABE-#xAABF] - - [#xAAC1] - - [#xAAEC-#xAAED] - - [#xAAF6] - - [#xABE5] - - [#xABE8] - - [#xABED] - - [#xFB1E] - - [#xFE00-#xFE0F] - - [#xFE20-#xFE2F] - - -
- -
Mn       ::= [#x300-#x36F#x483-#x487#x591-#x5BD#x5BF#x5C1-#x5C2#x5C4-#x5C5#x5C7#x610-#x61A#x64B-#x65F#x670#x6D6-#x6DC#x6DF-#x6E4#x6E7-#x6E8#x6EA-#x6ED#x711#x730-#x74A#x7A6-#x7B0#x7EB-#x7F3#x7FD#x816-#x819#x81B-#x823#x825-#x827#x829-#x82D#x859-#x85B#x898-#x89F#x8CA-#x8E1#x8E3-#x902#x93A#x93C#x941-#x948#x94D#x951-#x957#x962-#x963#x981#x9BC#x9C1-#x9C4#x9CD#x9E2-#x9E3#x9FE#xA01-#xA02#xA3C#xA41-#xA42#xA47-#xA48#xA4B-#xA4D#xA51#xA70-#xA71#xA75#xA81-#xA82#xABC#xAC1-#xAC5#xAC7-#xAC8#xACD#xAE2-#xAE3#xAFA-#xAFF#xB01#xB3C#xB3F#xB41-#xB44#xB4D#xB55-#xB56#xB62-#xB63#xB82#xBC0#xBCD#xC00#xC04#xC3C#xC3E-#xC40#xC46-#xC48#xC4A-#xC4D#xC55-#xC56#xC62-#xC63#xC81#xCBC#xCBF#xCC6#xCCC-#xCCD#xCE2-#xCE3#xD00-#xD01#xD3B-#xD3C#xD41-#xD44#xD4D#xD62-#xD63#xD81#xDCA#xDD2-#xDD4#xDD6#xE31#xE34-#xE3A#xE47-#xE4E#xEB1#xEB4-#xEBC#xEC8-#xECE#xF18-#xF19#xF35#xF37#xF39#xF71-#xF7E#xF80-#xF84#xF86-#xF87#xF8D-#xF97#xF99-#xFBC#xFC6#x102D-#x1030#x1032-#x1037#x1039-#x103A#x103D-#x103E#x1058-#x1059#x105E-#x1060#x1071-#x1074#x1082#x1085-#x1086#x108D#x109D#x135D-#x135F#x1712-#x1714#x1732-#x1733#x1752-#x1753#x1772-#x1773#x17B4-#x17B5#x17B7-#x17BD#x17C6#x17C9-#x17D3#x17DD#x180B-#x180D#x180F#x1885-#x1886#x18A9#x1920-#x1922#x1927-#x1928#x1932#x1939-#x193B#x1A17-#x1A18#x1A1B#x1A56#x1A58-#x1A5E#x1A60#x1A62#x1A65-#x1A6C#x1A73-#x1A7C#x1A7F#x1AB0-#x1ABD#x1ABF-#x1ACE#x1B00-#x1B03#x1B34#x1B36-#x1B3A#x1B3C#x1B42#x1B6B-#x1B73#x1B80-#x1B81#x1BA2-#x1BA5#x1BA8-#x1BA9#x1BAB-#x1BAD#x1BE6#x1BE8-#x1BE9#x1BED#x1BEF-#x1BF1#x1C2C-#x1C33#x1C36-#x1C37#x1CD0-#x1CD2#x1CD4-#x1CE0#x1CE2-#x1CE8#x1CED#x1CF4#x1CF8-#x1CF9#x1DC0-#x1DFF#x20D0-#x20DC#x20E1#x20E5-#x20F0#x2CEF-#x2CF1#x2D7F#x2DE0-#x2DFF#x302A-#x302D#x3099-#x309A#xA66F#xA674-#xA67D#xA69E-#xA69F#xA6F0-#xA6F1#xA802#xA806#xA80B#xA825-#xA826#xA82C#xA8C4-#xA8C5#xA8E0-#xA8F1#xA8FF#xA926-#xA92D#xA947-#xA951#xA980-#xA982#xA9B3#xA9B6-#xA9B9#xA9BC-#xA9BD#xA9E5#xAA29-#xAA2E#xAA31-#xAA32#xAA35-#xAA36#xAA43#xAA4C#xAA7C#xAAB0#xAAB2-#xAAB4#xAAB7-#xAAB8#xAABE-#xAABF#xAAC1#xAAEC-#xAAED#xAAF6#xABE5#xABE8#xABED#xFB1E#xFE00-#xFE0F#xFE20-#xFE2F]
-
- Referenced by: -
- - -====================================================================================================================== -Nd -====================================================================================================================== - - -.. raw:: html - - - - - - [0-9] - - [#x660-#x669] - - [#x6F0-#x6F9] - - [#x7C0-#x7C9] - - [#x966-#x96F] - - [#x9E6-#x9EF] - - [#xA66-#xA6F] - - [#xAE6-#xAEF] - - [#xB66-#xB6F] - - [#xBE6-#xBEF] - - [#xC66-#xC6F] - - [#xCE6-#xCEF] - - [#xD66-#xD6F] - - [#xDE6-#xDEF] - - [#xE50-#xE59] - - [#xED0-#xED9] - - [#xF20-#xF29] - - [#x1040-#x1049] - - [#x1090-#x1099] - - [#x17E0-#x17E9] - - [#x1810-#x1819] - - [#x1946-#x194F] - - [#x19D0-#x19D9] - - [#x1A80-#x1A89] - - [#x1A90-#x1A99] - - [#x1B50-#x1B59] - - [#x1BB0-#x1BB9] - - [#x1C40-#x1C49] - - [#x1C50-#x1C59] - - [#xA620-#xA629] - - [#xA8D0-#xA8D9] - - [#xA900-#xA909] - - [#xA9D0-#xA9D9] - - [#xA9F0-#xA9F9] - - [#xAA50-#xAA59] - - [#xABF0-#xABF9] - - [#xFF10-#xFF19] - - -
- -
Nd       ::= [0-9#x660-#x669#x6F0-#x6F9#x7C0-#x7C9#x966-#x96F#x9E6-#x9EF#xA66-#xA6F#xAE6-#xAEF#xB66-#xB6F#xBE6-#xBEF#xC66-#xC6F#xCE6-#xCEF#xD66-#xD6F#xDE6-#xDEF#xE50-#xE59#xED0-#xED9#xF20-#xF29#x1040-#x1049#x1090-#x1099#x17E0-#x17E9#x1810-#x1819#x1946-#x194F#x19D0-#x19D9#x1A80-#x1A89#x1A90-#x1A99#x1B50-#x1B59#x1BB0-#x1BB9#x1C40-#x1C49#x1C50-#x1C59#xA620-#xA629#xA8D0-#xA8D9#xA900-#xA909#xA9D0-#xA9D9#xA9F0-#xA9F9#xAA50-#xAA59#xABF0-#xABF9#xFF10-#xFF19]
-
- Referenced by: -
- - -====================================================================================================================== -Pc -====================================================================================================================== - - -.. raw:: html - - - - - - [#x203F-#x2040] - - [#x2054] - - [#xFE33-#xFE34] - - [#xFE4D-#xFE4F] - - [#xFF3F] - - -
- -
Pc       ::= [#x203F-#x2040#x2054#xFE33-#xFE34#xFE4D-#xFE4F#xFF3F]
-
- Referenced by: -
- - -====================================================================================================================== -CJK -====================================================================================================================== - - -.. raw:: html - - - - - - [#xAC00-#xD7A3] - - [#x4E00-#x9FFF] - - -
- -
CJK      ::= [#xAC00-#xD7A3#x4E00-#x9FFF]
-
- - -====================================================================================================================== -ESC -====================================================================================================================== - - -.. raw:: html - - - - - - \ - - n - - t - - b - - r - - f - - \ - - " - - -
- -
ESC      ::= '\' [ntbrf\"]
-
- Referenced by: -
- - -====================================================================================================================== -S_CHAR_LITERAL -====================================================================================================================== - - -.. raw:: html - - - - - - U - - E - - N - - R - - B - - RB - - _utf8 - - q'{ - - . - - }' - - ' - - ESC - \' - - [^'\] - - '' - - [^'] - - ' - - q'( - - . - - )' - - q'[ - - . - - ]' - - q'' - - . - - '' - - -
- - -
         ::= ( [UENRB] | 'RB' | '_utf8' )? ( "'" ( ( ESC | "\'" | [^'\] )* | ( "''" | [^'] )+ ) "'" | "q'{" .* "}'" | "q'(" .* ")'" | "q'[" .* "]'" | "q''" .* "''" )
-
- - -====================================================================================================================== -S_QUOTED_IDENTIFIER -====================================================================================================================== - - -.. raw:: html - - - - - - " - - "" - - [^"#xA#xD] - - " - - $$ - - [^$] - - $$ - - ` - - [^`#xA#xD] - - ` - - [ - - [^#x5D#xA#xD] - - ] - - -
- - -
         ::= '"' ( '""' | [^"#xA#xD] )* '"'
-
           | '$$' [^$]* '$$'
-
           | '`' [^`#xA#xD]+ '`'
-
           | '[' [^#x5D#xA#xD]* ']'
-
- - -====================================================================================================================== -EOF -====================================================================================================================== - - -.. raw:: html - - - - - - $ - - -
- -
EOF      ::= $
-
- Referenced by: -
- - \ No newline at end of file From 454c2f2a2d19325308b6dcaac3ce473c55b03ecc Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 16:42:40 +0700 Subject: [PATCH 08/77] doc: many website cosmetics about Release vs. Snapshot --- .github/workflows/gradle.yml | 2 +- .github/workflows/sphinx.yml | 2 +- build.gradle | 2 +- src/main/resources/rr/xhtml2rst.xsl | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2d4c48c32..93a363013 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -29,7 +29,7 @@ jobs: java-version: '11' distribution: 'temurin' - name: Build with Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@2.4.2 with: arguments: build check # arguments: build check publish diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index e35f98b59..09647ca44 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -21,7 +21,7 @@ jobs: ref: master fetch-depth: 0 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@2.4.2 - name: Run build with Gradle Wrapper run: FLOATING_TOC=false gradle --no-build-cache clean xmldoc sphinx - name: Deploy diff --git a/build.gradle b/build.gradle index 32d5ed8c6..4974164dc 100644 --- a/build.gradle +++ b/build.gradle @@ -161,7 +161,7 @@ tasks.register('xmldoc', Javadoc) { options.doclet = "com.github.markusbernhardt.xmldoclet.XmlDoclet" title = "API $version" options.addBooleanOption("rst", true) - options.addBooleanOption("withFloatingToc", true) + options.addBooleanOption("withFloatingToc", Boolean.parseBoolean(System.getenv().getOrDefault("FLOATING_TOC", "true"))) options.addStringOption("basePackage", "net.sf.jsqlparser") options.addStringOption("filename", outFile.getName()) diff --git a/src/main/resources/rr/xhtml2rst.xsl b/src/main/resources/rr/xhtml2rst.xsl index 666113762..b29b03413 100644 --- a/src/main/resources/rr/xhtml2rst.xsl +++ b/src/main/resources/rr/xhtml2rst.xsl @@ -49,13 +49,13 @@ ********************************************************************* -SQL Syntax JSQLParser- +SQL Syntax |JSQLPARSER_SNAPSHOT_VERSION| |JSQLPARSER_VERSION| ********************************************************************* -The EBNF and Railroad Diagrams for JSQLParser- +The EBNF and Railroad Diagrams for |JSQLPARSER_SNAPSHOT_VERSION| |JSQLPARSER_VERSION| . From be55d269536ab7916a5b5b0550d1e53ad90698f8 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 16:44:56 +0700 Subject: [PATCH 09/77] build: fix GH action's version --- .github/workflows/gradle.yml | 2 +- .github/workflows/sphinx.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 93a363013..2bb2eac4a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -29,7 +29,7 @@ jobs: java-version: '11' distribution: 'temurin' - name: Build with Gradle - uses: gradle/gradle-build-action@2.4.2 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: build check # arguments: build check publish diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 09647ca44..ccbe94dc7 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -21,7 +21,7 @@ jobs: ref: master fetch-depth: 0 - name: Setup Gradle - uses: gradle/gradle-build-action@2.4.2 + uses: gradle/gradle-build-action@v2.4.2 - name: Run build with Gradle Wrapper run: FLOATING_TOC=false gradle --no-build-cache clean xmldoc sphinx - name: Deploy From 08c9fe62840a4b3eb02e9b0a9e4b90e19df88819 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 2 Sep 2023 17:08:44 +0700 Subject: [PATCH 10/77] doc: update the README --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 61316bea2..f3449956a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [JSqlParser (4.6 Stable or 4.7 Snapshot)](https://jsqlparser.github.io/JSqlParser) drawing +# [JSqlParser 4.7 Website](https://jsqlparser.github.io/JSqlParser) drawing ![Build Status](https://github.com/JSQLParser/JSqlParser/actions/workflows/maven.yml/badge.svg) @@ -54,9 +54,9 @@ Assertions.assertEquals("b", b.getColumnName()); **JSqlParser** aims to support the SQL standard as well as all major RDBMS. Any missing syntax or features can be added on demand. -| RDBMS | Statements | -|------------------------------------|-----------------------------------------| -| Oracle
MS SQL Server and Sybase
PostgreSQL
MySQL and MariaDB
DB2
H2 and HSQLDB and Derby
SQLite| `SELECT`
`INSERT`, `UPDATE`, `UPSERT`, `MERGE`
`DELETE`, `TRUNCATE TABLE`
`CREATE ...`, `ALTER ....`, `DROP ...`
`WITH ...` | +| RDBMS | Statements | +|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| Oracle
MS SQL Server and Sybase
Postgres
MySQL and MariaDB
DB2
H2 and HSQLDB and Derby
SQLite | `SELECT`
`INSERT`, `UPDATE`, `UPSERT`, `MERGE`
`DELETE`, `TRUNCATE TABLE`
`CREATE ...`, `ALTER ....`, `DROP ...`
`WITH ...` | **JSqlParser** can also be used to create SQL Statements from Java Code with a fluent API (see [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#build-a-sql-statements)). @@ -65,12 +65,11 @@ Assertions.assertEquals("b", b.getColumnName()); [**General SQL Parser**](http://www.sqlparser.com/features/introduce.php?utm_source=github-jsqlparser&utm_medium=text-general) looks pretty good, with extended SQL syntax (like PL/SQL and T-SQL) and java + .NET APIs. The tool is commercial (license available online), with a free download option. ## [Documentation](https://jsqlparser.github.io/JSqlParser) - -### [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#parse-a-sql-statements) -### [Build Instructions](https://jsqlparser.github.io/JSqlParser/usage.html) -### [Contribution](https://jsqlparser.github.io/JSqlParser/contribution.html) -### [Change Log](https://jsqlparser.github.io/JSqlParser/changelog.html#latest-changes-since-jsqlparser-version) -### [Issues](https://github.com/JSQLParser/JSqlParser/issues) + 1. [Samples](https://jsqlparser.github.io/JSqlParser/usage.html#parse-a-sql-statements) + 2. [Build Instructions](https://jsqlparser.github.io/JSqlParser/usage.html) and [Maven Artifact](https://jsqlparser.github.io/JSqlParser/usage.html#build-dependencies) + 3. [Contribution](https://jsqlparser.github.io/JSqlParser/contribution.html) + 4. [Change Log](https://jsqlparser.github.io/JSqlParser/changelog.html#latest-changes-since-jsqlparser-version) + 5. [Issues](https://github.com/JSQLParser/JSqlParser/issues) ## License From ecd40386585a5197a38e17a08781d40248eea030 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Tue, 5 Sep 2023 17:22:25 +0700 Subject: [PATCH 11/77] fix: allow `RAW` as `CreateParameter` - fixes #1858 --- src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../sf/jsqlparser/statement/create/CreateTableTest.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index f0dfae767..17d300637 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -5645,7 +5645,7 @@ List CreateParameter(): | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk= + | tk= | tk= | tk="=" ) { param.add(tk.image); } diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java index fd5b8d3c0..dbe046e1b 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java @@ -1043,4 +1043,12 @@ void testCreateTableWithNextValueFor() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + @Test + void testIssue1858() throws JSQLParserException { + String sqlStr = "CREATE TABLE \"foo\"\n" + + "(\n" + + " event_sk bigint identity NOT NULL encode RAW\n" + + ") compound sortkey ( date_key )"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } From daee30f7ae88bea16b721ef8bd3d82f932cf55e5 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Wed, 6 Sep 2023 18:04:02 +0700 Subject: [PATCH 12/77] fix: optional `Expression` in `FETCH` clause - fixes #1859 --- .../sf/jsqlparser/statement/select/Fetch.java | 55 +++++++++++++++++-- .../util/deparser/SelectDeParser.java | 9 ++- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 39 +++++++++++-- .../statement/select/FetchTest.java | 6 ++ .../statement/select/SelectTest.java | 39 ++++++++----- 5 files changed, 120 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java b/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java index 1bf4c1eba..f4590d7df 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Fetch.java @@ -9,14 +9,19 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.JdbcParameter; +import net.sf.jsqlparser.expression.LongValue; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class Fetch implements Serializable { private Expression expression = null; private boolean isFetchParamFirst = false; - private String fetchParam = "ROW"; + private final List fetchParameters = new ArrayList<>(); @Deprecated public long getRowCount() { @@ -46,8 +51,22 @@ public JdbcParameter getFetchJdbcParameter() { return expression instanceof JdbcParameter ? (JdbcParameter) expression : null; } + public Fetch addFetchParameter(String parameter) { + fetchParameters.add(parameter); + return this; + } + + public List getFetchParameters() { + return this.fetchParameters; + } + + @Deprecated public String getFetchParam() { - return fetchParam; + String parameterStr = ""; + for (String p : fetchParameters) { + parameterStr += " " + p; + } + return parameterStr.trim(); } public boolean isFetchParamFirst() { @@ -59,30 +78,54 @@ public void setFetchJdbcParameter(JdbcParameter jdbc) { this.setExpression(jdbc); } + @Deprecated public void setFetchParam(String s) { - this.fetchParam = s; + fetchParameters.clear(); + if (s != null) { + fetchParameters.addAll(Arrays.asList(s.trim().split("\\s+"))); + } } public void setFetchParamFirst(boolean b) { this.isFetchParamFirst = b; } + public StringBuilder appendTo(StringBuilder builder) { + builder.append(" FETCH"); + if (isFetchParamFirst) { + builder.append(" FIRST"); + } else { + builder.append(" NEXT"); + } + + if (expression != null) { + builder.append(" ").append(expression); + } + + for (String s : fetchParameters) { + builder.append(" ").append(s); + } + return builder; + } + @Override public String toString() { - return " FETCH " + (isFetchParamFirst ? "FIRST" : "NEXT") + " " + expression.toString() - + " " + fetchParam + " ONLY"; + return appendTo(new StringBuilder()).toString(); } + @Deprecated public Fetch withRowCount(long rowCount) { this.setRowCount(rowCount); return this; } + @Deprecated public Fetch withFetchJdbcParameter(JdbcParameter fetchJdbcParameter) { this.setFetchJdbcParameter(fetchJdbcParameter); return this; } + @Deprecated public Fetch withFetchParam(String fetchParam) { this.setFetchParam(fetchParam); return this; 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 0ba071794..9a1f6f9cb 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -419,16 +419,19 @@ public void visit(Offset offset) { } public void visit(Fetch fetch) { - // FETCH (FIRST | NEXT) row_count (ROW | ROWS) ONLY buffer.append(" FETCH "); if (fetch.isFetchParamFirst()) { buffer.append("FIRST "); } else { buffer.append("NEXT "); } - fetch.getExpression().accept(expressionVisitor); - buffer.append(" ").append(fetch.getFetchParam()).append(" ONLY"); + if (fetch.getExpression() != null) { + fetch.getExpression().accept(expressionVisitor); + } + for (String p : fetch.getFetchParameters()) { + buffer.append(" ").append(p); + } } public ExpressionVisitor getExpressionVisitor() { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 17d300637..93ecfc537 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3075,13 +3075,44 @@ Fetch Fetch(): Fetch fetch = new Fetch(); Token token = null; Expression expression; + List fetchParameters = new ArrayList(); } { ( - // sqlserver-oracle-> FETCH (FIRST | NEXT) expression (ROW | ROWS) ONLY - ( { fetch.setFetchParamFirst(true); } | ) - expression = Expression() { fetch.setExpression(expression); } - ( { fetch.setFetchParam("ROWS"); } | ) + LOOKAHEAD(3) ( + ( { fetch.setFetchParamFirst(true); } | ) + ( + { fetch.addFetchParameter("ROWS"); } + | + { fetch.addFetchParameter("ROW"); } + ) + ( + { fetch.addFetchParameter("ONLY"); } + | + { fetch.addFetchParameter("WITH TIES"); } + ) + ) + | + ( + ( { fetch.setFetchParamFirst(true); } | ) + + // Expression is optional according to https://www.h2database.com/html/commands.html#select + expression = Expression() { fetch.setExpression(expression); } + [ { fetch.addFetchParameter("PERCENT"); } ] + ( + ( + { fetch.addFetchParameter("ROWS"); } + | + { fetch.addFetchParameter("ROW"); } + ) + + ( + { fetch.addFetchParameter("ONLY"); } + | + { fetch.addFetchParameter("WITH TIES"); } + ) + ) + ) ) { return fetch; diff --git a/src/test/java/net/sf/jsqlparser/statement/select/FetchTest.java b/src/test/java/net/sf/jsqlparser/statement/select/FetchTest.java index 8ccbd2bb8..0d97006ec 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/FetchTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/FetchTest.java @@ -32,4 +32,10 @@ void getExpression() throws JSQLParserException { Fetch fetch = plainSelect.getFetch(); Assertions.assertInstanceOf(ParenthesedSelect.class, fetch.getExpression()); } + + @Test + void testFetchWithoutExpressionIssue1859() throws JSQLParserException { + String sqlStr = "select 1 from test.dual fetch first row only"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } 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 afba71f3a..9dea9eaaa 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -578,14 +578,17 @@ public void testLimitSqlServer1() throws JSQLParserException { Select select = (Select) parserManager.parse(new StringReader(statement)); assertNotNull(select.getOffset()); + assertEquals("3", + select.getOffset().getOffset().toString()); assertEquals("ROWS", select.getOffset().getOffsetParam()); + assertNotNull(select.getFetch()); - assertEquals("ROWS", select.getFetch().getFetchParam()); assertFalse(select.getFetch().isFetchParamFirst()); - assertNull(select.getFetch().getFetchJdbcParameter()); - assertEquals("3", - select.getOffset().getOffset().toString()); - assertEquals(5, select.getFetch().getRowCount()); + assertEquals("5", select.getFetch().getExpression().toString()); + org.assertj.core.api.Assertions + .assertThat(select.getFetch().getFetchParameters()) + .containsExactly("ROWS", "ONLY"); + assertStatementCanBeDeparsedAs(select, statement); } @@ -598,13 +601,15 @@ public void testLimitSqlServer2() throws JSQLParserException { Select select = (Select) parserManager.parse(new StringReader(statement)); assertNotNull(select.getOffset()); - assertNotNull(select.getFetch()); assertEquals("ROW", select.getOffset().getOffsetParam()); - assertEquals("ROW", select.getFetch().getFetchParam()); + + assertNotNull(select.getFetch()); assertTrue(select.getFetch().isFetchParamFirst()); - assertEquals(new LongValue(3), - select.getOffset().getOffset()); - assertEquals(5, select.getFetch().getRowCount()); + assertEquals("5", select.getFetch().getExpression().toString()); + org.assertj.core.api.Assertions + .assertThat(select.getFetch().getFetchParameters()) + .containsExactly("ROW", "ONLY"); + assertStatementCanBeDeparsedAs(select, statement); } @@ -634,9 +639,11 @@ public void testLimitSqlServer4() throws JSQLParserException { assertNull(select.getOffset()); assertNotNull(select.getFetch()); - assertEquals("ROWS", select.getFetch().getFetchParam()); assertFalse(select.getFetch().isFetchParamFirst()); - assertEquals(5, select.getFetch().getRowCount()); + assertEquals("5", select.getFetch().getExpression().toString()); + org.assertj.core.api.Assertions + .assertThat(select.getFetch().getFetchParameters()) + .containsExactly("ROWS", "ONLY"); assertStatementCanBeDeparsedAs(select, statement); } @@ -650,12 +657,14 @@ public void testLimitSqlServerJdbcParameters() throws JSQLParserException { assertNotNull(select.getOffset()); assertEquals("ROWS", select.getOffset().getOffsetParam()); assertNotNull(select.getFetch()); - assertEquals("ROWS", select.getFetch().getFetchParam()); assertFalse(select.getFetch().isFetchParamFirst()); + assertEquals("?", select.getFetch().getExpression().toString()); + org.assertj.core.api.Assertions + .assertThat(select.getFetch().getFetchParameters()) + .containsExactly("ROWS", "ONLY"); assertEquals("?", select.getOffset().getOffset().toString()); - assertEquals("?", select.getFetch().getFetchJdbcParameter() - .toString()); + assertStatementCanBeDeparsedAs(select, statement); } From 1a6bb7a379bdd22f528f192d1c889dc576c63c8d Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Wed, 6 Sep 2023 18:04:38 +0700 Subject: [PATCH 13/77] doc: write about unsupported SQL syntax --- src/site/sphinx/index.rst | 1 + src/site/sphinx/unsupported.rst | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/site/sphinx/unsupported.rst diff --git a/src/site/sphinx/index.rst b/src/site/sphinx/index.rst index 18569eaab..916e4c07b 100644 --- a/src/site/sphinx/index.rst +++ b/src/site/sphinx/index.rst @@ -15,6 +15,7 @@ Java SQL Parser Library migration SQL Grammar Stable SQL Grammar Snapshot + Unsupported Grammar Java API Stable Java API Snapshot keywords diff --git a/src/site/sphinx/unsupported.rst b/src/site/sphinx/unsupported.rst new file mode 100644 index 000000000..b6489a84a --- /dev/null +++ b/src/site/sphinx/unsupported.rst @@ -0,0 +1,57 @@ +*************************************** +Unsupported Grammar of various RDBMS +*************************************** + +*JSQLParser* is a RDBMS agnostic parser with a certain focus on SQL:2016 Standard compliant Queries and the "Big Four" (Oracle, MS SQL Server, Postgres, MySQL/MariaDB). +We would like to recommend writing portable, standard compliant SQL in general. + +- Postgres Implicit cast is not supported. + + .. code-block:: java + + SELECT date '2022-12-31'; + SELECT double precision 1; + + +- Oracle PL/SQL blocks are not support. + + .. code-block:: sql + + DECLARE + num NUMBER; + BEGIN + num := 10; + dbms_output.put_line('The number is ' || num); + END; + + + +- Oracle `INSERT ALL ...` is not supported + + .. code-block:: sql + + INSERT ALL + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + INTO mytable (column1, column2, column_n) VALUES (expr1, expr2, expr_n) + SELECT * FROM dual; + +- DDL statements + + While *JSQLParser* provides a lot of generic support for DDL statements, it is possible that certain RDBMS specific syntax (especially about indices, encodings, compression) won't be supported. + +- `JSON` or `XML` specific syntax and functions + + While *JSQLParser* provides a lot of generic support for `JSON` or `XML` processing, it is possible that certain RDBMS specific syntax or functions won't be supported. + +- Interval Operators + + Anything like `DAY HOUR MINUTE SECOND [TO HOUR MINUTE SECOND]` is not supported.: + + .. code-block:: sql + + values cast ((time '12:03:34' - time '11:57:23') minute to second as varchar(8)); + + + + From da13d7dc1dd1608bb38469b100aeb879cce354d8 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 11 Sep 2023 14:39:37 +0700 Subject: [PATCH 14/77] feat: `CREATE INDEX IF NOT EXISTS...` - fixes #1861 --- .../statement/create/index/CreateIndex.java | 20 +++++++- .../util/deparser/CreateIndexDeParser.java | 11 +++-- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 12 ++--- .../statement/create/CreateIndexTest.java | 48 +++++++++++-------- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java index 806eda9a6..dfec6e930 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java @@ -22,6 +22,17 @@ public class CreateIndex implements Statement { private Index index; private List tailParameters; + public boolean isUsingIfNotExists() { + return usingIfNotExists; + } + + public CreateIndex setUsingIfNotExists(boolean usingIfNotExists) { + this.usingIfNotExists = usingIfNotExists; + return this; + } + + private boolean usingIfNotExists = false; + @Override public void accept(StatementVisitor statementVisitor) { statementVisitor.visit(this); @@ -63,6 +74,9 @@ public String toString() { } buffer.append("INDEX "); + if (usingIfNotExists) { + buffer.append("IF NOT EXISTS "); + } buffer.append(index.getName()); buffer.append(" ON "); buffer.append(table.getFullyQualifiedName()); @@ -77,8 +91,10 @@ public String toString() { buffer.append( index.getColumns().stream() - .map(cp -> cp.columnName + (cp.getParams() != null ? " " + String.join(" ", cp.getParams()) : "")).collect(joining(", ")) - ); + .map(cp -> cp.columnName + (cp.getParams() != null + ? " " + String.join(" ", cp.getParams()) + : "")) + .collect(joining(", "))); buffer.append(")"); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java index 0f2aae236..5a292b283 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java @@ -9,11 +9,11 @@ */ package net.sf.jsqlparser.util.deparser; -import static java.util.stream.Collectors.joining; - import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.Index; +import static java.util.stream.Collectors.joining; + public class CreateIndexDeParser extends AbstractDeParser { public CreateIndexDeParser(StringBuilder buffer) { @@ -32,6 +32,9 @@ public void deParse(CreateIndex createIndex) { } buffer.append("INDEX "); + if (createIndex.isUsingIfNotExists()) { + buffer.append("IF NOT EXISTS "); + } buffer.append(index.getName()); buffer.append(" ON "); buffer.append(createIndex.getTable().getFullyQualifiedName()); @@ -45,7 +48,9 @@ public void deParse(CreateIndex createIndex) { if (index.getColumnsNames() != null) { buffer.append(" ("); buffer.append(index.getColumnWithParams().stream() - .map(cp -> cp.columnName + (cp.getParams() != null ? " " + String.join(" ", cp.getParams()) : "")) + .map(cp -> cp.columnName + + (cp.getParams() != null ? " " + String.join(" ", cp.getParams()) + : "")) .collect(joining(", "))); buffer.append(")"); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 93ecfc537..f933ffef9 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -5167,10 +5167,8 @@ CreateIndex CreateIndex(): CreateIndex createIndex = new CreateIndex(); Table table = null; List colNames; - //Token columnName; Token using; Index index = null; - //String name = null; List parameter = new ArrayList(); List tailParameters = new ArrayList(); List name; @@ -5178,18 +5176,14 @@ CreateIndex CreateIndex(): { [ parameter=CreateParameter() ] - index = Index() { index.setType(parameter.isEmpty()?null:parameter.get(0)); } + + [ LOOKAHEAD(2) { createIndex.setUsingIfNotExists(true);} ] + index = Index() { index.setType(parameter.isEmpty() ? null : parameter.get(0)); } table=Table() - [ using= {index.setUsing(using.image);} ] - colNames = ColumnNamesWithParamsList() - - /* [ tailParameter = CreateParameter() {} ] */ - ( parameter=CreateParameter() { tailParameters.addAll(parameter); } )* - { index.setColumns(colNames); createIndex.setIndex(index); diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java index a159215b7..9901a8488 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java @@ -9,15 +9,17 @@ */ package net.sf.jsqlparser.statement.create; -import java.io.StringReader; -import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.statement.create.index.CreateIndex; -import static net.sf.jsqlparser.test.TestUtils.*; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; +import java.util.List; + +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import org.junit.jupiter.api.Test; public class CreateIndexTest { @@ -25,8 +27,7 @@ public class CreateIndexTest { @Test public void testCreateIndex() throws JSQLParserException { - String statement - = "CREATE INDEX myindex ON mytab (mycol, mycol2)"; + String statement = "CREATE INDEX myindex ON mytab (mycol, mycol2)"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(2, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex", createIndex.getIndex().getName()); @@ -38,8 +39,7 @@ public void testCreateIndex() throws JSQLParserException { @Test public void testCreateIndex2() throws JSQLParserException { - String statement - = "CREATE mytype INDEX myindex ON mytab (mycol, mycol2)"; + String statement = "CREATE mytype INDEX myindex ON mytab (mycol, mycol2)"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(2, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex", createIndex.getIndex().getName()); @@ -51,8 +51,7 @@ public void testCreateIndex2() throws JSQLParserException { @Test public void testCreateIndex3() throws JSQLParserException { - String statement - = "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2, mycol3)"; + String statement = "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2, mycol3)"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(3, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex", createIndex.getIndex().getName()); @@ -63,8 +62,7 @@ public void testCreateIndex3() throws JSQLParserException { @Test public void testCreateIndex4() throws JSQLParserException { - String statement - = "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2 (75), mycol3)"; + String statement = "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2 (75), mycol3)"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(3, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex", createIndex.getIndex().getName()); @@ -75,8 +73,8 @@ public void testCreateIndex4() throws JSQLParserException { @Test public void testCreateIndex5() throws JSQLParserException { - String statement - = "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2 (75), mycol3) mymodifiers"; + String statement = + "CREATE mytype INDEX myindex ON mytab (mycol ASC, mycol2 (75), mycol3) mymodifiers"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(3, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex", createIndex.getIndex().getName()); @@ -93,8 +91,7 @@ public void testCreateIndex6() throws JSQLParserException { @Test public void testCreateIndex7() throws JSQLParserException { - String statement - = "CREATE INDEX myindex1 ON mytab USING GIST (mycol)"; + String statement = "CREATE INDEX myindex1 ON mytab USING GIST (mycol)"; CreateIndex createIndex = (CreateIndex) parserManager.parse(new StringReader(statement)); assertEquals(1, createIndex.getIndex().getColumnsNames().size()); assertEquals("myindex1", createIndex.getIndex().getName()); @@ -108,23 +105,25 @@ public void testCreateIndex7() throws JSQLParserException { @Test public void testCreateIndexIssue633() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("CREATE INDEX idx_american_football_action_plays_1 ON american_football_action_plays USING btree (play_type)"); + assertSqlCanBeParsedAndDeparsed( + "CREATE INDEX idx_american_football_action_plays_1 ON american_football_action_plays USING btree (play_type)"); } @Test public void testFullIndexNameIssue936() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("CREATE INDEX \"TS\".\"IDX\" ON \"TEST\" (\"ID\" ASC) TABLESPACE \"TS\""); + assertSqlCanBeParsedAndDeparsed( + "CREATE INDEX \"TS\".\"IDX\" ON \"TEST\" (\"ID\" ASC) TABLESPACE \"TS\""); } @Test public void testFullIndexNameIssue936_2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("CREATE INDEX \"TS\".\"IDX\" ON \"TEST\" (\"ID\") TABLESPACE \"TS\""); + assertSqlCanBeParsedAndDeparsed( + "CREATE INDEX \"TS\".\"IDX\" ON \"TEST\" (\"ID\") TABLESPACE \"TS\""); } @Test public void testCreateIndexTrailingOptions() throws JSQLParserException { - String statement - = "CREATE UNIQUE INDEX cfe.version_info_idx2\n" + String statement = "CREATE UNIQUE INDEX cfe.version_info_idx2\n" + " ON cfe.version_info ( major_version\n" + " , minor_version\n" + " , patch_level ) parallel compress nologging\n" @@ -136,4 +135,11 @@ public void testCreateIndexTrailingOptions() throws JSQLParserException { assertEquals(tailParameters.get(1), "compress"); assertEquals(tailParameters.get(2), "nologging"); } + + @Test + void testIfNotExistsIssue1861() throws JSQLParserException { + String sqlStr = + "CREATE INDEX IF NOT EXISTS test_test_idx ON test.test USING btree (\"time\")"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } From 6955c4391e65a33ab09ae9b9cc68bcde33b3fa6b Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Tue, 22 Nov 2016 21:31:26 +0530 Subject: [PATCH 15/77] Support for postgres overlap operator && added, natural left/right/full outer joins added --- .../expression/ExpressionVisitor.java | 5 +- .../expression/ExpressionVisitorAdapter.java | 6 + .../operators/relational/DoubleAnd.java | 41 + .../sf/jsqlparser/util/TablesNamesFinder.java | 8 + .../util/deparser/ExpressionDeParser.java | 7 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 157 +- .../sf/jsqlparser/test/select/SelectTest.java | 2289 +++++++++++++++++ 7 files changed, 2436 insertions(+), 77 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java create mode 100644 src/test/java/net/sf/jsqlparser/test/select/SelectTest.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index b1bc3ffe5..b35bcb792 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -43,6 +43,7 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;//Added by mathew on 21st Nov 2016 import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; @@ -123,7 +124,9 @@ public interface ExpressionVisitor { void visit(MinorThanEquals minorThanEquals); - void visit(NotEqualsTo notEqualsTo); + void visit(NotEqualsTo notEqualsTo); + + void visit(DoubleAnd doubleAnd);//Added by mathew on 21st Nov 2016 void visit(ParenthesedSelect selectBody); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 1da72c3d2..60881666f 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -254,6 +254,12 @@ public void visit(MinorThanEquals expr) { public void visit(NotEqualsTo expr) { visitBinaryExpression(expr); } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(DoubleAnd expr) { + visitBinaryExpression(expr); + } @Override public void visit(Column column) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java new file mode 100644 index 000000000..8b531cafd --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java @@ -0,0 +1,41 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 1st Aug 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class DoubleAnd extends ComparisonOperator { + + public DoubleAnd() { + super("&&"); + } + + public DoubleAnd(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index d8fc4cf7b..16e2caf50 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -469,6 +469,14 @@ public void visit(Multiplication multiplication) { public void visit(NotEqualsTo notEqualsTo) { visitBinaryExpression(notEqualsTo); } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(DoubleAnd doubleAnd) { + visitBinaryExpression(doubleAnd); + } @Override public void visit(NullValue nullValue) { 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 ee6a94a56..a1e7f8e9d 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -401,6 +401,13 @@ public void visit(NotEqualsTo notEqualsTo) { " " + notEqualsTo.getStringExpression() + " "); } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(DoubleAnd doubleAnd) { + visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); + + } @Override public void visit(NullValue nullValue) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 301b4f629..0e12a67ed 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -182,7 +182,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | -| +| | | | @@ -413,8 +413,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | -| @@ -482,10 +482,13 @@ TOKEN : /* Statement Separators */ TOKEN : /* Operators */ { " ()* "="> -| )* "="> -| )* ">"> -| )* "="> -| )* "|"> +| )* "="> +| )* ">"> +| )* "="> +| )* "|"> +| )* ">"> +| /*added by mathew on 21st Nov 2016*/ } TOKEN : /* Date/Time with time zones */ @@ -600,7 +603,7 @@ TOKEN: Statement Statement() #Statement: -{ +{ IfElseStatement ifElseStatement = null; Statement stm = null; Statement stm2 = null; @@ -609,10 +612,10 @@ Statement Statement() #Statement: { try { ( - condition=Condition() + condition=Condition() ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] - [ LOOKAHEAD(2) + [ LOOKAHEAD(2) ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } [ { ifElseStatement.setUsingSemicolonForElseStatement(true); }] ] @@ -641,7 +644,7 @@ Statement Statement() #Statement: } Statement SingleStatement() : -{ +{ Statement stm = null; List with = null; } @@ -752,7 +755,7 @@ Block Block() #Block : { error_skipto(ST_SEMICOLON); } else { throw e; - } + } } { @@ -778,9 +781,9 @@ Statements Statements() #Statements: { try { ( ( - condition=Condition() + condition=Condition() ( stm = SingleStatement() | stm = Block() ) { ifElseStatement = new IfElseStatement(condition, stm); } - [ LOOKAHEAD(2) + [ LOOKAHEAD(2) [ { ifElseStatement.setUsingSemicolonForIfStatement(true); } ] ( stm2 = SingleStatement() | stm2 = Block() ) { ifElseStatement.setElseStatement(stm2); } ] @@ -789,7 +792,7 @@ Statements Statements() #Statements: { ) | ( - stm = SingleStatement() + stm = SingleStatement() | stm = Block() [ LOOKAHEAD(2) ] @@ -974,11 +977,11 @@ RenameTableStatement RenameTableStatement(): { Token token; } { - + [ LOOKAHEAD(2) { usingTableKeyword = true; } ] [ LOOKAHEAD(2) { usesIfExistsKeyword = true; } ] oldName = Table() - [ ( + [ ( token= { waitDirective = "WAIT " + token.image; } | { waitDirective = "NOWAIT"; } @@ -986,8 +989,8 @@ RenameTableStatement RenameTableStatement(): { newName = Table() - { - renameTableStatement = new RenameTableStatement(oldName, newName, usingTableKeyword, usesIfExistsKeyword, waitDirective); + { + renameTableStatement = new RenameTableStatement(oldName, newName, usingTableKeyword, usesIfExistsKeyword, waitDirective); } ( @@ -1000,7 +1003,7 @@ RenameTableStatement RenameTableStatement(): { } )* - { + { return renameTableStatement; } } @@ -1013,21 +1016,21 @@ PurgeStatement PurgeStatement(): { Token userToken = null; } { - + ( table=Table() { purgeStatement = new PurgeStatement(table); } - | + | index=Index() { purgeStatement = new PurgeStatement(index); } | { purgeStatement = new PurgeStatement(PurgeObjectType.RECYCLEBIN); } | { purgeStatement = new PurgeStatement(PurgeObjectType.DBA_RECYCLEBIN); } | - tableSpaceToken= [ userToken= ] { + tableSpaceToken= [ userToken= ] { purgeStatement = new PurgeStatement( PurgeObjectType.TABLESPACE , tableSpaceToken.image - , userToken!=null ? userToken.image : null); + , userToken!=null ? userToken.image : null); } ) @@ -1747,7 +1750,7 @@ The following tokens are allowed as Names for Schema, Table, Column and Aliases String RelObjectNameWithoutValue() : { Token tk = null; } { - ( tk= | tk= | tk= | tk= | tk= | tk= | tk= + ( 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="INTERPRET" | 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="REPEATABLE" | 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="SEED" | 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; } } @@ -3377,19 +3380,21 @@ Expression RegularCondition() #RegularCondition: [ "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ] - ( LOOKAHEAD(2) - ">" { result = new GreaterThan(); } - | "<" { result = new MinorThan(); } - | "=" { result = new EqualsTo(); } - | token= { result = new GreaterThanEquals(token.image); } - | token= { result = new MinorThanEquals(token.image); } - | token= { result = new NotEqualsTo(token.image); } - | token= { result = new NotEqualsTo(token.image); } - | "@@" { result = new Matches(); } - | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } - | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } - | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } - | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } + ( LOOKAHEAD(2) + ">" { result = new GreaterThan(); } + | "<" { result = new MinorThan(); } + | "=" { result = new EqualsTo(); } + | token= { result = new GreaterThanEquals(token.image); } + | token= { result = new MinorThanEquals(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | token= { result = new DoubleAnd(token.image); } + | "@@" { result = new Matches(); } + | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } + | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } + | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } + | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } + | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } | "@>" { result = new JsonOperator("@>"); } | "<@" { result = new JsonOperator("<@"); } @@ -5076,9 +5081,9 @@ XMLSerializeExpr XMLSerializeExpr(): { ColDataType dataType; } { - - "(" - "(" + + "(" + "(" "(" expression=SimpleExpression() ")" [ orderByElements=OrderByElements() ] ")" @@ -5161,7 +5166,7 @@ List ColumnNamesWithParamsList() : { Index Index(): { List name; -} +} { name= RelObjectNameList() { return new Index().withName(name).withType(""); } } @@ -5321,8 +5326,8 @@ CreateTable CreateTable(boolean isUsingOrReplace): [ LOOKAHEAD(2) { createTable.setIfNotExists(true); }] table=Table() - [ LOOKAHEAD(2) ( - LOOKAHEAD(3) + [ LOOKAHEAD(2) ( + LOOKAHEAD(3) ("(" tableColumn=RelObjectName() { columns.add(tableColumn); } ("," tableColumn=RelObjectName() { columns.add(tableColumn); } )* ")") | ("(" @@ -5534,8 +5539,8 @@ ColDataType ColDataType(): | tk= | tk= | tk= | tk= | tk= | tk= ) [ "." (tk2= | tk2=) ] { if (tk2!=null) colDataType.setDataType(tk.image + "." + tk2.image); else colDataType.setDataType(tk.image); } - | tk= [LOOKAHEAD(2) tk2=] - { if (tk2!=null) colDataType.setDataType(tk.image + " " + tk2.image); else colDataType.setDataType(tk.image); } + | tk= [LOOKAHEAD(2) tk2=] + { if (tk2!=null) colDataType.setDataType(tk.image + " " + tk2.image); else colDataType.setDataType(tk.image); } | LOOKAHEAD(2) tk= tk2= {colDataType.setDataType(tk.image + " " + tk2.image);} | tk= { colDataType.setDataType(tk.image);} ) @@ -6266,12 +6271,12 @@ AlterExpression AlterExpression(): tk= { alterExp.setCommentText(tk.image); } ) | - tokens = captureRest() { + tokens = captureRest() { alterExp.setOperation(AlterOperation.UNSPECIFIC); StringBuilder optionalSpecifier = new StringBuilder(); int i=0; - for (String s: tokens) + for (String s: tokens) if (! (s.equals(";") || s.equals("\n\n\n")) ) { if (i>0) optionalSpecifier.append( " " ); @@ -6366,18 +6371,18 @@ AlterSession AlterSession(): } { ( - ( + ( ( { operation = AlterSessionOperation.ADVISE_COMMIT; } | { operation = AlterSessionOperation.ADVISE_ROLLBACK; } | { operation = AlterSessionOperation.ADVISE_NOTHING; } ) ) | - ( + ( { operation = AlterSessionOperation.CLOSE_DATABASE_LINK; } ) - | - ( + | + ( ( { operation = AlterSessionOperation.ENABLE_COMMIT_IN_PROCEDURE; } | { operation = AlterSessionOperation.ENABLE_GUARD; } | ( { operation = AlterSessionOperation.ENABLE_PARALLEL_DML; } @@ -6388,7 +6393,7 @@ AlterSession AlterSession(): ) ) | - ( + ( ( { operation = AlterSessionOperation.DISABLE_COMMIT_IN_PROCEDURE; } | { operation = AlterSessionOperation.DISABLE_GUARD; } | ( { operation = AlterSessionOperation.DISABLE_PARALLEL_DML; } @@ -6399,19 +6404,19 @@ AlterSession AlterSession(): ) ) | - ( + ( ( { operation = AlterSessionOperation.FORCE_PARALLEL_DML; } | { operation = AlterSessionOperation.FORCE_PARALLEL_DDL; } | { operation = AlterSessionOperation.FORCE_PARALLEL_QUERY; } ) ) | - ( + ( { operation = AlterSessionOperation.SET; } ) ) - ( ( token = + ( ( token = | token = | token = "=" | token = @@ -6432,37 +6437,37 @@ AlterSystemStatement AlterSystemStatement(): } { ( - ( + ( "ARCHIVE" "LOG" { operation = AlterSystemOperation.ARCHIVE_LOG; } ) | - ( + ( "CHECKPOINT" { operation = AlterSystemOperation.CHECKPOINT; } ) | - ( + ( "DUMP" "ACTIVE" "SESSION" "HISTORY" { operation = AlterSystemOperation.DUMP_ACTIVE_SESSION_HISTORY; } ) - | - ( - ( + | + ( + ( "DISTRIBUTED RECOVERY" { operation = AlterSystemOperation.ENABLE_DISTRIBUTED_RECOVERY; } | "RESTRICTED SESSION" { operation = AlterSystemOperation.ENABLE_DISTRIBUTED_RECOVERY; } ) ) | - ( - ( + ( + ( "DISTRIBUTED RECOVERY" { operation = AlterSystemOperation.DISABLE_DISTRIBUTED_RECOVERY; } | "RESTRICTED SESSION" { operation = AlterSystemOperation.DISABLE_RESTRICTED_SESSION; } ) ) | - ( + ( "FLUSH" { operation = AlterSystemOperation.FLUSH; } ) | - ( + ( "DISCONNECT" "SESSION" { operation = AlterSystemOperation.DISCONNECT_SESSION; } ) | @@ -6470,19 +6475,19 @@ AlterSystemStatement AlterSystemStatement(): "KILL SESSION" { operation = AlterSystemOperation.KILL_SESSION; } ) | - ( + ( "SWITCH" { operation = AlterSystemOperation.SWITCH; } ) | - ( + ( "SUSPEND" { operation = AlterSystemOperation.SUSPEND; } ) | - ( + ( "RESUME" { operation = AlterSystemOperation.RESUME; } ) | - ( + ( "QUIESCE" "RESTRICTED" { operation = AlterSystemOperation.QUIESCE; } ) | @@ -6490,19 +6495,19 @@ AlterSystemStatement AlterSystemStatement(): "UNQUIESCE" { operation = AlterSystemOperation.UNQUIESCE; } ) | - ( + ( "SHUTDOWN" { operation = AlterSystemOperation.SHUTDOWN; } ) | - ( + ( "REGISTER" { operation = AlterSystemOperation.REGISTER; } ) | - ( + ( "SET" { operation = AlterSystemOperation.SET; } ) | - ( + ( "RESET" { operation = AlterSystemOperation.RESET; } ) ) @@ -6552,14 +6557,14 @@ RollbackStatement RollbackStatement(): { rollbackStatement = new RollbackStatement(); } [ { rollbackStatement.setUsingWorkKeyword(true); } ] [ ( - [ { rollbackStatement.setUsingSavepointKeyword(true); }] + [ { rollbackStatement.setUsingSavepointKeyword(true); }] token= { rollbackStatement.setSavepointName(token.image); } ) | ( token= { rollbackStatement.setForceDistributedTransactionIdentifier(token.image); } ) ] - + { return rollbackStatement; } diff --git a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java new file mode 100644 index 000000000..d161c1826 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -0,0 +1,2289 @@ +package net.sf.jsqlparser.test.select; + +import junit.framework.*; +import net.sf.jsqlparser.*; +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.arithmetic.*; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.parser.*; +import net.sf.jsqlparser.schema.*; +import net.sf.jsqlparser.statement.*; +import net.sf.jsqlparser.statement.select.*; +import org.apache.commons.io.*; + +import java.io.*; +import java.util.*; + +import static net.sf.jsqlparser.test.TestUtils.*; +import net.sf.jsqlparser.util.TablesNamesFinder; + +public class SelectTest extends TestCase { + + CCJSqlParserManager parserManager = new CCJSqlParserManager(); + + public SelectTest(String arg0) { + super(arg0); + } + + // From statement multipart + public void testMultiPartTableNameWithServerNameAndDatabaseNameAndSchemaName() throws Exception { + final String statement = "SELECT columnName FROM [server-name\\server-instance].databaseName.schemaName.tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithServerNameAndDatabaseName() throws Exception { + final String statement = "SELECT columnName FROM [server-name\\server-instance].databaseName..tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithServerNameAndSchemaName() throws Exception { + final String statement = "SELECT columnName FROM [server-name\\server-instance]..schemaName.tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithServerProblem() throws Exception { + final String statement = "SELECT * FROM LINK_100.htsac.dbo.t_transfer_num a"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testMultiPartTableNameWithServerName() throws Exception { + final String statement = "SELECT columnName FROM [server-name\\server-instance]...tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithDatabaseNameAndSchemaName() throws Exception { + final String statement = "SELECT columnName FROM databaseName.schemaName.tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithDatabaseName() throws Exception { + final String statement = "SELECT columnName FROM databaseName..tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithSchemaName() throws Exception { + final String statement = "SELECT columnName FROM schemaName.tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartTableNameWithColumnName() throws Exception { + final String statement = "SELECT columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + // Select statement statement multipart + public void testMultiPartColumnNameWithDatabaseNameAndSchemaNameAndTableName() throws Exception { + final String statement = "SELECT databaseName.schemaName.tableName.columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testMultiPartColumnNameWithDatabaseNameAndSchemaName() { + final String statement = "SELECT databaseName.schemaName..columnName FROM tableName"; + Select select; + try { + select = (Select) parserManager.parse(new StringReader(statement)); + fail("must not work"); + } catch (JSQLParserException ex) { + //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void testMultiPartColumnNameWithDatabaseNameAndTableName() throws Exception { + final String statement = "SELECT databaseName..tableName.columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + checkMultipartIdentifier(select, "columnName", "databaseName..tableName.columnName"); + } + + public void testMultiPartColumnNameWithDatabaseName() { + final String statement = "SELECT databaseName...columnName FROM tableName"; + Select select; + try { + select = (Select) parserManager.parse(new StringReader(statement)); + fail("must not work"); + } catch (JSQLParserException ex) { + //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void testMultiPartColumnNameWithSchemaNameAndTableName() throws Exception { + final String statement = "SELECT schemaName.tableName.columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + checkMultipartIdentifier(select, "columnName", "schemaName.tableName.columnName"); + } + + public void testMultiPartColumnNameWithSchemaName() { + final String statement = "SELECT schemaName..columnName FROM tableName"; + Select select; + try { + select = (Select) parserManager.parse(new StringReader(statement)); + fail("must not work"); + } catch (JSQLParserException ex) { + //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public void testMultiPartColumnNameWithTableName() throws Exception { + final String statement = "SELECT tableName.columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + checkMultipartIdentifier(select, "columnName", "tableName.columnName"); + } + + public void testMultiPartColumnName() throws Exception { + final String statement = "SELECT columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + checkMultipartIdentifier(select, "columnName", "columnName"); + } + + void checkMultipartIdentifier(Select select, String columnName, String fullColumnName) { + final Expression expr = ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()).getSelectItems().get(0)).getExpression(); + assertTrue(expr instanceof Column); + Column col = (Column) expr; + assertEquals(columnName, col.getColumnName()); + assertEquals(fullColumnName, col.getFullyQualifiedName()); + } + + public void testAllColumnsFromTable() throws Exception { + final String statement = "SELECT tableName.* FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + assertTrue(((PlainSelect) select.getSelectBody()).getSelectItems().get(0) instanceof AllTableColumns); + } + + public void testSimpleSigns() throws JSQLParserException { + final String statement = "SELECT +1, -1 FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testSimpleAdditionsAndSubtractionsWithSigns() throws JSQLParserException { + final String statement = "SELECT 1 - 1, 1 + 1, -1 - 1, -1 + 1, +1 + 1, +1 - 1 FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testOperationsWithSigns() throws JSQLParserException { + Expression expr = CCJSqlParserUtil.parseExpression("1 - -1"); + assertEquals("1 - -1", expr.toString()); + assertTrue(expr instanceof Subtraction); + Subtraction sub = (Subtraction) expr; + assertTrue(sub.getLeftExpression() instanceof LongValue); + assertTrue(sub.getRightExpression() instanceof SignedExpression); + + SignedExpression sexpr = (SignedExpression) sub.getRightExpression(); + assertEquals('-', sexpr.getSign()); + assertEquals("1", sexpr.getExpression().toString()); + } + + public void testSignedColumns() throws JSQLParserException { + final String statement = "SELECT -columnName, +columnName, +(columnName), -(columnName) FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testSigns() throws Exception { + final String statement = "SELECT (-(1)), -(1), (-(columnName)), -(columnName), (-1), -1, (-columnName), -columnName FROM tableName"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testLimit() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 3, ?"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset()); + assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); + + // toString uses standard syntax + statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT ? OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?"; + select = (Select) parserManager.parse(new StringReader(statement)); + + assertNull(((PlainSelect) select.getSelectBody()).getLimit()); + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 3, 4"; + select = (Select) parserManager.parse(new StringReader(statement)); + SetOperationList setList = (SetOperationList) select.getSelectBody(); + assertEquals(3, setList.getLimit().getOffset()); + assertEquals(4, setList.getLimit().getRowCount()); + + // toString uses standard syntax + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION ALL " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) UNION ALL " + + "(SELECT * FROM mytable3 WHERE mytable4.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + + } + + public void testLimit2() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 3, ?"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset()); + assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); + + // toString uses standard syntax + statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT ? OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT NULL OFFSET 3"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertEquals(-1, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); + assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 0 OFFSET 3"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertEquals(0, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); + assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?"; + select = (Select) parserManager.parse(new StringReader(statement)); + + assertNull(((PlainSelect) select.getSelectBody()).getLimit()); + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 3, 4"; + select = (Select) parserManager.parse(new StringReader(statement)); + SetOperationList setList = (SetOperationList) select.getSelectBody(); + assertEquals(3, setList.getLimit().getOffset()); + assertEquals(4, setList.getLimit().getRowCount()); + + // toString uses standard syntax + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION ALL " + + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) UNION ALL " + + "(SELECT * FROM mytable3 WHERE mytable4.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testLimitSqlServer1() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS FETCH NEXT 5 ROWS ONLY"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); + assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); + assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); + assertFalse(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); + assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchJdbcParameter()); + assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); + assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testLimitSqlServer2() throws JSQLParserException { + // Alternative with the other keywords + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROW FETCH FIRST 5 ROW ONLY"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); + assertEquals("ROW", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); + assertEquals("ROW", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); + assertTrue(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); + assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); + assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testLimitSqlServer3() throws JSQLParserException { + // Query with no Fetch + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertNull(((PlainSelect) select.getSelectBody()).getFetch()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); + assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testLimitSqlServer4() throws JSQLParserException { + // For Oracle syntax, query with no offset + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id FETCH NEXT 5 ROWS ONLY"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); + assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); + assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testLimitSqlServerJdbcParameters() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); + assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); + assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); + assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); + assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); + assertTrue(((PlainSelect) select.getSelectBody()).getFetch().isFetchJdbcParameter()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testTop() throws JSQLParserException { + String statement = "SELECT TOP 3 * FROM mytable WHERE mytable.col = 9"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(3, ((LongValue) ((PlainSelect) select.getSelectBody()).getTop().getExpression()).getValue()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "select top 5 foo from bar"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertEquals(5, ((LongValue) ((PlainSelect) select.getSelectBody()).getTop().getExpression()).getValue()); + } + + public void testTopWithParenthesis() throws JSQLParserException { + final String firstColumnName = "alias.columnName1"; + final String secondColumnName = "alias.columnName2"; + final String statement = "SELECT TOP (5) PERCENT " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + + final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + final Top top = selectBody.getTop(); + assertEquals("5", top.getExpression().toString()); + assertTrue(top.hasParenthesis()); + assertTrue(top.isPercentage()); + + final List selectItems = selectBody.getSelectItems(); + assertEquals(2, selectItems.size()); + assertEquals(firstColumnName, selectItems.get(0).toString()); + assertEquals(secondColumnName, selectItems.get(1).toString()); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testSkip() throws JSQLParserException { + final String firstColumnName = "alias.columnName1"; + final String secondColumnName = "alias.columnName2"; + final String statement = "SELECT SKIP 5 " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + + final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + final Skip skip = selectBody.getSkip(); + assertEquals((long) 5, (long) skip.getRowCount()); + assertNull(skip.getJdbcParameter()); + assertNull(skip.getVariable()); + + final List selectItems = selectBody.getSelectItems(); + assertEquals(2, selectItems.size()); + assertEquals(firstColumnName, selectItems.get(0).toString()); + assertEquals(secondColumnName, selectItems.get(1).toString()); + + assertStatementCanBeDeparsedAs(select, statement); + + final String statement2 = "SELECT SKIP skipVar c1, c2 FROM t"; + final Select select2 = (Select) parserManager.parse(new StringReader(statement2)); + + final PlainSelect selectBody2 = (PlainSelect) select2.getSelectBody(); + + final Skip skip2 = selectBody2.getSkip(); + assertNull(skip2.getRowCount()); + assertNull(skip2.getJdbcParameter()); + assertEquals("skipVar", skip2.getVariable()); + + final List selectItems2 = selectBody2.getSelectItems(); + assertEquals(2, selectItems2.size()); + assertEquals("c1", selectItems2.get(0).toString()); + assertEquals("c2", selectItems2.get(1).toString()); + + assertStatementCanBeDeparsedAs(select2, statement2); + } + + public void testFirst() throws JSQLParserException { + final String firstColumnName = "alias.columnName1"; + final String secondColumnName = "alias.columnName2"; + final String statement = "SELECT FIRST 5 " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + + final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + final First limit = selectBody.getFirst(); + assertEquals((long) 5, (long) limit.getRowCount()); + assertNull(limit.getJdbcParameter()); + assertEquals(First.Keyword.FIRST, limit.getKeyword()); + + final List selectItems = selectBody.getSelectItems(); + assertEquals(2, selectItems.size()); + assertEquals(firstColumnName, selectItems.get(0).toString()); + assertEquals(secondColumnName, selectItems.get(1).toString()); + + assertStatementCanBeDeparsedAs(select, statement); + + final String statement2 = "SELECT FIRST firstVar c1, c2 FROM t"; + final Select select2 = (Select) parserManager.parse(new StringReader(statement2)); + + final PlainSelect selectBody2 = (PlainSelect) select2.getSelectBody(); + + final First first2 = selectBody2.getFirst(); + assertNull(first2.getRowCount()); + assertNull(first2.getJdbcParameter()); + assertEquals("firstVar", first2.getVariable()); + + final List selectItems2 = selectBody2.getSelectItems(); + assertEquals(2, selectItems2.size()); + assertEquals("c1", selectItems2.get(0).toString()); + assertEquals("c2", selectItems2.get(1).toString()); + + assertStatementCanBeDeparsedAs(select2, statement2); + } + + public void testFirstWithKeywordLimit() throws JSQLParserException { + final String firstColumnName = "alias.columnName1"; + final String secondColumnName = "alias.columnName2"; + final String statement = "SELECT LIMIT ? " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + + final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + final First limit = selectBody.getFirst(); + assertNull(limit.getRowCount()); + assertNotNull(limit.getJdbcParameter()); + assertNull(limit.getJdbcParameter().getIndex()); + assertEquals(First.Keyword.LIMIT, limit.getKeyword()); + + final List selectItems = selectBody.getSelectItems(); + assertEquals(2, selectItems.size()); + assertEquals(firstColumnName, selectItems.get(0).toString()); + assertEquals(secondColumnName, selectItems.get(1).toString()); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testSkipFirst() throws JSQLParserException { + final String statement = "SELECT SKIP ?1 FIRST f1 c1, c2 FROM t1"; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + + final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); + + final Skip skip = selectBody.getSkip(); + assertNotNull(skip.getJdbcParameter()); + assertNotNull(skip.getJdbcParameter().getIndex()); + assertEquals((int) 1, (int) skip.getJdbcParameter().getIndex()); + assertNull(skip.getVariable()); + final First first = selectBody.getFirst(); + assertNull(first.getJdbcParameter()); + assertNull(first.getRowCount()); + assertEquals("f1", first.getVariable()); + + final List selectItems = selectBody.getSelectItems(); + assertEquals(2, selectItems.size()); + assertEquals("c1", selectItems.get(0).toString()); + assertEquals("c2", selectItems.get(1).toString()); + + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testSelectItems() throws JSQLParserException { + String statement = "SELECT myid AS MYID, mycol, tab.*, schema.tab.*, mytab.mycol2, myschema.mytab.mycol, myschema.mytab.* FROM mytable WHERE mytable.col = 9"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + final List selectItems = plainSelect.getSelectItems(); + assertEquals("MYID", ((SelectExpressionItem) selectItems.get(0)).getAlias().getName()); + assertEquals("mycol", ((Column) ((SelectExpressionItem) selectItems.get(1)).getExpression()).getColumnName()); + assertEquals("tab", ((AllTableColumns) selectItems.get(2)).getTable().getName()); + assertEquals("schema", ((AllTableColumns) selectItems.get(3)).getTable().getSchemaName()); + assertEquals("schema.tab", ((AllTableColumns) selectItems.get(3)).getTable().getFullyQualifiedName()); + assertEquals("mytab.mycol2", ((Column) ((SelectExpressionItem) selectItems.get(4)).getExpression()).getFullyQualifiedName()); + assertEquals("myschema.mytab.mycol", ((Column) ((SelectExpressionItem) selectItems.get(5)).getExpression()).getFullyQualifiedName()); + assertEquals("myschema.mytab", ((AllTableColumns) selectItems.get(6)).getTable().getFullyQualifiedName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT myid AS MYID, (SELECT MAX(ID) AS myid2 FROM mytable2) AS myalias FROM mytable WHERE mytable.col = 9"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("myalias", ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT (myid + myid2) AS MYID FROM mytable WHERE mytable.col = 9"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("MYID", ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testUnion() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " + + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + "SELECT * FROM mytable2 LIMIT 3, 4"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + SetOperationList setList = (SetOperationList) select.getSelectBody(); + assertEquals(3, setList.getSelects().size()); + assertEquals("mytable", ((Table) ((PlainSelect) setList.getSelects().get(0)).getFromItem()).getName()); + assertEquals("mytable3", ((Table) ((PlainSelect) setList.getSelects().get(1)).getFromItem()).getName()); + assertEquals("mytable2", ((Table) ((PlainSelect) setList.getSelects().get(2)).getFromItem()).getName()); + assertEquals(3, ((PlainSelect) setList.getSelects().get(2)).getLimit().getOffset()); + + // use brakets for toString + // use standard limit syntax + String statementToString = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " + + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + + "SELECT * FROM mytable2 LIMIT 3, 4"; + assertStatementCanBeDeparsedAs(select, statementToString); + } + + public void testUnion2() throws JSQLParserException { + String statement = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " + + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + "SELECT * FROM mytable2 LIMIT 3 OFFSET 4"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + SetOperationList setList = (SetOperationList) select.getSelectBody(); + assertEquals(3, setList.getSelects().size()); + assertEquals("mytable", ((Table) ((PlainSelect) setList.getSelects().get(0)).getFromItem()).getName()); + assertEquals("mytable3", ((Table) ((PlainSelect) setList.getSelects().get(1)).getFromItem()).getName()); + assertEquals("mytable2", ((Table) ((PlainSelect) setList.getSelects().get(2)).getFromItem()).getName()); + assertEquals(0, ((PlainSelect) setList.getSelects().get(2)).getLimit().getOffset()); + assertEquals(4, ((PlainSelect) setList.getSelects().get(2)).getOffset().getOffset()); + + // use brakets for toString + // use standard limit syntax + String statementToString = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " + + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + + "SELECT * FROM mytable2 LIMIT 3 OFFSET 4"; + assertStatementCanBeDeparsedAs(select, statementToString); + } + + + public void testDistinct() throws JSQLParserException { + String statement = "SELECT DISTINCT ON (myid) myid, mycol FROM mytable WHERE mytable.col = 9"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("myid", + ((Column) ((SelectExpressionItem) plainSelect.getDistinct().getOnSelectItems().get(0)).getExpression()) + .getColumnName()); + assertEquals("mycol", + ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()).getColumnName()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testDistinctTop() throws JSQLParserException { + String statement = "SELECT DISTINCT TOP 5 myid, mycol FROM mytable WHERE mytable.col = 9"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("myid", + ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression()) + .getColumnName()); + assertEquals("mycol", + ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()).getColumnName()); + assertNotNull(plainSelect.getTop()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testDistinctTop2() { + String statement = "SELECT TOP 5 DISTINCT myid, mycol FROM mytable WHERE mytable.col = 9"; + try { + parserManager.parse(new StringReader(statement)); + fail("sould not work"); + } catch (JSQLParserException ex) { + } + } + + public void testFrom() throws JSQLParserException { + String statement = "SELECT * FROM mytable as mytable0, mytable1 alias_tab1, mytable2 as alias_tab2, (SELECT * FROM mytable3) AS mytable4 WHERE mytable.col = 9"; + String statementToString = "SELECT * FROM mytable AS mytable0, mytable1 alias_tab1, mytable2 AS alias_tab2, (SELECT * FROM mytable3) AS mytable4 WHERE mytable.col = 9"; + + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(3, plainSelect.getJoins().size()); + assertEquals("mytable0", plainSelect.getFromItem().getAlias().getName()); + assertEquals("alias_tab1", plainSelect.getJoins().get(0).getRightItem().getAlias().getName()); + assertEquals("alias_tab2", plainSelect.getJoins().get(1).getRightItem().getAlias().getName()); + assertEquals("mytable4", plainSelect.getJoins().get(2).getRightItem().getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statementToString); + } + + public void testJoin() throws JSQLParserException { + String statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(1, plainSelect.getJoins().size()); + assertEquals("tab2", ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + assertEquals("tab1.id", + ((Column) ((EqualsTo) plainSelect.getJoins().get(0).getOnExpression()).getLeftExpression()) + .getFullyQualifiedName()); + assertTrue(plainSelect.getJoins().get(0).isOuter()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id INNER JOIN tab3"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(2, plainSelect.getJoins().size()); + assertEquals("tab3", ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); + assertFalse(plainSelect.getJoins().get(1).isOuter()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id JOIN tab3"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(2, plainSelect.getJoins().size()); + assertEquals("tab3", ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); + assertFalse(plainSelect.getJoins().get(1).isOuter()); + assertStatementCanBeDeparsedAs(select, statement); + + // implicit INNER + statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id INNER JOIN tab3"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM TA2 LEFT OUTER JOIN O USING (col1, col2) WHERE D.OasSD = 'asdf' AND (kj >= 4 OR l < 'sdf')"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM tab1 INNER JOIN tab2 USING (id, id2)"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(1, plainSelect.getJoins().size()); + assertEquals("tab2", ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); + assertFalse(plainSelect.getJoins().get(0).isOuter()); + assertEquals(2, plainSelect.getJoins().get(0).getUsingColumns().size()); + assertEquals("id2", + plainSelect.getJoins().get(0).getUsingColumns().get(1).getFullyQualifiedName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM tab1 RIGHT OUTER JOIN tab2 USING (id, id2)"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT * FROM foo AS f LEFT OUTER JOIN (bar AS b RIGHT OUTER JOIN baz AS z ON f.id = z.id) ON f.id = b.id"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + + } + + public void testFunctions() throws JSQLParserException { + String statement = "SELECT MAX(id) AS max FROM mytable WHERE mytable.col = 9"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("max", ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT MAX(id), AVG(pro) AS myavg FROM mytable WHERE mytable.col = 9 GROUP BY pro"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("myavg", ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT MAX(a, b, c), COUNT(*), D FROM tab1 GROUP BY D"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + Function fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); + assertEquals("MAX", fun.getName()); + assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); + assertTrue(((Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()) + .isAllColumns()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT {fn MAX(a, b, c)}, COUNT(*), D FROM tab1 GROUP BY D"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); + assertTrue(fun.isEscaped()); + assertEquals("MAX", fun.getName()); + assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); + assertTrue(((Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()) + .isAllColumns()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT ab.MAX(a, b, c), cd.COUNT(*), D FROM tab1 GROUP BY D"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); + assertEquals("ab.MAX", fun.getName()); + assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); + fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression(); + assertEquals("cd.COUNT", fun.getName()); + assertTrue(fun.isAllColumns()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testWhere() throws JSQLParserException { + + final String statement = "SELECT * FROM tab1 WHERE"; + String whereToString = "(a + b + c / d + e * f) * (a / b * (a + b)) > ?"; + PlainSelect plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " + + whereToString))).getSelectBody(); + assertTrue(plainSelect.getWhere() instanceof GreaterThan); + assertTrue(((GreaterThan) plainSelect.getWhere()).getLeftExpression() instanceof Multiplication); + assertEquals(statement + " " + whereToString, plainSelect.toString()); + + assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); + + whereToString = "(7 * s + 9 / 3) NOT BETWEEN 3 AND ?"; + plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " + whereToString))) + .getSelectBody(); + + assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); + assertEquals(statement + " " + whereToString, plainSelect.toString()); + + whereToString = "a / b NOT IN (?, 's''adf', 234.2)"; + plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " + whereToString))) + .getSelectBody(); + + assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); + assertEquals(statement + " " + whereToString, plainSelect.toString()); + + whereToString = " NOT 0 = 0"; + plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + whereToString))) + .getSelectBody(); + + whereToString = " NOT (0 = 0)"; + plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + whereToString))) + .getSelectBody(); + + assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString.trim()); + assertEquals(statement + whereToString, plainSelect.toString()); + } + + public void testGroupBy() throws JSQLParserException { + String statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(1, plainSelect.getGroupByColumnReferences().size()); + assertEquals("tab1.b", ((Column) plainSelect.getGroupByColumnReferences().get(0)).getFullyQualifiedName()); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY 2, 3"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(2, plainSelect.getGroupByColumnReferences().size()); + assertEquals(2, ((LongValue) plainSelect.getGroupByColumnReferences().get(0)).getValue()); + assertEquals(3, ((LongValue) plainSelect.getGroupByColumnReferences().get(1)).getValue()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testHaving() throws JSQLParserException { + String statement = "SELECT MAX(tab1.b) FROM tab1 WHERE a > 34 GROUP BY tab1.b HAVING MAX(tab1.b) > 56"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertTrue(plainSelect.getHaving() instanceof GreaterThan); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT MAX(tab1.b) FROM tab1 WHERE a > 34 HAVING MAX(tab1.b) IN (56, 32, 3, ?)"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertTrue(plainSelect.getHaving() instanceof InExpression); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testExists() throws JSQLParserException { + String statement = "SELECT * FROM tab1 WHERE "; + String where = "EXISTS (SELECT * FROM tab2)"; + statement += where; + Statement parsed = parserManager.parse(new StringReader(statement)); + + assertEquals(statement, parsed.toString()); + + PlainSelect plainSelect = (PlainSelect) ((Select) parsed).getSelectBody(); + assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), where); + } + + public void testOrderBy() throws JSQLParserException { + // TODO: should there be a DESC marker in the OrderByElement class? + String statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a DESC, tab1.b ASC"; + String statementToString = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a DESC, tab1.b ASC"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(2, plainSelect.getOrderByElements().size()); + assertEquals("tab1.a", + ((Column) plainSelect.getOrderByElements().get(0).getExpression()) + .getFullyQualifiedName()); + assertEquals("b", + ((Column) plainSelect.getOrderByElements().get(1).getExpression()).getColumnName()); + assertTrue(plainSelect.getOrderByElements().get(1).isAsc()); + assertFalse(plainSelect.getOrderByElements().get(0).isAsc()); + assertStatementCanBeDeparsedAs(select, statementToString); + + statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a, 2"; + select = (Select) parserManager.parse(new StringReader(statement)); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals(2, plainSelect.getOrderByElements().size()); + assertEquals("a", + ((Column) plainSelect.getOrderByElements().get(0).getExpression()).getColumnName()); + assertEquals(2, + ((LongValue) plainSelect.getOrderByElements().get(1).getExpression()).getValue()); + assertStatementCanBeDeparsedAs(select, statement); + + } + + public void testOrderByNullsFirst() throws JSQLParserException { + String statement = "SELECT a FROM tab1 ORDER BY a NULLS FIRST"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testTimestamp() throws JSQLParserException { + String statement = "SELECT * FROM tab1 WHERE a > {ts '2004-04-30 04:05:34.56'}"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("2004-04-30 04:05:34.56", + ((TimestampValue) ((GreaterThan) plainSelect.getWhere()).getRightExpression()).getValue().toString()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testTime() throws JSQLParserException { + String statement = "SELECT * FROM tab1 WHERE a > {t '04:05:34'}"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("04:05:34", + (((TimeValue) ((GreaterThan) plainSelect.getWhere()).getRightExpression()).getValue()).toString()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testBetweenDate() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE col BETWEEN {d '2015-09-19'} AND {d '2015-09-24'}"); + } + + public void testCase() throws JSQLParserException { + String statement = "SELECT a, CASE b WHEN 1 THEN 2 END FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE WHEN (a > 2) THEN 3 END) AS b FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE WHEN a > 2 THEN 3 ELSE 4 END) AS b FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE b WHEN 1 THEN 2 WHEN 3 THEN 4 ELSE 5 END) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE " + "WHEN b > 1 THEN 'BBB' " + "WHEN a = 3 THEN 'AAA' " + "END) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE " + "WHEN b > 1 THEN 'BBB' " + "WHEN a = 3 THEN 'AAA' " + "END) FROM tab1 " + + "WHERE c = (CASE " + "WHEN d <> 3 THEN 5 " + "ELSE 10 " + "END)"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, CASE a " + "WHEN 'b' THEN 'BBB' " + "WHEN 'a' THEN 'AAA' " + "END AS b FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a FROM tab1 WHERE CASE b WHEN 1 THEN 2 WHEN 3 THEN 4 ELSE 5 END > 34"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a FROM tab1 WHERE CASE b WHEN 1 THEN 2 + 3 ELSE 4 END > 34"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT a, (CASE " + "WHEN (CASE a WHEN 1 THEN 10 ELSE 20 END) > 15 THEN 'BBB' " + + // "WHEN (SELECT c FROM tab2 WHERE d = 2) = 3 THEN 'AAA' " + + "END) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + } + + public void testReplaceAsFunction() throws JSQLParserException { + String statement = "SELECT REPLACE(a, 'b', c) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + + Statement stmt = CCJSqlParserUtil.parse(statement); + Select select = (Select) stmt; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + assertEquals(1, plainSelect.getSelectItems().size()); + Expression expression = ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); + assertTrue(expression instanceof Function); + Function func = (Function) expression; + assertEquals("REPLACE", func.getName()); + assertEquals(3, func.getParameters().getExpressions().size()); + } + + public void testLike() throws JSQLParserException { + String statement = "SELECT * FROM tab1 WHERE a LIKE 'test'"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("test", ((StringValue) ((LikeExpression) plainSelect.getWhere()).getRightExpression()).getValue()); + + statement = "SELECT * FROM tab1 WHERE a LIKE 'test' ESCAPE 'test2'"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + plainSelect = (PlainSelect) select.getSelectBody(); + assertEquals("test", ((StringValue) ((LikeExpression) plainSelect.getWhere()).getRightExpression()).getValue()); + assertEquals("test2", ((LikeExpression) plainSelect.getWhere()).getEscape()); + } + + public void testIlike() throws JSQLParserException { + String statement = "SELECT col1 FROM table1 WHERE col1 ILIKE '%hello%'"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testSelectOrderHaving() throws JSQLParserException { + String statement = "SELECT units, count(units) AS num FROM currency GROUP BY units HAVING count(units) > 1 ORDER BY num"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testDouble() throws JSQLParserException { + String statement = "SELECT 1e2, * FROM mytable WHERE mytable.col = 9"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1e2, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) + .getSelectItems().get(0)).getExpression()).getValue(), 0); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 1.e2"; + select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1e2, + ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) + .getRightExpression()).getValue(), 0); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 1.2e2"; + select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1.2e2, + ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) + .getRightExpression()).getValue(), 0); + assertStatementCanBeDeparsedAs(select, statement); + + statement = "SELECT * FROM mytable WHERE mytable.col = 2e2"; + select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(2e2, + ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) + .getRightExpression()).getValue(), 0); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testDouble2() throws JSQLParserException { + String statement = "SELECT 1.e22 FROM mytable"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1e22, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) + .getSelectItems().get(0)).getExpression()).getValue(), 0); + } + + public void testDouble3() throws JSQLParserException { + String statement = "SELECT 1. FROM mytable"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1.0, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) + .getSelectItems().get(0)).getExpression()).getValue(), 0); + } + + public void testDouble4() throws JSQLParserException { + String statement = "SELECT 1.2e22 FROM mytable"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + + assertEquals(1.2e22, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) + .getSelectItems().get(0)).getExpression()).getValue(), 0); + } + + public void testWith() throws JSQLParserException { + String statement = "WITH DINFO (DEPTNO, AVGSALARY, EMPCOUNT) AS " + + "(SELECT OTHERS.WORKDEPT, AVG(OTHERS.SALARY), COUNT(*) FROM EMPLOYEE AS OTHERS " + + "GROUP BY OTHERS.WORKDEPT), DINFOMAX AS (SELECT MAX(AVGSALARY) AS AVGMAX FROM DINFO) " + + "SELECT THIS_EMP.EMPNO, THIS_EMP.SALARY, DINFO.AVGSALARY, DINFO.EMPCOUNT, DINFOMAX.AVGMAX " + + "FROM EMPLOYEE AS THIS_EMP INNER JOIN DINFO INNER JOIN DINFOMAX " + + "WHERE THIS_EMP.JOB = 'SALESREP' AND THIS_EMP.WORKDEPT = DINFO.DEPTNO"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testWithRecursive() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("WITH RECURSIVE t (n) AS ((SELECT 1) UNION ALL (SELECT n + 1 FROM t WHERE n < 100)) SELECT sum(n) FROM t"); + } + + public void testSelectAliasInQuotes() throws JSQLParserException { + String statement = "SELECT mycolumn AS \"My Column Name\" FROM mytable"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testSelectAliasWithoutAs() throws JSQLParserException { + String statement = "SELECT mycolumn \"My Column Name\" FROM mytable"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testSelectJoinWithComma() throws JSQLParserException { + String statement = "SELECT cb.Genus, cb.Species FROM Coleccion_de_Briofitas AS cb, unigeoestados AS es " + + "WHERE es.nombre = \"Tamaulipas\" AND cb.the_geom = es.geom"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testDeparser() throws JSQLParserException { + String statement = "SELECT a.OWNERLASTNAME, a.OWNERFIRSTNAME " + "FROM ANTIQUEOWNERS AS a, ANTIQUES AS b " + + "WHERE b.BUYERID = a.OWNERID AND b.ITEM = 'Chair'"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT count(DISTINCT f + 4) FROM a"; + assertSqlCanBeParsedAndDeparsed(statement); + + statement = "SELECT count(DISTINCT f, g, h) FROM a"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testCount2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(ALL col1 + col2) FROM mytable"); + } + + public void testMysqlQuote() throws JSQLParserException { + String statement = "SELECT `a.OWNERLASTNAME`, `OWNERFIRSTNAME` " + "FROM `ANTIQUEOWNERS` AS a, ANTIQUES AS b " + + "WHERE b.BUYERID = a.OWNERID AND b.ITEM = 'Chair'"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testConcat() throws JSQLParserException { + String statement = "SELECT a || b || c + 4 FROM t"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testConcatProblem2() throws JSQLParserException { + String stmt = "SELECT MAX(((((" + + "(SPA.SOORTAANLEVERPERIODE)::VARCHAR (2) || (VARCHAR(SPA.AANLEVERPERIODEJAAR))::VARCHAR (4)" + + ") || TO_CHAR(SPA.AANLEVERPERIODEVOLGNR, 'FM09'::VARCHAR)" + + ") || TO_CHAR((10000 - SPA.VERSCHIJNINGSVOLGNR), 'FM0999'::VARCHAR)" + + ") || (SPA.GESLACHT)::VARCHAR (1))) AS GESLACHT_TMP FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_1() throws JSQLParserException { + String stmt = "SELECT TO_CHAR(SPA.AANLEVERPERIODEVOLGNR, 'FM09'::VARCHAR) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_2() throws JSQLParserException { + String stmt = "SELECT MAX((SPA.SOORTAANLEVERPERIODE)::VARCHAR (2) || (VARCHAR(SPA.AANLEVERPERIODEJAAR))::VARCHAR (4)) AS GESLACHT_TMP FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_3() throws JSQLParserException { + String stmt = "SELECT TO_CHAR((10000 - SPA.VERSCHIJNINGSVOLGNR), 'FM0999'::VARCHAR) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_4() throws JSQLParserException { + String stmt = "SELECT (SPA.GESLACHT)::VARCHAR (1) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_5() throws JSQLParserException { + String stmt = "SELECT max((a || b) || c) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_5_1() throws JSQLParserException { + String stmt = "SELECT (a || b) || c FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_5_2() throws JSQLParserException { + String stmt = "SELECT (a + b) + c FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testConcatProblem2_6() throws JSQLParserException { + String stmt = "SELECT max(a || b || c) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testMatches() throws JSQLParserException { + String statement = "SELECT * FROM team WHERE team.search_column @@ to_tsquery('new & york & yankees')"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testGroupByExpression() throws JSQLParserException { + String statement = "SELECT col1, col2, col1 + col2, sum(col8)" + " FROM table1 " + + "GROUP BY col1, col2, col1 + col2"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testBitwise() throws JSQLParserException { + String statement = "SELECT col1 & 32, col2 ^ col1, col1 | col2" + " FROM table1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testSelectFunction() throws JSQLParserException { + String statement = "SELECT 1 + 2 AS sum"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testWeirdSelect() throws JSQLParserException { + String sql = "select r.reviews_id, substring(rd.reviews_text, 100) as reviews_text, r.reviews_rating, r.date_added, r.customers_name from reviews r, reviews_description rd where r.products_id = '19' and r.reviews_id = rd.reviews_id and rd.languages_id = '1' and r.reviews_status = 1 order by r.reviews_id desc limit 0, 6"; + parserManager.parse(new StringReader(sql)); + } + + public void testCast() throws JSQLParserException { + String stmt = "SELECT CAST(a AS varchar) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + stmt = "SELECT CAST(a AS varchar2) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastInCast() throws JSQLParserException { + String stmt = "SELECT CAST(CAST(a AS numeric) AS varchar) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastInCast2() throws JSQLParserException { + String stmt = "SELECT CAST('test' + CAST(assertEqual AS numeric) AS varchar) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem() throws JSQLParserException { + String stmt = "SELECT CAST(col1 AS varchar (256)) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem2() throws JSQLParserException { + String stmt = "SELECT col1::varchar FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem3() throws JSQLParserException { + String stmt = "SELECT col1::varchar (256) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem4() throws JSQLParserException { + String stmt = "SELECT 5::varchar (256) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem5() throws JSQLParserException { + String stmt = "SELECT 5.67::varchar (256) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem6() throws JSQLParserException { + String stmt = "SELECT 'test'::character varying FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem7() throws JSQLParserException { + String stmt = "SELECT CAST('test' AS character varying) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCastTypeProblem8() throws JSQLParserException { + String stmt = "SELECT CAST('123' AS double precision) FROM tabelle1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testCaseElseAddition() throws JSQLParserException { + String stmt = "SELECT CASE WHEN 1 + 3 > 20 THEN 0 ELSE 1000 + 1 END AS d FROM dual"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testBrackets() throws JSQLParserException { + String stmt = "SELECT table_a.name AS [Test] FROM table_a"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testBrackets2() throws JSQLParserException { + String stmt = "SELECT [a] FROM t"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlServer_Modulo_Proz() throws Exception { + String stmt = "SELECT 5 % 2 FROM A"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlServer_Modulo_mod() throws Exception { + String stmt = "SELECT mod(5, 2) FROM A"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlServer_Modulo() throws Exception { + String stmt = "SELECT convert(varchar(255), DATEDIFF(month, year1, abc_datum) / 12) + ' year, ' + convert(varchar(255), DATEDIFF(month, year2, abc_datum) % 12) + ' month' FROM test_table"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testIsNot() throws JSQLParserException { + String stmt = "SELECT * FROM test WHERE a IS NOT NULL"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testIsNot2() throws JSQLParserException { + //the deparser delivers always a IS NOT NULL even for NOT a IS NULL + String stmt = "SELECT * FROM test WHERE NOT a IS NULL"; + Statement parsed = parserManager.parse(new StringReader(stmt)); + assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM test WHERE a IS NOT NULL"); + } + + public void testProblemSqlAnalytic() throws JSQLParserException { + String stmt = "SELECT a, row_number() OVER (ORDER BY a) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic2() throws JSQLParserException { + String stmt = "SELECT a, row_number() OVER (ORDER BY a, b) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic3() throws JSQLParserException { + String stmt = "SELECT a, row_number() OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic4EmptyOver() throws JSQLParserException { + String stmt = "SELECT a, row_number() OVER () AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic5AggregateColumnValue() throws JSQLParserException { + String stmt = "SELECT a, sum(b) OVER () AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic6AggregateColumnValue() throws JSQLParserException { + String stmt = "SELECT a, sum(b + 5) OVER (ORDER BY a) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic7Count() throws JSQLParserException { + String stmt = "SELECT count(*) OVER () AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic8Complex() throws JSQLParserException { + String stmt = "SELECT ID, NAME, SALARY, SUM(SALARY) OVER () AS SUM_SAL, AVG(SALARY) OVER () AS AVG_SAL, MIN(SALARY) OVER () AS MIN_SAL, MAX(SALARY) OVER () AS MAX_SAL, COUNT(*) OVER () AS ROWS2 FROM STAFF WHERE ID < 60 ORDER BY ID"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic9CommaListPartition() throws JSQLParserException { + String stmt = "SELECT a, row_number() OVER (PARTITION BY c, d ORDER BY a, b) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic10Lag() throws JSQLParserException { + String stmt = "SELECT a, lag(a, 1) OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemSqlAnalytic11Lag() throws JSQLParserException { + String stmt = "SELECT a, lag(a, 1, 0) OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testAnalyticFunction12() throws JSQLParserException { + String statement = "SELECT SUM(a) OVER (PARTITION BY b ORDER BY c) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction13() throws JSQLParserException { + String statement = "SELECT SUM(a) OVER () FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction14() throws JSQLParserException { + String statement = "SELECT SUM(a) OVER (PARTITION BY b ) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction15() throws JSQLParserException { + String statement = "SELECT SUM(a) OVER (ORDER BY c) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction16() throws JSQLParserException { + String statement = "SELECT SUM(a) OVER (ORDER BY c NULLS FIRST) FROM tab1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction17() throws JSQLParserException { + String statement = "SELECT AVG(sal) OVER (PARTITION BY deptno ORDER BY sal ROWS BETWEEN 0 PRECEDING AND 0 PRECEDING) AS avg_of_current_sal FROM emp"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunction18() throws JSQLParserException { + String statement = "SELECT AVG(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE CURRENT ROW) AS avg_of_current_sal FROM emp"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunctionProblem1() throws JSQLParserException { + String statement = "SELECT last_value(s.revenue_hold) OVER (PARTITION BY s.id_d_insertion_order, s.id_d_product_ad_attr, trunc(s.date_id, 'mm') ORDER BY s.date_id) AS col FROM s"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testAnalyticFunctionProblem1b() throws JSQLParserException { + String statement = "SELECT last_value(s.revenue_hold) OVER (PARTITION BY s.id_d_insertion_order, s.id_d_product_ad_attr, trunc(s.date_id, 'mm') ORDER BY s.date_id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS col FROM s"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testFunctionLeft() throws JSQLParserException { + String statement = "SELECT left(table1.col1, 4) FROM table1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testFunctionRight() throws JSQLParserException { + String statement = "SELECT right(table1.col1, 4) FROM table1"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + public void testOracleJoin() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a = tabelle2.b(+)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleJoin2() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) = tabelle2.b"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleJoin2_1() throws JSQLParserException { + String[] values = new String[]{"(+)", "( +)", "(+ )", "( + )", " (+) "}; + for (String value : values) { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a" + value + " = tabelle2.b", true); + } + } + + public void testOracleJoin2_2() throws JSQLParserException { + String[] values = new String[]{"(+)", "( +)", "(+ )", "( + )", " (+) "}; + for (String value : values) { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a = tabelle2.b" + value, true); + } + } + + public void testOracleJoin3() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) > tabelle2.b"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleJoin3_1() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a > tabelle2.b(+)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleJoin4() throws JSQLParserException { + String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) = tabelle2.b AND tabelle1.b(+) IN ('A', 'B')"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleJoinIssue318() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM TBL_A, TBL_B, TBL_C WHERE TBL_A.ID(+) = TBL_B.ID AND TBL_C.ROOM(+) = TBL_B.ROOM"); + } + + public void testProblemSqlIntersect() throws Exception { + String stmt = "(SELECT * FROM a) INTERSECT (SELECT * FROM b)"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT * FROM a INTERSECT SELECT * FROM b"; + Statement parsed = parserManager.parse(new StringReader(stmt)); + assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a INTERSECT SELECT * FROM b"); + } + + public void testProblemSqlExcept() throws Exception { + String stmt = "(SELECT * FROM a) EXCEPT (SELECT * FROM b)"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT * FROM a EXCEPT SELECT * FROM b"; + Statement parsed = parserManager.parse(new StringReader(stmt)); + assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a EXCEPT SELECT * FROM b"); + } + + public void testProblemSqlMinus() throws Exception { + String stmt = "(SELECT * FROM a) MINUS (SELECT * FROM b)"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT * FROM a MINUS SELECT * FROM b"; + Statement parsed = parserManager.parse(new StringReader(stmt)); + assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a MINUS SELECT * FROM b"); + } + + public void testProblemSqlCombinedSets() throws Exception { + String stmt = "(SELECT * FROM a) INTERSECT (SELECT * FROM b) UNION (SELECT * FROM c)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithStatement() throws JSQLParserException { + String stmt = "WITH test AS (SELECT mslink FROM feature) SELECT * FROM feature WHERE mslink IN (SELECT mslink FROM test)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithUnionProblem() throws JSQLParserException { + String stmt = "WITH test AS ((SELECT mslink FROM tablea) UNION (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithUnionAllProblem() throws JSQLParserException { + String stmt = "WITH test AS ((SELECT mslink FROM tablea) UNION ALL (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithUnionProblem3() throws JSQLParserException { + String stmt = "WITH test AS ((SELECT mslink, CAST(tablea.fname AS varchar) FROM tablea INNER JOIN tableb ON tablea.mslink = tableb.mslink AND tableb.deleted = 0 WHERE tablea.fname IS NULL AND 1 = 0) UNION ALL (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithUnionProblem4() throws JSQLParserException { + String stmt = "WITH hist AS ((SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, 0 AS level, CAST(gl.mslink AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 WHERE gl.parent IS NULL AND gl.mslink <> 0) UNION ALL (SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, hist.level + 1 AS level, CAST(hist.path + '.' + CAST(gl.mslink AS VARCHAR) AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 INNER JOIN hist ON gl.parent = hist.mslink WHERE gl.mslink <> 0)) SELECT mslink, space(level * 4) + txt AS txt, nr, feature, path FROM hist WHERE EXISTS (SELECT feature FROM tablec WHERE mslink = 0 AND ((feature IN (1, 2) AND hist.feature = 3) OR (feature IN (4) AND hist.feature = 2)))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testWithUnionProblem5() throws JSQLParserException { + String stmt = "WITH hist AS ((SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, 0 AS level, CAST(gl.mslink AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 WHERE gl.parent IS NULL AND gl.mslink <> 0) UNION ALL (SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, hist.level + 1 AS level, CAST(hist.path + '.' + CAST(gl.mslink AS VARCHAR) AS VARCHAR) AS path, 5 AS feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 INNER JOIN hist ON gl.parent = hist.mslink WHERE gl.mslink <> 0)) SELECT * FROM hist"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testExtractFrom1() throws JSQLParserException { + String stmt = "SELECT EXTRACT(month FROM datecolumn) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testExtractFrom2() throws JSQLParserException { + String stmt = "SELECT EXTRACT(year FROM now()) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testExtractFrom3() throws JSQLParserException { + String stmt = "SELECT EXTRACT(year FROM (now() - 2)) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testExtractFrom4() throws JSQLParserException { + String stmt = "SELECT EXTRACT(minutes FROM now() - '01:22:00') FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemFunction() throws JSQLParserException { + String stmt = "SELECT test() FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + Statement parsed = CCJSqlParserUtil.parse(stmt); + Select select = (Select) parsed; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + SelectItem get = plainSelect.getSelectItems().get(0); + SelectExpressionItem item = (SelectExpressionItem) get; + assertTrue(item.getExpression() instanceof Function); + assertEquals("test", ((Function) item.getExpression()).getName()); + } + + public void testProblemFunction2() throws JSQLParserException { + String stmt = "SELECT sysdate FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testProblemFunction3() throws JSQLParserException { + String stmt = "SELECT TRUNCATE(col) FROM testtable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testAdditionalLettersGerman() throws JSQLParserException { + String stmt = "SELECT colä, colö, colü FROM testtableäöü"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT colA, colÖ, colÜ FROM testtableÄÖÜ"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT Äcol FROM testtableÄÖÜ"; + assertSqlCanBeParsedAndDeparsed(stmt); + + stmt = "SELECT ßcolß FROM testtableß"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testAdditionalLettersSpanish() throws JSQLParserException { + String stmt = "SELECT * FROM años"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testMultiTableJoin() throws JSQLParserException { + String stmt = "SELECT * FROM taba INNER JOIN tabb ON taba.a = tabb.a, tabc LEFT JOIN tabd ON tabc.c = tabd.c"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testTableCrossJoin() throws JSQLParserException { + String stmt = "SELECT * FROM taba CROSS JOIN tabb"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testLateral1() throws JSQLParserException { + String stmt = "SELECT O.ORDERID, O.CUSTNAME, OL.LINETOTAL FROM ORDERS AS O, LATERAL(SELECT SUM(NETAMT) AS LINETOTAL FROM ORDERLINES AS LINES WHERE LINES.ORDERID = O.ORDERID) AS OL"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testLateralComplex1() throws IOException, JSQLParserException { + String stmt = IOUtils.toString(SelectTest.class.getResourceAsStream("complex-lateral-select-request.txt")); + Select select = (Select) parserManager.parse(new StringReader(stmt)); + assertEquals("SELECT O.ORDERID, O.CUSTNAME, OL.LINETOTAL, OC.ORDCHGTOTAL, OT.TAXTOTAL FROM ORDERS O, LATERAL(SELECT SUM(NETAMT) AS LINETOTAL FROM ORDERLINES LINES WHERE LINES.ORDERID = O.ORDERID) AS OL, LATERAL(SELECT SUM(CHGAMT) AS ORDCHGTOTAL FROM ORDERCHARGES CHARGES WHERE LINES.ORDERID = O.ORDERID) AS OC, LATERAL(SELECT SUM(TAXAMT) AS TAXTOTAL FROM ORDERTAXES TAXES WHERE TAXES.ORDERID = O.ORDERID) AS OT", select.toString()); + } + + public void testValues() throws JSQLParserException { + String stmt = "SELECT * FROM (VALUES (1, 2), (3, 4)) AS test"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testValues2() throws JSQLParserException { + String stmt = "SELECT * FROM (VALUES 1, 2, 3, 4) AS test"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testValues3() throws JSQLParserException { + String stmt = "SELECT * FROM (VALUES 1, 2, 3, 4) AS test(a)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testValues4() throws JSQLParserException { + String stmt = "SELECT * FROM (VALUES (1, 2), (3, 4)) AS test(a, b)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testValues5() throws JSQLParserException { + String stmt = "SELECT X, Y FROM (VALUES (0, 'a'), (1, 'b')) AS MY_TEMP_TABLE(X, Y)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testValues6BothVariants() throws JSQLParserException { + String stmt = "SELECT I FROM (VALUES 1, 2, 3) AS MY_TEMP_TABLE(I) WHERE I IN (SELECT * FROM (VALUES 1, 2) AS TEST)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testInterval1() throws JSQLParserException { + String stmt = "SELECT 5 + INTERVAL '3 days'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testInterval2() throws JSQLParserException { + String stmt = "SELECT to_timestamp(to_char(now() - INTERVAL '45 MINUTE', 'YYYY-MM-DD-HH24:')) AS START_TIME FROM tab1"; + assertSqlCanBeParsedAndDeparsed(stmt); + + Statement st = CCJSqlParserUtil.parse(stmt); + Select select = (Select) st; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + assertEquals(1, plainSelect.getSelectItems().size()); + SelectExpressionItem item = (SelectExpressionItem) plainSelect.getSelectItems().get(0); + Function function = (Function) item.getExpression(); + + assertEquals("to_timestamp", function.getName()); + + assertEquals(1, function.getParameters().getExpressions().size()); + + Function func2 = (Function) function.getParameters().getExpressions().get(0); + + assertEquals("to_char", func2.getName()); + + assertEquals(2, func2.getParameters().getExpressions().size()); + Subtraction sub = (Subtraction) func2.getParameters().getExpressions().get(0); + assertTrue(sub.getRightExpression() instanceof IntervalExpression); + IntervalExpression iexpr = (IntervalExpression) sub.getRightExpression(); + + assertEquals("'45 MINUTE'", iexpr.getParameter()); + } + + public void testInterval3() throws JSQLParserException { + String stmt = "SELECT 5 + INTERVAL '3' day"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testInterval4() throws JSQLParserException { + String stmt = "SELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testInterval5_Issue228() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT ADDDATE(timeColumn1, INTERVAL 420 MINUTES) AS timeColumn1 FROM tbl"); + assertSqlCanBeParsedAndDeparsed("SELECT ADDDATE(timeColumn1, INTERVAL -420 MINUTES) AS timeColumn1 FROM tbl"); + } + + public void testMultiValueIn() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE (a, b, c) IN (SELECT a, b, c FROM mytable2)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testMultiValueIn2() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE (trim(a), trim(b)) IN (SELECT a, b FROM mytable2)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivot1() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN ('val1'))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivot2() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN (10, 20, 30))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivot3() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT (count(a) AS vals FOR b IN (10 AS d1, 20, 30 AS d3))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivot4() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT (count(a), sum(b) FOR b IN (10, 20, 30))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivot5() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR (b, c) IN ((10, 'a'), (20, 'b'), (30, 'c')))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivotXml1() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN ('val1'))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivotXml2() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN (SELECT vals FROM myothertable))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivotXml3() throws JSQLParserException { + String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN (ANY))"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPivotXmlSubquery1() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM (SELECT times_purchased, state_code FROM customers t) PIVOT (count(state_code) FOR state_code IN ('NY', 'CT', 'NJ', 'FL', 'MO')) ORDER BY times_purchased"); + } + + public void testPivotFunction() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT to_char((SELECT col1 FROM (SELECT times_purchased, state_code FROM customers t) PIVOT (count(state_code) FOR state_code IN ('NY', 'CT', 'NJ', 'FL', 'MO')) ORDER BY times_purchased)) FROM DUAL"); + } + + public void testRegexpLike1() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE REGEXP_LIKE(first_name, '^Ste(v|ph)en$')"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testRegexpLike2() throws JSQLParserException { + String stmt = "SELECT CASE WHEN REGEXP_LIKE(first_name, '^Ste(v|ph)en$') THEN 1 ELSE 2 END FROM mytable"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testRegexpMySQL() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE first_name REGEXP '^Ste(v|ph)en$'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testRegexpBinaryMySQL() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE first_name REGEXP BINARY '^Ste(v|ph)en$'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testBooleanFunction1() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE test_func(col1)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testNamedParameter() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE b = :param"; + assertSqlCanBeParsedAndDeparsed(stmt); + + Statement st = CCJSqlParserUtil.parse(stmt); + Select select = (Select) st; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + Expression exp = ((BinaryExpression) plainSelect.getWhere()).getRightExpression(); + assertTrue(exp instanceof JdbcNamedParameter); + JdbcNamedParameter namedParameter = (JdbcNamedParameter) exp; + assertEquals("param", namedParameter.getName()); + + } + + public void testNamedParameter2() throws JSQLParserException { + String stmt = "SELECT * FROM mytable WHERE a = :param OR a = :param2 AND b = :param3"; + assertSqlCanBeParsedAndDeparsed(stmt); + + Statement st = CCJSqlParserUtil.parse(stmt); + Select select = (Select) st; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + Expression exp_l = ((BinaryExpression) plainSelect.getWhere()).getLeftExpression(); + Expression exp_r = ((BinaryExpression) plainSelect.getWhere()).getRightExpression(); + Expression exp_rl = ((BinaryExpression) exp_r).getLeftExpression(); + Expression exp_rr = ((BinaryExpression) exp_r).getRightExpression(); + + Expression exp_param1 = ((BinaryExpression) exp_l).getRightExpression(); + Expression exp_param2 = ((BinaryExpression) exp_rl).getRightExpression(); + Expression exp_param3 = ((BinaryExpression) exp_rr).getRightExpression(); + + assertTrue(exp_param1 instanceof JdbcNamedParameter); + assertTrue(exp_param2 instanceof JdbcNamedParameter); + assertTrue(exp_param3 instanceof JdbcNamedParameter); + + JdbcNamedParameter namedParameter1 = (JdbcNamedParameter) exp_param1; + JdbcNamedParameter namedParameter2 = (JdbcNamedParameter) exp_param2; + JdbcNamedParameter namedParameter3 = (JdbcNamedParameter) exp_param3; + + assertEquals("param", namedParameter1.getName()); + assertEquals("param2", namedParameter2.getName()); + assertEquals("param3", namedParameter3.getName()); + } + + public void testComplexUnion1() throws IOException, JSQLParserException { + String stmt = "(SELECT 'abc-' || coalesce(mytab.a::varchar, '') AS a, mytab.b, mytab.c AS st, mytab.d, mytab.e FROM mytab WHERE mytab.del = 0) UNION (SELECT 'cde-' || coalesce(mytab2.a::varchar, '') AS a, mytab2.b, mytab2.bezeichnung AS c, 0 AS d, 0 AS e FROM mytab2 WHERE mytab2.del = 0)"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleHierarchicalQuery() throws JSQLParserException { + String stmt = "SELECT last_name, employee_id, manager_id FROM employees CONNECT BY employee_id = manager_id ORDER BY last_name"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleHierarchicalQuery2() throws JSQLParserException { + String stmt = "SELECT employee_id, last_name, manager_id FROM employees CONNECT BY PRIOR employee_id = manager_id"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleHierarchicalQuery3() throws JSQLParserException { + String stmt = "SELECT last_name, employee_id, manager_id, LEVEL FROM employees START WITH employee_id = 100 CONNECT BY PRIOR employee_id = manager_id ORDER SIBLINGS BY last_name"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleHierarchicalQuery4() throws JSQLParserException { + String stmt = "SELECT last_name, employee_id, manager_id, LEVEL FROM employees CONNECT BY PRIOR employee_id = manager_id START WITH employee_id = 100 ORDER SIBLINGS BY last_name"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testOracleHierarchicalQueryIssue196() throws JSQLParserException { + String stmt = "SELECT num1, num2, level FROM carol_tmp START WITH num2 = 1008 CONNECT BY num2 = PRIOR num1 ORDER BY level DESC"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPostgreSQLRegExpCaseSensitiveMatch() throws JSQLParserException { + String stmt = "SELECT a, b FROM foo WHERE a ~ '[help].*'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPostgreSQLRegExpCaseSensitiveMatch2() throws JSQLParserException { + String stmt = "SELECT a, b FROM foo WHERE a ~* '[help].*'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPostgreSQLRegExpCaseSensitiveMatch3() throws JSQLParserException { + String stmt = "SELECT a, b FROM foo WHERE a !~ '[help].*'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testPostgreSQLRegExpCaseSensitiveMatch4() throws JSQLParserException { + String stmt = "SELECT a, b FROM foo WHERE a !~* '[help].*'"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testReservedKeyword() throws JSQLParserException { + final String statement = "SELECT cast, do, extract, first, following, last, materialized, nulls, partition, range, row, rows, siblings, value, xml FROM tableName"; // all of these are legal in SQL server; 'row' and 'rows' are not legal on Oracle, though; + final Select select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testReservedKeyword2() throws JSQLParserException { + final String stmt = "SELECT open FROM tableName"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testReservedKeyword3() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable1 t JOIN mytable2 AS prior ON t.id = prior.id"); + } + + public void testCharacterSetClause() throws JSQLParserException { + String stmt = "SELECT DISTINCT CAST(`view0`.`nick2` AS CHAR (8000) CHARACTER SET utf8) AS `v0` FROM people `view0` WHERE `view0`.`nick2` IS NOT NULL"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + public void testNotEqualsTo() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a != b"); + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <> b"); + } + + /*Added by Mathew on 21st Nov 2016*/ + public void testDoubleAnd() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); + } + + public void testJsonExpression() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram"); + assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'description' = 'milk'"); + assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'quantity' = 12::TEXT"); + //assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE CAST(sale->'items'->>'quantity' AS integer) = 2"); + assertSqlCanBeParsedAndDeparsed("SELECT SUM(CAST(sale->'items'->>'quantity' AS integer)) AS total_quantity_sold FROM sales"); + assertSqlCanBeParsedAndDeparsed("SELECT sale->>'items' FROM sales"); + assertSqlCanBeParsedAndDeparsed("SELECT json_typeof(sale->'items'), json_typeof(sale->'items'->'quantity') FROM sales"); + + + //The following staments can be parsed but not deparsed + for (String statement : new String[]{ + "SELECT doc->'site_name' FROM websites WHERE doc @> '{\"tags\":[{\"term\":\"paris\"}, {\"term\":\"food\"}]}'", + "SELECT * FROM sales where sale ->'items' @> '[{\"count\":0}]'", + "SELECT * FROM sales where sale ->'items' ? 'name'", + "SELECT * FROM sales where sale ->'items' -# 'name'" + }){ + Select select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement, true); + } + } + + public void testSelectInto1() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * INTO user_copy FROM user"); + } + + public void testSelectForUpdate() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM user_table FOR UPDATE"); + } + + public void testSelectForUpdate2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM emp WHERE empno = ? FOR UPDATE"); + } + + public void testSelectJoin() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT pg_class.relname, pg_attribute.attname, pg_constraint.conname " + + "FROM pg_constraint JOIN pg_class ON pg_class.oid = pg_constraint.conrelid" + + " JOIN pg_attribute ON pg_attribute.attrelid = pg_constraint.conrelid" + + " WHERE pg_constraint.contype = 'u' AND (pg_attribute.attnum = ANY(pg_constraint.conkey))" + + " ORDER BY pg_constraint.conname"); + } + + public void testSelectJoin2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM pg_constraint WHERE pg_attribute.attnum = ANY(pg_constraint.conkey)"); + } + + public void testAnyConditionSubSelect() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT e1.empno, e1.sal FROM emp e1 WHERE e1.sal > ANY (SELECT e2.sal FROM emp e2 WHERE e2.deptno = 10)"); + } + + public void testAllConditionSubSelect() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT e1.empno, e1.sal FROM emp e1 WHERE e1.sal > ALL (SELECT e2.sal FROM emp e2 WHERE e2.deptno = 10)"); + } + + public void testSelectOracleColl() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM the_table tt WHERE TT.COL1 = lines(idx).COL1"); + } + + public void testSelectInnerWith() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM (WITH actor AS (SELECT 'a' aid FROM DUAL) SELECT aid FROM actor)"); + } + + public void testSelectWithinGroup() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT LISTAGG(col1, '##') WITHIN GROUP (ORDER BY col1) FROM table1"); + } + + public void testSelectUserVariable() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT @col FROM t1"); + } + + public void testSelectNumericBind() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT a FROM b WHERE c = :1"); + } + + public void testSelectBrackets() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT avg((123.250)::numeric)"); + } + + public void testSelectBrackets2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT (EXTRACT(epoch FROM age(d1, d2)) / 2)::numeric"); + } + + public void testSelectBrackets3() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT avg((EXTRACT(epoch FROM age(d1, d2)) / 2)::numeric)"); + } + + public void testSelectBrackets4() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT (1 / 2)::numeric"); + } + + public void testSelectForUpdateOfTable() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT foo.*, bar.* FROM foo, bar WHERE foo.id = bar.foo_id FOR UPDATE OF foo"); + } + + public void testSelectWithBrackets() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("(SELECT 1 FROM mytable)"); + } + + public void testSelectWithBrackets2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("(SELECT 1)"); + } + + public void testSelectWithoutFrom() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT footable.foocolumn"); + } + + public void testSelectKeywordPercent() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT percent FROM MY_TABLE"); + } + + public void testSelectJPQLPositionalParameter() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT email FROM users WHERE (type LIKE 'B') AND (username LIKE ?1)"); + } + + public void testSelectKeep() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT col1, min(col2) KEEP (DENSE_RANK FIRST ORDER BY col3), col4 FROM table1 GROUP BY col5 ORDER BY col3"); + } + + public void testSelectKeepOver() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY commission_pct) OVER (PARTITION BY department_id ) \"Worst\" FROM employees ORDER BY department_id, salary"); + } + + public void testGroupConcat() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT student_name, GROUP_CONCAT(DISTINCT test_score ORDER BY test_score DESC SEPARATOR ' ') FROM student GROUP BY student_name"); + } + + public void testRowConstructor1() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE (col1, col2) = (SELECT col3, col4 FROM t2 WHERE id = 10)"); + } + + public void testRowConstructor2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE ROW(col1, col2) = (SELECT col3, col4 FROM t2 WHERE id = 10)"); + } + + public void testIssue154() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT d.id, d.uuid, d.name, d.amount, d.percentage, d.modified_time FROM discount d LEFT OUTER JOIN discount_category dc ON d.id = dc.discount_id WHERE merchant_id = ? AND deleted = ? AND dc.discount_id IS NULL AND modified_time < ? AND modified_time >= ? ORDER BY modified_time"); + } + + public void testIssue154_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT r.id, r.uuid, r.name, r.system_role FROM role r WHERE r.merchant_id = ? AND r.deleted_time IS NULL ORDER BY r.id DESC"); + } + + public void testIssue160_signedParameter() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT start_date WHERE start_date > DATEADD(HH, -?, GETDATE())"); + } + + public void testIssue160_signedParameter2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE -? = 5"); + } + + public void testIssue162_doubleUserVar() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT @@SPID AS ID, SYSTEM_USER AS \"Login Name\", USER AS \"User Name\""); + } + + public void testIssue167_singleQuoteEscape() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT 'a'"); + assertSqlCanBeParsedAndDeparsed("SELECT ''''"); + assertSqlCanBeParsedAndDeparsed("SELECT '\\''"); + assertSqlCanBeParsedAndDeparsed("SELECT 'ab''ab'"); + assertSqlCanBeParsedAndDeparsed("SELECT 'ab\\'ab'"); + } + + /** + * These are accepted due to reading one backslash and a double quote. + */ + public void testIssue167_singleQuoteEscape2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT '\\'''"); + assertSqlCanBeParsedAndDeparsed("SELECT '\\\\\\''"); + } + + public void testIssue77_singleQuoteEscape2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT 'test\\'' FROM dual"); + } + + public void testIssue223_singleQuoteEscape() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT '\\'test\\''"); + } + + public void testOracleHint() throws JSQLParserException { + assertOracleHintExists("SELECT /*+ SOMEHINT */ * FROM mytable", true, "SOMEHINT"); + assertOracleHintExists("SELECT /*+ MORE HINTS POSSIBLE */ * FROM mytable", true, "MORE HINTS POSSIBLE"); + assertOracleHintExists("SELECT /*+ MORE\nHINTS\t\nPOSSIBLE */ * FROM mytable", true, "MORE\nHINTS\t\nPOSSIBLE"); + assertOracleHintExists("SELECT /*+ leading(sn di md sh ot) cardinality(ot 1000) */ c, b FROM mytable", true, "leading(sn di md sh ot) cardinality(ot 1000)"); + assertOracleHintExists("SELECT /*+ ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" + + " USE_NL (glcc glf) USE_MERGE (gp gsb) */\n" + + " b.application_id\n" + + "FROM jl_br_journals j,\n" + + " po_vendors p", true, "ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" + + " USE_NL (glcc glf) USE_MERGE (gp gsb)"); + assertOracleHintExists("SELECT /*+ROWID(emp)*/ /*+ THIS IS NOT HINT! ***/ * \n" + + "FROM emp \n" + + "WHERE rowid > 'AAAAtkAABAAAFNTAAA' AND empno = 155", false, "ROWID(emp)"); + assertOracleHintExists("SELECT /*+ INDEX(patients sex_index) use sex_index because there are few\n" + + " male patients */ name, height, weight\n" + + "FROM patients\n" + + "WHERE sex = 'm'", true, "INDEX(patients sex_index) use sex_index because there are few\n male patients"); + assertOracleHintExists("SELECT /*+INDEX_COMBINE(emp sal_bmi hiredate_bmi)*/ * \n" + + "FROM emp \n" + + "WHERE sal < 50000 AND hiredate < '01-JAN-1990'", true, "INDEX_COMBINE(emp sal_bmi hiredate_bmi)"); + assertOracleHintExists("SELECT --+ CLUSTER \n" + + "emp.ename, deptno\n" + + "FROM emp, dept\n" + + "WHERE deptno = 10 \n" + + "AND emp.deptno = dept.deptno", true, "CLUSTER"); + assertOracleHintExists("SELECT --+ CLUSTER \n --+ some other comment, not hint\n /* even more comments */ * from dual", false, "CLUSTER"); + assertOracleHintExists("(SELECT * from t1) UNION (select /*+ CLUSTER */ * from dual)", true, null, "CLUSTER"); + assertOracleHintExists("(SELECT * from t1) UNION (select /*+ CLUSTER */ * from dual) UNION (select * from dual)", true, null, "CLUSTER", null); + assertOracleHintExists("(SELECT --+ HINT1 HINT2 HINT3\n * from t1) UNION (select /*+ HINT4 HINT5 */ * from dual)", true, "HINT1 HINT2 HINT3", "HINT4 HINT5"); + + } + + public void testOracleHintExpression() throws JSQLParserException { + String statement = "SELECT --+ HINT\n * FROM tab1"; + Statement parsed = parserManager.parse(new StringReader(statement)); + + assertEquals(statement, parsed.toString()); + PlainSelect plainSelect = (PlainSelect) ((Select) parsed).getSelectBody(); + assertExpressionCanBeDeparsedAs(plainSelect.getOracleHint(), "--+ HINT\n"); + } + + public void testTableFunctionWithNoParams() throws Exception { + final String statement = "SELECT f2 FROM SOME_FUNCTION()"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + assertTrue(plainSelect.getFromItem() instanceof TableFunction); + TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); + Function function = fromItem.getFunction(); + assertNotNull(function); + assertEquals("SOME_FUNCTION", function.getName()); + assertNull(function.getParameters()); + assertNull(fromItem.getAlias()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testTableFunctionWithParams() throws Exception { + final String statement = "SELECT f2 FROM SOME_FUNCTION(1, 'val')"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + assertTrue(plainSelect.getFromItem() instanceof TableFunction); + TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); + Function function = fromItem.getFunction(); + assertNotNull(function); + assertEquals("SOME_FUNCTION", function.getName()); + + // verify params + assertNotNull(function.getParameters()); + List expressions = function.getParameters().getExpressions(); + assertEquals(2, expressions.size()); + + Expression firstParam = expressions.get(0); + assertNotNull(firstParam); + assertTrue(firstParam instanceof LongValue); + assertEquals(1l, ((LongValue) firstParam).getValue()); + + Expression secondParam = expressions.get(1); + assertNotNull(secondParam); + assertTrue(secondParam instanceof StringValue); + assertEquals("val", ((StringValue) secondParam).getValue()); + + assertNull(fromItem.getAlias()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testTableFunctionWithAlias() throws Exception { + final String statement = "SELECT f2 FROM SOME_FUNCTION() AS z"; + Select select = (Select) parserManager.parse(new StringReader(statement)); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + assertTrue(plainSelect.getFromItem() instanceof TableFunction); + TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); + Function function = fromItem.getFunction(); + assertNotNull(function); + + assertEquals("SOME_FUNCTION", function.getName()); + assertNull(function.getParameters()); + assertNotNull(fromItem.getAlias()); + assertEquals("z", fromItem.getAlias().getName()); + assertStatementCanBeDeparsedAs(select, statement); + } + + public void testIssue151_tableFunction() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tables a LEFT JOIN getdata() b ON a.id = b.id"); + } + + public void testIssue217_keywordSeparator() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT Separator"); + } + + public void testIssue215_possibleEndlessParsing() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN ((value LIKE '%t1%') OR (value LIKE '%t2%')) THEN 't1s' WHEN ((((((((((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%')) OR (value LIKE '%t10%')) OR (value LIKE '%t10%')) OR (value LIKE '%CIO%')) OR (value LIKE '%cio%')) OR (value LIKE '%Cio%')) OR (value LIKE '%t11%')) OR (value LIKE '%t11%')) THEN 't' WHEN ((((value LIKE '%t12%') OR (value LIKE '%t12%')) OR (value LIKE '%VP%')) OR (value LIKE '%vp%')) THEN 'Vice t12s' WHEN ((((((value LIKE '% IT %') OR (value LIKE '%t13%')) OR (value LIKE '%t13%')) OR (value LIKE '% it %')) OR (value LIKE '%tech%')) OR (value LIKE '%Tech%')) THEN 'IT' WHEN ((((value LIKE '%Analyst%') OR (value LIKE '%t14%')) OR (value LIKE '%Analytic%')) OR (value LIKE '%analytic%')) THEN 'Analysts' WHEN ((value LIKE '%Manager%') OR (value LIKE '%manager%')) THEN 't15' ELSE 'Other' END) FROM tab1"); + } + + public void testIssue215_possibleEndlessParsing2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN ((value LIKE '%t1%') OR (value LIKE '%t2%')) THEN 't1s' ELSE 'Other' END) FROM tab1"); + } + + public void testIssue215_possibleEndlessParsing3() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((((((((((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%')) OR (value LIKE '%t10%')) OR (value LIKE '%t10%')) OR (value LIKE '%CIO%')) OR (value LIKE '%cio%')) OR (value LIKE '%Cio%')) OR (value LIKE '%t11%')) OR (value LIKE '%t11%'))"); + } + + public void testIssue215_possibleEndlessParsing4() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((value LIKE '%t3%') OR (value LIKE '%t3%'))"); + } + + public void testIssue215_possibleEndlessParsing5() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%'))"); + } + + public void testIssue215_possibleEndlessParsing6() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE (((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%'))"); + } + + public void testIssue215_possibleEndlessParsing7() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE (((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%'))"); + } + + public void testIssue230_cascadeKeyword() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT t.cascade AS cas FROM t"); + } + + public void testBooleanValue() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT col FROM t WHERE a"); + } + + public void testBooleanValue2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT col FROM t WHERE 3 < 5 AND a"); + } + + public void testNotWithoutParenthesisIssue234() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM \"Persons\" WHERE NOT \"F_NAME\" = 'John'"); + } + + public void testWhereIssue240_1() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE 1"); + } + + public void testWhereIssue240_0() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE 0"); + } + + public void testWhereIssue240_notBoolean() { + try { + CCJSqlParserUtil.parse("SELECT count(*) FROM mytable WHERE 5"); + fail("should not be parsed"); + } catch (JSQLParserException ex) { + + } + } + + public void testWhereIssue240_true() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE true"); + } + + public void testWhereIssue240_false() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE false"); + } + + public void testWhereIssue241KeywordEnd() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT l.end FROM lessons l"); + } + + public void testSpeedTestIssue235() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tbl WHERE (ROUND((((((period_diff(date_format(tbl.CD, '%Y%m'), date_format(SUBTIME(CURRENT_TIMESTAMP(), 25200), '%Y%m')) + month(SUBTIME(CURRENT_TIMESTAMP(), 25200))) - MONTH('2012-02-01')) - 1) / 3) - ROUND((((month(SUBTIME(CURRENT_TIMESTAMP(),25200)) - MONTH('2012-02-01')) - 1) / 3)))) = -3)", true); + } + + public void testSpeedTestIssue235_2() throws IOException, JSQLParserException { + String stmt = IOUtils.toString(SelectTest.class.getResourceAsStream("large-sql-issue-235.txt")); + assertSqlCanBeParsedAndDeparsed(stmt, true); + } + + public void testCastVarCharMaxIssue245() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT CAST('foo' AS NVARCHAR (MAX))"); + } + + public void testNestedFunctionCallIssue253() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT (replace_regex(replace_regex(replace_regex(get_json_string(a_column, 'value'), '\\n', ' '), '\\r', ' '), '\\\\', '\\\\\\\\')) FROM a_table WHERE b_column = 'value'"); + } + + public void testEscapedBackslashIssue253() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT replace_regex('test', '\\\\', '\\\\\\\\')"); + } + + public void testKeywordTableIssue261() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT column_value FROM table(VARCHAR_LIST_TYPE())"); + } + + public void testTopExpressionIssue243() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT TOP (? + 1) * FROM MyTable"); + } + + public void testTopExpressionIssue243_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT TOP (CAST(? AS INT)) * FROM MyTable"); + } + + public void testFunctionIssue284() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT NVL((SELECT 1 FROM DUAL), 1) AS A FROM TEST1"); + } + + public void testFunctionDateTimeValues() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tab1 WHERE a > TIMESTAMP '2004-04-30 04:05:34.56'"); + } + + public void testUniqueInsteadOfDistinctIssue299() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT UNIQUE trunc(timez(ludate)+ 8/24) bus_dt, j.object j_name , timez(j.starttime) START_TIME , timez(j.endtime) END_TIME FROM TEST_1 j", true); + } + + public void testProblemSqlIssue265() throws IOException, JSQLParserException { + String sqls = IOUtils.toString(SelectTest.class.getResourceAsStream("large-sql-with-issue-265.txt")); + Statements stmts = CCJSqlParserUtil.parseStatements(sqls); + assertEquals(2, stmts.getStatements().size()); + } + + public void testProblemSqlIssue330() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT COUNT(*) FROM C_Invoice WHERE IsSOTrx='Y' AND (Processed='N' OR Updated>(current_timestamp - CAST('90 days' AS interval))) AND C_Invoice.AD_Client_ID IN(0,1010016) AND C_Invoice.AD_Org_ID IN(0,1010053,1010095,1010094)", true); + } + + public void testProblemSqlIssue330_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT CAST('90 days' AS interval)"); + } +// won't fix due to lookahead impact on parser +// public void testKeywordOrderAsColumnnameIssue333() throws JSQLParserException { +// assertSqlCanBeParsedAndDeparsed("SELECT choice.response_choice_id AS uuid, choice.digit AS digit, choice.text_response AS textResponse, choice.voice_prompt AS voicePrompt, choice.action AS action, choice.contribution AS contribution, choice.order_num AS order, choice.description AS description, choice.is_join_conference AS joinConference, choice.voice_prompt_language_code AS voicePromptLanguageCode, choice.text_response_language_code AS textResponseLanguageCode, choice.description_language_code AS descriptionLanguageCode, choice.rec_phrase AS recordingPhrase FROM response_choices choice WHERE choice.presentation_id = ? ORDER BY choice.order_num", true); +// } + + public void testProblemKeywordCommitIssue341() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT id, commit FROM table1"); + } + + public void testProblemIsIssue331() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT C_DocType.C_DocType_ID,NULL,COALESCE(C_DocType_Trl.Name,C_DocType.Name) AS Name,C_DocType.IsActive FROM C_DocType LEFT JOIN C_DocType_TRL ON (C_DocType.C_DocType_ID=C_DocType_Trl.C_DocType_ID AND C_DocType_Trl.AD_Language='es_AR') WHERE C_DocType.AD_Client_ID=1010016 AND C_DocType.AD_Client_ID IN (0,1010016) AND C_DocType.c_doctype_id in ( select c_doctype2.c_doctype_id from c_doctype as c_doctype2 where substring( c_doctype2.printname,6, length(c_doctype2.printname) ) = ( select letra from c_letra_comprobante as clc where clc.c_letra_comprobante_id = 1010039) ) AND ( (1010094!=0 AND C_DocType.ad_org_id = 1010094) OR 1010094=0 ) ORDER BY 3 LIMIT 2000", true); + } +} From 727c732fd2178430139ad1697973a6470f048688 Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Tue, 22 Nov 2016 23:41:31 +0530 Subject: [PATCH 16/77] Support for Array Contains (&>) and ContainedBy (<&) operator added --- .../expression/ExpressionVisitor.java | 6 +++ .../expression/ExpressionVisitorAdapter.java | 13 ++++++ .../operators/relational/ContainedBy.java | 42 +++++++++++++++++++ .../operators/relational/Contains.java | 42 +++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 16 +++++++ .../util/deparser/ExpressionDeParser.java | 14 +++++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 4 ++ .../sf/jsqlparser/test/select/SelectTest.java | 10 +++++ 8 files changed, 147 insertions(+) create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java create mode 100644 src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index b35bcb792..95f12f0b9 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -44,6 +44,8 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;//Added by mathew on 21st Nov 2016 +import net.sf.jsqlparser.expression.operators.relational.Contains;//Added by mathew on 21st Nov 2016 +import net.sf.jsqlparser.expression.operators.relational.ContainedBy;//Added by mathew on 21st Nov 2016 import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; @@ -127,6 +129,10 @@ public interface ExpressionVisitor { void visit(NotEqualsTo notEqualsTo); void visit(DoubleAnd doubleAnd);//Added by mathew on 21st Nov 2016 + + void visit(Contains contains);//Added by mathew on 21st Nov 2016 + + void visit(ContainedBy containedBy);//Added by mathew on 21st Nov 2016 void visit(ParenthesedSelect selectBody); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 60881666f..07b943025 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -260,6 +260,19 @@ public void visit(NotEqualsTo expr) { public void visit(DoubleAnd expr) { visitBinaryExpression(expr); } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(Contains expr) { + visitBinaryExpression(expr); + } + + /*Added by mathew on 21st Nov 2016*/ + @Override + public void visit(ContainedBy expr) { + visitBinaryExpression(expr); + } + @Override public void visit(Column column) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java new file mode 100644 index 000000000..ab154f18f --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java @@ -0,0 +1,42 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 21st Nov 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class ContainedBy extends ComparisonOperator { + + public ContainedBy() { + super("<&"); + } + + public ContainedBy(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java new file mode 100644 index 000000000..98dcac02d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java @@ -0,0 +1,42 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +/*Added by Mathew on 21st Nov 2016*/ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.expression.ExpressionVisitor; + +public class Contains extends ComparisonOperator { + + public Contains() { + super("&>"); + } + + public Contains(String operator) { + super(operator); + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } +} + diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 16e2caf50..c1f920e65 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -477,6 +477,22 @@ public void visit(NotEqualsTo notEqualsTo) { public void visit(DoubleAnd doubleAnd) { visitBinaryExpression(doubleAnd); } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(Contains contains) { + visitBinaryExpression(contains); + } + + /* Added by Mathew on 21st Nov 2016 + * + */ + @Override + public void visit(ContainedBy containedBy) { + visitBinaryExpression(containedBy); + } @Override public void visit(NullValue nullValue) { 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 a1e7f8e9d..67feadc41 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -408,6 +408,20 @@ public void visit(DoubleAnd doubleAnd) { visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(Contains contains) { + visitOldOracleJoinBinaryExpression(contains, " " + contains.getStringExpression() + " "); + + } + + /*Added by Mathew on November 21st 2016*/ + @Override + public void visit(ContainedBy containedBy) { + visitOldOracleJoinBinaryExpression(containedBy, " " + containedBy.getStringExpression() + " "); + + } @Override public void visit(NullValue nullValue) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 0e12a67ed..aafbecb1d 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -489,6 +489,8 @@ TOKEN : /* Operators */ | )* "|"> | )* ">"> | /*added by mathew on 21st Nov 2016*/ +| "> /*added by mathew on 21st Nov 2016*/ +| /*added by mathew on 21st Nov 2016*/ } TOKEN : /* Date/Time with time zones */ @@ -3389,6 +3391,8 @@ Expression RegularCondition() #RegularCondition: | token= { result = new NotEqualsTo(token.image); } | token= { result = new NotEqualsTo(token.image); } | token= { result = new DoubleAnd(token.image); } + | token= { result = new DoubleAnd(token.image); } + | token= { result = new DoubleAnd(token.image); } | "@@" { result = new Matches(); } | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } diff --git a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java index d161c1826..6b63cb006 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -1852,6 +1852,16 @@ public void testNotEqualsTo() throws JSQLParserException { public void testDoubleAnd() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); } + + /*Added by Mathew on 21st Nov 2016*/ + public void testContains() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a &> b"); + } + + /*Added by Mathew on 21st Nov 2016*/ + public void testContainedBy() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <& b"); + } public void testJsonExpression() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram"); From 59104fd96f29a2e28e93a04746545d7acd05283b Mon Sep 17 00:00:00 2001 From: MathewJoseph31 Date: Wed, 23 Nov 2016 15:24:02 +0530 Subject: [PATCH 17/77] Support for Nested With Clauses Added --- .../jsqlparser/util/WithTransformation.java | 415 ++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 src/main/java/net/sf/jsqlparser/util/WithTransformation.java diff --git a/src/main/java/net/sf/jsqlparser/util/WithTransformation.java b/src/main/java/net/sf/jsqlparser/util/WithTransformation.java new file mode 100644 index 000000000..deeb24054 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/WithTransformation.java @@ -0,0 +1,415 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * @author mathew joseph (MathewJoseph31) + * + */ +package net.sf.jsqlparser.util; + +import java.util.List; + +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.AllComparisonExpression; +import net.sf.jsqlparser.expression.AnyComparisonExpression; +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.InExpression; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SelectExpressionItem; +import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.SubJoin; +import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.statement.select.WithItem; + +public class WithTransformation { + SelectBody selectBody; + java.util.logging.Logger logger=java.util.logging.Logger.getLogger(WithTransformation.class.getName()); + + public WithTransformation(SelectBody selBody,List withItemsList) { + selectBody=selBody; + if(withItemsList==null||withItemsList.isEmpty()) + return; + for(int i=0;i), and Join ( in the above case ), deal with them separately + */ + + SubJoin transformSubJoinForWithAs(WithItem srcWithItem, SubJoin subJoin){ + FromItem leftFromItem=subJoin.getLeft(); + //checks the left item (assumed to be a Table), if its name is the same as the name of the + // input with item, the name is substitued by its corresponding definition + if(leftFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)leftFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + subJoin.setLeft(sub); + } + } + //checks if the left item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call + else if(leftFromItem instanceof SubJoin){ + transformSubJoinForWithAs(srcWithItem,(SubJoin)leftFromItem); + } + // if fromitem is a subselect , then call the corresponding method that handles it + else if(leftFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect leftSubSelect=(SubSelect) leftFromItem; + transformSubSelectForWithAs(srcWithItem, leftSubSelect); + } + // deals with the right item of the subjoin + FromItem rightFromItem=subJoin.getJoin().getRightItem(); + //checks if the right item (assumed to be a Table or SubJoin itself) is a Table, then if its name is the same as the name of the + // input with item, the name is substituted by its corresponding definition + + if(rightFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)rightFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + subJoin.getJoin().setRightItem(sub); + } + } + //checks if the right item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call + else if(rightFromItem instanceof SubJoin){ + transformSubJoinForWithAs(srcWithItem,(SubJoin)rightFromItem); + } + else if(rightFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect rightSubSelect=(SubSelect) rightFromItem; + transformSubSelectForWithAs(srcWithItem, rightSubSelect); + } + return subJoin; + } + + /** @author mathew + * Transforms the given input PlainSelect statement by substituting alias name of its first argument + * with its corresponding definition in its select body + * + */ + + PlainSelect transformPlainSelectForWithAs(WithItem srcWithItem, PlainSelect tarSelectClause){ + // Starts by dealing with from items, a from item can be a Table name, a subselect statement, or a subjoin statement + FromItem tarFromItem=tarSelectClause.getFromItem(); + // if fromitem is a Table then check if its name is equal to the name of the input withitem, if yes substitutes its occurence + // by its definition + if(tarFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found as from item"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + tarSelectClause.setFromItem(sub); + } + } + // if fromitem is a subselect , then call the corresponding method that handles it + else if(tarFromItem instanceof SubSelect){ + logger.info("processing subselect"); + SubSelect tarSubSelect=(SubSelect) tarFromItem; + transformSubSelectForWithAs(srcWithItem, tarSubSelect); + } + // if fromitem is a subjoin, then call the corresponding method that handles it + else if(tarFromItem instanceof SubJoin){ + logger.info("processing subjoin"); + SubJoin tarSubJoin=(SubJoin)tarFromItem; + tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); + } + + // WITH AS names can be in joins as well, call the corresponding method that handles it + if(tarSelectClause.getJoins() != null ){ + logger.info("processing joins"); + List joinList = tarSelectClause.getJoins(); + transformJoinsForWithAs(srcWithItem, joinList); + + } + // Now deal with its where clauses + transformWhereClauseForWithAs(srcWithItem, tarSelectClause.getWhere()); + return tarSelectClause; + } + + /** @author mathew + * Extract the select statement from the subselect. It can be either a PlainSelect + * or a SetOperation, deal with their transformations separately + */ + + private void transformSubSelectForWithAs(WithItem srcWithItem, SubSelect tarSubSelect) { + //when the select body is a plain select statement, then call the corresponding method that handles it + if(tarSubSelect.getSelectBody() instanceof PlainSelect){ + PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); + transformPlainSelectForWithAs(srcWithItem, tempSelect); + } + //where the select body is a set operation (union, intersect etc), then call the corresponding method that handles it + else if(tarSubSelect.getSelectBody() instanceof SetOperationList){ + SetOperationList setOpList=(SetOperationList)tarSubSelect.getSelectBody(); + transformSetOperationListForWithAs(srcWithItem, setOpList); + } + + } + + /** @author mathew + * Transforms set operation statements eg: (A Union (B INTERSECT C) + * Splits the operands and deals with them separately + */ + + private void transformSetOperationListForWithAs(WithItem srcWithItem, SetOperationList setOpList) { + // TODO Auto-generated method stub + for(SelectBody selBody:setOpList.getSelects()){ + if(selBody instanceof PlainSelect){ + PlainSelect tarSelectClause=(PlainSelect) selBody; + transformPlainSelectForWithAs(srcWithItem, tarSelectClause); + } + else if(selBody instanceof SetOperationList){ + transformSetOperationListForWithAs(srcWithItem, (SetOperationList)selBody); + } + } + + } + + /** @author mathew + * Transforms joinList in the from clause of a select statement by looking for occurences + * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect + * Each join item is assumed to be a table, a subselect, or a subjoin + */ + private void transformJoinsForWithAs(WithItem srcWithItem, List joinList){ + for(int k=0; k < joinList.size(); k++){ + Join jcl = joinList.get(k); + FromItem tarJoinFromItem=jcl.getRightItem(); + //if the join item is a table, then call the corresponding method that handles it + if(tarJoinFromItem instanceof net.sf.jsqlparser.schema.Table){ + net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarJoinFromItem; + if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ + logger.info("found with alias in from item of join"); + //Get withItem and create new SubSelect if fromItem name is equal to with item name + Alias a; + if(tarTable.getAlias()!=null) + a=new Alias(tarTable.getAlias().getName()); + else + a = new Alias(srcWithItem.getName()); + a.setUseAs(true); + SubSelect sub = new SubSelect(); + sub.setSelectBody(srcWithItem.getSelectBody()); + sub.setAlias(a); + jcl.setRightItem(sub); + } + } + //if join item is a subselect, then call the corresponding method that handles it + else if(tarJoinFromItem instanceof SubSelect){ + logger.info("processing subselect in join"); + SubSelect tarSubSelect=(SubSelect) tarJoinFromItem; + PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); + transformPlainSelectForWithAs(srcWithItem, tempSelect); + } + //if join item is a subjoin, then call the corresponding method that handles it + else if(tarJoinFromItem instanceof SubJoin){ + logger.info("processing subjoin in join"); + SubJoin tarSubJoin=(SubJoin)tarJoinFromItem; + tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); + } + + } + } + + /** @author mathew + * Transforms the expression in the where clause of a select statement by looking for occurences + * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect + */ + private void transformWhereClauseForWithAs(WithItem srcWithItem, Expression whereClause) { + //if whereClause is a subselect, then call the corresponding method that handles it + if(whereClause instanceof SubSelect){ + SubSelect subSelect=(SubSelect) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(subSelect.getSelectBody())); + } + //if whereClause is a BinaryExpression (AND, OR etc.), then + //split its operands and recursive calls using them as arguments + if(whereClause instanceof BinaryExpression){ + BinaryExpression binExpression = ((BinaryExpression) whereClause); + transformWhereClauseForWithAs(srcWithItem, binExpression.getLeftExpression()); + transformWhereClauseForWithAs(srcWithItem, binExpression.getRightExpression()); + } + //if whereClause is a Exists Expression, then recurisively call using its right expression as argument + else if(whereClause instanceof ExistsExpression){ + logger.info("transforming exists in where clause"); + ExistsExpression existsExpression = (ExistsExpression)whereClause; + transformWhereClauseForWithAs(srcWithItem,existsExpression.getRightExpression()); + } + //if whereClause is a InExpression, then handle its left and right operands separately + else if(whereClause instanceof InExpression){ + logger.info("transforming inExpression in where clause"); + InExpression inExpression = (InExpression)whereClause; + if (inExpression.getLeftItemsList() instanceof SubSelect){ + transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getLeftItemsList()); + } + if(inExpression.getRightItemsList() instanceof SubSelect){ + transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getRightItemsList()); + } + } + //if whereClause is a All comparision Expression, then recurisively call using its subselect as argument + else if(whereClause instanceof AllComparisonExpression){ + logger.info("transforming all comparison in where clause"); + AllComparisonExpression ace = (AllComparisonExpression) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); + } + //if whereClause is a Any comparision Expression, then recurisively call using its subselect as argument + else if(whereClause instanceof AnyComparisonExpression){ + logger.info("transforming any comparison in where clause"); + AnyComparisonExpression ace = (AnyComparisonExpression) whereClause; + transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); + } + + } + + + + /** @author mathew + * normalize column names in with items eg: With A(a) as (select name from ...) + * is normalized to With A(a) as (select name as a from ...) + */ + private WithItem normalizeWithItem(WithItem withItem) { + // TODO Auto-generated method stub + + if(withItem.getSelectBody() instanceof PlainSelect){ + PlainSelect selectClause =(PlainSelect) withItem.getSelectBody(); + normalizeSelectedColumnsForWithItem(withItem, selectClause); + + } + if(withItem.getSelectBody() instanceof SetOperationList){ + SetOperationList setOpList=(SetOperationList)withItem.getSelectBody(); + normalizeSelectedColumnsForWithItem(withItem, setOpList); + + } + logger.info(" normalized with item "+withItem.getName()+" withItemBody: "+withItem.getSelectBody()); + return withItem; + } + + + /** @author mathew + * deals with normalization of Setoperation Queries. Eg. A, B, C are normalized individually + * in a select query of the form A UNION B UNION C + */ + private void normalizeSelectedColumnsForWithItem(WithItem withItem, SetOperationList setOpList) { + for(SelectBody selBody:setOpList.getSelects()){ + if(selBody instanceof PlainSelect){ + PlainSelect selectClause=(PlainSelect) selBody; + normalizeSelectedColumnsForWithItem(withItem, selectClause); + } + else if(selBody instanceof SetOperationList){ + normalizeSelectedColumnsForWithItem(withItem, (SetOperationList)selBody); + } + } + } + + /** @author mathew + * deals with normalization of columns items in PlainSelect Queries. eg: With A(a) as (select name from ...) + * is normalized to With A(a) as (select name as a from ...) + * + */ + private void normalizeSelectedColumnsForWithItem(WithItem withItem, PlainSelect selectClause) { + // TODO Auto-generated method stub + if(withItem.getWithItemList()!=null &&!withItem.getWithItemList().isEmpty() ){ + for(int i=0;ii){ + SelectItem sItem=selectClause.getSelectItems().get(i); + if(sItem instanceof SelectExpressionItem){ + SelectExpressionItem selExpItem=(SelectExpressionItem)sItem; + Alias a = new Alias(withSelItem.toString()); + a.setUseAs(true); + selExpItem.setAlias(a); + } + } + } + } + } + + public SelectBody getSelectBody(){ + return selectBody; + } + + +} From 09d6dfe7bc7acb82b90292783c90f13b5dd8f253 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Tue, 12 Sep 2023 19:58:26 +0700 Subject: [PATCH 18/77] feat: Postgres `Contains` and `ContainedBy` Operators - sourced from https://github.com/MathewJoseph31/JSqlParser/tree/add-postgress-nested-WithClause - resolves PR #361 --- .../expression/ExpressionVisitor.java | 19 +- .../expression/ExpressionVisitorAdapter.java | 11 +- .../operators/relational/ContainedBy.java | 23 +- .../operators/relational/Contains.java | 23 +- .../operators/relational/DoubleAnd.java | 23 +- .../sf/jsqlparser/util/TablesNamesFinder.java | 18 +- .../jsqlparser/util/WithTransformation.java | 415 --- .../util/deparser/ExpressionDeParser.java | 31 +- .../validator/ExpressionValidator.java | 18 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 45 +- .../relational/ComparisonOperatorTest.java | 38 + .../sf/jsqlparser/test/select/SelectTest.java | 2299 ----------------- 12 files changed, 124 insertions(+), 2839 deletions(-) delete mode 100644 src/main/java/net/sf/jsqlparser/util/WithTransformation.java create mode 100644 src/test/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperatorTest.java delete mode 100644 src/test/java/net/sf/jsqlparser/test/select/SelectTest.java diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index 95f12f0b9..ec6be6809 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -43,9 +43,12 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; -import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;//Added by mathew on 21st Nov 2016 -import net.sf.jsqlparser.expression.operators.relational.Contains;//Added by mathew on 21st Nov 2016 -import net.sf.jsqlparser.expression.operators.relational.ContainedBy;//Added by mathew on 21st Nov 2016 +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;// Added by mathew on 21st Nov + // 2016 +import net.sf.jsqlparser.expression.operators.relational.Contains;// Added by mathew on 21st Nov + // 2016 +import net.sf.jsqlparser.expression.operators.relational.ContainedBy;// Added by mathew on 21st Nov + // 2016 import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; @@ -126,13 +129,13 @@ public interface ExpressionVisitor { void visit(MinorThanEquals minorThanEquals); - void visit(NotEqualsTo notEqualsTo); + void visit(NotEqualsTo notEqualsTo); - void visit(DoubleAnd doubleAnd);//Added by mathew on 21st Nov 2016 - - void visit(Contains contains);//Added by mathew on 21st Nov 2016 + void visit(DoubleAnd doubleAnd);// Added by mathew on 21st Nov 2016 - void visit(ContainedBy containedBy);//Added by mathew on 21st Nov 2016 + void visit(Contains contains);// Added by mathew on 21st Nov 2016 + + void visit(ContainedBy containedBy);// Added by mathew on 21st Nov 2016 void visit(ParenthesedSelect selectBody); diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 07b943025..4e5515383 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -25,6 +25,9 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.conditional.XorExpression; import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; @@ -254,26 +257,22 @@ public void visit(MinorThanEquals expr) { public void visit(NotEqualsTo expr) { visitBinaryExpression(expr); } - - /*Added by mathew on 21st Nov 2016*/ + @Override public void visit(DoubleAnd expr) { visitBinaryExpression(expr); } - - /*Added by mathew on 21st Nov 2016*/ + @Override public void visit(Contains expr) { visitBinaryExpression(expr); } - /*Added by mathew on 21st Nov 2016*/ @Override public void visit(ContainedBy expr) { visitBinaryExpression(expr); } - @Override public void visit(Column column) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java index ab154f18f..b0a5abcbb 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ContainedBy.java @@ -1,25 +1,12 @@ -/* +/*- * #%L * JSQLParser library * %% - * Copyright (C) 2004 - 2013 JSQLParser + * Copyright (C) 2004 - 2023 JSQLParser * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 * #L% */ -/*Added by Mathew on 21st Nov 2016*/ package net.sf.jsqlparser.expression.operators.relational; import net.sf.jsqlparser.expression.ExpressionVisitor; @@ -30,10 +17,6 @@ public ContainedBy() { super("<&"); } - public ContainedBy(String operator) { - super(operator); - } - @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java index 98dcac02d..c362dc2be 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/Contains.java @@ -1,25 +1,12 @@ -/* +/*- * #%L * JSQLParser library * %% - * Copyright (C) 2004 - 2013 JSQLParser + * Copyright (C) 2004 - 2023 JSQLParser * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 * #L% */ -/*Added by Mathew on 21st Nov 2016*/ package net.sf.jsqlparser.expression.operators.relational; import net.sf.jsqlparser.expression.ExpressionVisitor; @@ -30,10 +17,6 @@ public Contains() { super("&>"); } - public Contains(String operator) { - super(operator); - } - @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java index 8b531cafd..c060cbc52 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/DoubleAnd.java @@ -1,25 +1,12 @@ -/* +/*- * #%L * JSQLParser library * %% - * Copyright (C) 2004 - 2013 JSQLParser + * Copyright (C) 2004 - 2023 JSQLParser * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 * #L% */ -/*Added by Mathew on 1st Aug 2016*/ package net.sf.jsqlparser.expression.operators.relational; import net.sf.jsqlparser.expression.ExpressionVisitor; @@ -30,10 +17,6 @@ public DoubleAnd() { super("&&"); } - public DoubleAnd(String operator) { - super(operator); - } - @Override public void accept(ExpressionVisitor expressionVisitor) { expressionVisitor.visit(this); diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index c1f920e65..e2789454a 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -78,6 +78,9 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.conditional.XorExpression; import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; @@ -469,26 +472,17 @@ public void visit(Multiplication multiplication) { public void visit(NotEqualsTo notEqualsTo) { visitBinaryExpression(notEqualsTo); } - - /* Added by Mathew on 21st Nov 2016 - * - */ + @Override public void visit(DoubleAnd doubleAnd) { visitBinaryExpression(doubleAnd); } - - /* Added by Mathew on 21st Nov 2016 - * - */ + @Override public void visit(Contains contains) { visitBinaryExpression(contains); } - - /* Added by Mathew on 21st Nov 2016 - * - */ + @Override public void visit(ContainedBy containedBy) { visitBinaryExpression(containedBy); diff --git a/src/main/java/net/sf/jsqlparser/util/WithTransformation.java b/src/main/java/net/sf/jsqlparser/util/WithTransformation.java deleted file mode 100644 index deeb24054..000000000 --- a/src/main/java/net/sf/jsqlparser/util/WithTransformation.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * #%L - * JSQLParser library - * %% - * Copyright (C) 2004 - 2016 JSQLParser - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 2.1 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * . - * #L% - */ - -/** - * @author mathew joseph (MathewJoseph31) - * - */ -package net.sf.jsqlparser.util; - -import java.util.List; - -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.AllComparisonExpression; -import net.sf.jsqlparser.expression.AnyComparisonExpression; -import net.sf.jsqlparser.expression.BinaryExpression; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; -import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.statement.select.FromItem; -import net.sf.jsqlparser.statement.select.Join; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.SelectBody; -import net.sf.jsqlparser.statement.select.SelectExpressionItem; -import net.sf.jsqlparser.statement.select.SelectItem; -import net.sf.jsqlparser.statement.select.SetOperationList; -import net.sf.jsqlparser.statement.select.SubJoin; -import net.sf.jsqlparser.statement.select.SubSelect; -import net.sf.jsqlparser.statement.select.WithItem; - -public class WithTransformation { - SelectBody selectBody; - java.util.logging.Logger logger=java.util.logging.Logger.getLogger(WithTransformation.class.getName()); - - public WithTransformation(SelectBody selBody,List withItemsList) { - selectBody=selBody; - if(withItemsList==null||withItemsList.isEmpty()) - return; - for(int i=0;i), and Join ( in the above case ), deal with them separately - */ - - SubJoin transformSubJoinForWithAs(WithItem srcWithItem, SubJoin subJoin){ - FromItem leftFromItem=subJoin.getLeft(); - //checks the left item (assumed to be a Table), if its name is the same as the name of the - // input with item, the name is substitued by its corresponding definition - if(leftFromItem instanceof net.sf.jsqlparser.schema.Table){ - net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)leftFromItem; - if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ - logger.info("found as from item"); - //Get withItem and create new SubSelect if fromItem name is equal to with item name - Alias a; - if(tarTable.getAlias()!=null) - a=new Alias(tarTable.getAlias().getName()); - else - a = new Alias(srcWithItem.getName()); - a.setUseAs(true); - SubSelect sub = new SubSelect(); - sub.setSelectBody(srcWithItem.getSelectBody()); - sub.setAlias(a); - subJoin.setLeft(sub); - } - } - //checks if the left item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call - else if(leftFromItem instanceof SubJoin){ - transformSubJoinForWithAs(srcWithItem,(SubJoin)leftFromItem); - } - // if fromitem is a subselect , then call the corresponding method that handles it - else if(leftFromItem instanceof SubSelect){ - logger.info("processing subselect"); - SubSelect leftSubSelect=(SubSelect) leftFromItem; - transformSubSelectForWithAs(srcWithItem, leftSubSelect); - } - // deals with the right item of the subjoin - FromItem rightFromItem=subJoin.getJoin().getRightItem(); - //checks if the right item (assumed to be a Table or SubJoin itself) is a Table, then if its name is the same as the name of the - // input with item, the name is substituted by its corresponding definition - - if(rightFromItem instanceof net.sf.jsqlparser.schema.Table){ - net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)rightFromItem; - if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ - logger.info("found as from item"); - //Get withItem and create new SubSelect if fromItem name is equal to with item name - Alias a; - if(tarTable.getAlias()!=null) - a=new Alias(tarTable.getAlias().getName()); - else - a = new Alias(srcWithItem.getName()); - a.setUseAs(true); - SubSelect sub = new SubSelect(); - sub.setSelectBody(srcWithItem.getSelectBody()); - sub.setAlias(a); - subJoin.getJoin().setRightItem(sub); - } - } - //checks if the right item (assumed to be a Table or SubJoin itself) is a SubJoin, if then recursive call - else if(rightFromItem instanceof SubJoin){ - transformSubJoinForWithAs(srcWithItem,(SubJoin)rightFromItem); - } - else if(rightFromItem instanceof SubSelect){ - logger.info("processing subselect"); - SubSelect rightSubSelect=(SubSelect) rightFromItem; - transformSubSelectForWithAs(srcWithItem, rightSubSelect); - } - return subJoin; - } - - /** @author mathew - * Transforms the given input PlainSelect statement by substituting alias name of its first argument - * with its corresponding definition in its select body - * - */ - - PlainSelect transformPlainSelectForWithAs(WithItem srcWithItem, PlainSelect tarSelectClause){ - // Starts by dealing with from items, a from item can be a Table name, a subselect statement, or a subjoin statement - FromItem tarFromItem=tarSelectClause.getFromItem(); - // if fromitem is a Table then check if its name is equal to the name of the input withitem, if yes substitutes its occurence - // by its definition - if(tarFromItem instanceof net.sf.jsqlparser.schema.Table){ - net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarFromItem; - if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ - logger.info("found as from item"); - //Get withItem and create new SubSelect if fromItem name is equal to with item name - Alias a; - if(tarTable.getAlias()!=null) - a=new Alias(tarTable.getAlias().getName()); - else - a = new Alias(srcWithItem.getName()); - a.setUseAs(true); - SubSelect sub = new SubSelect(); - sub.setSelectBody(srcWithItem.getSelectBody()); - sub.setAlias(a); - tarSelectClause.setFromItem(sub); - } - } - // if fromitem is a subselect , then call the corresponding method that handles it - else if(tarFromItem instanceof SubSelect){ - logger.info("processing subselect"); - SubSelect tarSubSelect=(SubSelect) tarFromItem; - transformSubSelectForWithAs(srcWithItem, tarSubSelect); - } - // if fromitem is a subjoin, then call the corresponding method that handles it - else if(tarFromItem instanceof SubJoin){ - logger.info("processing subjoin"); - SubJoin tarSubJoin=(SubJoin)tarFromItem; - tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); - } - - // WITH AS names can be in joins as well, call the corresponding method that handles it - if(tarSelectClause.getJoins() != null ){ - logger.info("processing joins"); - List joinList = tarSelectClause.getJoins(); - transformJoinsForWithAs(srcWithItem, joinList); - - } - // Now deal with its where clauses - transformWhereClauseForWithAs(srcWithItem, tarSelectClause.getWhere()); - return tarSelectClause; - } - - /** @author mathew - * Extract the select statement from the subselect. It can be either a PlainSelect - * or a SetOperation, deal with their transformations separately - */ - - private void transformSubSelectForWithAs(WithItem srcWithItem, SubSelect tarSubSelect) { - //when the select body is a plain select statement, then call the corresponding method that handles it - if(tarSubSelect.getSelectBody() instanceof PlainSelect){ - PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); - transformPlainSelectForWithAs(srcWithItem, tempSelect); - } - //where the select body is a set operation (union, intersect etc), then call the corresponding method that handles it - else if(tarSubSelect.getSelectBody() instanceof SetOperationList){ - SetOperationList setOpList=(SetOperationList)tarSubSelect.getSelectBody(); - transformSetOperationListForWithAs(srcWithItem, setOpList); - } - - } - - /** @author mathew - * Transforms set operation statements eg: (A Union (B INTERSECT C) - * Splits the operands and deals with them separately - */ - - private void transformSetOperationListForWithAs(WithItem srcWithItem, SetOperationList setOpList) { - // TODO Auto-generated method stub - for(SelectBody selBody:setOpList.getSelects()){ - if(selBody instanceof PlainSelect){ - PlainSelect tarSelectClause=(PlainSelect) selBody; - transformPlainSelectForWithAs(srcWithItem, tarSelectClause); - } - else if(selBody instanceof SetOperationList){ - transformSetOperationListForWithAs(srcWithItem, (SetOperationList)selBody); - } - } - - } - - /** @author mathew - * Transforms joinList in the from clause of a select statement by looking for occurences - * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect - * Each join item is assumed to be a table, a subselect, or a subjoin - */ - private void transformJoinsForWithAs(WithItem srcWithItem, List joinList){ - for(int k=0; k < joinList.size(); k++){ - Join jcl = joinList.get(k); - FromItem tarJoinFromItem=jcl.getRightItem(); - //if the join item is a table, then call the corresponding method that handles it - if(tarJoinFromItem instanceof net.sf.jsqlparser.schema.Table){ - net.sf.jsqlparser.schema.Table tarTable= (net.sf.jsqlparser.schema.Table)tarJoinFromItem; - if(tarTable.getName().equalsIgnoreCase(srcWithItem.getName())){ - logger.info("found with alias in from item of join"); - //Get withItem and create new SubSelect if fromItem name is equal to with item name - Alias a; - if(tarTable.getAlias()!=null) - a=new Alias(tarTable.getAlias().getName()); - else - a = new Alias(srcWithItem.getName()); - a.setUseAs(true); - SubSelect sub = new SubSelect(); - sub.setSelectBody(srcWithItem.getSelectBody()); - sub.setAlias(a); - jcl.setRightItem(sub); - } - } - //if join item is a subselect, then call the corresponding method that handles it - else if(tarJoinFromItem instanceof SubSelect){ - logger.info("processing subselect in join"); - SubSelect tarSubSelect=(SubSelect) tarJoinFromItem; - PlainSelect tempSelect=(PlainSelect)tarSubSelect.getSelectBody(); - transformPlainSelectForWithAs(srcWithItem, tempSelect); - } - //if join item is a subjoin, then call the corresponding method that handles it - else if(tarJoinFromItem instanceof SubJoin){ - logger.info("processing subjoin in join"); - SubJoin tarSubJoin=(SubJoin)tarJoinFromItem; - tarSubJoin=transformSubJoinForWithAs(srcWithItem,tarSubJoin); - } - - } - } - - /** @author mathew - * Transforms the expression in the where clause of a select statement by looking for occurences - * of the name of the input with item, if any occurence is found its corresponding definition is substituted as a subselect - */ - private void transformWhereClauseForWithAs(WithItem srcWithItem, Expression whereClause) { - //if whereClause is a subselect, then call the corresponding method that handles it - if(whereClause instanceof SubSelect){ - SubSelect subSelect=(SubSelect) whereClause; - transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(subSelect.getSelectBody())); - } - //if whereClause is a BinaryExpression (AND, OR etc.), then - //split its operands and recursive calls using them as arguments - if(whereClause instanceof BinaryExpression){ - BinaryExpression binExpression = ((BinaryExpression) whereClause); - transformWhereClauseForWithAs(srcWithItem, binExpression.getLeftExpression()); - transformWhereClauseForWithAs(srcWithItem, binExpression.getRightExpression()); - } - //if whereClause is a Exists Expression, then recurisively call using its right expression as argument - else if(whereClause instanceof ExistsExpression){ - logger.info("transforming exists in where clause"); - ExistsExpression existsExpression = (ExistsExpression)whereClause; - transformWhereClauseForWithAs(srcWithItem,existsExpression.getRightExpression()); - } - //if whereClause is a InExpression, then handle its left and right operands separately - else if(whereClause instanceof InExpression){ - logger.info("transforming inExpression in where clause"); - InExpression inExpression = (InExpression)whereClause; - if (inExpression.getLeftItemsList() instanceof SubSelect){ - transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getLeftItemsList()); - } - if(inExpression.getRightItemsList() instanceof SubSelect){ - transformWhereClauseForWithAs(srcWithItem, (SubSelect)inExpression.getRightItemsList()); - } - } - //if whereClause is a All comparision Expression, then recurisively call using its subselect as argument - else if(whereClause instanceof AllComparisonExpression){ - logger.info("transforming all comparison in where clause"); - AllComparisonExpression ace = (AllComparisonExpression) whereClause; - transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); - } - //if whereClause is a Any comparision Expression, then recurisively call using its subselect as argument - else if(whereClause instanceof AnyComparisonExpression){ - logger.info("transforming any comparison in where clause"); - AnyComparisonExpression ace = (AnyComparisonExpression) whereClause; - transformPlainSelectForWithAs(srcWithItem, (PlainSelect)(ace.getSubSelect().getSelectBody())); - } - - } - - - - /** @author mathew - * normalize column names in with items eg: With A(a) as (select name from ...) - * is normalized to With A(a) as (select name as a from ...) - */ - private WithItem normalizeWithItem(WithItem withItem) { - // TODO Auto-generated method stub - - if(withItem.getSelectBody() instanceof PlainSelect){ - PlainSelect selectClause =(PlainSelect) withItem.getSelectBody(); - normalizeSelectedColumnsForWithItem(withItem, selectClause); - - } - if(withItem.getSelectBody() instanceof SetOperationList){ - SetOperationList setOpList=(SetOperationList)withItem.getSelectBody(); - normalizeSelectedColumnsForWithItem(withItem, setOpList); - - } - logger.info(" normalized with item "+withItem.getName()+" withItemBody: "+withItem.getSelectBody()); - return withItem; - } - - - /** @author mathew - * deals with normalization of Setoperation Queries. Eg. A, B, C are normalized individually - * in a select query of the form A UNION B UNION C - */ - private void normalizeSelectedColumnsForWithItem(WithItem withItem, SetOperationList setOpList) { - for(SelectBody selBody:setOpList.getSelects()){ - if(selBody instanceof PlainSelect){ - PlainSelect selectClause=(PlainSelect) selBody; - normalizeSelectedColumnsForWithItem(withItem, selectClause); - } - else if(selBody instanceof SetOperationList){ - normalizeSelectedColumnsForWithItem(withItem, (SetOperationList)selBody); - } - } - } - - /** @author mathew - * deals with normalization of columns items in PlainSelect Queries. eg: With A(a) as (select name from ...) - * is normalized to With A(a) as (select name as a from ...) - * - */ - private void normalizeSelectedColumnsForWithItem(WithItem withItem, PlainSelect selectClause) { - // TODO Auto-generated method stub - if(withItem.getWithItemList()!=null &&!withItem.getWithItemList().isEmpty() ){ - for(int i=0;ii){ - SelectItem sItem=selectClause.getSelectItems().get(i); - if(sItem instanceof SelectExpressionItem){ - SelectExpressionItem selExpItem=(SelectExpressionItem)sItem; - Alias a = new Alias(withSelItem.toString()); - a.setUseAs(true); - selExpItem.setAlias(a); - } - } - } - } - } - - public SelectBody getSelectBody(){ - return selectBody; - } - - -} 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 67feadc41..16f3ef0b0 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -78,6 +78,9 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.conditional.XorExpression; import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; @@ -401,27 +404,25 @@ public void visit(NotEqualsTo notEqualsTo) { " " + notEqualsTo.getStringExpression() + " "); } - - /*Added by Mathew on November 21st 2016*/ + @Override - public void visit(DoubleAnd doubleAnd) { - visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); + public void visit(DoubleAnd doubleAnd) { + visitOldOracleJoinBinaryExpression(doubleAnd, " " + doubleAnd.getStringExpression() + " "); + + } - } - - /*Added by Mathew on November 21st 2016*/ @Override - public void visit(Contains contains) { - visitOldOracleJoinBinaryExpression(contains, " " + contains.getStringExpression() + " "); + public void visit(Contains contains) { + visitOldOracleJoinBinaryExpression(contains, " " + contains.getStringExpression() + " "); + + } - } - - /*Added by Mathew on November 21st 2016*/ @Override - public void visit(ContainedBy containedBy) { - visitOldOracleJoinBinaryExpression(containedBy, " " + containedBy.getStringExpression() + " "); + public void visit(ContainedBy containedBy) { + visitOldOracleJoinBinaryExpression(containedBy, + " " + containedBy.getStringExpression() + " "); - } + } @Override public void visit(NullValue nullValue) { 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 fcc554dd6..8ce8e0cf9 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 @@ -79,6 +79,9 @@ import net.sf.jsqlparser.expression.operators.conditional.OrExpression; import net.sf.jsqlparser.expression.operators.conditional.XorExpression; import net.sf.jsqlparser.expression.operators.relational.Between; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; @@ -290,6 +293,21 @@ public void visit(NotEqualsTo notEqualsTo) { " " + notEqualsTo.getStringExpression() + " "); } + @Override + public void visit(DoubleAnd doubleAnd) { + + } + + @Override + public void visit(Contains contains) { + + } + + @Override + public void visit(ContainedBy containedBy) { + + } + @Override public void visit(NullValue nullValue) { // nothing to validate diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index aafbecb1d..ad78a395a 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -138,7 +138,6 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | -| | | | @@ -485,12 +484,10 @@ TOKEN : /* Operators */ | )* "="> | )* ">"> | )* "="> -| )* "|"> -| )* ">"> -| /*added by mathew on 21st Nov 2016*/ -| "> /*added by mathew on 21st Nov 2016*/ -| /*added by mathew on 21st Nov 2016*/ +| +| "> +| } TOKEN : /* Date/Time with time zones */ @@ -3316,7 +3313,7 @@ Expression AndExpression() : ( LOOKAHEAD(2) { boolean useOperator = false; } - ( | {useOperator=true;} ) + ( | {useOperator=true;} ) ( LOOKAHEAD(Condition(), {!interrupted}) right=Condition() @@ -3382,23 +3379,23 @@ Expression RegularCondition() #RegularCondition: [ "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_RIGHT; } ] - ( LOOKAHEAD(2) - ">" { result = new GreaterThan(); } - | "<" { result = new MinorThan(); } - | "=" { result = new EqualsTo(); } - | token= { result = new GreaterThanEquals(token.image); } - | token= { result = new MinorThanEquals(token.image); } - | token= { result = new NotEqualsTo(token.image); } - | token= { result = new NotEqualsTo(token.image); } - | token= { result = new DoubleAnd(token.image); } - | token= { result = new DoubleAnd(token.image); } - | token= { result = new DoubleAnd(token.image); } - | "@@" { result = new Matches(); } - | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } - | [ { binary=true; } ] { result = new RegExpMySQLOperator(binary?RegExpMatchOperatorType.MATCH_CASESENSITIVE:RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } - | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } - | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } - | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } + ( + LOOKAHEAD(2) + ">" { result = new GreaterThan(); } + | "<" { result = new MinorThan(); } + | "=" { result = new EqualsTo(); } + | token= { result = new GreaterThanEquals(token.image); } + | token= { result = new MinorThanEquals(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | token= { result = new NotEqualsTo(token.image); } + | token= { result = new DoubleAnd(); } + | token= { result = new Contains(); } + | token= { result = new ContainedBy(); } + | "@@" { result = new Matches(); } + | "~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASESENSITIVE); } + | "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); } + | "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); } + | "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); } | "@>" { result = new JsonOperator("@>"); } | "<@" { result = new JsonOperator("<@"); } diff --git a/src/test/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperatorTest.java b/src/test/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperatorTest.java new file mode 100644 index 000000000..a2687d664 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/expression/operators/relational/ComparisonOperatorTest.java @@ -0,0 +1,38 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + + +class ComparisonOperatorTest { + + @Test + public void testDoubleAnd() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); + Assertions.assertInstanceOf(DoubleAnd.class, CCJSqlParserUtil.parseExpression("a && b")); + } + + @Test + public void testContains() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a &> b"); + Assertions.assertInstanceOf(Contains.class, CCJSqlParserUtil.parseExpression("a &> b")); + } + + @Test + public void testContainedBy() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <& b"); + Assertions.assertInstanceOf(ContainedBy.class, CCJSqlParserUtil.parseExpression("a <& b")); + } +} diff --git a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java deleted file mode 100644 index 6b63cb006..000000000 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ /dev/null @@ -1,2299 +0,0 @@ -package net.sf.jsqlparser.test.select; - -import junit.framework.*; -import net.sf.jsqlparser.*; -import net.sf.jsqlparser.expression.*; -import net.sf.jsqlparser.expression.operators.arithmetic.*; -import net.sf.jsqlparser.expression.operators.relational.*; -import net.sf.jsqlparser.parser.*; -import net.sf.jsqlparser.schema.*; -import net.sf.jsqlparser.statement.*; -import net.sf.jsqlparser.statement.select.*; -import org.apache.commons.io.*; - -import java.io.*; -import java.util.*; - -import static net.sf.jsqlparser.test.TestUtils.*; -import net.sf.jsqlparser.util.TablesNamesFinder; - -public class SelectTest extends TestCase { - - CCJSqlParserManager parserManager = new CCJSqlParserManager(); - - public SelectTest(String arg0) { - super(arg0); - } - - // From statement multipart - public void testMultiPartTableNameWithServerNameAndDatabaseNameAndSchemaName() throws Exception { - final String statement = "SELECT columnName FROM [server-name\\server-instance].databaseName.schemaName.tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithServerNameAndDatabaseName() throws Exception { - final String statement = "SELECT columnName FROM [server-name\\server-instance].databaseName..tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithServerNameAndSchemaName() throws Exception { - final String statement = "SELECT columnName FROM [server-name\\server-instance]..schemaName.tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithServerProblem() throws Exception { - final String statement = "SELECT * FROM LINK_100.htsac.dbo.t_transfer_num a"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testMultiPartTableNameWithServerName() throws Exception { - final String statement = "SELECT columnName FROM [server-name\\server-instance]...tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithDatabaseNameAndSchemaName() throws Exception { - final String statement = "SELECT columnName FROM databaseName.schemaName.tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithDatabaseName() throws Exception { - final String statement = "SELECT columnName FROM databaseName..tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithSchemaName() throws Exception { - final String statement = "SELECT columnName FROM schemaName.tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartTableNameWithColumnName() throws Exception { - final String statement = "SELECT columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - // Select statement statement multipart - public void testMultiPartColumnNameWithDatabaseNameAndSchemaNameAndTableName() throws Exception { - final String statement = "SELECT databaseName.schemaName.tableName.columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testMultiPartColumnNameWithDatabaseNameAndSchemaName() { - final String statement = "SELECT databaseName.schemaName..columnName FROM tableName"; - Select select; - try { - select = (Select) parserManager.parse(new StringReader(statement)); - fail("must not work"); - } catch (JSQLParserException ex) { - //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void testMultiPartColumnNameWithDatabaseNameAndTableName() throws Exception { - final String statement = "SELECT databaseName..tableName.columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - checkMultipartIdentifier(select, "columnName", "databaseName..tableName.columnName"); - } - - public void testMultiPartColumnNameWithDatabaseName() { - final String statement = "SELECT databaseName...columnName FROM tableName"; - Select select; - try { - select = (Select) parserManager.parse(new StringReader(statement)); - fail("must not work"); - } catch (JSQLParserException ex) { - //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void testMultiPartColumnNameWithSchemaNameAndTableName() throws Exception { - final String statement = "SELECT schemaName.tableName.columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - checkMultipartIdentifier(select, "columnName", "schemaName.tableName.columnName"); - } - - public void testMultiPartColumnNameWithSchemaName() { - final String statement = "SELECT schemaName..columnName FROM tableName"; - Select select; - try { - select = (Select) parserManager.parse(new StringReader(statement)); - fail("must not work"); - } catch (JSQLParserException ex) { - //Logger.getLogger(SelectTest.class.getName()).log(Level.SEVERE, null, ex); - } - } - - public void testMultiPartColumnNameWithTableName() throws Exception { - final String statement = "SELECT tableName.columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - checkMultipartIdentifier(select, "columnName", "tableName.columnName"); - } - - public void testMultiPartColumnName() throws Exception { - final String statement = "SELECT columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - checkMultipartIdentifier(select, "columnName", "columnName"); - } - - void checkMultipartIdentifier(Select select, String columnName, String fullColumnName) { - final Expression expr = ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()).getSelectItems().get(0)).getExpression(); - assertTrue(expr instanceof Column); - Column col = (Column) expr; - assertEquals(columnName, col.getColumnName()); - assertEquals(fullColumnName, col.getFullyQualifiedName()); - } - - public void testAllColumnsFromTable() throws Exception { - final String statement = "SELECT tableName.* FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - assertTrue(((PlainSelect) select.getSelectBody()).getSelectItems().get(0) instanceof AllTableColumns); - } - - public void testSimpleSigns() throws JSQLParserException { - final String statement = "SELECT +1, -1 FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testSimpleAdditionsAndSubtractionsWithSigns() throws JSQLParserException { - final String statement = "SELECT 1 - 1, 1 + 1, -1 - 1, -1 + 1, +1 + 1, +1 - 1 FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testOperationsWithSigns() throws JSQLParserException { - Expression expr = CCJSqlParserUtil.parseExpression("1 - -1"); - assertEquals("1 - -1", expr.toString()); - assertTrue(expr instanceof Subtraction); - Subtraction sub = (Subtraction) expr; - assertTrue(sub.getLeftExpression() instanceof LongValue); - assertTrue(sub.getRightExpression() instanceof SignedExpression); - - SignedExpression sexpr = (SignedExpression) sub.getRightExpression(); - assertEquals('-', sexpr.getSign()); - assertEquals("1", sexpr.getExpression().toString()); - } - - public void testSignedColumns() throws JSQLParserException { - final String statement = "SELECT -columnName, +columnName, +(columnName), -(columnName) FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testSigns() throws Exception { - final String statement = "SELECT (-(1)), -(1), (-(columnName)), -(columnName), (-1), -1, (-columnName), -columnName FROM tableName"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testLimit() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 3, ?"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset()); - assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); - - // toString uses standard syntax - statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT ? OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?"; - select = (Select) parserManager.parse(new StringReader(statement)); - - assertNull(((PlainSelect) select.getSelectBody()).getLimit()); - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 3, 4"; - select = (Select) parserManager.parse(new StringReader(statement)); - SetOperationList setList = (SetOperationList) select.getSelectBody(); - assertEquals(3, setList.getLimit().getOffset()); - assertEquals(4, setList.getLimit().getRowCount()); - - // toString uses standard syntax - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION ALL " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) UNION ALL " - + "(SELECT * FROM mytable3 WHERE mytable4.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - - } - - public void testLimit2() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 3, ?"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(3, ((PlainSelect) select.getSelectBody()).getLimit().getOffset()); - assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); - - // toString uses standard syntax - statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT ? OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT NULL OFFSET 3"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertEquals(-1, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); - assertTrue(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 9 LIMIT 0 OFFSET 3"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertEquals(0, ((PlainSelect) select.getSelectBody()).getLimit().getRowCount()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isRowCountJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isOffsetJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitAll()); - assertFalse(((PlainSelect) select.getSelectBody()).getLimit().isLimitNull()); - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?"; - select = (Select) parserManager.parse(new StringReader(statement)); - - assertNull(((PlainSelect) select.getSelectBody()).getLimit()); - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 3, 4"; - select = (Select) parserManager.parse(new StringReader(statement)); - SetOperationList setList = (SetOperationList) select.getSelectBody(); - assertEquals(3, setList.getLimit().getOffset()); - assertEquals(4, setList.getLimit().getRowCount()); - - // toString uses standard syntax - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "(SELECT * FROM mytable WHERE mytable.col = 9 OFFSET ?) UNION ALL " - + "(SELECT * FROM mytable2 WHERE mytable2.col = 9 OFFSET ?) UNION ALL " - + "(SELECT * FROM mytable3 WHERE mytable4.col = 9 OFFSET ?) LIMIT 4 OFFSET 3"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testLimitSqlServer1() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS FETCH NEXT 5 ROWS ONLY"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); - assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); - assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); - assertFalse(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); - assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchJdbcParameter()); - assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); - assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testLimitSqlServer2() throws JSQLParserException { - // Alternative with the other keywords - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROW FETCH FIRST 5 ROW ONLY"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); - assertEquals("ROW", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); - assertEquals("ROW", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); - assertTrue(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); - assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); - assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testLimitSqlServer3() throws JSQLParserException { - // Query with no Fetch - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET 3 ROWS"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertNull(((PlainSelect) select.getSelectBody()).getFetch()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); - assertEquals(3, ((PlainSelect) select.getSelectBody()).getOffset().getOffset()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testLimitSqlServer4() throws JSQLParserException { - // For Oracle syntax, query with no offset - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id FETCH NEXT 5 ROWS ONLY"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); - assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); - assertEquals(5, ((PlainSelect) select.getSelectBody()).getFetch().getRowCount()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testLimitSqlServerJdbcParameters() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 ORDER BY mytable.id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertNotNull(((PlainSelect) select.getSelectBody()).getOffset()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getOffset().getOffsetParam()); - assertNotNull(((PlainSelect) select.getSelectBody()).getFetch()); - assertEquals("ROWS", ((PlainSelect) select.getSelectBody()).getFetch().getFetchParam()); - assertFalse(((PlainSelect) select.getSelectBody()).getFetch().isFetchParamFirst()); - assertTrue(((PlainSelect) select.getSelectBody()).getOffset().isOffsetJdbcParameter()); - assertTrue(((PlainSelect) select.getSelectBody()).getFetch().isFetchJdbcParameter()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testTop() throws JSQLParserException { - String statement = "SELECT TOP 3 * FROM mytable WHERE mytable.col = 9"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(3, ((LongValue) ((PlainSelect) select.getSelectBody()).getTop().getExpression()).getValue()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "select top 5 foo from bar"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertEquals(5, ((LongValue) ((PlainSelect) select.getSelectBody()).getTop().getExpression()).getValue()); - } - - public void testTopWithParenthesis() throws JSQLParserException { - final String firstColumnName = "alias.columnName1"; - final String secondColumnName = "alias.columnName2"; - final String statement = "SELECT TOP (5) PERCENT " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - - final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - final Top top = selectBody.getTop(); - assertEquals("5", top.getExpression().toString()); - assertTrue(top.hasParenthesis()); - assertTrue(top.isPercentage()); - - final List selectItems = selectBody.getSelectItems(); - assertEquals(2, selectItems.size()); - assertEquals(firstColumnName, selectItems.get(0).toString()); - assertEquals(secondColumnName, selectItems.get(1).toString()); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testSkip() throws JSQLParserException { - final String firstColumnName = "alias.columnName1"; - final String secondColumnName = "alias.columnName2"; - final String statement = "SELECT SKIP 5 " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - - final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - final Skip skip = selectBody.getSkip(); - assertEquals((long) 5, (long) skip.getRowCount()); - assertNull(skip.getJdbcParameter()); - assertNull(skip.getVariable()); - - final List selectItems = selectBody.getSelectItems(); - assertEquals(2, selectItems.size()); - assertEquals(firstColumnName, selectItems.get(0).toString()); - assertEquals(secondColumnName, selectItems.get(1).toString()); - - assertStatementCanBeDeparsedAs(select, statement); - - final String statement2 = "SELECT SKIP skipVar c1, c2 FROM t"; - final Select select2 = (Select) parserManager.parse(new StringReader(statement2)); - - final PlainSelect selectBody2 = (PlainSelect) select2.getSelectBody(); - - final Skip skip2 = selectBody2.getSkip(); - assertNull(skip2.getRowCount()); - assertNull(skip2.getJdbcParameter()); - assertEquals("skipVar", skip2.getVariable()); - - final List selectItems2 = selectBody2.getSelectItems(); - assertEquals(2, selectItems2.size()); - assertEquals("c1", selectItems2.get(0).toString()); - assertEquals("c2", selectItems2.get(1).toString()); - - assertStatementCanBeDeparsedAs(select2, statement2); - } - - public void testFirst() throws JSQLParserException { - final String firstColumnName = "alias.columnName1"; - final String secondColumnName = "alias.columnName2"; - final String statement = "SELECT FIRST 5 " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - - final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - final First limit = selectBody.getFirst(); - assertEquals((long) 5, (long) limit.getRowCount()); - assertNull(limit.getJdbcParameter()); - assertEquals(First.Keyword.FIRST, limit.getKeyword()); - - final List selectItems = selectBody.getSelectItems(); - assertEquals(2, selectItems.size()); - assertEquals(firstColumnName, selectItems.get(0).toString()); - assertEquals(secondColumnName, selectItems.get(1).toString()); - - assertStatementCanBeDeparsedAs(select, statement); - - final String statement2 = "SELECT FIRST firstVar c1, c2 FROM t"; - final Select select2 = (Select) parserManager.parse(new StringReader(statement2)); - - final PlainSelect selectBody2 = (PlainSelect) select2.getSelectBody(); - - final First first2 = selectBody2.getFirst(); - assertNull(first2.getRowCount()); - assertNull(first2.getJdbcParameter()); - assertEquals("firstVar", first2.getVariable()); - - final List selectItems2 = selectBody2.getSelectItems(); - assertEquals(2, selectItems2.size()); - assertEquals("c1", selectItems2.get(0).toString()); - assertEquals("c2", selectItems2.get(1).toString()); - - assertStatementCanBeDeparsedAs(select2, statement2); - } - - public void testFirstWithKeywordLimit() throws JSQLParserException { - final String firstColumnName = "alias.columnName1"; - final String secondColumnName = "alias.columnName2"; - final String statement = "SELECT LIMIT ? " + firstColumnName + ", " + secondColumnName + " FROM schemaName.tableName alias ORDER BY " + secondColumnName + " DESC"; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - - final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - final First limit = selectBody.getFirst(); - assertNull(limit.getRowCount()); - assertNotNull(limit.getJdbcParameter()); - assertNull(limit.getJdbcParameter().getIndex()); - assertEquals(First.Keyword.LIMIT, limit.getKeyword()); - - final List selectItems = selectBody.getSelectItems(); - assertEquals(2, selectItems.size()); - assertEquals(firstColumnName, selectItems.get(0).toString()); - assertEquals(secondColumnName, selectItems.get(1).toString()); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testSkipFirst() throws JSQLParserException { - final String statement = "SELECT SKIP ?1 FIRST f1 c1, c2 FROM t1"; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - - final PlainSelect selectBody = (PlainSelect) select.getSelectBody(); - - final Skip skip = selectBody.getSkip(); - assertNotNull(skip.getJdbcParameter()); - assertNotNull(skip.getJdbcParameter().getIndex()); - assertEquals((int) 1, (int) skip.getJdbcParameter().getIndex()); - assertNull(skip.getVariable()); - final First first = selectBody.getFirst(); - assertNull(first.getJdbcParameter()); - assertNull(first.getRowCount()); - assertEquals("f1", first.getVariable()); - - final List selectItems = selectBody.getSelectItems(); - assertEquals(2, selectItems.size()); - assertEquals("c1", selectItems.get(0).toString()); - assertEquals("c2", selectItems.get(1).toString()); - - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testSelectItems() throws JSQLParserException { - String statement = "SELECT myid AS MYID, mycol, tab.*, schema.tab.*, mytab.mycol2, myschema.mytab.mycol, myschema.mytab.* FROM mytable WHERE mytable.col = 9"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - final List selectItems = plainSelect.getSelectItems(); - assertEquals("MYID", ((SelectExpressionItem) selectItems.get(0)).getAlias().getName()); - assertEquals("mycol", ((Column) ((SelectExpressionItem) selectItems.get(1)).getExpression()).getColumnName()); - assertEquals("tab", ((AllTableColumns) selectItems.get(2)).getTable().getName()); - assertEquals("schema", ((AllTableColumns) selectItems.get(3)).getTable().getSchemaName()); - assertEquals("schema.tab", ((AllTableColumns) selectItems.get(3)).getTable().getFullyQualifiedName()); - assertEquals("mytab.mycol2", ((Column) ((SelectExpressionItem) selectItems.get(4)).getExpression()).getFullyQualifiedName()); - assertEquals("myschema.mytab.mycol", ((Column) ((SelectExpressionItem) selectItems.get(5)).getExpression()).getFullyQualifiedName()); - assertEquals("myschema.mytab", ((AllTableColumns) selectItems.get(6)).getTable().getFullyQualifiedName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT myid AS MYID, (SELECT MAX(ID) AS myid2 FROM mytable2) AS myalias FROM mytable WHERE mytable.col = 9"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("myalias", ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT (myid + myid2) AS MYID FROM mytable WHERE mytable.col = 9"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("MYID", ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testUnion() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " - + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + "SELECT * FROM mytable2 LIMIT 3, 4"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - SetOperationList setList = (SetOperationList) select.getSelectBody(); - assertEquals(3, setList.getSelects().size()); - assertEquals("mytable", ((Table) ((PlainSelect) setList.getSelects().get(0)).getFromItem()).getName()); - assertEquals("mytable3", ((Table) ((PlainSelect) setList.getSelects().get(1)).getFromItem()).getName()); - assertEquals("mytable2", ((Table) ((PlainSelect) setList.getSelects().get(2)).getFromItem()).getName()); - assertEquals(3, ((PlainSelect) setList.getSelects().get(2)).getLimit().getOffset()); - - // use brakets for toString - // use standard limit syntax - String statementToString = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " - + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " - + "SELECT * FROM mytable2 LIMIT 3, 4"; - assertStatementCanBeDeparsedAs(select, statementToString); - } - - public void testUnion2() throws JSQLParserException { - String statement = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " - + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " + "SELECT * FROM mytable2 LIMIT 3 OFFSET 4"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - SetOperationList setList = (SetOperationList) select.getSelectBody(); - assertEquals(3, setList.getSelects().size()); - assertEquals("mytable", ((Table) ((PlainSelect) setList.getSelects().get(0)).getFromItem()).getName()); - assertEquals("mytable3", ((Table) ((PlainSelect) setList.getSelects().get(1)).getFromItem()).getName()); - assertEquals("mytable2", ((Table) ((PlainSelect) setList.getSelects().get(2)).getFromItem()).getName()); - assertEquals(0, ((PlainSelect) setList.getSelects().get(2)).getLimit().getOffset()); - assertEquals(4, ((PlainSelect) setList.getSelects().get(2)).getOffset().getOffset()); - - // use brakets for toString - // use standard limit syntax - String statementToString = "SELECT * FROM mytable WHERE mytable.col = 9 UNION " - + "SELECT * FROM mytable3 WHERE mytable3.col = ? UNION " - + "SELECT * FROM mytable2 LIMIT 3 OFFSET 4"; - assertStatementCanBeDeparsedAs(select, statementToString); - } - - - public void testDistinct() throws JSQLParserException { - String statement = "SELECT DISTINCT ON (myid) myid, mycol FROM mytable WHERE mytable.col = 9"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("myid", - ((Column) ((SelectExpressionItem) plainSelect.getDistinct().getOnSelectItems().get(0)).getExpression()) - .getColumnName()); - assertEquals("mycol", - ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()).getColumnName()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testDistinctTop() throws JSQLParserException { - String statement = "SELECT DISTINCT TOP 5 myid, mycol FROM mytable WHERE mytable.col = 9"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("myid", - ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression()) - .getColumnName()); - assertEquals("mycol", - ((Column) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()).getColumnName()); - assertNotNull(plainSelect.getTop()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testDistinctTop2() { - String statement = "SELECT TOP 5 DISTINCT myid, mycol FROM mytable WHERE mytable.col = 9"; - try { - parserManager.parse(new StringReader(statement)); - fail("sould not work"); - } catch (JSQLParserException ex) { - } - } - - public void testFrom() throws JSQLParserException { - String statement = "SELECT * FROM mytable as mytable0, mytable1 alias_tab1, mytable2 as alias_tab2, (SELECT * FROM mytable3) AS mytable4 WHERE mytable.col = 9"; - String statementToString = "SELECT * FROM mytable AS mytable0, mytable1 alias_tab1, mytable2 AS alias_tab2, (SELECT * FROM mytable3) AS mytable4 WHERE mytable.col = 9"; - - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(3, plainSelect.getJoins().size()); - assertEquals("mytable0", plainSelect.getFromItem().getAlias().getName()); - assertEquals("alias_tab1", plainSelect.getJoins().get(0).getRightItem().getAlias().getName()); - assertEquals("alias_tab2", plainSelect.getJoins().get(1).getRightItem().getAlias().getName()); - assertEquals("mytable4", plainSelect.getJoins().get(2).getRightItem().getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statementToString); - } - - public void testJoin() throws JSQLParserException { - String statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(1, plainSelect.getJoins().size()); - assertEquals("tab2", ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); - assertEquals("tab1.id", - ((Column) ((EqualsTo) plainSelect.getJoins().get(0).getOnExpression()).getLeftExpression()) - .getFullyQualifiedName()); - assertTrue(plainSelect.getJoins().get(0).isOuter()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id INNER JOIN tab3"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(2, plainSelect.getJoins().size()); - assertEquals("tab3", ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); - assertFalse(plainSelect.getJoins().get(1).isOuter()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id JOIN tab3"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(2, plainSelect.getJoins().size()); - assertEquals("tab3", ((Table) plainSelect.getJoins().get(1).getRightItem()).getFullyQualifiedName()); - assertFalse(plainSelect.getJoins().get(1).isOuter()); - assertStatementCanBeDeparsedAs(select, statement); - - // implicit INNER - statement = "SELECT * FROM tab1 LEFT OUTER JOIN tab2 ON tab1.id = tab2.id INNER JOIN tab3"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM TA2 LEFT OUTER JOIN O USING (col1, col2) WHERE D.OasSD = 'asdf' AND (kj >= 4 OR l < 'sdf')"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM tab1 INNER JOIN tab2 USING (id, id2)"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(1, plainSelect.getJoins().size()); - assertEquals("tab2", ((Table) plainSelect.getJoins().get(0).getRightItem()).getFullyQualifiedName()); - assertFalse(plainSelect.getJoins().get(0).isOuter()); - assertEquals(2, plainSelect.getJoins().get(0).getUsingColumns().size()); - assertEquals("id2", - plainSelect.getJoins().get(0).getUsingColumns().get(1).getFullyQualifiedName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM tab1 RIGHT OUTER JOIN tab2 USING (id, id2)"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT * FROM foo AS f LEFT OUTER JOIN (bar AS b RIGHT OUTER JOIN baz AS z ON f.id = z.id) ON f.id = b.id"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - - } - - public void testFunctions() throws JSQLParserException { - String statement = "SELECT MAX(id) AS max FROM mytable WHERE mytable.col = 9"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("max", ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT MAX(id), AVG(pro) AS myavg FROM mytable WHERE mytable.col = 9 GROUP BY pro"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("myavg", ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT MAX(a, b, c), COUNT(*), D FROM tab1 GROUP BY D"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - Function fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); - assertEquals("MAX", fun.getName()); - assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); - assertTrue(((Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()) - .isAllColumns()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT {fn MAX(a, b, c)}, COUNT(*), D FROM tab1 GROUP BY D"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); - assertTrue(fun.isEscaped()); - assertEquals("MAX", fun.getName()); - assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); - assertTrue(((Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression()) - .isAllColumns()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT ab.MAX(a, b, c), cd.COUNT(*), D FROM tab1 GROUP BY D"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); - assertEquals("ab.MAX", fun.getName()); - assertEquals("b", ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); - fun = (Function) ((SelectExpressionItem) plainSelect.getSelectItems().get(1)).getExpression(); - assertEquals("cd.COUNT", fun.getName()); - assertTrue(fun.isAllColumns()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testWhere() throws JSQLParserException { - - final String statement = "SELECT * FROM tab1 WHERE"; - String whereToString = "(a + b + c / d + e * f) * (a / b * (a + b)) > ?"; - PlainSelect plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " - + whereToString))).getSelectBody(); - assertTrue(plainSelect.getWhere() instanceof GreaterThan); - assertTrue(((GreaterThan) plainSelect.getWhere()).getLeftExpression() instanceof Multiplication); - assertEquals(statement + " " + whereToString, plainSelect.toString()); - - assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); - - whereToString = "(7 * s + 9 / 3) NOT BETWEEN 3 AND ?"; - plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " + whereToString))) - .getSelectBody(); - - assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); - assertEquals(statement + " " + whereToString, plainSelect.toString()); - - whereToString = "a / b NOT IN (?, 's''adf', 234.2)"; - plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + " " + whereToString))) - .getSelectBody(); - - assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString); - assertEquals(statement + " " + whereToString, plainSelect.toString()); - - whereToString = " NOT 0 = 0"; - plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + whereToString))) - .getSelectBody(); - - whereToString = " NOT (0 = 0)"; - plainSelect = (PlainSelect) ((Select) parserManager.parse(new StringReader(statement + whereToString))) - .getSelectBody(); - - assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), whereToString.trim()); - assertEquals(statement + whereToString, plainSelect.toString()); - } - - public void testGroupBy() throws JSQLParserException { - String statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(1, plainSelect.getGroupByColumnReferences().size()); - assertEquals("tab1.b", ((Column) plainSelect.getGroupByColumnReferences().get(0)).getFullyQualifiedName()); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY 2, 3"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(2, plainSelect.getGroupByColumnReferences().size()); - assertEquals(2, ((LongValue) plainSelect.getGroupByColumnReferences().get(0)).getValue()); - assertEquals(3, ((LongValue) plainSelect.getGroupByColumnReferences().get(1)).getValue()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testHaving() throws JSQLParserException { - String statement = "SELECT MAX(tab1.b) FROM tab1 WHERE a > 34 GROUP BY tab1.b HAVING MAX(tab1.b) > 56"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertTrue(plainSelect.getHaving() instanceof GreaterThan); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT MAX(tab1.b) FROM tab1 WHERE a > 34 HAVING MAX(tab1.b) IN (56, 32, 3, ?)"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertTrue(plainSelect.getHaving() instanceof InExpression); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testExists() throws JSQLParserException { - String statement = "SELECT * FROM tab1 WHERE "; - String where = "EXISTS (SELECT * FROM tab2)"; - statement += where; - Statement parsed = parserManager.parse(new StringReader(statement)); - - assertEquals(statement, parsed.toString()); - - PlainSelect plainSelect = (PlainSelect) ((Select) parsed).getSelectBody(); - assertExpressionCanBeDeparsedAs(plainSelect.getWhere(), where); - } - - public void testOrderBy() throws JSQLParserException { - // TODO: should there be a DESC marker in the OrderByElement class? - String statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a DESC, tab1.b ASC"; - String statementToString = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a DESC, tab1.b ASC"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(2, plainSelect.getOrderByElements().size()); - assertEquals("tab1.a", - ((Column) plainSelect.getOrderByElements().get(0).getExpression()) - .getFullyQualifiedName()); - assertEquals("b", - ((Column) plainSelect.getOrderByElements().get(1).getExpression()).getColumnName()); - assertTrue(plainSelect.getOrderByElements().get(1).isAsc()); - assertFalse(plainSelect.getOrderByElements().get(0).isAsc()); - assertStatementCanBeDeparsedAs(select, statementToString); - - statement = "SELECT * FROM tab1 WHERE a > 34 GROUP BY tab1.b ORDER BY tab1.a, 2"; - select = (Select) parserManager.parse(new StringReader(statement)); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals(2, plainSelect.getOrderByElements().size()); - assertEquals("a", - ((Column) plainSelect.getOrderByElements().get(0).getExpression()).getColumnName()); - assertEquals(2, - ((LongValue) plainSelect.getOrderByElements().get(1).getExpression()).getValue()); - assertStatementCanBeDeparsedAs(select, statement); - - } - - public void testOrderByNullsFirst() throws JSQLParserException { - String statement = "SELECT a FROM tab1 ORDER BY a NULLS FIRST"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testTimestamp() throws JSQLParserException { - String statement = "SELECT * FROM tab1 WHERE a > {ts '2004-04-30 04:05:34.56'}"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("2004-04-30 04:05:34.56", - ((TimestampValue) ((GreaterThan) plainSelect.getWhere()).getRightExpression()).getValue().toString()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testTime() throws JSQLParserException { - String statement = "SELECT * FROM tab1 WHERE a > {t '04:05:34'}"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("04:05:34", - (((TimeValue) ((GreaterThan) plainSelect.getWhere()).getRightExpression()).getValue()).toString()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testBetweenDate() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE col BETWEEN {d '2015-09-19'} AND {d '2015-09-24'}"); - } - - public void testCase() throws JSQLParserException { - String statement = "SELECT a, CASE b WHEN 1 THEN 2 END FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE WHEN (a > 2) THEN 3 END) AS b FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE WHEN a > 2 THEN 3 ELSE 4 END) AS b FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE b WHEN 1 THEN 2 WHEN 3 THEN 4 ELSE 5 END) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE " + "WHEN b > 1 THEN 'BBB' " + "WHEN a = 3 THEN 'AAA' " + "END) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE " + "WHEN b > 1 THEN 'BBB' " + "WHEN a = 3 THEN 'AAA' " + "END) FROM tab1 " - + "WHERE c = (CASE " + "WHEN d <> 3 THEN 5 " + "ELSE 10 " + "END)"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, CASE a " + "WHEN 'b' THEN 'BBB' " + "WHEN 'a' THEN 'AAA' " + "END AS b FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a FROM tab1 WHERE CASE b WHEN 1 THEN 2 WHEN 3 THEN 4 ELSE 5 END > 34"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a FROM tab1 WHERE CASE b WHEN 1 THEN 2 + 3 ELSE 4 END > 34"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT a, (CASE " + "WHEN (CASE a WHEN 1 THEN 10 ELSE 20 END) > 15 THEN 'BBB' " - + // "WHEN (SELECT c FROM tab2 WHERE d = 2) = 3 THEN 'AAA' " + - "END) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - } - - public void testReplaceAsFunction() throws JSQLParserException { - String statement = "SELECT REPLACE(a, 'b', c) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - - Statement stmt = CCJSqlParserUtil.parse(statement); - Select select = (Select) stmt; - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - assertEquals(1, plainSelect.getSelectItems().size()); - Expression expression = ((SelectExpressionItem) plainSelect.getSelectItems().get(0)).getExpression(); - assertTrue(expression instanceof Function); - Function func = (Function) expression; - assertEquals("REPLACE", func.getName()); - assertEquals(3, func.getParameters().getExpressions().size()); - } - - public void testLike() throws JSQLParserException { - String statement = "SELECT * FROM tab1 WHERE a LIKE 'test'"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("test", ((StringValue) ((LikeExpression) plainSelect.getWhere()).getRightExpression()).getValue()); - - statement = "SELECT * FROM tab1 WHERE a LIKE 'test' ESCAPE 'test2'"; - select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - plainSelect = (PlainSelect) select.getSelectBody(); - assertEquals("test", ((StringValue) ((LikeExpression) plainSelect.getWhere()).getRightExpression()).getValue()); - assertEquals("test2", ((LikeExpression) plainSelect.getWhere()).getEscape()); - } - - public void testIlike() throws JSQLParserException { - String statement = "SELECT col1 FROM table1 WHERE col1 ILIKE '%hello%'"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testSelectOrderHaving() throws JSQLParserException { - String statement = "SELECT units, count(units) AS num FROM currency GROUP BY units HAVING count(units) > 1 ORDER BY num"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testDouble() throws JSQLParserException { - String statement = "SELECT 1e2, * FROM mytable WHERE mytable.col = 9"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1e2, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) - .getSelectItems().get(0)).getExpression()).getValue(), 0); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 1.e2"; - select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1e2, - ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) - .getRightExpression()).getValue(), 0); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 1.2e2"; - select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1.2e2, - ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) - .getRightExpression()).getValue(), 0); - assertStatementCanBeDeparsedAs(select, statement); - - statement = "SELECT * FROM mytable WHERE mytable.col = 2e2"; - select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(2e2, - ((DoubleValue) ((BinaryExpression) ((PlainSelect) select.getSelectBody()).getWhere()) - .getRightExpression()).getValue(), 0); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testDouble2() throws JSQLParserException { - String statement = "SELECT 1.e22 FROM mytable"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1e22, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) - .getSelectItems().get(0)).getExpression()).getValue(), 0); - } - - public void testDouble3() throws JSQLParserException { - String statement = "SELECT 1. FROM mytable"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1.0, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) - .getSelectItems().get(0)).getExpression()).getValue(), 0); - } - - public void testDouble4() throws JSQLParserException { - String statement = "SELECT 1.2e22 FROM mytable"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - - assertEquals(1.2e22, ((DoubleValue) ((SelectExpressionItem) ((PlainSelect) select.getSelectBody()) - .getSelectItems().get(0)).getExpression()).getValue(), 0); - } - - public void testWith() throws JSQLParserException { - String statement = "WITH DINFO (DEPTNO, AVGSALARY, EMPCOUNT) AS " - + "(SELECT OTHERS.WORKDEPT, AVG(OTHERS.SALARY), COUNT(*) FROM EMPLOYEE AS OTHERS " - + "GROUP BY OTHERS.WORKDEPT), DINFOMAX AS (SELECT MAX(AVGSALARY) AS AVGMAX FROM DINFO) " - + "SELECT THIS_EMP.EMPNO, THIS_EMP.SALARY, DINFO.AVGSALARY, DINFO.EMPCOUNT, DINFOMAX.AVGMAX " - + "FROM EMPLOYEE AS THIS_EMP INNER JOIN DINFO INNER JOIN DINFOMAX " - + "WHERE THIS_EMP.JOB = 'SALESREP' AND THIS_EMP.WORKDEPT = DINFO.DEPTNO"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testWithRecursive() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("WITH RECURSIVE t (n) AS ((SELECT 1) UNION ALL (SELECT n + 1 FROM t WHERE n < 100)) SELECT sum(n) FROM t"); - } - - public void testSelectAliasInQuotes() throws JSQLParserException { - String statement = "SELECT mycolumn AS \"My Column Name\" FROM mytable"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testSelectAliasWithoutAs() throws JSQLParserException { - String statement = "SELECT mycolumn \"My Column Name\" FROM mytable"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testSelectJoinWithComma() throws JSQLParserException { - String statement = "SELECT cb.Genus, cb.Species FROM Coleccion_de_Briofitas AS cb, unigeoestados AS es " - + "WHERE es.nombre = \"Tamaulipas\" AND cb.the_geom = es.geom"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testDeparser() throws JSQLParserException { - String statement = "SELECT a.OWNERLASTNAME, a.OWNERFIRSTNAME " + "FROM ANTIQUEOWNERS AS a, ANTIQUES AS b " - + "WHERE b.BUYERID = a.OWNERID AND b.ITEM = 'Chair'"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT count(DISTINCT f + 4) FROM a"; - assertSqlCanBeParsedAndDeparsed(statement); - - statement = "SELECT count(DISTINCT f, g, h) FROM a"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testCount2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(ALL col1 + col2) FROM mytable"); - } - - public void testMysqlQuote() throws JSQLParserException { - String statement = "SELECT `a.OWNERLASTNAME`, `OWNERFIRSTNAME` " + "FROM `ANTIQUEOWNERS` AS a, ANTIQUES AS b " - + "WHERE b.BUYERID = a.OWNERID AND b.ITEM = 'Chair'"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testConcat() throws JSQLParserException { - String statement = "SELECT a || b || c + 4 FROM t"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testConcatProblem2() throws JSQLParserException { - String stmt = "SELECT MAX(((((" - + "(SPA.SOORTAANLEVERPERIODE)::VARCHAR (2) || (VARCHAR(SPA.AANLEVERPERIODEJAAR))::VARCHAR (4)" - + ") || TO_CHAR(SPA.AANLEVERPERIODEVOLGNR, 'FM09'::VARCHAR)" - + ") || TO_CHAR((10000 - SPA.VERSCHIJNINGSVOLGNR), 'FM0999'::VARCHAR)" - + ") || (SPA.GESLACHT)::VARCHAR (1))) AS GESLACHT_TMP FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_1() throws JSQLParserException { - String stmt = "SELECT TO_CHAR(SPA.AANLEVERPERIODEVOLGNR, 'FM09'::VARCHAR) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_2() throws JSQLParserException { - String stmt = "SELECT MAX((SPA.SOORTAANLEVERPERIODE)::VARCHAR (2) || (VARCHAR(SPA.AANLEVERPERIODEJAAR))::VARCHAR (4)) AS GESLACHT_TMP FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_3() throws JSQLParserException { - String stmt = "SELECT TO_CHAR((10000 - SPA.VERSCHIJNINGSVOLGNR), 'FM0999'::VARCHAR) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_4() throws JSQLParserException { - String stmt = "SELECT (SPA.GESLACHT)::VARCHAR (1) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_5() throws JSQLParserException { - String stmt = "SELECT max((a || b) || c) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_5_1() throws JSQLParserException { - String stmt = "SELECT (a || b) || c FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_5_2() throws JSQLParserException { - String stmt = "SELECT (a + b) + c FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testConcatProblem2_6() throws JSQLParserException { - String stmt = "SELECT max(a || b || c) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testMatches() throws JSQLParserException { - String statement = "SELECT * FROM team WHERE team.search_column @@ to_tsquery('new & york & yankees')"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testGroupByExpression() throws JSQLParserException { - String statement = "SELECT col1, col2, col1 + col2, sum(col8)" + " FROM table1 " - + "GROUP BY col1, col2, col1 + col2"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testBitwise() throws JSQLParserException { - String statement = "SELECT col1 & 32, col2 ^ col1, col1 | col2" + " FROM table1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testSelectFunction() throws JSQLParserException { - String statement = "SELECT 1 + 2 AS sum"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testWeirdSelect() throws JSQLParserException { - String sql = "select r.reviews_id, substring(rd.reviews_text, 100) as reviews_text, r.reviews_rating, r.date_added, r.customers_name from reviews r, reviews_description rd where r.products_id = '19' and r.reviews_id = rd.reviews_id and rd.languages_id = '1' and r.reviews_status = 1 order by r.reviews_id desc limit 0, 6"; - parserManager.parse(new StringReader(sql)); - } - - public void testCast() throws JSQLParserException { - String stmt = "SELECT CAST(a AS varchar) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - stmt = "SELECT CAST(a AS varchar2) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastInCast() throws JSQLParserException { - String stmt = "SELECT CAST(CAST(a AS numeric) AS varchar) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastInCast2() throws JSQLParserException { - String stmt = "SELECT CAST('test' + CAST(assertEqual AS numeric) AS varchar) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem() throws JSQLParserException { - String stmt = "SELECT CAST(col1 AS varchar (256)) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem2() throws JSQLParserException { - String stmt = "SELECT col1::varchar FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem3() throws JSQLParserException { - String stmt = "SELECT col1::varchar (256) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem4() throws JSQLParserException { - String stmt = "SELECT 5::varchar (256) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem5() throws JSQLParserException { - String stmt = "SELECT 5.67::varchar (256) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem6() throws JSQLParserException { - String stmt = "SELECT 'test'::character varying FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem7() throws JSQLParserException { - String stmt = "SELECT CAST('test' AS character varying) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCastTypeProblem8() throws JSQLParserException { - String stmt = "SELECT CAST('123' AS double precision) FROM tabelle1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testCaseElseAddition() throws JSQLParserException { - String stmt = "SELECT CASE WHEN 1 + 3 > 20 THEN 0 ELSE 1000 + 1 END AS d FROM dual"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testBrackets() throws JSQLParserException { - String stmt = "SELECT table_a.name AS [Test] FROM table_a"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testBrackets2() throws JSQLParserException { - String stmt = "SELECT [a] FROM t"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlServer_Modulo_Proz() throws Exception { - String stmt = "SELECT 5 % 2 FROM A"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlServer_Modulo_mod() throws Exception { - String stmt = "SELECT mod(5, 2) FROM A"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlServer_Modulo() throws Exception { - String stmt = "SELECT convert(varchar(255), DATEDIFF(month, year1, abc_datum) / 12) + ' year, ' + convert(varchar(255), DATEDIFF(month, year2, abc_datum) % 12) + ' month' FROM test_table"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testIsNot() throws JSQLParserException { - String stmt = "SELECT * FROM test WHERE a IS NOT NULL"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testIsNot2() throws JSQLParserException { - //the deparser delivers always a IS NOT NULL even for NOT a IS NULL - String stmt = "SELECT * FROM test WHERE NOT a IS NULL"; - Statement parsed = parserManager.parse(new StringReader(stmt)); - assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM test WHERE a IS NOT NULL"); - } - - public void testProblemSqlAnalytic() throws JSQLParserException { - String stmt = "SELECT a, row_number() OVER (ORDER BY a) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic2() throws JSQLParserException { - String stmt = "SELECT a, row_number() OVER (ORDER BY a, b) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic3() throws JSQLParserException { - String stmt = "SELECT a, row_number() OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic4EmptyOver() throws JSQLParserException { - String stmt = "SELECT a, row_number() OVER () AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic5AggregateColumnValue() throws JSQLParserException { - String stmt = "SELECT a, sum(b) OVER () AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic6AggregateColumnValue() throws JSQLParserException { - String stmt = "SELECT a, sum(b + 5) OVER (ORDER BY a) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic7Count() throws JSQLParserException { - String stmt = "SELECT count(*) OVER () AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic8Complex() throws JSQLParserException { - String stmt = "SELECT ID, NAME, SALARY, SUM(SALARY) OVER () AS SUM_SAL, AVG(SALARY) OVER () AS AVG_SAL, MIN(SALARY) OVER () AS MIN_SAL, MAX(SALARY) OVER () AS MAX_SAL, COUNT(*) OVER () AS ROWS2 FROM STAFF WHERE ID < 60 ORDER BY ID"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic9CommaListPartition() throws JSQLParserException { - String stmt = "SELECT a, row_number() OVER (PARTITION BY c, d ORDER BY a, b) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic10Lag() throws JSQLParserException { - String stmt = "SELECT a, lag(a, 1) OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemSqlAnalytic11Lag() throws JSQLParserException { - String stmt = "SELECT a, lag(a, 1, 0) OVER (PARTITION BY c ORDER BY a, b) AS n FROM table1"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testAnalyticFunction12() throws JSQLParserException { - String statement = "SELECT SUM(a) OVER (PARTITION BY b ORDER BY c) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction13() throws JSQLParserException { - String statement = "SELECT SUM(a) OVER () FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction14() throws JSQLParserException { - String statement = "SELECT SUM(a) OVER (PARTITION BY b ) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction15() throws JSQLParserException { - String statement = "SELECT SUM(a) OVER (ORDER BY c) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction16() throws JSQLParserException { - String statement = "SELECT SUM(a) OVER (ORDER BY c NULLS FIRST) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction17() throws JSQLParserException { - String statement = "SELECT AVG(sal) OVER (PARTITION BY deptno ORDER BY sal ROWS BETWEEN 0 PRECEDING AND 0 PRECEDING) AS avg_of_current_sal FROM emp"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunction18() throws JSQLParserException { - String statement = "SELECT AVG(sal) OVER (PARTITION BY deptno ORDER BY sal RANGE CURRENT ROW) AS avg_of_current_sal FROM emp"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunctionProblem1() throws JSQLParserException { - String statement = "SELECT last_value(s.revenue_hold) OVER (PARTITION BY s.id_d_insertion_order, s.id_d_product_ad_attr, trunc(s.date_id, 'mm') ORDER BY s.date_id) AS col FROM s"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testAnalyticFunctionProblem1b() throws JSQLParserException { - String statement = "SELECT last_value(s.revenue_hold) OVER (PARTITION BY s.id_d_insertion_order, s.id_d_product_ad_attr, trunc(s.date_id, 'mm') ORDER BY s.date_id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS col FROM s"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testFunctionLeft() throws JSQLParserException { - String statement = "SELECT left(table1.col1, 4) FROM table1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testFunctionRight() throws JSQLParserException { - String statement = "SELECT right(table1.col1, 4) FROM table1"; - assertSqlCanBeParsedAndDeparsed(statement); - } - - public void testOracleJoin() throws JSQLParserException { - String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a = tabelle2.b(+)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleJoin2() throws JSQLParserException { - String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) = tabelle2.b"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleJoin2_1() throws JSQLParserException { - String[] values = new String[]{"(+)", "( +)", "(+ )", "( + )", " (+) "}; - for (String value : values) { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a" + value + " = tabelle2.b", true); - } - } - - public void testOracleJoin2_2() throws JSQLParserException { - String[] values = new String[]{"(+)", "( +)", "(+ )", "( + )", " (+) "}; - for (String value : values) { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a = tabelle2.b" + value, true); - } - } - - public void testOracleJoin3() throws JSQLParserException { - String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) > tabelle2.b"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleJoin3_1() throws JSQLParserException { - String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a > tabelle2.b(+)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleJoin4() throws JSQLParserException { - String stmt = "SELECT * FROM tabelle1, tabelle2 WHERE tabelle1.a(+) = tabelle2.b AND tabelle1.b(+) IN ('A', 'B')"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleJoinIssue318() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM TBL_A, TBL_B, TBL_C WHERE TBL_A.ID(+) = TBL_B.ID AND TBL_C.ROOM(+) = TBL_B.ROOM"); - } - - public void testProblemSqlIntersect() throws Exception { - String stmt = "(SELECT * FROM a) INTERSECT (SELECT * FROM b)"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT * FROM a INTERSECT SELECT * FROM b"; - Statement parsed = parserManager.parse(new StringReader(stmt)); - assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a INTERSECT SELECT * FROM b"); - } - - public void testProblemSqlExcept() throws Exception { - String stmt = "(SELECT * FROM a) EXCEPT (SELECT * FROM b)"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT * FROM a EXCEPT SELECT * FROM b"; - Statement parsed = parserManager.parse(new StringReader(stmt)); - assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a EXCEPT SELECT * FROM b"); - } - - public void testProblemSqlMinus() throws Exception { - String stmt = "(SELECT * FROM a) MINUS (SELECT * FROM b)"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT * FROM a MINUS SELECT * FROM b"; - Statement parsed = parserManager.parse(new StringReader(stmt)); - assertStatementCanBeDeparsedAs(parsed, "SELECT * FROM a MINUS SELECT * FROM b"); - } - - public void testProblemSqlCombinedSets() throws Exception { - String stmt = "(SELECT * FROM a) INTERSECT (SELECT * FROM b) UNION (SELECT * FROM c)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithStatement() throws JSQLParserException { - String stmt = "WITH test AS (SELECT mslink FROM feature) SELECT * FROM feature WHERE mslink IN (SELECT mslink FROM test)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithUnionProblem() throws JSQLParserException { - String stmt = "WITH test AS ((SELECT mslink FROM tablea) UNION (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithUnionAllProblem() throws JSQLParserException { - String stmt = "WITH test AS ((SELECT mslink FROM tablea) UNION ALL (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithUnionProblem3() throws JSQLParserException { - String stmt = "WITH test AS ((SELECT mslink, CAST(tablea.fname AS varchar) FROM tablea INNER JOIN tableb ON tablea.mslink = tableb.mslink AND tableb.deleted = 0 WHERE tablea.fname IS NULL AND 1 = 0) UNION ALL (SELECT mslink FROM tableb)) SELECT * FROM tablea WHERE mslink IN (SELECT mslink FROM test)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithUnionProblem4() throws JSQLParserException { - String stmt = "WITH hist AS ((SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, 0 AS level, CAST(gl.mslink AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 WHERE gl.parent IS NULL AND gl.mslink <> 0) UNION ALL (SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, hist.level + 1 AS level, CAST(hist.path + '.' + CAST(gl.mslink AS VARCHAR) AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 INNER JOIN hist ON gl.parent = hist.mslink WHERE gl.mslink <> 0)) SELECT mslink, space(level * 4) + txt AS txt, nr, feature, path FROM hist WHERE EXISTS (SELECT feature FROM tablec WHERE mslink = 0 AND ((feature IN (1, 2) AND hist.feature = 3) OR (feature IN (4) AND hist.feature = 2)))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testWithUnionProblem5() throws JSQLParserException { - String stmt = "WITH hist AS ((SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, 0 AS level, CAST(gl.mslink AS VARCHAR) AS path, ae.feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 WHERE gl.parent IS NULL AND gl.mslink <> 0) UNION ALL (SELECT gl.mslink, ba.gl_name AS txt, ba.gl_nummer AS nr, hist.level + 1 AS level, CAST(hist.path + '.' + CAST(gl.mslink AS VARCHAR) AS VARCHAR) AS path, 5 AS feature FROM tablea AS gl INNER JOIN tableb AS ba ON gl.mslink = ba.gl_mslink INNER JOIN tablec AS ae ON gl.mslink = ae.mslink AND ae.deleted = 0 INNER JOIN hist ON gl.parent = hist.mslink WHERE gl.mslink <> 0)) SELECT * FROM hist"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testExtractFrom1() throws JSQLParserException { - String stmt = "SELECT EXTRACT(month FROM datecolumn) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testExtractFrom2() throws JSQLParserException { - String stmt = "SELECT EXTRACT(year FROM now()) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testExtractFrom3() throws JSQLParserException { - String stmt = "SELECT EXTRACT(year FROM (now() - 2)) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testExtractFrom4() throws JSQLParserException { - String stmt = "SELECT EXTRACT(minutes FROM now() - '01:22:00') FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemFunction() throws JSQLParserException { - String stmt = "SELECT test() FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - Statement parsed = CCJSqlParserUtil.parse(stmt); - Select select = (Select) parsed; - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - SelectItem get = plainSelect.getSelectItems().get(0); - SelectExpressionItem item = (SelectExpressionItem) get; - assertTrue(item.getExpression() instanceof Function); - assertEquals("test", ((Function) item.getExpression()).getName()); - } - - public void testProblemFunction2() throws JSQLParserException { - String stmt = "SELECT sysdate FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testProblemFunction3() throws JSQLParserException { - String stmt = "SELECT TRUNCATE(col) FROM testtable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testAdditionalLettersGerman() throws JSQLParserException { - String stmt = "SELECT colä, colö, colü FROM testtableäöü"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT colA, colÖ, colÜ FROM testtableÄÖÜ"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT Äcol FROM testtableÄÖÜ"; - assertSqlCanBeParsedAndDeparsed(stmt); - - stmt = "SELECT ßcolß FROM testtableß"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testAdditionalLettersSpanish() throws JSQLParserException { - String stmt = "SELECT * FROM años"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testMultiTableJoin() throws JSQLParserException { - String stmt = "SELECT * FROM taba INNER JOIN tabb ON taba.a = tabb.a, tabc LEFT JOIN tabd ON tabc.c = tabd.c"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testTableCrossJoin() throws JSQLParserException { - String stmt = "SELECT * FROM taba CROSS JOIN tabb"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testLateral1() throws JSQLParserException { - String stmt = "SELECT O.ORDERID, O.CUSTNAME, OL.LINETOTAL FROM ORDERS AS O, LATERAL(SELECT SUM(NETAMT) AS LINETOTAL FROM ORDERLINES AS LINES WHERE LINES.ORDERID = O.ORDERID) AS OL"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testLateralComplex1() throws IOException, JSQLParserException { - String stmt = IOUtils.toString(SelectTest.class.getResourceAsStream("complex-lateral-select-request.txt")); - Select select = (Select) parserManager.parse(new StringReader(stmt)); - assertEquals("SELECT O.ORDERID, O.CUSTNAME, OL.LINETOTAL, OC.ORDCHGTOTAL, OT.TAXTOTAL FROM ORDERS O, LATERAL(SELECT SUM(NETAMT) AS LINETOTAL FROM ORDERLINES LINES WHERE LINES.ORDERID = O.ORDERID) AS OL, LATERAL(SELECT SUM(CHGAMT) AS ORDCHGTOTAL FROM ORDERCHARGES CHARGES WHERE LINES.ORDERID = O.ORDERID) AS OC, LATERAL(SELECT SUM(TAXAMT) AS TAXTOTAL FROM ORDERTAXES TAXES WHERE TAXES.ORDERID = O.ORDERID) AS OT", select.toString()); - } - - public void testValues() throws JSQLParserException { - String stmt = "SELECT * FROM (VALUES (1, 2), (3, 4)) AS test"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testValues2() throws JSQLParserException { - String stmt = "SELECT * FROM (VALUES 1, 2, 3, 4) AS test"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testValues3() throws JSQLParserException { - String stmt = "SELECT * FROM (VALUES 1, 2, 3, 4) AS test(a)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testValues4() throws JSQLParserException { - String stmt = "SELECT * FROM (VALUES (1, 2), (3, 4)) AS test(a, b)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testValues5() throws JSQLParserException { - String stmt = "SELECT X, Y FROM (VALUES (0, 'a'), (1, 'b')) AS MY_TEMP_TABLE(X, Y)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testValues6BothVariants() throws JSQLParserException { - String stmt = "SELECT I FROM (VALUES 1, 2, 3) AS MY_TEMP_TABLE(I) WHERE I IN (SELECT * FROM (VALUES 1, 2) AS TEST)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testInterval1() throws JSQLParserException { - String stmt = "SELECT 5 + INTERVAL '3 days'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testInterval2() throws JSQLParserException { - String stmt = "SELECT to_timestamp(to_char(now() - INTERVAL '45 MINUTE', 'YYYY-MM-DD-HH24:')) AS START_TIME FROM tab1"; - assertSqlCanBeParsedAndDeparsed(stmt); - - Statement st = CCJSqlParserUtil.parse(stmt); - Select select = (Select) st; - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - assertEquals(1, plainSelect.getSelectItems().size()); - SelectExpressionItem item = (SelectExpressionItem) plainSelect.getSelectItems().get(0); - Function function = (Function) item.getExpression(); - - assertEquals("to_timestamp", function.getName()); - - assertEquals(1, function.getParameters().getExpressions().size()); - - Function func2 = (Function) function.getParameters().getExpressions().get(0); - - assertEquals("to_char", func2.getName()); - - assertEquals(2, func2.getParameters().getExpressions().size()); - Subtraction sub = (Subtraction) func2.getParameters().getExpressions().get(0); - assertTrue(sub.getRightExpression() instanceof IntervalExpression); - IntervalExpression iexpr = (IntervalExpression) sub.getRightExpression(); - - assertEquals("'45 MINUTE'", iexpr.getParameter()); - } - - public void testInterval3() throws JSQLParserException { - String stmt = "SELECT 5 + INTERVAL '3' day"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testInterval4() throws JSQLParserException { - String stmt = "SELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testInterval5_Issue228() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT ADDDATE(timeColumn1, INTERVAL 420 MINUTES) AS timeColumn1 FROM tbl"); - assertSqlCanBeParsedAndDeparsed("SELECT ADDDATE(timeColumn1, INTERVAL -420 MINUTES) AS timeColumn1 FROM tbl"); - } - - public void testMultiValueIn() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE (a, b, c) IN (SELECT a, b, c FROM mytable2)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testMultiValueIn2() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE (trim(a), trim(b)) IN (SELECT a, b FROM mytable2)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivot1() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN ('val1'))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivot2() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN (10, 20, 30))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivot3() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT (count(a) AS vals FOR b IN (10 AS d1, 20, 30 AS d3))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivot4() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT (count(a), sum(b) FOR b IN (10, 20, 30))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivot5() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR (b, c) IN ((10, 'a'), (20, 'b'), (30, 'c')))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivotXml1() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN ('val1'))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivotXml2() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN (SELECT vals FROM myothertable))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivotXml3() throws JSQLParserException { - String stmt = "SELECT * FROM mytable PIVOT XML (count(a) FOR b IN (ANY))"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPivotXmlSubquery1() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM (SELECT times_purchased, state_code FROM customers t) PIVOT (count(state_code) FOR state_code IN ('NY', 'CT', 'NJ', 'FL', 'MO')) ORDER BY times_purchased"); - } - - public void testPivotFunction() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT to_char((SELECT col1 FROM (SELECT times_purchased, state_code FROM customers t) PIVOT (count(state_code) FOR state_code IN ('NY', 'CT', 'NJ', 'FL', 'MO')) ORDER BY times_purchased)) FROM DUAL"); - } - - public void testRegexpLike1() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE REGEXP_LIKE(first_name, '^Ste(v|ph)en$')"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testRegexpLike2() throws JSQLParserException { - String stmt = "SELECT CASE WHEN REGEXP_LIKE(first_name, '^Ste(v|ph)en$') THEN 1 ELSE 2 END FROM mytable"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testRegexpMySQL() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE first_name REGEXP '^Ste(v|ph)en$'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testRegexpBinaryMySQL() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE first_name REGEXP BINARY '^Ste(v|ph)en$'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testBooleanFunction1() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE test_func(col1)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testNamedParameter() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE b = :param"; - assertSqlCanBeParsedAndDeparsed(stmt); - - Statement st = CCJSqlParserUtil.parse(stmt); - Select select = (Select) st; - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - Expression exp = ((BinaryExpression) plainSelect.getWhere()).getRightExpression(); - assertTrue(exp instanceof JdbcNamedParameter); - JdbcNamedParameter namedParameter = (JdbcNamedParameter) exp; - assertEquals("param", namedParameter.getName()); - - } - - public void testNamedParameter2() throws JSQLParserException { - String stmt = "SELECT * FROM mytable WHERE a = :param OR a = :param2 AND b = :param3"; - assertSqlCanBeParsedAndDeparsed(stmt); - - Statement st = CCJSqlParserUtil.parse(stmt); - Select select = (Select) st; - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - Expression exp_l = ((BinaryExpression) plainSelect.getWhere()).getLeftExpression(); - Expression exp_r = ((BinaryExpression) plainSelect.getWhere()).getRightExpression(); - Expression exp_rl = ((BinaryExpression) exp_r).getLeftExpression(); - Expression exp_rr = ((BinaryExpression) exp_r).getRightExpression(); - - Expression exp_param1 = ((BinaryExpression) exp_l).getRightExpression(); - Expression exp_param2 = ((BinaryExpression) exp_rl).getRightExpression(); - Expression exp_param3 = ((BinaryExpression) exp_rr).getRightExpression(); - - assertTrue(exp_param1 instanceof JdbcNamedParameter); - assertTrue(exp_param2 instanceof JdbcNamedParameter); - assertTrue(exp_param3 instanceof JdbcNamedParameter); - - JdbcNamedParameter namedParameter1 = (JdbcNamedParameter) exp_param1; - JdbcNamedParameter namedParameter2 = (JdbcNamedParameter) exp_param2; - JdbcNamedParameter namedParameter3 = (JdbcNamedParameter) exp_param3; - - assertEquals("param", namedParameter1.getName()); - assertEquals("param2", namedParameter2.getName()); - assertEquals("param3", namedParameter3.getName()); - } - - public void testComplexUnion1() throws IOException, JSQLParserException { - String stmt = "(SELECT 'abc-' || coalesce(mytab.a::varchar, '') AS a, mytab.b, mytab.c AS st, mytab.d, mytab.e FROM mytab WHERE mytab.del = 0) UNION (SELECT 'cde-' || coalesce(mytab2.a::varchar, '') AS a, mytab2.b, mytab2.bezeichnung AS c, 0 AS d, 0 AS e FROM mytab2 WHERE mytab2.del = 0)"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleHierarchicalQuery() throws JSQLParserException { - String stmt = "SELECT last_name, employee_id, manager_id FROM employees CONNECT BY employee_id = manager_id ORDER BY last_name"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleHierarchicalQuery2() throws JSQLParserException { - String stmt = "SELECT employee_id, last_name, manager_id FROM employees CONNECT BY PRIOR employee_id = manager_id"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleHierarchicalQuery3() throws JSQLParserException { - String stmt = "SELECT last_name, employee_id, manager_id, LEVEL FROM employees START WITH employee_id = 100 CONNECT BY PRIOR employee_id = manager_id ORDER SIBLINGS BY last_name"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleHierarchicalQuery4() throws JSQLParserException { - String stmt = "SELECT last_name, employee_id, manager_id, LEVEL FROM employees CONNECT BY PRIOR employee_id = manager_id START WITH employee_id = 100 ORDER SIBLINGS BY last_name"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testOracleHierarchicalQueryIssue196() throws JSQLParserException { - String stmt = "SELECT num1, num2, level FROM carol_tmp START WITH num2 = 1008 CONNECT BY num2 = PRIOR num1 ORDER BY level DESC"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPostgreSQLRegExpCaseSensitiveMatch() throws JSQLParserException { - String stmt = "SELECT a, b FROM foo WHERE a ~ '[help].*'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPostgreSQLRegExpCaseSensitiveMatch2() throws JSQLParserException { - String stmt = "SELECT a, b FROM foo WHERE a ~* '[help].*'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPostgreSQLRegExpCaseSensitiveMatch3() throws JSQLParserException { - String stmt = "SELECT a, b FROM foo WHERE a !~ '[help].*'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testPostgreSQLRegExpCaseSensitiveMatch4() throws JSQLParserException { - String stmt = "SELECT a, b FROM foo WHERE a !~* '[help].*'"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testReservedKeyword() throws JSQLParserException { - final String statement = "SELECT cast, do, extract, first, following, last, materialized, nulls, partition, range, row, rows, siblings, value, xml FROM tableName"; // all of these are legal in SQL server; 'row' and 'rows' are not legal on Oracle, though; - final Select select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testReservedKeyword2() throws JSQLParserException { - final String stmt = "SELECT open FROM tableName"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testReservedKeyword3() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable1 t JOIN mytable2 AS prior ON t.id = prior.id"); - } - - public void testCharacterSetClause() throws JSQLParserException { - String stmt = "SELECT DISTINCT CAST(`view0`.`nick2` AS CHAR (8000) CHARACTER SET utf8) AS `v0` FROM people `view0` WHERE `view0`.`nick2` IS NOT NULL"; - assertSqlCanBeParsedAndDeparsed(stmt); - } - - public void testNotEqualsTo() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a != b"); - assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <> b"); - } - - /*Added by Mathew on 21st Nov 2016*/ - public void testDoubleAnd() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a && b"); - } - - /*Added by Mathew on 21st Nov 2016*/ - public void testContains() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a &> b"); - } - - /*Added by Mathew on 21st Nov 2016*/ - public void testContainedBy() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM foo WHERE a <& b"); - } - - public void testJsonExpression() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram"); - assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'description' = 'milk'"); - assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'quantity' = 12::TEXT"); - //assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE CAST(sale->'items'->>'quantity' AS integer) = 2"); - assertSqlCanBeParsedAndDeparsed("SELECT SUM(CAST(sale->'items'->>'quantity' AS integer)) AS total_quantity_sold FROM sales"); - assertSqlCanBeParsedAndDeparsed("SELECT sale->>'items' FROM sales"); - assertSqlCanBeParsedAndDeparsed("SELECT json_typeof(sale->'items'), json_typeof(sale->'items'->'quantity') FROM sales"); - - - //The following staments can be parsed but not deparsed - for (String statement : new String[]{ - "SELECT doc->'site_name' FROM websites WHERE doc @> '{\"tags\":[{\"term\":\"paris\"}, {\"term\":\"food\"}]}'", - "SELECT * FROM sales where sale ->'items' @> '[{\"count\":0}]'", - "SELECT * FROM sales where sale ->'items' ? 'name'", - "SELECT * FROM sales where sale ->'items' -# 'name'" - }){ - Select select = (Select) parserManager.parse(new StringReader(statement)); - assertStatementCanBeDeparsedAs(select, statement, true); - } - } - - public void testSelectInto1() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * INTO user_copy FROM user"); - } - - public void testSelectForUpdate() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM user_table FOR UPDATE"); - } - - public void testSelectForUpdate2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM emp WHERE empno = ? FOR UPDATE"); - } - - public void testSelectJoin() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT pg_class.relname, pg_attribute.attname, pg_constraint.conname " - + "FROM pg_constraint JOIN pg_class ON pg_class.oid = pg_constraint.conrelid" - + " JOIN pg_attribute ON pg_attribute.attrelid = pg_constraint.conrelid" - + " WHERE pg_constraint.contype = 'u' AND (pg_attribute.attnum = ANY(pg_constraint.conkey))" - + " ORDER BY pg_constraint.conname"); - } - - public void testSelectJoin2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM pg_constraint WHERE pg_attribute.attnum = ANY(pg_constraint.conkey)"); - } - - public void testAnyConditionSubSelect() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT e1.empno, e1.sal FROM emp e1 WHERE e1.sal > ANY (SELECT e2.sal FROM emp e2 WHERE e2.deptno = 10)"); - } - - public void testAllConditionSubSelect() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT e1.empno, e1.sal FROM emp e1 WHERE e1.sal > ALL (SELECT e2.sal FROM emp e2 WHERE e2.deptno = 10)"); - } - - public void testSelectOracleColl() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM the_table tt WHERE TT.COL1 = lines(idx).COL1"); - } - - public void testSelectInnerWith() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM (WITH actor AS (SELECT 'a' aid FROM DUAL) SELECT aid FROM actor)"); - } - - public void testSelectWithinGroup() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT LISTAGG(col1, '##') WITHIN GROUP (ORDER BY col1) FROM table1"); - } - - public void testSelectUserVariable() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT @col FROM t1"); - } - - public void testSelectNumericBind() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT a FROM b WHERE c = :1"); - } - - public void testSelectBrackets() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT avg((123.250)::numeric)"); - } - - public void testSelectBrackets2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT (EXTRACT(epoch FROM age(d1, d2)) / 2)::numeric"); - } - - public void testSelectBrackets3() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT avg((EXTRACT(epoch FROM age(d1, d2)) / 2)::numeric)"); - } - - public void testSelectBrackets4() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT (1 / 2)::numeric"); - } - - public void testSelectForUpdateOfTable() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT foo.*, bar.* FROM foo, bar WHERE foo.id = bar.foo_id FOR UPDATE OF foo"); - } - - public void testSelectWithBrackets() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("(SELECT 1 FROM mytable)"); - } - - public void testSelectWithBrackets2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("(SELECT 1)"); - } - - public void testSelectWithoutFrom() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT footable.foocolumn"); - } - - public void testSelectKeywordPercent() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT percent FROM MY_TABLE"); - } - - public void testSelectJPQLPositionalParameter() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT email FROM users WHERE (type LIKE 'B') AND (username LIKE ?1)"); - } - - public void testSelectKeep() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT col1, min(col2) KEEP (DENSE_RANK FIRST ORDER BY col3), col4 FROM table1 GROUP BY col5 ORDER BY col3"); - } - - public void testSelectKeepOver() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY commission_pct) OVER (PARTITION BY department_id ) \"Worst\" FROM employees ORDER BY department_id, salary"); - } - - public void testGroupConcat() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT student_name, GROUP_CONCAT(DISTINCT test_score ORDER BY test_score DESC SEPARATOR ' ') FROM student GROUP BY student_name"); - } - - public void testRowConstructor1() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE (col1, col2) = (SELECT col3, col4 FROM t2 WHERE id = 10)"); - } - - public void testRowConstructor2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE ROW(col1, col2) = (SELECT col3, col4 FROM t2 WHERE id = 10)"); - } - - public void testIssue154() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT d.id, d.uuid, d.name, d.amount, d.percentage, d.modified_time FROM discount d LEFT OUTER JOIN discount_category dc ON d.id = dc.discount_id WHERE merchant_id = ? AND deleted = ? AND dc.discount_id IS NULL AND modified_time < ? AND modified_time >= ? ORDER BY modified_time"); - } - - public void testIssue154_2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT r.id, r.uuid, r.name, r.system_role FROM role r WHERE r.merchant_id = ? AND r.deleted_time IS NULL ORDER BY r.id DESC"); - } - - public void testIssue160_signedParameter() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT start_date WHERE start_date > DATEADD(HH, -?, GETDATE())"); - } - - public void testIssue160_signedParameter2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE -? = 5"); - } - - public void testIssue162_doubleUserVar() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT @@SPID AS ID, SYSTEM_USER AS \"Login Name\", USER AS \"User Name\""); - } - - public void testIssue167_singleQuoteEscape() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT 'a'"); - assertSqlCanBeParsedAndDeparsed("SELECT ''''"); - assertSqlCanBeParsedAndDeparsed("SELECT '\\''"); - assertSqlCanBeParsedAndDeparsed("SELECT 'ab''ab'"); - assertSqlCanBeParsedAndDeparsed("SELECT 'ab\\'ab'"); - } - - /** - * These are accepted due to reading one backslash and a double quote. - */ - public void testIssue167_singleQuoteEscape2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT '\\'''"); - assertSqlCanBeParsedAndDeparsed("SELECT '\\\\\\''"); - } - - public void testIssue77_singleQuoteEscape2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT 'test\\'' FROM dual"); - } - - public void testIssue223_singleQuoteEscape() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT '\\'test\\''"); - } - - public void testOracleHint() throws JSQLParserException { - assertOracleHintExists("SELECT /*+ SOMEHINT */ * FROM mytable", true, "SOMEHINT"); - assertOracleHintExists("SELECT /*+ MORE HINTS POSSIBLE */ * FROM mytable", true, "MORE HINTS POSSIBLE"); - assertOracleHintExists("SELECT /*+ MORE\nHINTS\t\nPOSSIBLE */ * FROM mytable", true, "MORE\nHINTS\t\nPOSSIBLE"); - assertOracleHintExists("SELECT /*+ leading(sn di md sh ot) cardinality(ot 1000) */ c, b FROM mytable", true, "leading(sn di md sh ot) cardinality(ot 1000)"); - assertOracleHintExists("SELECT /*+ ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" - + " USE_NL (glcc glf) USE_MERGE (gp gsb) */\n" - + " b.application_id\n" - + "FROM jl_br_journals j,\n" - + " po_vendors p", true, "ORDERED INDEX (b, jl_br_balances_n1) USE_NL (j b) \n" - + " USE_NL (glcc glf) USE_MERGE (gp gsb)"); - assertOracleHintExists("SELECT /*+ROWID(emp)*/ /*+ THIS IS NOT HINT! ***/ * \n" - + "FROM emp \n" - + "WHERE rowid > 'AAAAtkAABAAAFNTAAA' AND empno = 155", false, "ROWID(emp)"); - assertOracleHintExists("SELECT /*+ INDEX(patients sex_index) use sex_index because there are few\n" - + " male patients */ name, height, weight\n" - + "FROM patients\n" - + "WHERE sex = 'm'", true, "INDEX(patients sex_index) use sex_index because there are few\n male patients"); - assertOracleHintExists("SELECT /*+INDEX_COMBINE(emp sal_bmi hiredate_bmi)*/ * \n" - + "FROM emp \n" - + "WHERE sal < 50000 AND hiredate < '01-JAN-1990'", true, "INDEX_COMBINE(emp sal_bmi hiredate_bmi)"); - assertOracleHintExists("SELECT --+ CLUSTER \n" - + "emp.ename, deptno\n" - + "FROM emp, dept\n" - + "WHERE deptno = 10 \n" - + "AND emp.deptno = dept.deptno", true, "CLUSTER"); - assertOracleHintExists("SELECT --+ CLUSTER \n --+ some other comment, not hint\n /* even more comments */ * from dual", false, "CLUSTER"); - assertOracleHintExists("(SELECT * from t1) UNION (select /*+ CLUSTER */ * from dual)", true, null, "CLUSTER"); - assertOracleHintExists("(SELECT * from t1) UNION (select /*+ CLUSTER */ * from dual) UNION (select * from dual)", true, null, "CLUSTER", null); - assertOracleHintExists("(SELECT --+ HINT1 HINT2 HINT3\n * from t1) UNION (select /*+ HINT4 HINT5 */ * from dual)", true, "HINT1 HINT2 HINT3", "HINT4 HINT5"); - - } - - public void testOracleHintExpression() throws JSQLParserException { - String statement = "SELECT --+ HINT\n * FROM tab1"; - Statement parsed = parserManager.parse(new StringReader(statement)); - - assertEquals(statement, parsed.toString()); - PlainSelect plainSelect = (PlainSelect) ((Select) parsed).getSelectBody(); - assertExpressionCanBeDeparsedAs(plainSelect.getOracleHint(), "--+ HINT\n"); - } - - public void testTableFunctionWithNoParams() throws Exception { - final String statement = "SELECT f2 FROM SOME_FUNCTION()"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - assertTrue(plainSelect.getFromItem() instanceof TableFunction); - TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); - Function function = fromItem.getFunction(); - assertNotNull(function); - assertEquals("SOME_FUNCTION", function.getName()); - assertNull(function.getParameters()); - assertNull(fromItem.getAlias()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testTableFunctionWithParams() throws Exception { - final String statement = "SELECT f2 FROM SOME_FUNCTION(1, 'val')"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - assertTrue(plainSelect.getFromItem() instanceof TableFunction); - TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); - Function function = fromItem.getFunction(); - assertNotNull(function); - assertEquals("SOME_FUNCTION", function.getName()); - - // verify params - assertNotNull(function.getParameters()); - List expressions = function.getParameters().getExpressions(); - assertEquals(2, expressions.size()); - - Expression firstParam = expressions.get(0); - assertNotNull(firstParam); - assertTrue(firstParam instanceof LongValue); - assertEquals(1l, ((LongValue) firstParam).getValue()); - - Expression secondParam = expressions.get(1); - assertNotNull(secondParam); - assertTrue(secondParam instanceof StringValue); - assertEquals("val", ((StringValue) secondParam).getValue()); - - assertNull(fromItem.getAlias()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testTableFunctionWithAlias() throws Exception { - final String statement = "SELECT f2 FROM SOME_FUNCTION() AS z"; - Select select = (Select) parserManager.parse(new StringReader(statement)); - PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); - - assertTrue(plainSelect.getFromItem() instanceof TableFunction); - TableFunction fromItem = (TableFunction) plainSelect.getFromItem(); - Function function = fromItem.getFunction(); - assertNotNull(function); - - assertEquals("SOME_FUNCTION", function.getName()); - assertNull(function.getParameters()); - assertNotNull(fromItem.getAlias()); - assertEquals("z", fromItem.getAlias().getName()); - assertStatementCanBeDeparsedAs(select, statement); - } - - public void testIssue151_tableFunction() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM tables a LEFT JOIN getdata() b ON a.id = b.id"); - } - - public void testIssue217_keywordSeparator() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT Separator"); - } - - public void testIssue215_possibleEndlessParsing() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN ((value LIKE '%t1%') OR (value LIKE '%t2%')) THEN 't1s' WHEN ((((((((((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%')) OR (value LIKE '%t10%')) OR (value LIKE '%t10%')) OR (value LIKE '%CIO%')) OR (value LIKE '%cio%')) OR (value LIKE '%Cio%')) OR (value LIKE '%t11%')) OR (value LIKE '%t11%')) THEN 't' WHEN ((((value LIKE '%t12%') OR (value LIKE '%t12%')) OR (value LIKE '%VP%')) OR (value LIKE '%vp%')) THEN 'Vice t12s' WHEN ((((((value LIKE '% IT %') OR (value LIKE '%t13%')) OR (value LIKE '%t13%')) OR (value LIKE '% it %')) OR (value LIKE '%tech%')) OR (value LIKE '%Tech%')) THEN 'IT' WHEN ((((value LIKE '%Analyst%') OR (value LIKE '%t14%')) OR (value LIKE '%Analytic%')) OR (value LIKE '%analytic%')) THEN 'Analysts' WHEN ((value LIKE '%Manager%') OR (value LIKE '%manager%')) THEN 't15' ELSE 'Other' END) FROM tab1"); - } - - public void testIssue215_possibleEndlessParsing2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT (CASE WHEN ((value LIKE '%t1%') OR (value LIKE '%t2%')) THEN 't1s' ELSE 'Other' END) FROM tab1"); - } - - public void testIssue215_possibleEndlessParsing3() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((((((((((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%')) OR (value LIKE '%t10%')) OR (value LIKE '%t10%')) OR (value LIKE '%CIO%')) OR (value LIKE '%cio%')) OR (value LIKE '%Cio%')) OR (value LIKE '%t11%')) OR (value LIKE '%t11%'))"); - } - - public void testIssue215_possibleEndlessParsing4() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((value LIKE '%t3%') OR (value LIKE '%t3%'))"); - } - - public void testIssue215_possibleEndlessParsing5() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE ((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%'))"); - } - - public void testIssue215_possibleEndlessParsing6() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE (((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%'))"); - } - - public void testIssue215_possibleEndlessParsing7() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM mytable WHERE (((((((((((((((((((((value LIKE '%t3%') OR (value LIKE '%t3%')) OR (value LIKE '%t3%')) OR (value LIKE '%t4%')) OR (value LIKE '%t4%')) OR (value LIKE '%t5%')) OR (value LIKE '%t6%')) OR (value LIKE '%t6%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t7%')) OR (value LIKE '%t8%')) OR (value LIKE '%t8%')) OR (value LIKE '%CTO%')) OR (value LIKE '%cto%')) OR (value LIKE '%Cto%')) OR (value LIKE '%t9%')) OR (value LIKE '%t9%')) OR (value LIKE '%COO%')) OR (value LIKE '%coo%')) OR (value LIKE '%Coo%'))"); - } - - public void testIssue230_cascadeKeyword() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT t.cascade AS cas FROM t"); - } - - public void testBooleanValue() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT col FROM t WHERE a"); - } - - public void testBooleanValue2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT col FROM t WHERE 3 < 5 AND a"); - } - - public void testNotWithoutParenthesisIssue234() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM \"Persons\" WHERE NOT \"F_NAME\" = 'John'"); - } - - public void testWhereIssue240_1() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE 1"); - } - - public void testWhereIssue240_0() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE 0"); - } - - public void testWhereIssue240_notBoolean() { - try { - CCJSqlParserUtil.parse("SELECT count(*) FROM mytable WHERE 5"); - fail("should not be parsed"); - } catch (JSQLParserException ex) { - - } - } - - public void testWhereIssue240_true() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE true"); - } - - public void testWhereIssue240_false() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT count(*) FROM mytable WHERE false"); - } - - public void testWhereIssue241KeywordEnd() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT l.end FROM lessons l"); - } - - public void testSpeedTestIssue235() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM tbl WHERE (ROUND((((((period_diff(date_format(tbl.CD, '%Y%m'), date_format(SUBTIME(CURRENT_TIMESTAMP(), 25200), '%Y%m')) + month(SUBTIME(CURRENT_TIMESTAMP(), 25200))) - MONTH('2012-02-01')) - 1) / 3) - ROUND((((month(SUBTIME(CURRENT_TIMESTAMP(),25200)) - MONTH('2012-02-01')) - 1) / 3)))) = -3)", true); - } - - public void testSpeedTestIssue235_2() throws IOException, JSQLParserException { - String stmt = IOUtils.toString(SelectTest.class.getResourceAsStream("large-sql-issue-235.txt")); - assertSqlCanBeParsedAndDeparsed(stmt, true); - } - - public void testCastVarCharMaxIssue245() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT CAST('foo' AS NVARCHAR (MAX))"); - } - - public void testNestedFunctionCallIssue253() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT (replace_regex(replace_regex(replace_regex(get_json_string(a_column, 'value'), '\\n', ' '), '\\r', ' '), '\\\\', '\\\\\\\\')) FROM a_table WHERE b_column = 'value'"); - } - - public void testEscapedBackslashIssue253() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT replace_regex('test', '\\\\', '\\\\\\\\')"); - } - - public void testKeywordTableIssue261() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT column_value FROM table(VARCHAR_LIST_TYPE())"); - } - - public void testTopExpressionIssue243() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT TOP (? + 1) * FROM MyTable"); - } - - public void testTopExpressionIssue243_2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT TOP (CAST(? AS INT)) * FROM MyTable"); - } - - public void testFunctionIssue284() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT NVL((SELECT 1 FROM DUAL), 1) AS A FROM TEST1"); - } - - public void testFunctionDateTimeValues() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT * FROM tab1 WHERE a > TIMESTAMP '2004-04-30 04:05:34.56'"); - } - - public void testUniqueInsteadOfDistinctIssue299() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT UNIQUE trunc(timez(ludate)+ 8/24) bus_dt, j.object j_name , timez(j.starttime) START_TIME , timez(j.endtime) END_TIME FROM TEST_1 j", true); - } - - public void testProblemSqlIssue265() throws IOException, JSQLParserException { - String sqls = IOUtils.toString(SelectTest.class.getResourceAsStream("large-sql-with-issue-265.txt")); - Statements stmts = CCJSqlParserUtil.parseStatements(sqls); - assertEquals(2, stmts.getStatements().size()); - } - - public void testProblemSqlIssue330() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT COUNT(*) FROM C_Invoice WHERE IsSOTrx='Y' AND (Processed='N' OR Updated>(current_timestamp - CAST('90 days' AS interval))) AND C_Invoice.AD_Client_ID IN(0,1010016) AND C_Invoice.AD_Org_ID IN(0,1010053,1010095,1010094)", true); - } - - public void testProblemSqlIssue330_2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT CAST('90 days' AS interval)"); - } -// won't fix due to lookahead impact on parser -// public void testKeywordOrderAsColumnnameIssue333() throws JSQLParserException { -// assertSqlCanBeParsedAndDeparsed("SELECT choice.response_choice_id AS uuid, choice.digit AS digit, choice.text_response AS textResponse, choice.voice_prompt AS voicePrompt, choice.action AS action, choice.contribution AS contribution, choice.order_num AS order, choice.description AS description, choice.is_join_conference AS joinConference, choice.voice_prompt_language_code AS voicePromptLanguageCode, choice.text_response_language_code AS textResponseLanguageCode, choice.description_language_code AS descriptionLanguageCode, choice.rec_phrase AS recordingPhrase FROM response_choices choice WHERE choice.presentation_id = ? ORDER BY choice.order_num", true); -// } - - public void testProblemKeywordCommitIssue341() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT id, commit FROM table1"); - } - - public void testProblemIsIssue331() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("SELECT C_DocType.C_DocType_ID,NULL,COALESCE(C_DocType_Trl.Name,C_DocType.Name) AS Name,C_DocType.IsActive FROM C_DocType LEFT JOIN C_DocType_TRL ON (C_DocType.C_DocType_ID=C_DocType_Trl.C_DocType_ID AND C_DocType_Trl.AD_Language='es_AR') WHERE C_DocType.AD_Client_ID=1010016 AND C_DocType.AD_Client_ID IN (0,1010016) AND C_DocType.c_doctype_id in ( select c_doctype2.c_doctype_id from c_doctype as c_doctype2 where substring( c_doctype2.printname,6, length(c_doctype2.printname) ) = ( select letra from c_letra_comprobante as clc where clc.c_letra_comprobante_id = 1010039) ) AND ( (1010094!=0 AND C_DocType.ad_org_id = 1010094) OR 1010094=0 ) ORDER BY 3 LIMIT 2000", true); - } -} From 28a4c080b718aba3af3a15ccf2019e84e6170620 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Tue, 12 Sep 2023 20:04:14 +0700 Subject: [PATCH 19/77] feat: Postgres `Contains` and `ContainedBy` Operators - sourced from https://github.com/MathewJoseph31/JSqlParser/tree/add-postgress-nested-WithClause - resolves PR #361 - remove redundant comments --- .../jsqlparser/expression/ExpressionVisitor.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index ec6be6809..97c23e410 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -43,12 +43,9 @@ import net.sf.jsqlparser.expression.operators.relational.MinorThan; import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; -import net.sf.jsqlparser.expression.operators.relational.DoubleAnd;// Added by mathew on 21st Nov - // 2016 -import net.sf.jsqlparser.expression.operators.relational.Contains;// Added by mathew on 21st Nov - // 2016 -import net.sf.jsqlparser.expression.operators.relational.ContainedBy;// Added by mathew on 21st Nov - // 2016 +import net.sf.jsqlparser.expression.operators.relational.DoubleAnd; +import net.sf.jsqlparser.expression.operators.relational.Contains; +import net.sf.jsqlparser.expression.operators.relational.ContainedBy; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; import net.sf.jsqlparser.schema.Column; @@ -131,11 +128,11 @@ public interface ExpressionVisitor { void visit(NotEqualsTo notEqualsTo); - void visit(DoubleAnd doubleAnd);// Added by mathew on 21st Nov 2016 + void visit(DoubleAnd doubleAnd); - void visit(Contains contains);// Added by mathew on 21st Nov 2016 + void visit(Contains contains); - void visit(ContainedBy containedBy);// Added by mathew on 21st Nov 2016 + void visit(ContainedBy containedBy); void visit(ParenthesedSelect selectBody); From f919e00c30ff5df824b600afe0b32a3f00264969 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Wed, 13 Sep 2023 18:51:32 +0700 Subject: [PATCH 20/77] feat: MS SQL Server `UPDATE ...` Index Hint - sourced from https://github.com/joeqiao1984/JSqlParser/tree/indexHintsInUpdate - resolves PR #1662 --- .../java/net/sf/jsqlparser/statement/update/UpdateTest.java | 5 ++++- src/test/java/net/sf/jsqlparser/test/TestUtils.java | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java index 9bfeabf14..00de44dc2 100644 --- a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java @@ -24,6 +24,7 @@ import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static net.sf.jsqlparser.test.TestUtils.assertUpdateMysqlHintExists; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -210,7 +211,9 @@ public void testOracleHint() throws JSQLParserException { @Test public void testMysqlHint() throws JSQLParserException { - assertUpdateMysqlHintExists("UPDATE demo FORCE INDEX (idx_demo) SET col1 = NULL WHERE col2 = 1", true, "FORCE", "INDEX", "idx_demo"); + assertUpdateMysqlHintExists( + "UPDATE demo FORCE INDEX (idx_demo) SET col1 = NULL WHERE col2 = 1", true, "FORCE", + "INDEX", "idx_demo"); } @Test diff --git a/src/test/java/net/sf/jsqlparser/test/TestUtils.java b/src/test/java/net/sf/jsqlparser/test/TestUtils.java index f2ac37499..9ee16ae69 100644 --- a/src/test/java/net/sf/jsqlparser/test/TestUtils.java +++ b/src/test/java/net/sf/jsqlparser/test/TestUtils.java @@ -412,7 +412,8 @@ public static void assertOracleHintExists(String sql, boolean assertDeparser, St } } - public static void assertUpdateMysqlHintExists(String sql, boolean assertDeparser, String action, String qualifier, String... indexNames) + public static void assertUpdateMysqlHintExists(String sql, boolean assertDeparser, + String action, String qualifier, String... indexNames) throws JSQLParserException { if (assertDeparser) { assertSqlCanBeParsedAndDeparsed(sql, true); From 7bd42edaa0d9aed953af519a67c850541dbea950 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Wed, 13 Sep 2023 19:12:34 +0700 Subject: [PATCH 21/77] feat: MS SQL Server `Merge` `Output` clause --- .../sf/jsqlparser/statement/merge/Merge.java | 16 +++++ .../util/deparser/StatementDeParser.java | 4 ++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 7 +- .../jsqlparser/statement/merge/MergeTest.java | 66 +++++++++++++++++-- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java index 32dc3d3bd..b658e0b9f 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java @@ -13,6 +13,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.OutputClause; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; import net.sf.jsqlparser.statement.select.FromItem; @@ -37,6 +38,8 @@ public class Merge implements Statement { private MergeUpdate mergeUpdate; private boolean insertFirst = false; + private OutputClause outputClause; + public List getWithItemsList() { return withItemsList; } @@ -155,6 +158,15 @@ public void setInsertFirst(boolean insertFirst) { this.insertFirst = insertFirst; } + public OutputClause getOutputClause() { + return outputClause; + } + + public Merge setOutputClause(OutputClause outputClause) { + this.outputClause = outputClause; + return this; + } + @Override @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public String toString() { @@ -190,6 +202,10 @@ public String toString() { b.append(mergeInsert); } + if (outputClause != null) { + b.append(outputClause); + } + return b.toString(); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index e5833c433..55b11a781 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -244,6 +244,10 @@ public void visit(Merge merge) { if (!merge.isInsertFirst() && mergeInsert != null) { deparseMergeInsert(mergeInsert); } + + if (merge.getOutputClause() != null) { + merge.getOutputClause().appendTo(buffer); + } } private void deparseMergeInsert(MergeInsert mergeInsert) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 906560573..1507dbf02 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1647,6 +1647,7 @@ Statement Merge( List with ) : { Expression condition; MergeUpdate update; MergeInsert insert; + OutputClause outputClause; } { { merge.setOracleHint(getOracleHint()); } table=TableWithAlias() { merge.setTable(table); } @@ -1661,6 +1662,8 @@ Statement Merge( List with ) : { ) ] + [ outputClause = OutputClause() { merge.setOutputClause(outputClause); } ] + { return merge.withWithItemsList(with); } } @@ -1847,7 +1850,9 @@ Table TableWithAliasAndMysqlIndexHint(): MySQLIndexHint indexHint = null; } { - table=Table() [alias=Alias() { table.setAlias(alias); }] [indexHint=MySQLIndexHint() {table.setHint(indexHint);}] + table=Table() + [ LOOKAHEAD(2) alias=Alias() { table.setAlias(alias); } ] + [ LOOKAHEAD(2) indexHint=MySQLIndexHint() { table.setHint(indexHint); } ] { return table; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java index 9a59f8aa1..287b8b922 100644 --- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java @@ -173,11 +173,67 @@ public void testInsertMergeWhere() throws JSQLParserException { @Test public void testWith() throws JSQLParserException { - String statement = "" + "WITH a\n" + " AS (SELECT 1 id_instrument_ref)\n" + " , b\n" - + " AS (SELECT 1 id_instrument_ref)\n" + "MERGE INTO cfe.instrument_ref b\n" - + "using a\n" + "ON ( b.id_instrument_ref = a.id_instrument_ref )\n" - + "WHEN matched THEN\n" + " UPDATE SET b.id_instrument = 'a' "; - statement = "" + "WITH a\n" + " AS (SELECT 1 id_instrument_ref)\n" + "select * from a "; + String statement = "" + + "WITH a\n" + + " AS (SELECT 1 id_instrument_ref)\n" + + "select * from a "; assertSqlCanBeParsedAndDeparsed(statement, true); } + + @Test + public void testOutputClause() throws JSQLParserException { + String sqlStr = "" + + "WITH\n" + + " WMachine AS\n" + + " ( SELECT\n" + + " DISTINCT \n" + + " ProjCode,\n" + + " PlantCode,\n" + + " BuildingCode,\n" + + " FloorCode,\n" + + " Room\n" + + " FROM\n" + + " TAB_MachineLocation\n" + + " WHERE\n" + + " TRIM(Room) <> '' AND TRIM(Room) <> '-'\n" + + " ) \n" + + " MERGE INTO\n" + + " TAB_RoomLocation AS TRoom\n" + + " USING\n" + + " WMachine\n" + + " ON\n" + + " (\n" + + " TRoom.ProjCode = WMachine.ProjCode\n" + + " AND TRoom.PlantCode = WMachine.PlantCode\n" + + " AND TRoom.BuildingCode = WMachine.BuildingCode\n" + + " AND TRoom.FloorCode = WMachine.FloorCode\n" + + " AND TRoom.Room = WMachine.Room)\n" + + " WHEN NOT MATCHED /* BY TARGET */ THEN\n" + + " INSERT\n" + + " (\n" + + " ProjCode,\n" + + " PlantCode,\n" + + " BuildingCode,\n" + + " FloorCode,\n" + + " Room\n" + + " )\n" + + " VALUES\n" + + " (\n" + + " WMachine.ProjCode,\n" + + " WMachine.PlantCode,\n" + + " WMachine.BuildingCode,\n" + + " WMachine.FloorCode,\n" + + " WMachine.Room\n" + + " )\n" + + " OUTPUT GETDATE() AS TimeAction,\n" + + " $action as Action,\n" + + " INSERTED.ProjCode,\n" + + " INSERTED.PlantCode,\n" + + " INSERTED.BuildingCode,\n" + + " INSERTED.FloorCode,\n" + + " INSERTED.Room\n" + + " INTO\n" + + " TAB_MergeActions_RoomLocation"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } From e3afa5fbdebc7152107364b28240fcd0c18382de Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Wed, 13 Sep 2023 22:15:51 +0700 Subject: [PATCH 22/77] fix: Postgres `NextVal()` function - allow `NEXTVAL` token for function names - fixes #1863 - add test case --- .../parser/ParserKeywordsUtils.java | 198 +++++++++++------- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 6 +- src/site/sphinx/keywords.rst | 2 +- .../statement/select/PostgresTest.java | 6 + 4 files changed, 133 insertions(+), 79 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index 9306e01da..febd49a22 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -45,54 +45,103 @@ public class ParserKeywordsUtils { // Classification follows http://www.h2database.com/html/advanced.html#keywords public final static Object[][] ALL_RESERVED_KEYWORDS = { - {"ABSENT", RESTRICTED_JSQLPARSER}, {"ALL", RESTRICTED_SQL2016}, - {"AND", RESTRICTED_SQL2016}, {"ANY", RESTRICTED_JSQLPARSER}, {"AS", RESTRICTED_SQL2016}, - {"BETWEEN", RESTRICTED_SQL2016}, {"BOTH", RESTRICTED_SQL2016}, - {"CASEWHEN", RESTRICTED_ALIAS}, {"CHECK", RESTRICTED_SQL2016}, - {"CONNECT", RESTRICTED_ALIAS}, {"CONNECT_BY_ROOT", RESTRICTED_JSQLPARSER}, - {"CONSTRAINT", RESTRICTED_SQL2016}, {"CREATE", RESTRICTED_ALIAS}, - {"CROSS", RESTRICTED_SQL2016}, {"CURRENT", RESTRICTED_JSQLPARSER}, - {"DISTINCT", RESTRICTED_SQL2016}, {"DOUBLE", RESTRICTED_ALIAS}, - {"ELSE", RESTRICTED_JSQLPARSER}, {"EXCEPT", RESTRICTED_SQL2016}, - {"EXISTS", RESTRICTED_SQL2016}, {"FETCH", RESTRICTED_SQL2016}, - {"FINAL", RESTRICTED_JSQLPARSER}, {"FOR", RESTRICTED_SQL2016}, - {"FORCE", RESTRICTED_SQL2016}, {"FOREIGN", RESTRICTED_SQL2016}, - {"FROM", RESTRICTED_SQL2016}, {"FULL", RESTRICTED_SQL2016}, - {"GROUP", RESTRICTED_SQL2016}, {"GROUPING", RESTRICTED_ALIAS}, + {"ABSENT", RESTRICTED_JSQLPARSER}, + {"ALL", RESTRICTED_SQL2016}, + {"AND", RESTRICTED_SQL2016}, + {"ANY", RESTRICTED_JSQLPARSER}, + {"AS", RESTRICTED_SQL2016}, + {"BETWEEN", RESTRICTED_SQL2016}, + {"BOTH", RESTRICTED_SQL2016}, + {"CASEWHEN", RESTRICTED_ALIAS}, + {"CHECK", RESTRICTED_SQL2016}, + {"CONNECT", RESTRICTED_ALIAS}, + {"CONNECT_BY_ROOT", RESTRICTED_JSQLPARSER}, + {"CONSTRAINT", RESTRICTED_SQL2016}, + {"CREATE", RESTRICTED_ALIAS}, + {"CROSS", RESTRICTED_SQL2016}, + {"CURRENT", RESTRICTED_JSQLPARSER}, + {"DISTINCT", RESTRICTED_SQL2016}, + {"DOUBLE", RESTRICTED_ALIAS}, + {"ELSE", RESTRICTED_JSQLPARSER}, + {"EXCEPT", RESTRICTED_SQL2016}, + {"EXISTS", RESTRICTED_SQL2016}, + {"FETCH", RESTRICTED_SQL2016}, + {"FINAL", RESTRICTED_JSQLPARSER}, + {"FOR", RESTRICTED_SQL2016}, + {"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}, - {"INTERVAL", RESTRICTED_SQL2016}, {"INTO", RESTRICTED_JSQLPARSER}, - {"IS", RESTRICTED_SQL2016}, {"JOIN", RESTRICTED_JSQLPARSER}, - {"LATERAL", RESTRICTED_SQL2016}, {"LEFT", RESTRICTED_SQL2016}, - {"LIKE", RESTRICTED_SQL2016}, {"LIMIT", RESTRICTED_SQL2016}, - {"MINUS", RESTRICTED_SQL2016}, {"NATURAL", RESTRICTED_SQL2016}, - {"NOCYCLE", RESTRICTED_JSQLPARSER}, {"NOT", RESTRICTED_SQL2016}, - {"NULL", RESTRICTED_SQL2016}, {"OFFSET", RESTRICTED_SQL2016}, - {"ON", RESTRICTED_SQL2016}, {"ONLY", RESTRICTED_JSQLPARSER}, - {"OPTIMIZE", RESTRICTED_ALIAS}, {"OR", RESTRICTED_SQL2016}, - {"ORDER", RESTRICTED_SQL2016}, {"OUTER", RESTRICTED_JSQLPARSER}, - {"OUTPUT", RESTRICTED_JSQLPARSER}, {"OPTIMIZE ", RESTRICTED_JSQLPARSER}, - {"PIVOT", RESTRICTED_JSQLPARSER}, {"PROCEDURE", RESTRICTED_ALIAS}, - {"PUBLIC", RESTRICTED_ALIAS}, {"RECURSIVE", RESTRICTED_SQL2016}, - {"REGEXP", RESTRICTED_SQL2016}, {"RETURNING", RESTRICTED_JSQLPARSER}, - {"RIGHT", RESTRICTED_SQL2016}, {"SAMPLE", RESTRICTED_ALIAS}, {"SEL", 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}, + {"INTERVAL", RESTRICTED_SQL2016}, + {"INTO", RESTRICTED_JSQLPARSER}, + {"IS", RESTRICTED_SQL2016}, + {"JOIN", RESTRICTED_JSQLPARSER}, + {"LATERAL", RESTRICTED_SQL2016}, + {"LEFT", RESTRICTED_SQL2016}, + {"LIKE", RESTRICTED_SQL2016}, + {"LIMIT", RESTRICTED_SQL2016}, + {"MINUS", RESTRICTED_SQL2016}, + {"NATURAL", RESTRICTED_SQL2016}, + {"NOCYCLE", RESTRICTED_JSQLPARSER}, + {"NOT", RESTRICTED_SQL2016}, + {"NULL", RESTRICTED_SQL2016}, + {"OFFSET", RESTRICTED_SQL2016}, + {"ON", RESTRICTED_SQL2016}, + {"ONLY", RESTRICTED_JSQLPARSER}, + {"OPTIMIZE", RESTRICTED_ALIAS}, + {"OR", RESTRICTED_SQL2016}, + {"ORDER", RESTRICTED_SQL2016}, + {"OUTER", RESTRICTED_JSQLPARSER}, + {"OUTPUT", RESTRICTED_JSQLPARSER}, + {"OPTIMIZE ", RESTRICTED_JSQLPARSER}, + {"PIVOT", RESTRICTED_JSQLPARSER}, + {"PROCEDURE", RESTRICTED_ALIAS}, + {"PUBLIC", RESTRICTED_ALIAS}, + {"RECURSIVE", RESTRICTED_SQL2016}, + {"REGEXP", RESTRICTED_SQL2016}, + {"RETURNING", RESTRICTED_JSQLPARSER}, + {"RIGHT", RESTRICTED_SQL2016}, + {"SAMPLE", RESTRICTED_ALIAS}, + {"SEL", RESTRICTED_ALIAS}, {"SELECT", RESTRICTED_ALIAS}, - {"SEMI", RESTRICTED_JSQLPARSER}, {"SET", RESTRICTED_JSQLPARSER}, - {"SOME", RESTRICTED_JSQLPARSER}, {"START", RESTRICTED_JSQLPARSER}, - {"TABLES", RESTRICTED_ALIAS}, {"TOP", RESTRICTED_SQL2016}, - {"TRAILING", RESTRICTED_SQL2016}, {"UNBOUNDED", RESTRICTED_JSQLPARSER}, - {"UNION", RESTRICTED_SQL2016}, {"UNIQUE", RESTRICTED_SQL2016}, - {"UNPIVOT", RESTRICTED_JSQLPARSER}, {"USE", RESTRICTED_JSQLPARSER}, - {"USING", RESTRICTED_SQL2016}, {"SQL_CACHE", RESTRICTED_JSQLPARSER}, - {"SQL_CALC_FOUND_ROWS", RESTRICTED_JSQLPARSER}, {"SQL_NO_CACHE", RESTRICTED_JSQLPARSER}, - {"STRAIGHT_JOIN", RESTRICTED_JSQLPARSER}, {"TABLESAMPLE", RESTRICTED_ALIAS}, + {"SEMI", RESTRICTED_JSQLPARSER}, + {"SET", RESTRICTED_JSQLPARSER}, + {"SOME", RESTRICTED_JSQLPARSER}, + {"START", RESTRICTED_JSQLPARSER}, + {"TABLES", RESTRICTED_ALIAS}, + {"TOP", RESTRICTED_SQL2016}, + {"TRAILING", RESTRICTED_SQL2016}, + {"UNBOUNDED", RESTRICTED_JSQLPARSER}, + {"UNION", RESTRICTED_SQL2016}, + {"UNIQUE", RESTRICTED_SQL2016}, + {"UNPIVOT", RESTRICTED_JSQLPARSER}, + {"USE", RESTRICTED_JSQLPARSER}, + {"USING", RESTRICTED_SQL2016}, + {"SQL_CACHE", RESTRICTED_JSQLPARSER}, + {"SQL_CALC_FOUND_ROWS", RESTRICTED_JSQLPARSER}, + {"SQL_NO_CACHE", RESTRICTED_JSQLPARSER}, + {"STRAIGHT_JOIN", RESTRICTED_JSQLPARSER}, + {"TABLESAMPLE", RESTRICTED_ALIAS}, {"VALUE", RESTRICTED_JSQLPARSER}, - {"VALUES", RESTRICTED_SQL2016}, {"VARYING", RESTRICTED_JSQLPARSER}, - {"WHEN", RESTRICTED_SQL2016}, {"WHERE", RESTRICTED_SQL2016}, - {"WINDOW", RESTRICTED_SQL2016}, {"WITH", RESTRICTED_SQL2016}, - {"XOR", RESTRICTED_JSQLPARSER}, {"XMLSERIALIZE", RESTRICTED_JSQLPARSER} + {"VALUES", RESTRICTED_SQL2016}, + {"VARYING", RESTRICTED_JSQLPARSER}, + {"WHEN", RESTRICTED_SQL2016}, + {"WHERE", RESTRICTED_SQL2016}, + {"WINDOW", RESTRICTED_SQL2016}, + {"WITH", RESTRICTED_SQL2016}, + {"XOR", RESTRICTED_JSQLPARSER}, + {"XMLSERIALIZE", RESTRICTED_JSQLPARSER}, // add keywords from the composite token definitions: // tk= | tk= | tk= @@ -100,23 +149,26 @@ public class ParserKeywordsUtils { // simple keywords // @todo: figure out a way to remove these composite tokens, as they do more harm than // good - , {"SEL", RESTRICTED_JSQLPARSER}, {"SELECT", RESTRICTED_JSQLPARSER} - - , {"DATE", RESTRICTED_JSQLPARSER}, {"TIME", RESTRICTED_JSQLPARSER}, - {"TIMESTAMP", RESTRICTED_JSQLPARSER} - - , {"YEAR", RESTRICTED_JSQLPARSER}, {"MONTH", RESTRICTED_JSQLPARSER}, - {"DAY", RESTRICTED_JSQLPARSER}, {"HOUR", RESTRICTED_JSQLPARSER}, - {"MINUTE", RESTRICTED_JSQLPARSER}, {"SECOND", RESTRICTED_JSQLPARSER} - - , {"SUBSTR", RESTRICTED_JSQLPARSER}, {"SUBSTRING", RESTRICTED_JSQLPARSER}, - {"TRIM", RESTRICTED_JSQLPARSER}, {"POSITION", RESTRICTED_JSQLPARSER}, - {"OVERLAY", RESTRICTED_JSQLPARSER} - - , {"NEXTVAL", RESTRICTED_JSQLPARSER} + {"SEL", RESTRICTED_JSQLPARSER}, + {"SELECT", RESTRICTED_JSQLPARSER}, + {"DATE", RESTRICTED_JSQLPARSER}, + {"TIME", RESTRICTED_JSQLPARSER}, + {"TIMESTAMP", RESTRICTED_JSQLPARSER}, + {"YEAR", RESTRICTED_JSQLPARSER}, + {"MONTH", RESTRICTED_JSQLPARSER}, + {"DAY", RESTRICTED_JSQLPARSER}, + {"HOUR", RESTRICTED_JSQLPARSER}, + {"MINUTE", RESTRICTED_JSQLPARSER}, + {"SECOND", RESTRICTED_JSQLPARSER}, + {"SUBSTR", RESTRICTED_JSQLPARSER}, + {"SUBSTRING", RESTRICTED_JSQLPARSER}, + {"TRIM", RESTRICTED_JSQLPARSER}, + {"POSITION", RESTRICTED_JSQLPARSER}, + {"OVERLAY", RESTRICTED_JSQLPARSER}, + {"NEXTVAL", RESTRICTED_COLUMN}, // @todo: Object Names should not start with Hex-Prefix, we shall not find that Token - , {"0x", RESTRICTED_JSQLPARSER} + {"0x", RESTRICTED_JSQLPARSER} }; @SuppressWarnings({"PMD.ExcessiveMethodLength"}) @@ -126,8 +178,7 @@ public static List getReservedKeywords(int restriction) { int value = (int) data[1]; // test if bit is not set - if ((value & restriction) == restriction - || (restriction & value) == value) { + if ((value & restriction) == restriction || (restriction & value) == value) { keywords.add((String) data[0]); } } @@ -136,7 +187,6 @@ public static List getReservedKeywords(int restriction) { } /** - * * @param args with: Grammar File, Keyword Documentation File * @throws Exception */ @@ -213,9 +263,7 @@ public static void buildGrammarForRelObjectNameWithoutValue(File file) throws Ex builder.append(" | tk=\"").append(keyword).append("\""); } - builder.append(" )\n" - + " { return tk.image; }\n" - + "}"); + builder.append(" )\n" + " { return tk.image; }\n" + "}"); replaceInFile(file, methodBlockPattern, builder.toString()); } @@ -244,9 +292,7 @@ public static void buildGrammarForRelObjectName(File file) throws Exception { builder.append(" | tk=\"").append(keyword).append("\""); } - builder.append(" )\n" - + " { return tk!=null ? tk.image : result; }\n" - + "}"); + builder.append(" )\n" + " { return tk!=null ? tk.image : result; }\n" + "}"); // @todo: Needs fine-tuning, we are not replacing this part yet // replaceInFile(file, pattern, builder.toString()); @@ -267,9 +313,7 @@ private static void replaceInFile(File file, Pattern pattern, String replacement } public static String rightPadding(String input, char ch, int length) { - return String - .format("%" + (-length) + "s", input) - .replace(' ', ch); + return String.format("%" + (-length) + "s", input).replace(' ', ch); } public static void writeKeywordsDocumentationFile(File file) throws IOException { @@ -293,13 +337,15 @@ public static void writeKeywordsDocumentationFile(File file) throws IOException int value = (int) keywordDefinition[1]; int restriction = RESTRICTED_JSQLPARSER; - String s = - (value & restriction) == restriction || (restriction & value) == value ? "Yes" - : ""; + String s = (value & restriction) == restriction || (restriction & value) == value + ? "Yes" + : ""; builder.append(rightPadding(s, ' ', 11)).append(" | "); restriction = RESTRICTED_SQL2016; - s = (value & restriction) == restriction || (restriction & value) == value ? "Yes" : ""; + s = (value & restriction) == restriction || (restriction & value) == value + ? "Yes" + : ""; builder.append(rightPadding(s, ' ', 9)).append(" | "); builder.append("\n"); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 1507dbf02..452b0b4d5 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1752,7 +1752,7 @@ The following tokens are allowed as Names for Schema, Table, Column and Aliases String RelObjectNameWithoutValue() : { Token tk = null; } { - ( tk= | tk= | tk= | tk= | tk= | tk= | tk= + ( 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="INTERPRET" | 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="REPEATABLE" | 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="SEED" | 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; } } @@ -1797,7 +1797,9 @@ String RelObjectNameExt(): ( result=RelObjectName() | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk= | tk= | tk= | tk= ) + | tk= | tk= | tk= | tk= + | tk= + ) { return tk!=null ? tk.image : result; } } diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst index 40778c18d..6b551c801 100644 --- a/src/site/sphinx/keywords.rst +++ b/src/site/sphinx/keywords.rst @@ -233,7 +233,7 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | OVERLAY | Yes | Yes | +----------------------+-------------+-----------+ -| NEXTVAL | Yes | Yes | +| NEXTVAL | Yes | | +----------------------+-------------+-----------+ | 0x | Yes | Yes | +----------------------+-------------+-----------+ diff --git a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java index 921435026..ffa2c8012 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java @@ -95,4 +95,10 @@ void testPostgresQuotingIssue1335() throws JSQLParserException { "\"column\"\"with\"\"quotes\"", selectItems.get(0).getExpression(Column.class).getColumnName()); } + + @Test + void testNextValueIssue1863() throws JSQLParserException { + String sqlStr = "SELECT nextval('client_id_seq')"; + assertSqlCanBeParsedAndDeparsed(sqlStr); + } } From 5f09ec4914fbdd1e25f3719cc3768063ba8a055b Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 22 Sep 2023 15:20:26 +0700 Subject: [PATCH 23/77] feat: T-SQL Join Hints - fixes #1866 - fixes #1864 --- config/pmd/ruleset.xml | 7 +++- .../sf/jsqlparser/statement/select/Join.java | 15 ++++++++ .../jsqlparser/statement/select/JoinHint.java | 22 +++++++++++ .../util/deparser/SelectDeParser.java | 3 ++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 38 +++++++++++++++---- .../statement/create/CreateTableTest.java | 8 ++++ .../statement/select/JoinHintTest.java | 36 ++++++++++++++++++ 7 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml index fdfa1169d..6b8c681a6 100644 --- a/config/pmd/ruleset.xml +++ b/config/pmd/ruleset.xml @@ -77,7 +77,12 @@ under the License. - + + + + + + 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 efaa3e6c7..d057a5381 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Join.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Optional; +@SuppressWarnings({"PMD.CyclomaticComplexity"}) public class Join extends ASTNodeAccessImpl { private boolean outer = false; @@ -39,6 +40,8 @@ public class Join extends ASTNodeAccessImpl { private final LinkedList usingColumns = new LinkedList<>(); private KSQLJoinWindow joinWindow; + private JoinHint joinHint = null; + public boolean isSimple() { return simple; } @@ -377,6 +380,15 @@ public void setJoinWindow(KSQLJoinWindow joinWindow) { this.joinWindow = joinWindow; } + public JoinHint getJoinHint() { + return joinHint; + } + + public Join setJoinHint(JoinHint joinHint) { + this.joinHint = joinHint; + return this; + } + @Override @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public String toString() { @@ -418,6 +430,9 @@ public String toString() { } else if (isApply()) { builder.append("APPLY "); } else { + if (joinHint != null) { + builder.append(joinHint).append(" "); + } builder.append("JOIN "); } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java new file mode 100644 index 000000000..d98b1d568 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java @@ -0,0 +1,22 @@ +package net.sf.jsqlparser.statement.select; + +/** + * Hints (Transact-SQL) - Join + * + * @link Hints + * (Transact-SQL) - Join + */ + +public class JoinHint { + private final String keyword; + + public JoinHint(String keyword) { + this.keyword = keyword; + } + + @Override + public String toString() { + return keyword; + } +} 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 9a1f6f9cb..75aac7a9e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -481,6 +481,9 @@ public void deparseJoin(Join join) { } else if (join.isApply()) { buffer.append(" APPLY "); } else { + if (join.getJoinHint() != null) { + buffer.append(" ").append(join.getJoinHint()); + } buffer.append(" JOIN "); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index b4112ee7f..ccba7d845 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -183,6 +183,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -250,6 +251,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -270,6 +272,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -292,6 +295,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -363,6 +367,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -372,6 +377,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -1263,7 +1269,7 @@ ReturningClause ReturningClause(): List dataItems = null; } { - ( keyword= | keyword="RETURN" ) + ( keyword= | keyword= ) selectItems = SelectItemsList() [ @@ -1752,8 +1758,8 @@ The following tokens are allowed as Names for Schema, Table, Column and Aliases 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="INTERPRET" | 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="REPEATABLE" | 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="SEED" | 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" ) + ( 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONSTRAINTS" | 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="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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; } } @@ -2704,6 +2710,24 @@ List JoinsList(): } } +JoinHint JoinHint(): +{ + Token token; +} +{ + ( + token = + | + token = + | + token = + | + token = + ) + { + return new JoinHint(token.image); + } +} Join JoinerExpression() #JoinerExpression: { @@ -2713,7 +2737,7 @@ Join JoinerExpression() #JoinerExpression: Column tableColumn; List columns = null; KSQLJoinWindow joinWindow = null; - + JoinHint joinHint = null; } { [ { join.setGlobal(true); } ] @@ -2738,7 +2762,7 @@ Join JoinerExpression() #JoinerExpression: ] ( - + ( [ joinHint=JoinHint() {join.setJoinHint(joinHint); } ] ) | "," { join.setSimple(true); } ( { join.setOuter(true); } )? | @@ -5700,7 +5724,7 @@ List CreateParameter(): | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk="=" ) { param.add(tk.image); } @@ -6513,7 +6537,7 @@ AlterSystemStatement AlterSystemStatement(): ) | ( - "UNQUIESCE" { operation = AlterSystemOperation.UNQUIESCE; } + { operation = AlterSystemOperation.UNQUIESCE; } ) | ( diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java index dbe046e1b..d9c477159 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java @@ -1051,4 +1051,12 @@ void testIssue1858() throws JSQLParserException { + ") compound sortkey ( date_key )"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + void testIssue1864() throws JSQLParserException { + String sqlStr = "ALTER TABLE `test`.`test_table` " + + "MODIFY COLUMN `test` varchar(251) " + + " CHARACTER SET armscii8 COLLATE armscii8_bin NULL DEFAULT NULL FIRST"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java b/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java new file mode 100644 index 000000000..5336843f6 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java @@ -0,0 +1,36 @@ +package net.sf.jsqlparser.statement.select; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class JoinHintTest { + public static Stream sqlStrings() { + return Stream.of( + "SELECT p.Name, pr.ProductReviewID \n" + + "FROM Production.Product AS p \n" + + "LEFT OUTER HASH JOIN Production.ProductReview AS pr \n" + + "ON p.ProductID = pr.ProductID \n" + + "ORDER BY ProductReviewID DESC", + + "DELETE spqh \n" + + "FROM Sales.SalesPersonQuotaHistory AS spqh \n" + + " INNER LOOP JOIN Sales.SalesPerson AS sp \n" + + " ON spqh.SalesPersonID = sp.SalesPersonID \n" + + "WHERE sp.SalesYTD > 2500000.00", + + "SELECT poh.PurchaseOrderID, poh.OrderDate, pod.ProductID, pod.DueDate, poh.VendorID \n" + + "FROM Purchasing.PurchaseOrderHeader AS poh \n" + + "INNER MERGE JOIN Purchasing.PurchaseOrderDetail AS pod \n" + + " ON poh.PurchaseOrderID = pod.PurchaseOrderID"); + } + + @ParameterizedTest + @MethodSource("sqlStrings") + void testJoinHint(String sqlStr) throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } +} From 1088db7aea0b2f9d1a4a75ad70e5fc531639dbc4 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 20 Oct 2023 07:56:05 +0700 Subject: [PATCH 24/77] feat: `ColDataType` supports `PUBLIC` schema and all non-restricted keywords for type - fixes #1879 Signed-off-by: Andreas Reichel --- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 64 +++++++++++++------ .../create/table/ColDataTypeTest.java | 34 ++++++++++ 2 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index ccba7d845..7029df760 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -871,14 +871,15 @@ DeclareStatement Declare(): { } { userVariable = UserVariable() ( - ( "(" colDef = ColumnDefinition() - { - stmt.withUserVariable(userVariable) - .withDeclareType(DeclareType.TABLE) - .addColumnDefinition(colDef); - } - ("," colDef = ColumnDefinition() { stmt.addColumnDefinition(colDef); })* ")" - ) + LOOKAHEAD(2) ( + "(" colDef = ColumnDefinition() + { + stmt.withUserVariable(userVariable) + .withDeclareType(DeclareType.TABLE) + .addColumnDefinition(colDef); + } + ("," colDef = ColumnDefinition() { stmt.addColumnDefinition(colDef); })* ")" + ) | typeName = RelObjectName() { @@ -4866,7 +4867,7 @@ CastExpression CastExpression(): expression=SimpleExpression() { retval.setUseCastKeyword(true); } ( - ( + LOOKAHEAD(2) ( "(" columnDefinition=ColumnDefinition() { retval.addColumnDefinition(columnDefinition); } @@ -5560,6 +5561,8 @@ ColDataType ColDataType(): ColDataType colDataType = new ColDataType(); Token tk = null; Token tk2 = null; + String schema; + String type; List argumentsStringList = new ArrayList(); List array = new ArrayList(); List name; @@ -5567,7 +5570,7 @@ ColDataType ColDataType(): } { ( - tk= ( + LOOKAHEAD(2) tk= ( ("<" arrayType = ColDataType() ">") { colDataType.setDataType("ARRAY<" + arrayType.getDataType() + ">"); } @@ -5578,16 +5581,35 @@ ColDataType ColDataType(): | tk= "(" (tk2= | tk2=) ")" | tk= "(" (tk2= | tk2=) ")" ) { colDataType.setDataType(tk.image + " (" + tk2.image + ")"); } - | (tk= | tk=) [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } - | tk= [LOOKAHEAD(2) tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } - | ( tk= | tk= | tk= | tk= | tk= | tk= - | tk= | tk= | tk= | tk= | tk= | tk= ) - [ "." (tk2= | tk2=) ] - { if (tk2!=null) colDataType.setDataType(tk.image + "." + tk2.image); else colDataType.setDataType(tk.image); } - | tk= [LOOKAHEAD(2) tk2=] - { if (tk2!=null) colDataType.setDataType(tk.image + " " + tk2.image); else colDataType.setDataType(tk.image); } - | LOOKAHEAD(2) tk= tk2= {colDataType.setDataType(tk.image + " " + tk2.image);} - | tk= { colDataType.setDataType(tk.image);} + | + LOOKAHEAD(2) (tk= | tk=) [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } + | + tk= [LOOKAHEAD(2) tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } + | + ( + tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + | tk= + ) { schema = tk.image; } + [ "." type = RelObjectName() { schema += "." + type; } ] + { colDataType.setDataType(schema); } + | + tk= [LOOKAHEAD(2) tk2=] + { if (tk2!=null) colDataType.setDataType(tk.image + " " + tk2.image); else colDataType.setDataType(tk.image); } + | + LOOKAHEAD(2) tk= tk2= {colDataType.setDataType(tk.image + " " + tk2.image);} + | + tk= { colDataType.setDataType(tk.image);} ) [LOOKAHEAD(2) "(" {tk2 =null;} ( ( ( tk= [ LOOKAHEAD(2) (tk2= | tk2=) ] ) | tk= | tk= | tk= ) @@ -5943,7 +5965,7 @@ AlterExpression.ColumnDataType AlterExpressionColumnDataType(): } { columnName = RelObjectName() - ( { withType = true; } )? + ( LOOKAHEAD(2) { withType = true; } )? dataType = ColDataType() { columnSpecs = new ArrayList(); } ( parameter = CreateParameter() { columnSpecs.addAll(parameter); } )* { diff --git a/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java b/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java new file mode 100644 index 000000000..43c5b1fc3 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java @@ -0,0 +1,34 @@ +package net.sf.jsqlparser.statement.create.table; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + +class ColDataTypeTest { + @Test + void testPublicType() throws JSQLParserException { + String sqlStr = "select 1::public.integer"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testIssue1879() throws JSQLParserException { + String sqlStr = "CREATE TABLE public.film (\n" + + " film_id integer DEFAULT nextval('public.film_film_id_seq'::regclass) NOT NULL,\n" + + + " title character varying(255) NOT NULL,\n" + + " description text,\n" + + " release_year public.year,\n" + + " language_id smallint NOT NULL,\n" + + " rental_duration smallint DEFAULT 3 NOT NULL,\n" + + " rental_rate numeric(4,2) DEFAULT 4.99 NOT NULL,\n" + + " length smallint,\n" + + " replacement_cost numeric(5,2) DEFAULT 19.99 NOT NULL,\n" + + " rating public.mpaa_rating DEFAULT 'G'::public.mpaa_rating,\n" + + " last_update timestamp without time zone DEFAULT now() NOT NULL,\n" + + " special_features text[],\n" + + " fulltext tsvector NOT NULL\n" + + ")"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } +} From 0b564fcb048cd9a08f9586eefe53f6db2dbda3ee Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 20 Oct 2023 13:52:30 +0700 Subject: [PATCH 25/77] build: update Gradle wrapper 8.3 Signed-off-by: Andreas Reichel --- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 38440 zcmZ6SQ*<3%6s?oSw$a$O+t{{kpWwtNwr$(CZL_f&H8y*DImUnA_rrded+oL6w`Tc1 z#Mv!G{V@^ba0IDo(ixEwh?a02n}!DKIvnc^%1aoMq1+#kAhJLP_oX|jgEPsr;V`0k zL+b-2$5Bo(3c}Buz&-hcO_SO<6900dXYt$Ter6+Mad;kQkW8)Rc`!+|ia-jh^V%qoFb5JQ`

cw$=gQbRBDL9uPo|6z1d&0P$^g)iXXq+aG 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 CEjxVx delta 36364 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%0Dd@gG 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 Pg;r%T9-eUK&({9{Yi$yT diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdc9a83b1..ac72c34e8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d421..0adc8e1a5 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in From 64c0f13acec93ea111ad5e993dc1c8525496ff51 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 20 Oct 2023 13:59:47 +0700 Subject: [PATCH 26/77] build: update Gradle wrapper 8.4 Signed-off-by: Andreas Reichel --- gradle.properties | 5 +++++ gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f1dda8d41..522c7558e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,3 +10,8 @@ org.gradle.parallel=true # Enable configure on demand. org.gradle.configureondemand=true +# see https://docs.gradle.org/current/userguide/upgrading_version_8.html#xml_parsing_now_requires_recent_parsers +systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl +systemProp.javax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl +systemProp.javax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac72c34e8..3fa8f862f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 97e9229d15df7d6707a7c3cb1d25c0ba2dfe3b14 Mon Sep 17 00:00:00 2001 From: human-user <68408844+human-user@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:07:04 +0800 Subject: [PATCH 27/77] Unit tests support multi-os and higher versions of jdk (#1886) * fix: tokenBlockPattern support \r\n or \r * test: remove nashorn ignore annotation to support jdk11+ --- .../net/sf/jsqlparser/parser/ParserKeywordsUtils.java | 2 +- .../net/sf/jsqlparser/util/TablesNamesFinderTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index febd49a22..a8182344b 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -214,7 +214,7 @@ public static void main(String[] args) throws Exception { public static TreeSet getAllKeywordsUsingRegex(File file) throws IOException { Pattern tokenBlockPattern = Pattern.compile( - "TOKEN\\s*:\\s*(?:/\\*.*\\*/*)\\n\\{(?:[^\\}\\{]+|\\{(?:[^\\}\\{]+|\\{[^\\}\\{]*\\})*\\})*\\}", + "TOKEN\\s*:\\s*(?:/\\*.*\\*/*)(?:\\r?\\n|\\r)\\{(?:[^\\}\\{]+|\\{(?:[^\\}\\{]+|\\{[^\\}\\{]*\\})*\\})*\\}", Pattern.MULTILINE); Pattern tokenStringValuePattern = Pattern.compile("\\\"(\\w{2,})\\\"", Pattern.MULTILINE); diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index 7d122df53..e08c5032c 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -9,7 +9,6 @@ */ package net.sf.jsqlparser.util; -import jdk.nashorn.internal.ir.annotations.Ignore; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.parser.CCJSqlParserManager; @@ -23,6 +22,7 @@ import net.sf.jsqlparser.statement.simpleparsing.CCJSqlParserManagerTest; import net.sf.jsqlparser.test.TestException; import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.io.BufferedReader; @@ -41,17 +41,17 @@ public class TablesNamesFinderTest { private static final CCJSqlParserManager PARSER_MANAGER = new CCJSqlParserManager(); - @Ignore + @Disabled public void testRUBiSTableList() throws Exception { runTestOnResource("/RUBiS-select-requests.txt"); } - @Ignore + @Disabled public void testMoreComplexExamples() throws Exception { runTestOnResource("complex-select-requests.txt"); } - @Ignore + @Disabled public void testComplexMergeExamples() throws Exception { runTestOnResource("complex-merge-requests.txt"); } From 36b806dede06260e2f123c050bb1de7067d814e8 Mon Sep 17 00:00:00 2001 From: David Goss Date: Fri, 10 Nov 2023 03:04:35 +0000 Subject: [PATCH 28/77] feat: add support for snowflake merge statements (#1887) * feat: support snowflake merge statements Adds support for the ON clause of a MERGE statement to be without enclosing parens, as is the convention in Snowflake. * feat: add support for and predicate in merge update * feat: add support for and predicate in merge insert --- .../sf/jsqlparser/statement/merge/Merge.java | 3 +- .../statement/merge/MergeInsert.java | 38 ++++++++++++++++--- .../statement/merge/MergeUpdate.java | 27 ++++++++++++- .../util/deparser/StatementDeParser.java | 17 +++++++-- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 16 +++++--- .../jsqlparser/statement/merge/MergeTest.java | 27 +++++++++++++ 6 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java index b658e0b9f..8d69bbce4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java @@ -186,9 +186,8 @@ public String toString() { b.append(table); b.append(" USING "); b.append(fromItem); - b.append(" ON ("); + b.append(" ON "); b.append(onCondition); - b.append(")"); if (insertFirst && mergeInsert != null) { b.append(mergeInsert); diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java index bc9ea695a..f6affa3ef 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java @@ -20,10 +20,19 @@ public class MergeInsert implements Serializable { + private Expression andPredicate; private ExpressionList columns; private ExpressionList values; private Expression whereCondition; + public Expression getAndPredicate() { + return andPredicate; + } + + public void setAndPredicate(Expression andPredicate) { + this.andPredicate = andPredicate; + } + public ExpressionList getColumns() { return columns; } @@ -50,12 +59,25 @@ public void setWhereCondition(Expression whereCondition) { @Override public String toString() { - return " WHEN NOT MATCHED THEN INSERT " - + (columns != null ? columns.toString() : "") - + " VALUES " + values.toString() - + (whereCondition != null - ? " WHERE " + whereCondition - : ""); + StringBuilder b = new StringBuilder(); + b.append(" WHEN NOT MATCHED"); + if (andPredicate != null) { + b.append(" AND ").append(andPredicate.toString()); + } + b.append(" THEN INSERT "); + if (columns != null) { + b.append(columns.toString()); + } + b.append(" VALUES ").append(values.toString()); + if (whereCondition != null) { + b.append(" WHERE ").append(whereCondition.toString()); + } + return b.toString(); + } + + public MergeInsert withAndPredicate(Expression andPredicate) { + this.setAndPredicate(andPredicate); + return this; } public MergeInsert withColumns(ExpressionList columns) { @@ -95,6 +117,10 @@ public MergeInsert withWhereCondition(Expression whereCondition) { return this; } + public E getAndPredicate(Class type) { + return type.cast(getAndPredicate()); + } + public E getWhereCondition(Class type) { return type.cast(getWhereCondition()); } diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java index e69c595f9..10df947e7 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java @@ -18,9 +18,13 @@ public class MergeUpdate implements Serializable { private List updateSets; + private Expression andPredicate; private Expression whereCondition; private Expression deleteWhereCondition; + public MergeUpdate() { + } + public MergeUpdate(List updateSets) { this.updateSets = updateSets; } @@ -34,6 +38,14 @@ public MergeUpdate setUpdateSets(List updateSets) { return this; } + public Expression getAndPredicate() { + return andPredicate; + } + + public void setAndPredicate(Expression andPredicate) { + this.andPredicate = andPredicate; + } + public Expression getWhereCondition() { return whereCondition; } @@ -53,7 +65,11 @@ public void setDeleteWhereCondition(Expression deleteWhereCondition) { @Override public String toString() { StringBuilder b = new StringBuilder(); - b.append(" WHEN MATCHED THEN UPDATE SET "); + b.append(" WHEN MATCHED"); + if (andPredicate != null) { + b.append(" AND ").append(andPredicate.toString()); + } + b.append(" THEN UPDATE SET "); UpdateSet.appendUpdateSetsTo(b, updateSets); if (whereCondition != null) { @@ -65,6 +81,11 @@ public String toString() { return b.toString(); } + public MergeUpdate withAndPredicate(Expression andPredicate) { + this.setAndPredicate(andPredicate); + return this; + } + public MergeUpdate withWhereCondition(Expression whereCondition) { this.setWhereCondition(whereCondition); return this; @@ -75,6 +96,10 @@ public MergeUpdate withDeleteWhereCondition(Expression deleteWhereCondition) { return this; } + public E getAndPredicate(Class type) { + return type.cast(getAndPredicate()); +} + public E getWhereCondition(Class type) { return type.cast(getWhereCondition()); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index 55b11a781..732ef3e39 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -216,9 +216,8 @@ public void visit(Merge merge) { buffer.append(" USING "); merge.getFromItem().accept(selectDeParser); - buffer.append(" ON ("); + buffer.append(" ON "); merge.getOnCondition().accept(expressionDeParser); - buffer.append(")"); MergeInsert mergeInsert = merge.getMergeInsert(); MergeUpdate mergeUpdate = merge.getMergeUpdate(); @@ -227,7 +226,12 @@ public void visit(Merge merge) { } if (mergeUpdate != null) { - buffer.append(" WHEN MATCHED THEN UPDATE SET "); + buffer.append(" WHEN MATCHED"); + if (mergeUpdate.getAndPredicate() != null) { + buffer.append(" AND "); + mergeUpdate.getAndPredicate().accept(expressionDeParser); + } + buffer.append(" THEN UPDATE SET "); deparseUpdateSets(mergeUpdate.getUpdateSets(), buffer, expressionDeParser); if (mergeUpdate.getWhereCondition() != null) { @@ -251,7 +255,12 @@ public void visit(Merge merge) { } private void deparseMergeInsert(MergeInsert mergeInsert) { - buffer.append(" WHEN NOT MATCHED THEN INSERT "); + buffer.append(" WHEN NOT MATCHED"); + if (mergeInsert.getAndPredicate() != null) { + buffer.append(" AND "); + mergeInsert.getAndPredicate().accept(expressionDeParser); + } + buffer.append(" THEN INSERT "); if (mergeInsert.getColumns() != null) { mergeInsert.getColumns().accept(expressionDeParser); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 7029df760..7905024a5 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1659,7 +1659,7 @@ Statement Merge( List with ) : { { { merge.setOracleHint(getOracleHint()); } table=TableWithAlias() { merge.setTable(table); } fromItem = FromItem() { merge.setFromItem(fromItem); } - "(" condition = Expression() { merge.setOnCondition(condition); } ")" + condition = Expression() { merge.setOnCondition(condition); } [ ( LOOKAHEAD(2) update = MergeUpdateClause() { merge.setMergeUpdate(update); } @@ -1675,14 +1675,17 @@ Statement Merge( List with ) : { } MergeUpdate MergeUpdateClause() : { - MergeUpdate mu; + MergeUpdate mu = new MergeUpdate(); List updateSets; + Expression predicate; Expression condition; } { - + + [ predicate = Expression() { mu.setAndPredicate(predicate); } ] + - updateSets = UpdateSets() { mu = new MergeUpdate(updateSets); } + updateSets = UpdateSets() { mu.setUpdateSets(updateSets); } [ condition = Expression() { mu.setWhereCondition(condition); }] [ condition = Expression() { mu.setDeleteWhereCondition(condition); } ] @@ -1692,12 +1695,15 @@ MergeUpdate MergeUpdateClause() : { MergeInsert MergeInsertClause() : { MergeInsert mi = new MergeInsert(); + Expression predicate; ExpressionList columns; ExpressionList expList; Expression condition; } { - + + [ predicate = Expression() { mi.setAndPredicate(predicate); } ] + [ "(" columns = ColumnList() ")" { diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java index 287b8b922..5034be8cd 100644 --- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java @@ -236,4 +236,31 @@ public void testOutputClause() throws JSQLParserException { + " TAB_MergeActions_RoomLocation"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + public void testSnowflakeMergeStatementSimple() throws JSQLParserException { + String sql = "MERGE INTO target\n" + + " USING src ON target.k = src.k\n" + + " WHEN MATCHED THEN UPDATE SET target.v = src.v"; + + assertSqlCanBeParsedAndDeparsed(sql, true); + } + + @Test + public void testSnowflakeMergeStatementWithMatchedAndPredicate() throws JSQLParserException { + String sql = "MERGE INTO target\n" + + " USING src ON target.k = src.k\n" + + " WHEN MATCHED AND src.v = 11 THEN UPDATE SET target.v = src.v"; + + assertSqlCanBeParsedAndDeparsed(sql, true); + } + + @Test + void testSnowflakeMergeStatementWithNotMatchedAndPredicate() throws JSQLParserException { + String sql = "MERGE INTO target USING (select k, max(v) as v from src group by k) AS b ON target.k = b.k\n" + + " WHEN MATCHED THEN UPDATE SET target.v = b.v\n" + + " WHEN NOT MATCHED AND b.v != 11 THEN INSERT (k, v) VALUES (b.k, b.v)"; + + assertSqlCanBeParsedAndDeparsed(sql, true); + } } From 902e4c46f783985c75c42d98f5686d065e95b5ad Mon Sep 17 00:00:00 2001 From: Ed Sabol Date: Thu, 9 Nov 2023 22:05:39 -0500 Subject: [PATCH 29/77] Fix typo in migration.rst (#1888) Found a typo in the 4.7 migration document. Trivial PR. Please merge. --- src/site/sphinx/migration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/sphinx/migration.rst b/src/site/sphinx/migration.rst index 94f299fab..b21a961c0 100644 --- a/src/site/sphinx/migration.rst +++ b/src/site/sphinx/migration.rst @@ -2,7 +2,7 @@ Migration to 4.7 ********************************* -The new version of JSQLParser 4.7 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. +The new version of JSQLParser 4.7 is a rewrite in order to simplify 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. From dd6cf23150f480401fcb76cbbc15a1fcd660fea3 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sun, 12 Nov 2023 08:01:52 +0700 Subject: [PATCH 30/77] fix: make `GLOBAL` a restricted keyword, not usable as an Alias - fixes #1883 Signed-off-by: Andreas Reichel --- .../jsqlparser/parser/ParserKeywordsUtils.java | 1 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 3 ++- src/site/sphinx/keywords.rst | 2 ++ .../statement/select/ClickHouseTest.java | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java index a8182344b..6aa5686c9 100644 --- a/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java +++ b/src/main/java/net/sf/jsqlparser/parser/ParserKeywordsUtils.java @@ -72,6 +72,7 @@ public class ParserKeywordsUtils { {"FOREIGN", RESTRICTED_SQL2016}, {"FROM", RESTRICTED_SQL2016}, {"FULL", RESTRICTED_SQL2016}, + {"GLOBAL", RESTRICTED_ALIAS}, {"GROUP", RESTRICTED_SQL2016}, {"GROUPING", RESTRICTED_ALIAS}, {"QUALIFY", RESTRICTED_ALIAS}, diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 7905024a5..28acb3034 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1766,7 +1766,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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONSTRAINTS" | 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="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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" ) + | 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONSTRAINTS" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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; } } @@ -1812,6 +1812,7 @@ String RelObjectNameExt(): | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= + | tk= ) { return tk!=null ? tk.image : result; } } diff --git a/src/site/sphinx/keywords.rst b/src/site/sphinx/keywords.rst index 6b551c801..af0dd839f 100644 --- a/src/site/sphinx/keywords.rst +++ b/src/site/sphinx/keywords.rst @@ -61,6 +61,8 @@ The following Keywords are **restricted** in JSQLParser-|JSQLPARSER_VERSION| and +----------------------+-------------+-----------+ | FULL | Yes | Yes | +----------------------+-------------+-----------+ +| GLOBAL | Yes | | ++----------------------+-------------+-----------+ | GROUP | Yes | Yes | +----------------------+-------------+-----------+ | GROUPING | Yes | | diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java index 26f2ec33c..72b8508df 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -10,7 +10,10 @@ package net.sf.jsqlparser.statement.select; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; @@ -41,4 +44,19 @@ public void testGlobalIn() throws JSQLParserException { "SELECT lo_linenumber,lo_orderkey from lo_linenumber where lo_linenumber global in (1,2,3)"; assertSqlCanBeParsedAndDeparsed(sql, true); } + + @Test + public void testGlobalKeywordIssue1883() throws JSQLParserException { + String sqlStr = "select a.* from a global join b on a.name = b.name "; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true); + Assertions.assertTrue(select.getJoins().get(0).isGlobal()); + + Assertions.assertThrows( + JSQLParserException.class, new Executable() { + @Override + public void execute() throws Throwable { + CCJSqlParserUtil.parse("select a.* from a global"); + } + }, "Fail when restricted keyword GLOBAL is used as an Alias."); + } } From 15b9aef7ca0541660def2636376636039ad79774 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 9 Dec 2023 11:54:48 +0700 Subject: [PATCH 31/77] fix: `UpdateSet` shall not have brackets with single element only - fixes #1910 --- .../statement/merge/MergeUpdate.java | 5 ++--- .../jsqlparser/statement/update/UpdateSet.java | 4 ++-- .../jsqlparser/statement/merge/MergeTest.java | 8 +++++--- .../statement/update/UpdateTest.java | 18 ++++++++++++++++++ .../jsqlparser/util/TablesNamesFinderTest.java | 4 ++-- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java index 10df947e7..38e9261a2 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java @@ -22,8 +22,7 @@ public class MergeUpdate implements Serializable { private Expression whereCondition; private Expression deleteWhereCondition; - public MergeUpdate() { - } + public MergeUpdate() {} public MergeUpdate(List updateSets) { this.updateSets = updateSets; @@ -98,7 +97,7 @@ public MergeUpdate withDeleteWhereCondition(Expression deleteWhereCondition) { public E getAndPredicate(Class type) { return type.cast(getAndPredicate()); -} + } public E getWhereCondition(Class type) { return type.cast(getWhereCondition()); 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 7c6fca540..84299c0fb 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/UpdateSet.java @@ -72,7 +72,7 @@ public void add(Column column, Expression value) { * @param column */ public void add(Column column) { - if (columns.size() < 2 && !(columns instanceof ParenthesedExpressionList)) { + if (!columns.isEmpty() && !(columns instanceof ParenthesedExpressionList)) { columns = new ParenthesedExpressionList<>(columns); } columns.add(column); @@ -85,7 +85,7 @@ public void add(Column column) { * @param expression */ public void add(Expression expression) { - if (values.size() < 2 && !(values instanceof ParenthesedExpressionList)) { + if (!values.isEmpty() && !(values instanceof ParenthesedExpressionList)) { values = new ParenthesedExpressionList<>(values); } values.add(expression); diff --git a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java index 5034be8cd..edf0baa92 100644 --- a/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/merge/MergeTest.java @@ -257,9 +257,11 @@ public void testSnowflakeMergeStatementWithMatchedAndPredicate() throws JSQLPars @Test void testSnowflakeMergeStatementWithNotMatchedAndPredicate() throws JSQLParserException { - String sql = "MERGE INTO target USING (select k, max(v) as v from src group by k) AS b ON target.k = b.k\n" + - " WHEN MATCHED THEN UPDATE SET target.v = b.v\n" + - " WHEN NOT MATCHED AND b.v != 11 THEN INSERT (k, v) VALUES (b.k, b.v)"; + String sql = + "MERGE INTO target USING (select k, max(v) as v from src group by k) AS b ON target.k = b.k\n" + + + " WHEN MATCHED THEN UPDATE SET target.v = b.v\n" + + " WHEN NOT MATCHED AND b.v != 11 THEN INSERT (k, v) VALUES (b.k, b.v)"; assertSqlCanBeParsedAndDeparsed(sql, true); } diff --git a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java index 00de44dc2..342d1eea6 100644 --- a/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/update/UpdateTest.java @@ -18,6 +18,8 @@ import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Test; import java.io.StringReader; @@ -362,4 +364,20 @@ void testArrayColumnsIssue1083() throws JSQLParserException { sqlStr = "update utilisateur set listes[0:3] = (1,2,3,4)"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + void testIssue1910() throws JSQLParserException { + Update update = new Update(); + update.setTable(new Table("sys_dept")); + + UpdateSet updateSet = new UpdateSet(new Column("deleted"), new LongValue(1L)); + update.addUpdateSet(updateSet); + + TestUtils.assertStatementCanBeDeparsedAs(update, "UPDATE sys_dept SET deleted = 1", true); + + updateSet.add(new Column("created"), new LongValue(2L)); + + TestUtils.assertStatementCanBeDeparsedAs(update, + "UPDATE sys_dept SET (deleted, created) = (1,2)", true); + } } diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index e08c5032c..1cf2b90be 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -490,10 +490,10 @@ public void testConnectedByRootOperator() throws JSQLParserException { void testJoinSubSelect() throws JSQLParserException { String sqlStr = "select * from A left join B on A.id=B.id and A.age = (select age from C)"; Set tableNames = TablesNamesFinder.findTables(sqlStr); - assertThat( tableNames ).containsExactlyInAnyOrder("A", "B", "C"); + assertThat(tableNames).containsExactlyInAnyOrder("A", "B", "C"); String exprStr = "A.id=B.id and A.age = (select age from C)"; tableNames = TablesNamesFinder.findTablesInExpression(exprStr); - assertThat( tableNames ).containsExactlyInAnyOrder("A", "B", "C"); + assertThat(tableNames).containsExactlyInAnyOrder("A", "B", "C"); } } From 085d7504235e58ca71297fc74ff70d505312ab76 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 9 Dec 2023 18:23:32 +0700 Subject: [PATCH 32/77] feat: `SimpleFunction` for faster parsing of simple, but deep nested functions - fixes #1889 --- .../expression/AnalyticExpression.java | 2 +- .../sf/jsqlparser/expression/Function.java | 29 +++++-- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 66 ++++++++++----- .../expression/CaseExpressionTest.java | 81 ++++++++++++------- .../statement/select/SelectTest.java | 13 ++- .../select/oracle-tests/model_clause12.sql | 3 +- .../select/oracle-tests/model_clause13.sql | 3 +- 7 files changed, 138 insertions(+), 59 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java index 75e53c0fa..fc7d7d466 100644 --- a/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/AnalyticExpression.java @@ -53,7 +53,7 @@ public AnalyticExpression(Function function) { unique = function.isUnique(); funcOrderBy = function.getOrderByElements(); - ExpressionList list = function.getParameters(); + ExpressionList list = function.getParameters(); if (list != null) { if (list.getExpressions().size() > 3) { throw new IllegalArgumentException( diff --git a/src/main/java/net/sf/jsqlparser/expression/Function.java b/src/main/java/net/sf/jsqlparser/expression/Function.java index 70066fc07..9a7c2d9d9 100644 --- a/src/main/java/net/sf/jsqlparser/expression/Function.java +++ b/src/main/java/net/sf/jsqlparser/expression/Function.java @@ -24,8 +24,8 @@ public class Function extends ASTNodeAccessImpl implements Expression { private List nameparts; - private ExpressionList parameters; - private NamedExpressionList namedParameters; + private ExpressionList parameters; + private NamedExpressionList namedParameters; private boolean allColumns = false; private boolean distinct = false; private boolean unique = false; @@ -58,6 +58,11 @@ public Function withName(String name) { return this; } + public Function withName(List nameparts) { + this.nameparts = nameparts; + return this; + } + public void setName(List string) { nameparts = string; } @@ -115,11 +120,19 @@ public void setUnique(boolean b) { * * @return the list of parameters of the function (if any, else null) */ - public ExpressionList getParameters() { + public ExpressionList getParameters() { return parameters; } - public void setParameters(ExpressionList list) { + public void setParameters(Expression... expressions) { + if (expressions.length == 1 && expressions[0] instanceof ExpressionList) { + parameters = (ExpressionList) expressions[0]; + } else { + parameters = new ExpressionList<>(expressions); + } + } + + public void setParameters(ExpressionList list) { parameters = list; } @@ -132,7 +145,7 @@ public NamedExpressionList getNamedParameters() { return namedParameters; } - public void setNamedParameters(NamedExpressionList list) { + public void setNamedParameters(NamedExpressionList list) { namedParameters = list; } @@ -267,16 +280,16 @@ public Function withIgnoreNulls(boolean ignoreNulls) { return this; } - public Function withParameters(ExpressionList parameters) { + public Function withParameters(ExpressionList parameters) { this.setParameters(parameters); return this; } public Function withParameters(Expression... parameters) { - return withParameters(new ExpressionList(parameters)); + return withParameters(new ExpressionList<>(parameters)); } - public Function withNamedParameters(NamedExpressionList namedParameters) { + public Function withNamedParameters(NamedExpressionList namedParameters) { this.setNamedParameters(namedParameters); return this; } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 28acb3034..3dbbef74f 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3467,7 +3467,7 @@ Expression RegularCondition() #RegularCondition: [ LOOKAHEAD(2) "(" "+" ")" { oracleJoin=EqualsTo.ORACLE_JOIN_LEFT; } ] { - BinaryExpression regCond = (BinaryExpression) result; + BinaryExpression regCond = (BinaryExpression) result; regCond.setLeftExpression(leftExpression); regCond.setRightExpression(rightExpression); @@ -4122,8 +4122,6 @@ Expression PrimaryExpression() #PrimaryExpression: | LOOKAHEAD(JsonAggregateFunction(), {!interrupted}) retval = JsonAggregateFunction() - /* | LOOKAHEAD(FunctionWithCondParams()) retval = FunctionWithCondParams() */ - | LOOKAHEAD(FullTextSearch(), {!interrupted}) retval = FullTextSearch() | LOOKAHEAD(Function(), {!interrupted}) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ] @@ -4902,7 +4900,7 @@ Expression CaseWhenExpression() #CaseWhenExpression: } { { caseCounter++; } - [ switchExp=Expression() ] + [ LOOKAHEAD(2) switchExp=Expression() ] ( clause=WhenThenSearchCondition() { whenClauses.add(clause); } )+ [ @@ -5028,17 +5026,18 @@ FullTextSearch FullTextSearch() : { Function Function() #Function: { - Function retval = new Function(); + Function function; } { ( - "{" { retval.setEscaped(true); } InternalFunction(retval) "}" - | LOOKAHEAD(3) retval = SpecialStringFunctionWithNamedParameters() - | InternalFunction(retval) + "{" function = InternalFunction(true) "}" + | LOOKAHEAD( SimpleFunction(), { getAsBoolean(Feature.allowComplexParsing) }) function = SimpleFunction() + | LOOKAHEAD(3) function = SpecialStringFunctionWithNamedParameters() + | function = InternalFunction(false) ) { - linkAST(retval,jjtThis); - return retval; + linkAST(function,jjtThis); + return function; } } @@ -5068,15 +5067,47 @@ Function SpecialStringFunctionWithNamedParameters() : } } -Function InternalFunction(Function retval) : +// a simplified function with only one parameter +// useful for parsing nested functions fast +Function SimpleFunction(): { + List name; + Expression expr=null; +} +{ + name = RelObjectNameList() + "(" + [ + ( + "*" { expr = new AllColumns(); } + | + LOOKAHEAD( AllTableColumns() ) expr=AllTableColumns() + | + LOOKAHEAD( SimpleFunction() ) expr = SimpleFunction() + | + LOOKAHEAD( RegularCondition() ) expr = RegularCondition() + | + LOOKAHEAD( SimpleExpressionList() ) expr = SimpleExpressionList() + ) + ] + ")" + { + Function function = new Function().withName(name); + if (expr!=null) { + function.setParameters(expr); + } + + return function; + } +} + +Function InternalFunction(boolean escaped): +{ + Function retval = new Function(); List funcName; - Expression allColumnsExpression = null; ExpressionList expressionList = null; KeepExpression keep = null; - Select expr = null; - Token tk1 = null; - Token tk2 = null; + Expression expr = null; Expression attributeExpression = null; Column attributeColumn = null; List orderByList; @@ -5095,10 +5126,6 @@ Function InternalFunction(Function retval) : ) ] ( - "*" { expressionList = new ExpressionList(new AllColumns()); } - | - LOOKAHEAD( AllTableColumns() ) allColumnsExpression=AllTableColumns() { expressionList = new ExpressionList(allColumnsExpression); } - | LOOKAHEAD(3) expressionList=ExpressionList() [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ] | expr = Select() { expressionList = new ExpressionList(expr); } @@ -5120,6 +5147,7 @@ Function InternalFunction(Function retval) : [ LOOKAHEAD(2) keep = KeepExpression() ] { + retval.setEscaped(escaped); retval.setParameters(expressionList); retval.setName(funcName); retval.setKeep(keep); diff --git a/src/test/java/net/sf/jsqlparser/expression/CaseExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/CaseExpressionTest.java index 0e7d3a6d8..e86f92a2b 100644 --- a/src/test/java/net/sf/jsqlparser/expression/CaseExpressionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/CaseExpressionTest.java @@ -19,67 +19,80 @@ public class CaseExpressionTest { @Test public void testSimpleCase() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true THEN 1 ELSE 2 END", + true); } @Test public void testCaseBinaryAndWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true & false THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true WHEN true & false THEN 1 ELSE 2 END", true); } @Test public void testCaseBinaryOrWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true | false THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true WHEN true | false THEN 1 ELSE 2 END", true); } @Test public void testCaseExclamationWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN !true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN !true THEN 1 ELSE 2 END", + true); } @Test public void testCaseNotWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN NOT true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true WHEN NOT true THEN 1 ELSE 2 END", true); } @Test public void testCaseAndWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true AND false THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true WHEN true AND false THEN 1 ELSE 2 END", true); } @Test public void testCaseOrWhen() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true WHEN true OR false THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true WHEN true OR false THEN 1 ELSE 2 END", true); } @Test public void testCaseExclamationSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE !true WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE !true WHEN true THEN 1 ELSE 2 END", + true); } @Test public void testCaseNotSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE NOT true WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE NOT true WHEN true THEN 1 ELSE 2 END", true); } @Test public void testCaseBinaryAndSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true & false WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true & false WHEN true THEN 1 ELSE 2 END", true); } @Test public void testCaseBinaryOrSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true | false WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true | false WHEN true THEN 1 ELSE 2 END", true); } @Test public void testCaseAndSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true AND false WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true AND false WHEN true THEN 1 ELSE 2 END", true); } @Test public void testCaseOrSwitch() throws JSQLParserException { - TestUtils.assertExpressionCanBeParsedAndDeparsed("CASE true OR false WHEN true THEN 1 ELSE 2 END", true); + TestUtils.assertExpressionCanBeParsedAndDeparsed( + "CASE true OR false WHEN true THEN 1 ELSE 2 END", true); } @Test @@ -112,24 +125,38 @@ public void testInnerCaseWithConcatInElsePart() throws JSQLParserException { @Test public void testCaseInsideBrackets() throws JSQLParserException { String sqlStr = "SELECT ( CASE\n" - + " WHEN something\n" - + " THEN CASE\n" - + " WHEN something2\n" - + " THEN 1\n" - + " ELSE 0\n" - + " END + 1\n" - + " ELSE 0\n" - + " END ) + 1 \n" - + "FROM test"; + + " WHEN something\n" + + " THEN CASE\n" + + " WHEN something2\n" + + " THEN 1\n" + + " ELSE 0\n" + + " END + 1\n" + + " ELSE 0\n" + + " END ) + 1 \n" + + "FROM test"; TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); sqlStr = "SELECT\n" - + "(CASE WHEN FIELD_A=0 THEN FIELD_B\n" - + "WHEN FIELD_C >FIELD_D THEN (CASE WHEN FIELD_A>0 THEN\n" - + "(FIELD_B)/(FIELD_A/(DATEDIFF(DAY,:started,:end)+1))\n" - + "ELSE 0 END)-FIELD_D ELSE 0 END)*FIELD_A/(DATEDIFF(DAY,:started,:end)+1) AS UNNECESSARY_COMPLEX_EXPRESSION\n" - + "FROM TEST"; + + "(CASE WHEN FIELD_A=0 THEN FIELD_B\n" + + "WHEN FIELD_C >FIELD_D THEN (CASE WHEN FIELD_A>0 THEN\n" + + "(FIELD_B)/(FIELD_A/(DATEDIFF(DAY,:started,:end)+1))\n" + + "ELSE 0 END)-FIELD_D ELSE 0 END)*FIELD_A/(DATEDIFF(DAY,:started,:end)+1) AS UNNECESSARY_COMPLEX_EXPRESSION\n" + + "FROM TEST"; + + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + + @Test + void testPerformanceIssue1889() throws JSQLParserException { + String sqlStr = "SELECT " + + "SUM(SUM(CASE\n" + + " WHEN IssueDeadline IS NULL THEN 'Indeterminate'\n" + + " WHEN IssueDeadline < CONVERT(DATETIME, CONVERT(DATE, COALESCE(IssueClosedOn, CONVERT(DATETIME, CONVERT(DATE, GETDATE()), 121)))) THEN 'PastDue'\n" + + " WHEN (IssueDeadline>=CONVERT(DATETIME, CONVERT(DATE, GETDATE()), 121)\n" + + " AND IssueDeadline<=CONVERT(DATETIME, CONVERT(DATE, GETDATE()+3), 121)) THEN 'Alert'\n" + + " ELSE 'OnTime'\n" + + " END = 'PastDue'))\n"; TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); } 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 90fd5d81b..f4a0b77f6 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -1211,7 +1211,7 @@ public void testFunctions() throws JSQLParserException { .getExpression(); assertEquals("MAX", fun.getName()); assertEquals("b", - ((Column) fun.getParameters().getExpressions().get(1)).getFullyQualifiedName()); + ((Column) fun.getParameters().get(1)).getFullyQualifiedName()); assertTrue(((Function) (plainSelect.getSelectItems().get(1)) .getExpression()).getParameters().getExpressions().get(0) instanceof AllColumns); assertStatementCanBeDeparsedAs(select, statement); @@ -4210,7 +4210,16 @@ public void testFuncConditionParameter2() throws JSQLParserException { @Test public void testFuncConditionParameter3() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( - "SELECT CAST((MAX(CAST(IIF(isnumeric(license_no) = 1, license_no, 0) AS INT)) + 2) AS varchar) FROM lcps.t_license WHERE profession_id = 60 and license_type = 100 and YEAR(issue_date) % 2 = case when YEAR(issue_date) % 2 = 0 then 0 else 1 end and ISNUMERIC(license_no) = 1", + "SELECT cast( ( Max( cast( Iif( Isnumeric( license_no ) = 1, license_no, 0 ) AS INT ) ) + 2 ) AS VARCHAR )\n" + + "FROM lcps.t_license\n" + + "WHERE profession_id = 60\n" + + " AND license_type = 100\n" + + " AND Year( issue_date ) % 2 = CASE\n" + + " WHEN Year( issue_date ) % 2 = 0\n" + + " THEN 0\n" + + " ELSE 1\n" + + " END\n" + + " AND Isnumeric( license_no ) = 1", true); } diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause12.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause12.sql index 5a5dfb3c7..8201dcabe 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause12.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause12.sql @@ -26,4 +26,5 @@ level2[any] = case when org_level[cv()] = 2 then ename [cv()] end, level3[any] = case when org_level[cv()] = 3 then ename [cv()] end, level4[any] = case when org_level[cv()] = 4 then ename [cv()] end ) ---@FAILURE: Encountered unexpected token: "return" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "return" recorded first on Aug 3, 2021, 7:20:08 AM +--@FAILURE: Encountered unexpected token: "return" "RETURN" recorded first on 9 Dec 2023, 18:20:45 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause13.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause13.sql index 0e5f1026e..e9e35ce56 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause13.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/model_clause13.sql @@ -30,4 +30,5 @@ level3[any] = case when org_level[cv()] = 3 then ename [cv()] end, level4[any] = case when org_level[cv()] = 4 then ename [cv()] end ))) ---@FAILURE: Encountered unexpected token: "return" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "return" recorded first on Aug 3, 2021, 7:20:08 AM +--@FAILURE: Encountered unexpected token: "return" "RETURN" recorded first on 9 Dec 2023, 18:20:44 \ No newline at end of file From 425c72eb7d7f931be6f4b2c971dcf4da5a62388b Mon Sep 17 00:00:00 2001 From: jxnu-liguobin <568845948@qq.com> Date: Mon, 11 Dec 2023 11:58:43 +0800 Subject: [PATCH 33/77] feat: Add support for `REFRESH MATERIALIZED VIEW` (#1911) * Support REFRESH MATERIALIZED VIEW * Fix * Rename * Rename * fmt * fmt * Add test * Fix jjt * Fix jjt * Fix jjt * typo * rebase master --- .../sf/jsqlparser/parser/feature/Feature.java | 9 ++ .../statement/StatementVisitor.java | 3 + .../statement/StatementVisitorAdapter.java | 6 + .../RefreshMaterializedViewStatement.java | 107 ++++++++++++++++++ .../statement/refresh/RefreshMode.java | 19 ++++ .../jsqlparser/statement/select/JoinHint.java | 9 ++ .../sf/jsqlparser/util/TablesNamesFinder.java | 17 ++- ...reshMaterializedViewStatementDeParser.java | 53 +++++++++ .../util/deparser/StatementDeParser.java | 13 ++- .../validation/feature/PostgresqlVersion.java | 21 ++-- ...eshMaterializedViewStatementValidator.java | 39 +++++++ .../validator/StatementValidator.java | 6 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 33 +++++- .../RefreshMaterializedViewStatementTest.java | 28 +++++ .../statement/UnsupportedStatementTest.java | 13 ++- .../builder/ReflectionModelTest.java | 12 +- .../create/table/ColDataTypeTest.java | 9 ++ .../statement/select/JoinHintTest.java | 12 +- .../util/TablesNamesFinderTest.java | 50 ++++++-- ...aterializedViewStatementValidatorTest.java | 66 +++++++++++ 20 files changed, 483 insertions(+), 42 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java create mode 100644 src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java create mode 100644 src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java create mode 100644 src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java create mode 100644 src/test/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidatorTest.java diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 7d7e94a72..3d5825f5d 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -44,6 +44,7 @@ import net.sf.jsqlparser.statement.execute.Execute; import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Fetch; import net.sf.jsqlparser.statement.select.First; import net.sf.jsqlparser.statement.select.KSQLWindow; @@ -391,6 +392,14 @@ public enum Feature { * @see AlterView */ alterView, + + /** + * SQL "REFRESH MATERIALIZED VIEW" statement is allowed + * + * @see RefreshMaterializedViewStatement + */ + refreshMaterializedView, refreshMaterializedWithDataView, refreshMaterializedWithNoDataView, + /** * SQL "REPLACE VIEW" statement is allowed * diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java index 86621f749..46d4e0d96 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -29,6 +29,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.show.ShowIndexStatement; import net.sf.jsqlparser.statement.show.ShowTablesStatement; @@ -68,6 +69,8 @@ public interface StatementVisitor { void visit(AlterView alterView); + void visit(RefreshMaterializedViewStatement materializedView); + void visit(Alter alter); void visit(Statements stmts); diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index 251fcc15b..72d2a540a 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -29,6 +29,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.show.ShowIndexStatement; import net.sf.jsqlparser.statement.show.ShowTablesStatement; @@ -222,4 +223,9 @@ public void visit(AlterSystemStatement alterSystemStatement) {} public void visit(UnsupportedStatement unsupportedStatement) { } + + @Override + public void visit(RefreshMaterializedViewStatement materializedView) { + + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java new file mode 100644 index 000000000..86b530ed3 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java @@ -0,0 +1,107 @@ +/*- + * #%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.statement.refresh; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] name [ WITH [ NO ] DATA ] + *

+ * https://www.postgresql.org/docs/16/sql-refreshmaterializedview.html + * + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatement implements Statement { + + private Table view; + private RefreshMode refreshMode; + private boolean concurrently = false; + + public RefreshMaterializedViewStatement() {} + + public RefreshMaterializedViewStatement(Table view, boolean concurrently, + RefreshMode refreshMode) { + this.refreshMode = refreshMode; + this.concurrently = concurrently; + this.view = view; + } + + public Table getView() { + return view; + } + + public void setView(Table view) { + this.view = view; + } + + public RefreshMode getRefreshMode() { + return refreshMode; + } + + public void setRefreshMode(RefreshMode refreshMode) { + this.refreshMode = refreshMode; + } + + public boolean isConcurrently() { + return concurrently; + } + + public void setConcurrently(boolean concurrently) { + this.concurrently = concurrently; + } + + @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("REFRESH MATERIALIZED VIEW "); + if (this.refreshMode == null) { + if (concurrently) { + builder.append("CONCURRENTLY "); + } + builder.append(view); + return builder.toString(); + } + switch (this.refreshMode) { + case WITH_DATA: + if (concurrently) { + builder.append("CONCURRENTLY "); + } + builder.append(view); + builder.append(" WITH DATA"); + break; + case WITH_NO_DATA: + builder.append(view); + if (!concurrently) { + builder.append(" WITH NO DATA"); + } + break; + } + return builder.toString(); + } + + @Override + public void accept(StatementVisitor statementVisitor) { + statementVisitor.visit(this); + } + + public RefreshMaterializedViewStatement withTableName(Table view) { + this.setView(view); + return this; + } + + public RefreshMaterializedViewStatement withConcurrently(boolean concurrently) { + this.setConcurrently(concurrently); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java new file mode 100644 index 000000000..fb78fac5d --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMode.java @@ -0,0 +1,19 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2022 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.refresh; + +public enum RefreshMode { + + DEFAULT, WITH_DATA, WITH_NO_DATA; + + public static RefreshMode from(String type) { + return Enum.valueOf(RefreshMode.class, type.toUpperCase()); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java index d98b1d568..099f73042 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/JoinHint.java @@ -1,3 +1,12 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.statement.select; /** diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index a99ad6315..44f0859a5 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -9,6 +9,11 @@ */ package net.sf.jsqlparser.util; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.AllValue; import net.sf.jsqlparser.expression.AnalyticExpression; @@ -146,6 +151,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; import net.sf.jsqlparser.statement.select.FromItemVisitor; @@ -169,12 +175,6 @@ import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.upsert.Upsert; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** * Find all used tables within an select statement. * @@ -986,6 +986,11 @@ public void visit(AlterView alterView) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } + @Override + public void visit(RefreshMaterializedViewStatement materializedView) { + visit(materializedView.getView()); + } + @Override public void visit(TimeKeyExpression timeKeyExpression) { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java new file mode 100644 index 000000000..0646a65fe --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java @@ -0,0 +1,53 @@ +/*- + * #%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.util.deparser; + +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; + +/** + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatementDeParser + extends AbstractDeParser { + + public RefreshMaterializedViewStatementDeParser(StringBuilder buffer) { + super(buffer); + } + + @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") + @Override + public void deParse(RefreshMaterializedViewStatement view) { + buffer.append("REFRESH MATERIALIZED VIEW "); + if (view.getRefreshMode() == null) { + if (view.isConcurrently()) { + buffer.append("CONCURRENTLY "); + } + buffer.append(view.getView()); + return; + } + switch (view.getRefreshMode()) { + case WITH_DATA: + if (view.isConcurrently()) { + buffer.append("CONCURRENTLY "); + } + buffer.append(view.getView()); + buffer.append(" WITH DATA"); + break; + case WITH_NO_DATA: + buffer.append(view.getView()); + if (view.isConcurrently()) { + buffer.append(" WITH NO DATA"); + } + break; + } + } + +} diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index 732ef3e39..b6006255d 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -9,6 +9,9 @@ */ package net.sf.jsqlparser.util.deparser; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; import net.sf.jsqlparser.statement.Block; import net.sf.jsqlparser.statement.Commit; import net.sf.jsqlparser.statement.CreateFunctionalStatement; @@ -50,6 +53,7 @@ import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.merge.MergeInsert; import net.sf.jsqlparser.statement.merge.MergeUpdate; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.show.ShowIndexStatement; @@ -58,10 +62,6 @@ import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.upsert.Upsert; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - public class StatementDeParser extends AbstractDeParser implements StatementVisitor { private final ExpressionDeParser expressionDeParser; @@ -104,6 +104,11 @@ public void visit(CreateView createView) { createViewDeParser.deParse(createView); } + @Override + public void visit(RefreshMaterializedViewStatement materializedViewStatement) { + new RefreshMaterializedViewStatementDeParser(buffer).deParse(materializedViewStatement); + } + @Override public void visit(AlterView alterView) { AlterViewDeParser alterViewDeParser = new AlterViewDeParser(buffer); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java index c5d3d898a..21cec80d2 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Set; - import net.sf.jsqlparser.parser.feature.Feature; /** @@ -33,7 +32,8 @@ public enum PostgresqlVersion implements Version { Feature.exprSimilarTo, // https://www.postgresql.org/docs/current/sql-select.html Feature.select, - Feature.selectGroupBy, Feature.function, Feature.tableFunction, Feature.lateralSubSelect, + Feature.selectGroupBy, Feature.function, Feature.tableFunction, + Feature.lateralSubSelect, Feature.selectHaving, // https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-GROUPING-SETS Feature.selectGroupByGroupingSets, @@ -107,6 +107,11 @@ public enum PostgresqlVersion implements Version { // https://www.postgresql.org/docs/current/sql-alterview.html // Feature.alterView, + // https://www.postgresql.org/docs/16/sql-refreshmaterializedview.html + Feature.refreshMaterializedView, + Feature.refreshMaterializedWithNoDataView, + Feature.refreshMaterializedWithDataView, + // https://www.postgresql.org/docs/current/sql-insert.html Feature.insert, Feature.insertValues, @@ -151,12 +156,9 @@ public enum PostgresqlVersion implements Version { // https://www.postgresql.org/docs/current/sql-reset.html Feature.reset, // https://www.postgresql.org/docs/current/sql-commit.html - Feature.commit - )), - V11("11", V10.copy().getFeatures()), - V12("12", V11.copy().getFeatures()), - V13("13", V12.copy().getFeatures()), - V14("14", V13.copy().getFeatures()); + Feature.commit)), V11("11", V10.copy().getFeatures()), V12("12", + V11.copy().getFeatures()), V13("13", + V12.copy().getFeatures()), V14("14", V13.copy().getFeatures()); private Set features; private String versionString; @@ -176,7 +178,8 @@ public enum PostgresqlVersion implements Version { * @param unsupported * @see #copy() to copy from previous version */ - PostgresqlVersion(String versionString, Set featuresSupported, Set unsupported) { + PostgresqlVersion(String versionString, Set featuresSupported, + Set unsupported) { this.versionString = versionString; this.features = featuresSupported; this.features.removeAll(unsupported); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java new file mode 100644 index 000000000..cca8785c2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidator.java @@ -0,0 +1,39 @@ +/*- + * #%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.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; +import net.sf.jsqlparser.util.validation.metadata.NamedObject; + +/** + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatementValidator + extends AbstractValidator { + + @Override + public void validate(RefreshMaterializedViewStatement viewStatement) { + validateFeatureAndName(Feature.refreshMaterializedView, NamedObject.table, + viewStatement.getView().getName()); + for (ValidationCapability c : getCapabilities()) { + // default + validateFeature(c, viewStatement.getRefreshMode() == null, + Feature.refreshMaterializedView); + // specify WITH DATA + validateOptionalFeature(c, viewStatement.getRefreshMode(), + Feature.refreshMaterializedWithDataView); + validateOptionalFeature(c, viewStatement.getRefreshMode(), + Feature.refreshMaterializedWithNoDataView); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java index 4bc3744b7..2f2932e78 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java @@ -51,6 +51,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.merge.Merge; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.show.ShowIndexStatement; import net.sf.jsqlparser.statement.show.ShowTablesStatement; @@ -85,6 +86,11 @@ public void visit(AlterView alterView) { getValidator(AlterViewValidator.class).validate(alterView); } + @Override + public void visit(RefreshMaterializedViewStatement materializedView) { + getValidator(RefreshMaterializedViewStatementValidator.class).validate(materializedView); + } + @Override public void visit(Delete delete) { getValidator(DeleteValidator.class).validate(delete); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 3dbbef74f..2045dfdda 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -59,6 +59,7 @@ import net.sf.jsqlparser.statement.drop.*; import net.sf.jsqlparser.statement.insert.*; import net.sf.jsqlparser.statement.execute.*; import net.sf.jsqlparser.statement.select.*; +import net.sf.jsqlparser.statement.refresh.*; import net.sf.jsqlparser.statement.show.*; import net.sf.jsqlparser.statement.truncate.*; import net.sf.jsqlparser.statement.update.*; @@ -191,6 +192,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -456,6 +458,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -696,6 +699,8 @@ Statement SingleStatement() : | stm = Show() | + stm = RefreshMaterializedView() + | stm = Use() | stm = SavepointStatement() @@ -1219,6 +1224,32 @@ ShowIndexStatement ShowIndex(): { } } +Statement RefreshMaterializedView(): { + Table view = null; + boolean concurrently = false; + RefreshMode refreshMode = null; + List captureRest; +} +{ + + [ LOOKAHEAD(2) { concurrently = true; } ] + view = Table() + [ + { refreshMode = RefreshMode.WITH_DATA; } + [ + { refreshMode = RefreshMode.WITH_NO_DATA; } + ] + + ] + captureRest = captureRest() + { + if (concurrently && refreshMode == RefreshMode.WITH_NO_DATA) { + return new UnsupportedStatement("REFRESH", captureRest); + } else { + return new RefreshMaterializedViewStatement(view, concurrently, refreshMode); + } + } +} // https://dev.mysql.com/doc/refman/8.0/en/show-tables.html ShowTablesStatement ShowTables(): { ShowTablesStatement showTablesStatement; @@ -1766,7 +1797,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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONSTRAINTS" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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" ) + | 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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; } } diff --git a/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java new file mode 100644 index 000000000..4ebba56c7 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java @@ -0,0 +1,28 @@ +/*- + * #%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.statement; + +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; + +import net.sf.jsqlparser.JSQLParserException; +import org.junit.jupiter.api.Test; + +/** + * + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatementTest { + + @Test + public void testSimpleUse() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("REFRESH MATERIALIZED VIEW my_view"); + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/UnsupportedStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/UnsupportedStatementTest.java index 1a7238b6b..c07a0b996 100644 --- a/src/test/java/net/sf/jsqlparser/statement/UnsupportedStatementTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/UnsupportedStatementTest.java @@ -9,6 +9,9 @@ */ package net.sf.jsqlparser.statement; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.select.Select; @@ -17,9 +20,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - public class UnsupportedStatementTest { @Test public void testSingleUnsupportedStatement() throws JSQLParserException { @@ -88,6 +88,13 @@ void testAlter() throws JSQLParserException { assertTrue(statement instanceof UnsupportedStatement); } + @Test + void testRefresh() throws JSQLParserException { + String sqlStr = "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH NO DATA"; + Statements statement = CCJSqlParserUtil.parseStatements(sqlStr); + assertTrue(statement.get(0) instanceof UnsupportedStatement); + } + @Test void testCreate() throws JSQLParserException { String sqlStr = 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 60a1a2d02..75ec41db1 100644 --- a/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java @@ -9,21 +9,21 @@ */ package net.sf.jsqlparser.statement.builder; +import static net.sf.jsqlparser.test.TestUtils.asList; + +import java.util.ArrayList; +import java.util.List; import net.sf.jsqlparser.expression.AnyType; import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperatorType; import net.sf.jsqlparser.schema.Sequence.ParameterType; import net.sf.jsqlparser.statement.ExplainStatement.OptionType; import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.refresh.RefreshMode; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.update.UpdateSet; import net.sf.jsqlparser.util.ReflectionTestUtils; import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.List; - -import static net.sf.jsqlparser.test.TestUtils.asList; - /** * Testing of setters, getters, with-/add-methods by calling them with random parameter-values *

    @@ -131,6 +131,8 @@ public class ReflectionModelTest { new net.sf.jsqlparser.statement.SetStatement("name", null), new net.sf.jsqlparser.statement.ShowColumnsStatement(), new net.sf.jsqlparser.statement.show.ShowIndexStatement(), + new net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement( + new net.sf.jsqlparser.schema.Table("my_view"), true, RefreshMode.WITH_DATA), new net.sf.jsqlparser.statement.ShowStatement(), new net.sf.jsqlparser.statement.Statements(), new net.sf.jsqlparser.statement.UseStatement(), diff --git a/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java b/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java index 43c5b1fc3..bc2cb5c4b 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/table/ColDataTypeTest.java @@ -1,3 +1,12 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.statement.create.table; import net.sf.jsqlparser.JSQLParserException; diff --git a/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java b/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java index 5336843f6..9f7a63132 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/JoinHintTest.java @@ -1,12 +1,20 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.statement.select; +import java.util.stream.Stream; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.util.stream.Stream; - class JoinHintTest { public static Stream sqlStrings() { return Stream.of( diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index 1cf2b90be..2f9566088 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -9,6 +9,17 @@ */ package net.sf.jsqlparser.util; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.util.List; +import java.util.Set; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.parser.CCJSqlParserManager; @@ -25,18 +36,6 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.StringReader; -import java.util.List; -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - public class TablesNamesFinderTest { private static final CCJSqlParserManager PARSER_MANAGER = new CCJSqlParserManager(); @@ -496,4 +495,31 @@ void testJoinSubSelect() throws JSQLParserException { tableNames = TablesNamesFinder.findTablesInExpression(exprStr); assertThat(tableNames).containsExactlyInAnyOrder("A", "B", "C"); } + + @Test + void testRefreshMaterializedView() throws JSQLParserException { + String sqlStr1 = "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH DATA"; + Set tableNames1 = TablesNamesFinder.findTables(sqlStr1); + assertThat(tableNames1).containsExactlyInAnyOrder("my_view"); + + String sqlStr2 = "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view"; + Set tableNames2 = TablesNamesFinder.findTables(sqlStr2); + assertThat(tableNames2).containsExactlyInAnyOrder("my_view"); + + String sqlStr3 = "REFRESH MATERIALIZED VIEW my_view"; + Set tableNames3 = TablesNamesFinder.findTables(sqlStr3); + assertThat(tableNames3).containsExactlyInAnyOrder("my_view"); + + String sqlStr4 = "REFRESH MATERIALIZED VIEW my_view WITH DATA"; + Set tableNames4 = TablesNamesFinder.findTables(sqlStr4); + assertThat(tableNames4).containsExactlyInAnyOrder("my_view"); + + String sqlStr5 = "REFRESH MATERIALIZED VIEW my_view WITH NO DATA"; + Set tableNames5 = TablesNamesFinder.findTables(sqlStr5); + assertThat(tableNames5).containsExactlyInAnyOrder("my_view"); + + String sqlStr6 = "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH NO DATA"; + Set tableNames6 = TablesNamesFinder.findTables(sqlStr6); + assertThat(tableNames6).isEmpty(); + } } diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidatorTest.java new file mode 100644 index 000000000..1444c2961 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/RefreshMaterializedViewStatementValidatorTest.java @@ -0,0 +1,66 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import java.util.Arrays; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.util.validation.ValidationTestAsserts; +import net.sf.jsqlparser.util.validation.feature.DatabaseType; +import net.sf.jsqlparser.util.validation.feature.FeaturesAllowed; +import org.junit.jupiter.api.Test; + +/** + * @author jxnu-liguobin + */ + +public class RefreshMaterializedViewStatementValidatorTest extends ValidationTestAsserts { + + @Test + public void testValidationRefresh() throws Exception { + for (String sql : Arrays.asList("REFRESH MATERIALIZED VIEW my_view")) { + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL); + } + } + + @Test + public void testValidationRefreshWithData() throws Exception { + for (String sql : Arrays + .asList("REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH DATA")) { + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL); + } + + for (String sql : Arrays.asList("REFRESH MATERIALIZED VIEW my_view WITH DATA")) { + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL); + } + } + + @Test + public void testValidationRefreshWithConcurrently() throws Exception { + for (String sql : Arrays.asList("REFRESH MATERIALIZED VIEW CONCURRENTLY my_view")) { + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL); + } + } + + + @Test + public void testValidationRefreshNotAllowed() throws Exception { + for (String sql : Arrays.asList("REFRESH MATERIALIZED VIEW my_view")) { + validateNotAllowed(sql, 1, 1, FeaturesAllowed.SELECT, + Feature.refreshMaterializedView); + } + + for (String sql : Arrays + .asList("REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH DATA")) { + validateNotAllowed(sql, 1, 1, FeaturesAllowed.SELECT, + Feature.refreshMaterializedView, Feature.refreshMaterializedWithDataView, + Feature.refreshMaterializedWithNoDataView); + } + } +} From 4d47e0ab7bc28725a875a75fba29edb5e390343e Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Thu, 14 Dec 2023 14:30:33 +0800 Subject: [PATCH 34/77] feat: Add support comment in `create view` for MySQL and MariaDb (#1913) * CreateView with comment for mysql * Add test * Fix * Fix * Fix * Fix --- .../sf/jsqlparser/parser/feature/Feature.java | 6 +++ .../java/net/sf/jsqlparser/schema/Column.java | 30 +++++++++--- .../statement/create/view/CreateView.java | 34 ++++++------- .../util/deparser/CreateViewDeParser.java | 8 ++- .../validation/feature/MariaDbVersion.java | 21 +++++--- .../util/validation/feature/MySqlVersion.java | 13 +++-- .../validator/CreateViewValidator.java | 2 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 49 +++++++++++++++++-- .../statement/create/CreateViewTest.java | 43 +++++++++++++--- .../validator/CreateViewValidatorTest.java | 17 +++++-- 10 files changed, 171 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 3d5825f5d..b59191844 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -483,6 +483,12 @@ public enum Feature { * SQL "CREATE MATERIALIZED VIEW" statement is allowed */ createViewMaterialized, + + /** + * SQL "CREATE VIEW(x comment 'x', y comment 'y') comment 'view'" statement is allowed + */ + createViewWithComment, + /** * SQL "CREATE TABLE" statement is allowed * diff --git a/src/main/java/net/sf/jsqlparser/schema/Column.java b/src/main/java/net/sf/jsqlparser/schema/Column.java index 684e0faf5..3f7721ca3 100644 --- a/src/main/java/net/sf/jsqlparser/schema/Column.java +++ b/src/main/java/net/sf/jsqlparser/schema/Column.java @@ -9,13 +9,12 @@ */ package net.sf.jsqlparser.schema; +import java.util.List; import net.sf.jsqlparser.expression.ArrayConstructor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; -import java.util.List; - /** * A column. It can have the table name it belongs to. */ @@ -23,6 +22,7 @@ public class Column extends ASTNodeAccessImpl implements Expression, MultiPartNa private Table table; private String columnName; + private String commentText; private ArrayConstructor arrayConstructor; public Column() {} @@ -56,19 +56,19 @@ public Column setArrayConstructor(ArrayConstructor arrayConstructor) { *

    * The inference is based only on local information, and not on the whole SQL command. For * example, consider the following query:

    - * + * *
          *  SELECT x FROM Foo
          * 
    - * + * *
    Given the {@code Column} called {@code x}, this method would return * {@code null}, and not the info about the table {@code Foo}. On the other hand, consider: *
    - * + * *
          *  SELECT t.x FROM Foo t
          * 
    - * + * *
    Here, we will get a {@code Table} object for a table called {@code t}. But * because the inference is local, such object will not know that {@code t} is just an alias for * {@code Foo}. @@ -114,6 +114,11 @@ public String getFullyQualifiedName(boolean aliases) { fqn.append(columnName); } + if (commentText != null) { + fqn.append(" COMMENT "); + fqn.append(commentText); + } + if (arrayConstructor != null) { fqn.append(arrayConstructor); } @@ -146,4 +151,17 @@ public Column withColumnName(String columnName) { this.setColumnName(columnName); return this; } + + public Column withCommentText(String commentText) { + this.setCommentText(commentText); + return this; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } + + public String getCommentText() { + return commentText; + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java index a623beb41..8381e50af 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/CreateView.java @@ -9,11 +9,9 @@ */ package net.sf.jsqlparser.statement.create.view; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Optional; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; @@ -25,13 +23,14 @@ public class CreateView implements Statement { private Table view; private Select select; private boolean orReplace = false; - private List columnNames = null; + private ExpressionList columnNames = null; private boolean materialized = false; private ForceOption force = ForceOption.NONE; private TemporaryOption temp = TemporaryOption.NONE; private AutoRefreshOption autoRefresh = AutoRefreshOption.NONE; private boolean withReadOnly = false; private boolean ifNotExists = false; + private List viewCommentOptions = null; @Override public void accept(StatementVisitor statementVisitor) { @@ -65,11 +64,11 @@ public void setSelect(Select select) { this.select = select; } - public List getColumnNames() { + public ExpressionList getColumnNames() { return columnNames; } - public void setColumnNames(List columnNames) { + public void setColumnNames(ExpressionList columnNames) { this.columnNames = columnNames; } @@ -145,7 +144,12 @@ public String toString() { sql.append(" AUTO REFRESH ").append(autoRefresh.name()); } if (columnNames != null) { - sql.append(PlainSelect.getStringList(columnNames, true, true)); + sql.append("("); + sql.append(columnNames); + sql.append(")"); + } + if (viewCommentOptions != null) { + sql.append(PlainSelect.getStringList(viewCommentOptions, false, false)); } sql.append(" AS ").append(select); if (isWithReadOnly()) { @@ -182,7 +186,7 @@ public CreateView withOrReplace(boolean orReplace) { return this; } - public CreateView withColumnNames(List columnNames) { + public CreateView withColumnNames(ExpressionList columnNames) { this.setColumnNames(columnNames); return this; } @@ -202,15 +206,11 @@ public CreateView withWithReadOnly(boolean withReadOnly) { return this; } - public CreateView addColumnNames(String... columnNames) { - List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new); - Collections.addAll(collection, columnNames); - return this.withColumnNames(collection); + public List getViewCommentOptions() { + return viewCommentOptions; } - public CreateView addColumnNames(Collection columnNames) { - List collection = Optional.ofNullable(getColumnNames()).orElseGet(ArrayList::new); - collection.addAll(columnNames); - return this.withColumnNames(collection); + public void setViewCommentOptions(List viewCommentOptions) { + this.viewCommentOptions = viewCommentOptions; } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java index 13973b06e..1f5662e68 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateViewDeParser.java @@ -67,7 +67,13 @@ public void deParse(CreateView createView) { buffer.append(" AUTO REFRESH ").append(createView.getAutoRefresh().name()); } if (createView.getColumnNames() != null) { - buffer.append(PlainSelect.getStringList(createView.getColumnNames(), true, true)); + buffer.append("("); + buffer.append(createView.getColumnNames()); + buffer.append(")"); + } + if (createView.getViewCommentOptions() != null) { + buffer.append( + PlainSelect.getStringList(createView.getViewCommentOptions(), false, false)); } buffer.append(" AS "); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java index 185da5664..983adb4fd 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MariaDbVersion.java @@ -9,11 +9,10 @@ */ package net.sf.jsqlparser.util.validation.feature; -import net.sf.jsqlparser.parser.feature.Feature; - import java.util.Collections; import java.util.EnumSet; import java.util.Set; +import net.sf.jsqlparser.parser.feature.Feature; /** * Please add Features supported and place a link to public documentation @@ -41,7 +40,8 @@ public enum MariaDbVersion implements Version { Feature.selectForUpdateSkipLocked, // https://mariadb.com/kb/en/join-syntax/ - Feature.join, Feature.joinSimple, Feature.joinRight, Feature.joinNatural, Feature.joinLeft, + Feature.join, Feature.joinSimple, Feature.joinRight, Feature.joinNatural, + Feature.joinLeft, Feature.joinCross, Feature.joinOuter, Feature.joinInner, Feature.joinStraight, Feature.joinUsingColumns, @@ -64,8 +64,10 @@ public enum MariaDbVersion implements Version { // https://mariadb.com/kb/en/insert/ Feature.insert, Feature.insertValues, Feature.values, - Feature.insertFromSelect, Feature.insertModifierPriority, Feature.insertModifierIgnore, - Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate, Feature.insertReturningExpressionList, + Feature.insertFromSelect, Feature.insertModifierPriority, + Feature.insertModifierIgnore, + Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate, + Feature.insertReturningExpressionList, // https://mariadb.com/kb/en/update/ Feature.update, @@ -96,7 +98,8 @@ public enum MariaDbVersion implements Version { Feature.dropView, // https://mariadb.com/kb/en/drop-sequence/ Feature.dropSequence, Feature.dropTableIfExists, Feature.dropIndexIfExists, - Feature.dropViewIfExists, Feature.dropSchemaIfExists, Feature.dropSequenceIfExists, + Feature.dropViewIfExists, Feature.dropSchemaIfExists, + Feature.dropSequenceIfExists, // https://mariadb.com/kb/en/replace/ Feature.upsert, @@ -110,9 +113,11 @@ public enum MariaDbVersion implements Version { // https://mariadb.com/kb/en/create-view/ Feature.createView, Feature.createOrReplaceView, + Feature.createViewWithComment, // https://mariadb.com/kb/en/create-table/ - Feature.createTable, Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings, + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, Feature.createTableFromSelect, Feature.createTableIfNotExists, // https://mariadb.com/kb/en/create-index/ Feature.createIndex, @@ -143,7 +148,7 @@ public enum MariaDbVersion implements Version { Feature.commit, // https://mariadb.com/kb/en/optimizer-hints/ Feature.mySqlHintStraightJoin, - Feature.mysqlCalcFoundRows, + Feature.mysqlCalcFoundRows, Feature.mysqlSqlCacheFlag)), ORACLE_MODE("oracle_mode", V10_5_4.copy().add(Feature.selectUnique).getFeatures()); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java index 01bbce4c1..d631344d8 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java @@ -9,11 +9,10 @@ */ package net.sf.jsqlparser.util.validation.feature; -import net.sf.jsqlparser.parser.feature.Feature; - import java.util.Collections; import java.util.EnumSet; import java.util.Set; +import net.sf.jsqlparser.parser.feature.Feature; /** * Please add Features supported and place a link to public documentation @@ -33,7 +32,8 @@ public enum MySqlVersion implements Version { // https://dev.mysql.com/doc/refman/8.0/en/select.html Feature.select, Feature.selectGroupBy, Feature.selectHaving, - Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam, Feature.orderBy, + Feature.limit, Feature.limitOffset, Feature.offset, Feature.offsetParam, + Feature.orderBy, Feature.selectForUpdate, Feature.selectForUpdateOfTable, Feature.selectForUpdateNoWait, @@ -51,7 +51,8 @@ public enum MySqlVersion implements Version { Feature.function, // https://dev.mysql.com/doc/refman/8.0/en/join.html - Feature.join, Feature.joinSimple, Feature.joinLeft, Feature.joinRight, Feature.joinOuter, + Feature.join, Feature.joinSimple, Feature.joinLeft, Feature.joinRight, + Feature.joinOuter, Feature.joinNatural, Feature.joinInner, Feature.joinCross, Feature.joinStraight, Feature.joinUsingColumns, @@ -99,9 +100,11 @@ public enum MySqlVersion implements Version { Feature.createSchema, // https://dev.mysql.com/doc/refman/8.0/en/create-view.html Feature.createView, + Feature.createViewWithComment, Feature.createOrReplaceView, // https://dev.mysql.com/doc/refman/8.0/en/create-table.html - Feature.createTable, Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings, + Feature.createTable, Feature.createTableCreateOptionStrings, + Feature.createTableTableOptionStrings, Feature.createTableFromSelect, Feature.createTableIfNotExists, // https://dev.mysql.com/doc/refman/8.0/en/create-index.html Feature.createIndex, diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java index 2723cfc25..e2910ada3 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidator.java @@ -33,6 +33,8 @@ public void validate(CreateView createView) { Feature.createViewTemporary); validateFeature(c, createView.isMaterialized(), Feature.createViewMaterialized); validateName(c, NamedObject.view, createView.getView().getFullyQualifiedName(), false); + validateFeature(c, createView.getViewCommentOptions() != null, + Feature.createViewWithComment); } SelectValidator v = getValidator(SelectValidator.class); Select select = createView.getSelect(); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 2045dfdda..91f4df886 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1767,16 +1767,18 @@ Column Column() #Column : { List data = new ArrayList(); ArrayConstructor arrayConstructor = null; + Token tk = null; } { data = RelObjectNameList() - + [ tk= ] // @todo: we better should return a SEQUENCE instead of a COLUMN [ "." { data.add("nextval"); } ] [ LOOKAHEAD(2) arrayConstructor = ArrayConstructor(false) ] { Column col = new Column(data); + if (tk != null) { col.withCommentText(tk.image); } if (arrayConstructor!=null) { col.setArrayConstructor(arrayConstructor); } @@ -5705,13 +5707,30 @@ Analyze Analyze(): } } +ExpressionList ColumnWithCommentList(): +{ + ExpressionList expressions = new ExpressionList(); + Column img = null; +} +{ + "(" + img=Column() { expressions.add(img); } + ( "," img=Column() { expressions.add(img); } )* + ")" + { + return expressions; + } +} + + CreateView CreateView(boolean isUsingOrReplace): { CreateView createView = new CreateView(); Table view = null; Select select = null; - List columnNames = null; + ExpressionList columnNames = null; Token tk = null; + List commentTokens = null; } { { createView.setOrReplace(isUsingOrReplace);} @@ -5727,13 +5746,36 @@ CreateView CreateView(boolean isUsingOrReplace): view=Table() { createView.setView(view); } [LOOKAHEAD(3) (tk= | tk=) { createView.setAutoRefresh(AutoRefreshOption.from(tk.image)); } ] [LOOKAHEAD(3) {createView.setIfNotExists(true);}] - [ columnNames = ColumnsNamesList() { createView.setColumnNames(columnNames); } ] + [ columnNames=ColumnWithCommentList( ) { createView.setColumnNames(columnNames); } ] + [ commentTokens=CreateViewTailComment( ) { createView.setViewCommentOptions(commentTokens); } ] select=Select( ) { createView.setSelect(select); } [ { createView.setWithReadOnly(true); } ] { return createView; } } +List CreateViewTailComment(): +{ + Token tk = null; + Token tk2 = null; + String op = null; + List result = new ArrayList(); +} +{ + tk= + [ "=" { op = "="; } ] + tk2 = { + result.add(""); + result.add(tk.image); + if (op != null) { + result.add(op); + } + result.add(tk2.image); + } + { return result;} +} + + ReferentialAction.Action Action(): { ReferentialAction.Action action = null; @@ -5778,7 +5820,6 @@ List CreateParameter(): Token tk = null, tk2 = null; Expression exp = null; ColDataType colDataType; - List param = new ArrayList(); } { diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java index 3499a83be..240a22fc3 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateViewTest.java @@ -9,8 +9,13 @@ */ package net.sf.jsqlparser.statement.create; -import java.io.StringReader; +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.StringReader; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.parser.CCJSqlParserUtil; @@ -20,14 +25,7 @@ import net.sf.jsqlparser.statement.create.view.CreateView; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.PlainSelect; -import static net.sf.jsqlparser.test.TestUtils.*; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - import org.assertj.core.api.ThrowableAssert.ThrowingCallable; -import static org.junit.jupiter.api.Assertions.assertTrue; - import org.junit.jupiter.api.Test; public class CreateViewTest { @@ -212,4 +210,33 @@ public void testCreateMaterializedViewIfNotExists() throws JSQLParserException { assertTrue(createView.isIfNotExists()); } + @Test + public void testCreateViewWithColumnComment() throws JSQLParserException { + String stmt = + "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') AS SELECT c1, C2 FROM t1 WITH READ ONLY"; + assertSqlCanBeParsedAndDeparsed(stmt); + + String stmt2 = + "CREATE VIEW v14(c1 COMMENT 'comment1', c2) AS SELECT c1, C2 FROM t1 WITH READ ONLY"; + assertSqlCanBeParsedAndDeparsed(stmt2); + + String stmt3 = + "CREATE VIEW v14(c1, c2) COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY"; + assertSqlCanBeParsedAndDeparsed(stmt3); + } + + @Test + public void testCreateViewWithTableComment1() throws JSQLParserException { + String stmt = + "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + + @Test + public void testCreateViewWithTableComment2() throws JSQLParserException { + String stmt = + "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY"; + assertSqlCanBeParsedAndDeparsed(stmt); + } + } diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java index 92d4cd294..4c94bd041 100644 --- a/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/CreateViewValidatorTest.java @@ -37,18 +37,21 @@ public void testValidateCreateViewNotAllowed() throws JSQLParserException { @Test public void testValidateCreateViewMaterialized() throws JSQLParserException { - validateNoErrors("CREATE MATERIALIZED VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE); + validateNoErrors("CREATE MATERIALIZED VIEW myview AS SELECT * FROM mytab", 1, + DatabaseType.ORACLE); } @Test public void testValidateCreateOrReplaceView() throws JSQLParserException { - validateNoErrors("CREATE OR REPLACE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE, + validateNoErrors("CREATE OR REPLACE VIEW myview AS SELECT * FROM mytab", 1, + DatabaseType.ORACLE, DatabaseType.POSTGRESQL, DatabaseType.MYSQL, DatabaseType.MARIADB, DatabaseType.H2); } @Test public void testValidateCreateForceView() throws JSQLParserException { - validateNoErrors("CREATE FORCE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE, DatabaseType.H2); + validateNoErrors("CREATE FORCE VIEW myview AS SELECT * FROM mytab", 1, DatabaseType.ORACLE, + DatabaseType.H2); } @Test @@ -66,4 +69,12 @@ public void testValidateCreateViewWith() throws JSQLParserException { validateNoErrors(sql, 1, DatabaseType.DATABASES); } } + + @Test + public void testValidateCreateViewWithComment() throws JSQLParserException { + validateNoErrors( + "CREATE VIEW v14(c1 COMMENT 'comment1', c2 COMMENT 'comment2') COMMENT = 'view' AS SELECT c1, C2 FROM t1 WITH READ ONLY", + 1, + DatabaseType.MYSQL, DatabaseType.MARIADB); + } } From 2ae1d53e56e45b27946a26081dee14b1c6d1254e Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Thu, 14 Dec 2023 15:22:57 +0800 Subject: [PATCH 35/77] Fix conflict (#1915) --- src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 91f4df886..347667f34 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1771,7 +1771,7 @@ Column Column() #Column : } { data = RelObjectNameList() - [ tk= ] + [ LOOKAHEAD(2) tk= ] // @todo: we better should return a SEQUENCE instead of a COLUMN [ "." { data.add("nextval"); } ] From 2ce72a34b34737e3f30deab67d3e97c51a254452 Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Fri, 15 Dec 2023 00:41:02 +0800 Subject: [PATCH 36/77] Fix: Support Drop Key (#1917) * Fix: Support Drop Key * Fix: Support Drop Key * Fix --- .../statement/create/table/Index.java | 22 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../jsqlparser/statement/alter/AlterTest.java | 239 +++++++++++------- 3 files changed, 164 insertions(+), 99 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java index 091dfb7af..93b672c96 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java @@ -81,12 +81,11 @@ public String getType() { } /** - * In postgresql, the index type (Btree, GIST, etc.) is indicated - * with a USING clause. - * Please note that: - * Oracle - the type might be BITMAP, indicating a bitmap kind of index - * MySQL - the type might be FULLTEXT or SPATIAL - * @param using + * In postgresql, the index type (Btree, GIST, etc.) is indicated with a USING clause. Please + * note that: Oracle - the type might be BITMAP, indicating a bitmap kind of index MySQL - the + * type might be FULLTEXT or SPATIAL + * + * @param using */ public void setUsing(String using) { this.using = using; @@ -135,8 +134,15 @@ public Index withIndexSpec(List idxSpec) { @Override public String toString() { String idxSpecText = PlainSelect.getStringList(idxSpec, false, false); - return ( type!=null ? type : "") + (!name.isEmpty() ? " " + getName() : "") + " " + PlainSelect. - getStringList(columns, true, true) + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); + String head = (type != null ? type : "") + (!name.isEmpty() ? " " + getName() : ""); + String tail = PlainSelect.getStringList(columns, true, true) + + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); + + if ("".equals(tail)) { + return head; + } + + return head + " " + tail; } public Index withType(String type) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 347667f34..f9dafcb4f 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -6392,7 +6392,7 @@ AlterExpression AlterExpression(): ) | ( - tk= + ( tk= | tk= ) ( tk2= | tk2= ) { index = new Index().withType(tk.image).withName(tk2.image); alterExp.setIndex(index); diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 0bc241766..0a6dc77fd 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -9,10 +9,16 @@ */ package net.sf.jsqlparser.statement.alter; +import static net.sf.jsqlparser.test.TestUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.util.Arrays; import java.util.Collections; import java.util.List; - import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; @@ -30,20 +36,14 @@ import net.sf.jsqlparser.statement.create.table.Index; import net.sf.jsqlparser.statement.create.table.Index.ColumnParams; import net.sf.jsqlparser.statement.create.table.NamedConstraint; -import static net.sf.jsqlparser.test.TestUtils.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class AlterTest { @Test public void testAlterTableAddColumn() throws JSQLParserException { - Statement stmt = CCJSqlParserUtil. - parse("ALTER TABLE mytable ADD COLUMN mycolumn varchar (255)"); + Statement stmt = + CCJSqlParserUtil.parse("ALTER TABLE mytable ADD COLUMN mycolumn varchar (255)"); assertTrue(stmt instanceof Alter); Alter alter = (Alter) stmt; assertEquals("mytable", alter.getTable().getFullyQualifiedName()); @@ -69,15 +69,16 @@ public void testAlterTableAddColumn_ColumnKeyWordImplicit() throws JSQLParserExc @Test - public void testAlterTableBackBrackets()throws JSQLParserException{ - String sql="ALTER TABLE tablename add column (field string comment 'aaaaa')"; + public void testAlterTableBackBrackets() throws JSQLParserException { + String sql = "ALTER TABLE tablename add column (field string comment 'aaaaa')"; Statement statement = CCJSqlParserUtil.parse(sql); - Alter alter=(Alter) statement; + Alter alter = (Alter) statement; System.out.println(alter.toString()); - String sql2="ALTER TABLE tablename add column (field string comment 'aaaaa', field2 string comment 'bbbbb');"; + String sql2 = + "ALTER TABLE tablename add column (field string comment 'aaaaa', field2 string comment 'bbbbb');"; Statement statement2 = CCJSqlParserUtil.parse(sql2); - Alter alter2=(Alter) statement2; + Alter alter2 = (Alter) statement2; System.out.println(alter2.toString()); } @@ -109,37 +110,44 @@ public void testAlterTablePrimaryKeyNoValidate() throws JSQLParserException { @Test public void testAlterTablePrimaryKeyDeferrableValidate() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE animals ADD PRIMARY KEY (id) DEFERRABLE VALIDATE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE animals ADD PRIMARY KEY (id) DEFERRABLE VALIDATE"); } @Test public void testAlterTablePrimaryKeyDeferrableDisableNoValidate() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE animals ADD PRIMARY KEY (id) DEFERRABLE DISABLE NOVALIDATE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE animals ADD PRIMARY KEY (id) DEFERRABLE DISABLE NOVALIDATE"); } @Test public void testAlterTableUniqueKey() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE `schema_migrations` ADD UNIQUE KEY `unique_schema_migrations` (`version`)"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE `schema_migrations` ADD UNIQUE KEY `unique_schema_migrations` (`version`)"); } @Test public void testAlterTableForgeignKey() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE CASCADE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE CASCADE"); } @Test public void testAlterTableAddConstraint() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT FK_RESOURCELINKTYPE_PARENTTYPE_PRIMARYKEY FOREIGN KEY (PARENTTYPE_PRIMARYKEY) REFERENCES RESOURCETYPE(PRIMARYKEY)"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT FK_RESOURCELINKTYPE_PARENTTYPE_PRIMARYKEY FOREIGN KEY (PARENTTYPE_PRIMARYKEY) REFERENCES RESOURCETYPE(PRIMARYKEY)"); } @Test public void testAlterTableAddConstraintWithConstraintState() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT FK_RESOURCELINKTYPE_PARENTTYPE_PRIMARYKEY FOREIGN KEY (PARENTTYPE_PRIMARYKEY) REFERENCES RESOURCETYPE(PRIMARYKEY) DEFERRABLE DISABLE NOVALIDATE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT FK_RESOURCELINKTYPE_PARENTTYPE_PRIMARYKEY FOREIGN KEY (PARENTTYPE_PRIMARYKEY) REFERENCES RESOURCETYPE(PRIMARYKEY) DEFERRABLE DISABLE NOVALIDATE"); } @Test public void testAlterTableAddConstraintWithConstraintState2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT RESOURCELINKTYPE_PRIMARYKEY PRIMARY KEY (PRIMARYKEY) DEFERRABLE NOVALIDATE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE RESOURCELINKTYPE ADD CONSTRAINT RESOURCELINKTYPE_PRIMARYKEY PRIMARY KEY (PRIMARYKEY) DEFERRABLE NOVALIDATE"); } @Test @@ -149,24 +157,28 @@ public void testAlterTableAddUniqueConstraint() throws JSQLParserException { @Test public void testAlterTableForeignKey2() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id)"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id)"); } @Test public void testAlterTableForeignKey3() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE RESTRICT"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE RESTRICT"); } @Test public void testAlterTableForeignKey4() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE SET NULL"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES ra_user (id) ON DELETE SET NULL"); } @Test public void testAlterTableForeignWithFkSchema() throws JSQLParserException { final String FK_SCHEMA_NAME = "my_schema"; final String FK_TABLE_NAME = "ra_user"; - String sql = "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES " + FK_SCHEMA_NAME + "." + FK_TABLE_NAME + " (id) ON DELETE SET NULL"; + String sql = "ALTER TABLE test ADD FOREIGN KEY (user_id) REFERENCES " + FK_SCHEMA_NAME + "." + + FK_TABLE_NAME + " (id) ON DELETE SET NULL"; assertSqlCanBeParsedAndDeparsed(sql); Alter alter = (Alter) CCJSqlParserUtil.parse(sql); @@ -176,6 +188,12 @@ public void testAlterTableForeignWithFkSchema() throws JSQLParserException { assertEquals(alterExpression.getFkSourceTable(), FK_TABLE_NAME); } + @Test + public void testAlterTableDropKey() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE ANV_ALERT_ACKNOWLEDGE_TYPE DROP KEY ALERT_ACKNOWLEDGE_TYPE_ID_NUK_1"); + } + @Test public void testAlterTableDropColumn() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE test DROP COLUMN YYY"); @@ -185,8 +203,8 @@ public void testAlterTableDropColumn() throws JSQLParserException { public void testAlterTableDropColumn2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable DROP COLUMN col1, DROP COLUMN col2"); - Statement stmt = CCJSqlParserUtil. - parse("ALTER TABLE mytable DROP COLUMN col1, DROP COLUMN col2"); + Statement stmt = + CCJSqlParserUtil.parse("ALTER TABLE mytable DROP COLUMN col1, DROP COLUMN col2"); Alter alter = (Alter) stmt; List alterExps = alter.getAlterExpressions(); AlterExpression col1Exp = alterExps.get(0); @@ -216,7 +234,7 @@ public void testAlterTablePK() throws JSQLParserException { assertStatementCanBeDeparsedAs(stmt, sql); AlterExpression alterExpression = ((Alter) stmt).getAlterExpressions().get(0); assertNull(alterExpression.getConstraintName()); - // TODO: should this pass? ==> assertEquals(alterExpression.getPkColumns().get(0), "ID"); + // TODO: should this pass? ==> assertEquals(alterExpression.getPkColumns().get(0), "ID"); assertEquals(alterExpression.getIndex().getColumnsNames().get(0), "`ID`"); } @@ -235,14 +253,17 @@ public void testAlterTableFK() throws JSQLParserException { @Test public void testAlterTableCheckConstraint() throws JSQLParserException { - String statement = "ALTER TABLE `Author` ADD CONSTRAINT name_not_empty CHECK (`NAME` <> '')"; + String statement = + "ALTER TABLE `Author` ADD CONSTRAINT name_not_empty CHECK (`NAME` <> '')"; Statement parsed = assertSqlCanBeParsedAndDeparsed(statement); Alter created = new Alter().withTable(new Table("`Author`")) .addAlterExpressions(Collections.singleton( - new AlterExpression().withOperation(AlterOperation.ADD).withIndex(new CheckConstraint() - .withName("name_not_empty") - .withExpression(new NotEqualsTo().withLeftExpression(new Column("`NAME`")) - .withRightExpression(new StringValue()))))); + new AlterExpression().withOperation(AlterOperation.ADD) + .withIndex(new CheckConstraint() + .withName("name_not_empty") + .withExpression(new NotEqualsTo() + .withLeftExpression(new Column("`NAME`")) + .withRightExpression(new StringValue()))))); assertDeparse(created, statement); assertEqualsObjectTree(parsed, created); } @@ -259,10 +280,11 @@ public void testAlterTableAddColumn3() throws JSQLParserException { @Test public void testAlterTableAddColumn4() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable ADD COLUMN col1 varchar (255), ADD COLUMN col2 integer"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE mytable ADD COLUMN col1 varchar (255), ADD COLUMN col2 integer"); - Statement stmt = CCJSqlParserUtil. - parse("ALTER TABLE mytable ADD COLUMN col1 varchar (255), ADD COLUMN col2 integer"); + Statement stmt = CCJSqlParserUtil.parse( + "ALTER TABLE mytable ADD COLUMN col1 varchar (255), ADD COLUMN col2 integer"); Alter alter = (Alter) stmt; List alterExps = alter.getAlterExpressions(); AlterExpression col1Exp = alterExps.get(0); @@ -308,12 +330,14 @@ public void testAlterTableAddColumn6() throws JSQLParserException { @Test public void testAlterTableModifyColumn1() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE animals MODIFY (col1 integer, col2 number (8, 2))"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE animals MODIFY (col1 integer, col2 number (8, 2))"); } @Test public void testAlterTableModifyColumn2() throws JSQLParserException { - Alter alter = (Alter) CCJSqlParserUtil.parse("ALTER TABLE mytable modify col1 timestamp (6)"); + Alter alter = + (Alter) CCJSqlParserUtil.parse("ALTER TABLE mytable modify col1 timestamp (6)"); AlterExpression alterExpression = alter.getAlterExpressions().get(0); // COLUMN keyword DOES NOT appear in deparsed statement, modify becomes all caps @@ -327,7 +351,8 @@ public void testAlterTableModifyColumn2() throws JSQLParserException { @Test public void testAlterTableAlterColumn() throws JSQLParserException { // http://www.postgresqltutorial.com/postgresql-change-column-type/ - String sql = "ALTER TABLE table_name ALTER COLUMN column_name_1 TYPE TIMESTAMP, ALTER COLUMN column_name_2 TYPE BOOLEAN"; + String sql = + "ALTER TABLE table_name ALTER COLUMN column_name_1 TYPE TIMESTAMP, ALTER COLUMN column_name_2 TYPE BOOLEAN"; assertSqlCanBeParsedAndDeparsed(sql); Alter alter = (Alter) CCJSqlParserUtil.parse(sql); @@ -368,13 +393,16 @@ public void testAlterTableChangeColumn4() throws JSQLParserException { @Test public void testAlterTableAddColumnWithZone() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable ADD COLUMN col1 timestamp with time zone"); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable ADD COLUMN col1 timestamp without time zone"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE mytable ADD COLUMN col1 timestamp with time zone"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE mytable ADD COLUMN col1 timestamp without time zone"); assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable ADD COLUMN col1 date with time zone"); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE mytable ADD COLUMN col1 date without time zone"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE mytable ADD COLUMN col1 date without time zone"); - Statement stmt = CCJSqlParserUtil. - parse("ALTER TABLE mytable ADD COLUMN col1 timestamp with time zone"); + Statement stmt = CCJSqlParserUtil + .parse("ALTER TABLE mytable ADD COLUMN col1 timestamp with time zone"); Alter alter = (Alter) stmt; List alterExps = alter.getAlterExpressions(); AlterExpression col1Exp = alterExps.get(0); @@ -414,78 +442,94 @@ public void testAddConstraintKeyIssue320() throws JSQLParserException { String constraintName2 = "table1_constraint_2"; for (String constraintType : Arrays.asList("UNIQUE KEY", "KEY")) { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " - + constraintType + " (" + columnName1 + ")"); - - assertSqlCanBeParsedAndDeparsed("ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " - + constraintType + " (" + columnName1 + ", " + columnName2 + ")"); - - assertSqlCanBeParsedAndDeparsed("ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " - + constraintType + " (" + columnName1 + ", " + columnName2 + "), ADD CONSTRAINT " - + constraintName2 + " " + constraintType + " (" + columnName3 + ", " + columnName4 + ")"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " + + constraintType + " (" + columnName1 + ")"); + + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " + + constraintType + " (" + columnName1 + ", " + columnName2 + ")"); + + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE " + tableName + " ADD CONSTRAINT " + constraintName1 + " " + + constraintType + " (" + columnName1 + ", " + columnName2 + + "), ADD CONSTRAINT " + + constraintName2 + " " + constraintType + " (" + columnName3 + ", " + + columnName4 + ")"); } } @Test public void testIssue633() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key UNIQUE (id)"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key UNIQUE (id)"); } @Test public void testIssue679() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE tb_session_status ADD INDEX idx_user_id_name (user_id, user_name(10)), ADD INDEX idx_user_name (user_name)"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE tb_session_status ADD INDEX idx_user_id_name (user_id, user_name(10)), ADD INDEX idx_user_name (user_name)"); } @Test public void testAlterTableIndex586() throws Exception { - Statement result = CCJSqlParserUtil.parse("ALTER TABLE biz_add_fee DROP INDEX operation_time, " - + "ADD UNIQUE INDEX operation_time (`operation_time`, `warehouse_code`, `customerid`, `fees_type`, `external_no`) " - + "USING BTREE, ALGORITHM = INPLACE"); - assertEquals("ALTER TABLE biz_add_fee DROP INDEX operation_time , " + Statement result = + CCJSqlParserUtil.parse("ALTER TABLE biz_add_fee DROP INDEX operation_time, " + + "ADD UNIQUE INDEX operation_time (`operation_time`, `warehouse_code`, `customerid`, `fees_type`, `external_no`) " + + "USING BTREE, ALGORITHM = INPLACE"); + assertEquals("ALTER TABLE biz_add_fee DROP INDEX operation_time, " + "ADD UNIQUE INDEX operation_time (`operation_time`, `warehouse_code`, `customerid`, `fees_type`, `external_no`) " + "USING BTREE, ALGORITHM = INPLACE", result.toString()); } @Test public void testIssue259() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE feature_v2 ADD COLUMN third_user_id int (10) unsigned DEFAULT '0' COMMENT '第三方用户id' after kdt_id"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE feature_v2 ADD COLUMN third_user_id int (10) unsigned DEFAULT '0' COMMENT '第三方用户id' after kdt_id"); } @Test public void testIssue633_2() throws JSQLParserException { - String statement = "CREATE INDEX idx_american_football_action_plays_1 ON american_football_action_plays USING btree (play_type)"; + String statement = + "CREATE INDEX idx_american_football_action_plays_1 ON american_football_action_plays USING btree (play_type)"; Statement parsed = assertSqlCanBeParsedAndDeparsed(statement); CreateIndex created = new CreateIndex() .withTable(new Table("american_football_action_plays")) .withIndex( new Index().withName("idx_american_football_action_plays_1") - .addColumns(new ColumnParams("play_type", null)).withUsing("btree") - ); + .addColumns(new ColumnParams("play_type", null)) + .withUsing("btree")); assertDeparse(created, statement); assertEqualsObjectTree(parsed, created); } @Test public void testAlterOnlyIssue928() throws JSQLParserException { - String statement = "ALTER TABLE ONLY categories ADD CONSTRAINT pk_categories PRIMARY KEY (category_id)"; + String statement = + "ALTER TABLE ONLY categories ADD CONSTRAINT pk_categories PRIMARY KEY (category_id)"; Statement parsed = assertSqlCanBeParsedAndDeparsed(statement); - Alter created = new Alter().withUseOnly(true).withTable(new Table("categories")).addAlterExpressions( - new AlterExpression().withOperation(AlterOperation.ADD).withIndex(new NamedConstraint() - .withName(Collections.singletonList( - "pk_categories")).withType("PRIMARY KEY") - .addColumns(new ColumnParams("category_id")))); + Alter created = new Alter().withUseOnly(true).withTable(new Table("categories")) + .addAlterExpressions( + new AlterExpression().withOperation(AlterOperation.ADD) + .withIndex(new NamedConstraint() + .withName(Collections.singletonList( + "pk_categories")) + .withType("PRIMARY KEY") + .addColumns(new ColumnParams("category_id")))); assertDeparse(created, statement); assertEqualsObjectTree(parsed, created); } @Test public void testAlterConstraintWithoutFKSourceColumnsIssue929() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (customer_id) REFERENCES customers"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (customer_id) REFERENCES customers"); } @Test public void testAlterTableAlterColumnDropNotNullIssue918() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE \"user_table_t\" ALTER COLUMN name DROP NOT NULL"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE \"user_table_t\" ALTER COLUMN name DROP NOT NULL"); } @Test @@ -509,15 +553,15 @@ public void testAlterTableRenameColumn() throws JSQLParserException { public void testAlterTableForeignKeyIssue981() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( "ALTER TABLE atconfigpro " - + "ADD CONSTRAINT atconfigpro_atconfignow_id_foreign FOREIGN KEY (atconfignow_id) REFERENCES atconfignow(id) ON DELETE CASCADE, " - + "ADD CONSTRAINT atconfigpro_attariff_id_foreign FOREIGN KEY (attariff_id) REFERENCES attariff(id) ON DELETE CASCADE"); + + "ADD CONSTRAINT atconfigpro_atconfignow_id_foreign FOREIGN KEY (atconfignow_id) REFERENCES atconfignow(id) ON DELETE CASCADE, " + + "ADD CONSTRAINT atconfigpro_attariff_id_foreign FOREIGN KEY (attariff_id) REFERENCES attariff(id) ON DELETE CASCADE"); } @Test public void testAlterTableForeignKeyIssue981_2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( "ALTER TABLE atconfigpro " - + "ADD CONSTRAINT atconfigpro_atconfignow_id_foreign FOREIGN KEY (atconfignow_id) REFERENCES atconfignow(id) ON DELETE CASCADE"); + + "ADD CONSTRAINT atconfigpro_atconfignow_id_foreign FOREIGN KEY (atconfignow_id) REFERENCES atconfignow(id) ON DELETE CASCADE"); } @Test @@ -537,7 +581,8 @@ public void testAlterTableColumnCommentIssue984() throws JSQLParserException { Statement parsed = assertSqlCanBeParsedAndDeparsed( statement); Alter created = new Alter().withTable(new Table("texto_fichero")) - .addAlterExpressions(new AlterExpression().withOperation(AlterOperation.MODIFY).withColumnName("id") + .addAlterExpressions(new AlterExpression().withOperation(AlterOperation.MODIFY) + .withColumnName("id") .withCommentText("'some comment'")); assertDeparse(created, statement); assertEqualsObjectTree(parsed, created); @@ -713,10 +758,12 @@ public void testIssue985_2() throws JSQLParserException { @Test public void testAlterTableDefaultValueTrueIssue926() throws JSQLParserException { - Alter parsed = (Alter) CCJSqlParserUtil.parse("ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT FALSE"); + Alter parsed = (Alter) CCJSqlParserUtil + .parse("ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT FALSE"); // There shall be no COLUMN where there is no COLUMN - assertStatementCanBeDeparsedAs(parsed, "ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT FALSE"); + assertStatementCanBeDeparsedAs(parsed, + "ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT FALSE"); } private void assertReferentialActionOnConstraint(Alter parsed, Action onUpdate, @@ -727,7 +774,8 @@ private void assertReferentialActionOnConstraint(Alter parsed, Action onUpdate, // remove line if deprecated methods are removed. index.setOnDeleteReferenceOption(index.getOnDeleteReferenceOption()); if (onDelete != null) { - assertEquals(new ReferentialAction(Type.DELETE, onDelete), index.getReferentialAction(Type.DELETE)); + assertEquals(new ReferentialAction(Type.DELETE, onDelete), + index.getReferentialAction(Type.DELETE)); } else { assertNull(index.getReferentialAction(Type.DELETE)); } @@ -735,7 +783,8 @@ private void assertReferentialActionOnConstraint(Alter parsed, Action onUpdate, // remove line if deprecated methods are removed. index.setOnUpdateReferenceOption(index.getOnUpdateReferenceOption()); if (onUpdate != null) { - assertEquals(new ReferentialAction(Type.UPDATE, onUpdate), index.getReferentialAction(Type.UPDATE)); + assertEquals(new ReferentialAction(Type.UPDATE, onUpdate), + index.getReferentialAction(Type.UPDATE)); } else { assertNull(index.getReferentialAction(Type.UPDATE)); } @@ -779,7 +828,8 @@ public void testRowFormatKeywordIssue1033() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE t1 MOVE TABLESPACE users", true); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test_tab MOVE PARTITION test_tab_q2 COMPRESS", true); + assertSqlCanBeParsedAndDeparsed("ALTER TABLE test_tab MOVE PARTITION test_tab_q2 COMPRESS", + true); } @Test @@ -797,15 +847,19 @@ public void testAlterTableDropConstraintsIssue1342() throws JSQLParserException @Test public void testAlterTableChangeColumnDropNotNull() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY COLUMN b DROP NOT NULL", true); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY (COLUMN b DROP NOT NULL, COLUMN c DROP NOT NULL)", true); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE a MODIFY (COLUMN b DROP NOT NULL, COLUMN c DROP NOT NULL)", true); } @Test public void testAlterTableChangeColumnDropDefault() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY COLUMN b DROP DEFAULT", true); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY (COLUMN b DROP DEFAULT, COLUMN c DROP DEFAULT)", true); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY (COLUMN b DROP NOT NULL, COLUMN b DROP DEFAULT)", true); - assertSqlCanBeParsedAndDeparsed("ALTER TABLE a MODIFY (COLUMN b DROP DEFAULT, COLUMN b DROP NOT NULL)", true); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE a MODIFY (COLUMN b DROP DEFAULT, COLUMN c DROP DEFAULT)", true); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE a MODIFY (COLUMN b DROP NOT NULL, COLUMN b DROP DEFAULT)", true); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE a MODIFY (COLUMN b DROP DEFAULT, COLUMN b DROP NOT NULL)", true); } @Test @@ -815,18 +869,20 @@ public void testAlterTableDropColumnIfExists() throws JSQLParserException { @Test public void testAlterTableDropMultipleColumnsIfExists() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test DROP COLUMN IF EXISTS name, DROP COLUMN IF EXISTS surname"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test DROP COLUMN IF EXISTS name, DROP COLUMN IF EXISTS surname"); } @Test public void testAlterTableDropMultipleColumnsIfExistsWithParams() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE test DROP COLUMN IF EXISTS name CASCADE, DROP COLUMN IF EXISTS surname CASCADE"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE test DROP COLUMN IF EXISTS name CASCADE, DROP COLUMN IF EXISTS surname CASCADE"); } @Test public void testAlterTableAddColumnSpanner7() throws JSQLParserException { final String sql = "ALTER TABLE ORDER_PATIENT ADD COLUMN FIRST_NAME_UPPERCASE STRING(MAX)" + - " AS (UPPER(FIRST_NAME)) STORED"; + " AS (UPPER(FIRST_NAME)) STORED"; Statement stmt = CCJSqlParserUtil.parse(sql); assertStatementCanBeDeparsedAs(stmt, sql, true); Alter alter = (Alter) stmt; @@ -855,9 +911,11 @@ public void testAlterTableAddColumnSpanner8() throws JSQLParserException { @Test public void testAlterColumnSetCommitTimestamp1() throws JSQLParserException { // @todo: properly implement SET OPTIONS, the current hack is terrible - // final String sql = "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=null)"; + // final String sql = "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET + // OPTIONS (allow_commit_timestamp=null)"; - final String sql = "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)"; + final String sql = + "ALTER TABLE FOCUS_PATIENT ALTER COLUMN UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)"; Statement stmt = CCJSqlParserUtil.parse(sql); assertStatementCanBeDeparsedAs(stmt, sql); Alter alter = (Alter) stmt; @@ -868,6 +926,7 @@ public void testAlterColumnSetCommitTimestamp1() throws JSQLParserException { assertEquals(1, col1Exp.getColDataTypeList().size()); ColumnDataType type = col1Exp.getColDataTypeList().get(0); assertEquals("UPDATE_DATE_TIME_GMT", type.getColumnName()); - assertEquals("UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)", type.toString()); + assertEquals("UPDATE_DATE_TIME_GMT SET OPTIONS (allow_commit_timestamp=true)", + type.toString()); } } From b0aff31314a8df46887d75be2663b182416cc40e Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Fri, 15 Dec 2023 12:56:55 +0800 Subject: [PATCH 37/77] Closed #1814, mysql and mariadb can use `index type` before `ON` (#1918) --- .../statement/create/index/CreateIndex.java | 23 +++++++++++++++---- .../util/deparser/CreateIndexDeParser.java | 14 +++++++---- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 18 ++++++++++++--- .../statement/create/CreateIndexTest.java | 20 ++++++++++------ 4 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java index dfec6e930..3fc4fcbf1 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/index/CreateIndex.java @@ -9,18 +9,27 @@ */ package net.sf.jsqlparser.statement.create.index; +import static java.util.stream.Collectors.joining; + +import java.util.*; import net.sf.jsqlparser.schema.*; import net.sf.jsqlparser.statement.*; import net.sf.jsqlparser.statement.create.table.*; -import java.util.*; -import static java.util.stream.Collectors.joining; - public class CreateIndex implements Statement { private Table table; private Index index; private List tailParameters; + private boolean indexTypeBeforeOn = false; + + public boolean isIndexTypeBeforeOn() { + return indexTypeBeforeOn; + } + + public void setIndexTypeBeforeOn(boolean indexTypeBeforeOn) { + this.indexTypeBeforeOn = indexTypeBeforeOn; + } public boolean isUsingIfNotExists() { return usingIfNotExists; @@ -78,10 +87,16 @@ public String toString() { buffer.append("IF NOT EXISTS "); } buffer.append(index.getName()); + + if (index.getUsing() != null && isIndexTypeBeforeOn()) { + buffer.append(" USING "); + buffer.append(index.getUsing()); + } + buffer.append(" ON "); buffer.append(table.getFullyQualifiedName()); - if (index.getUsing() != null) { + if (index.getUsing() != null && !isIndexTypeBeforeOn()) { buffer.append(" USING "); buffer.append(index.getUsing()); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java index 5a292b283..a82f478d7 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/CreateIndexDeParser.java @@ -9,11 +9,11 @@ */ package net.sf.jsqlparser.util.deparser; +import static java.util.stream.Collectors.joining; + import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.Index; -import static java.util.stream.Collectors.joining; - public class CreateIndexDeParser extends AbstractDeParser { public CreateIndexDeParser(StringBuilder buffer) { @@ -36,11 +36,17 @@ public void deParse(CreateIndex createIndex) { buffer.append("IF NOT EXISTS "); } buffer.append(index.getName()); + + String using = index.getUsing(); + if (using != null && createIndex.isIndexTypeBeforeOn()) { + buffer.append(" USING "); + buffer.append(using); + } + buffer.append(" ON "); buffer.append(createIndex.getTable().getFullyQualifiedName()); - String using = index.getUsing(); - if (using != null) { + if (using != null && !createIndex.isIndexTypeBeforeOn()) { buffer.append(" USING "); buffer.append(using); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index f9dafcb4f..824f6d6ce 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -5302,9 +5302,21 @@ CreateIndex CreateIndex(): [ LOOKAHEAD(2) { createIndex.setUsingIfNotExists(true);} ] index = Index() { index.setType(parameter.isEmpty() ? null : parameter.get(0)); } - - table=Table() - [ using= {index.setUsing(using.image);} ] + ( + LOOKAHEAD(3)( + table=Table() + [ using= { index.setUsing(using.image); } ] + ) + | + ( + [ using= { + index.setUsing(using.image); + createIndex.setIndexTypeBeforeOn(true); + } + ] + table=Table() + ) + ) colNames = ColumnNamesWithParamsList() ( parameter=CreateParameter() { tailParameters.addAll(parameter); } )* { diff --git a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java index 9901a8488..23b1d581a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/CreateIndexTest.java @@ -9,18 +9,17 @@ */ package net.sf.jsqlparser.statement.create; +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.StringReader; +import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.statement.create.index.CreateIndex; import org.junit.jupiter.api.Test; -import java.io.StringReader; -import java.util.List; - -import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - public class CreateIndexTest { private final CCJSqlParserManager parserManager = new CCJSqlParserManager(); @@ -142,4 +141,11 @@ void testIfNotExistsIssue1861() throws JSQLParserException { "CREATE INDEX IF NOT EXISTS test_test_idx ON test.test USING btree (\"time\")"; assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + void testCreateIndexIssue1814() throws JSQLParserException { + String sqlStr = + "CREATE INDEX idx_operationlog_operatetime_regioncode USING BTREE ON operation_log (operate_time,region_biz_code)"; + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } From 9d7329e284428e43918a0fbf02f7d33d95e2f4de Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 15 Dec 2023 13:53:49 +0700 Subject: [PATCH 38/77] build: fix GitHub gradle actions --- .github/workflows/sphinx.yml | 4 ++++ build.gradle | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index ccbe94dc7..1840bbe95 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -4,10 +4,14 @@ on: branches: [ "master" ] pull_request: branches: [ "master" ] + types: + - closed permissions: write-all jobs: docs: + if_merged: + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - uses: actions/setup-python@v4 diff --git a/build.gradle b/build.gradle index 4974164dc..465a31c6d 100644 --- a/build.gradle +++ b/build.gradle @@ -303,7 +303,7 @@ checkstyle { spotless { // optional: limit format enforcement to just the files changed by this feature branch - ratchetFrom 'origin/master' + ratchetFrom 'master' format 'misc', { // define the files to apply `misc` to From 48b3acbeef56b2d10c47eb397737154a4ef3c536 Mon Sep 17 00:00:00 2001 From: manticore-projects Date: Fri, 15 Dec 2023 16:11:51 +0700 Subject: [PATCH 39/77] Update build.gradle --- build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 465a31c6d..d9b14ef0e 100644 --- a/build.gradle +++ b/build.gradle @@ -303,7 +303,10 @@ checkstyle { spotless { // optional: limit format enforcement to just the files changed by this feature branch - ratchetFrom 'master' + // ratchetFrom 'master' + + // Enable diff check + diffCheck('HEAD') format 'misc', { // define the files to apply `misc` to From 546b3ee00e4c3f8363fbdcf16e52b84348c8946a Mon Sep 17 00:00:00 2001 From: manticore-projects Date: Fri, 15 Dec 2023 16:14:49 +0700 Subject: [PATCH 40/77] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d9b14ef0e..cca58b4d2 100644 --- a/build.gradle +++ b/build.gradle @@ -306,7 +306,7 @@ spotless { // ratchetFrom 'master' // Enable diff check - diffCheck('HEAD') + spotlessDiffCheck('HEAD') format 'misc', { // define the files to apply `misc` to From 37b3c0bd08a33c608a3e8e29dc727f67d2fee173 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 15 Dec 2023 16:34:41 +0700 Subject: [PATCH 41/77] build: fix GitHub gradle actions --- .github/workflows/gradle.yml | 4 +++- build.gradle | 5 +---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 2bb2eac4a..14411e519 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,7 +22,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up JDK 11 uses: actions/setup-java@v3 with: diff --git a/build.gradle b/build.gradle index cca58b4d2..4974164dc 100644 --- a/build.gradle +++ b/build.gradle @@ -303,10 +303,7 @@ checkstyle { spotless { // optional: limit format enforcement to just the files changed by this feature branch - // ratchetFrom 'master' - - // Enable diff check - spotlessDiffCheck('HEAD') + ratchetFrom 'origin/master' format 'misc', { // define the files to apply `misc` to From 262ef55b1d3bd22c630fe30895e729f5de28f23c Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Fri, 15 Dec 2023 20:04:06 +0800 Subject: [PATCH 42/77] Fix: [MySQL] Add `COMMENT` for `ALTER ADD` (#1919) * Closed #1906, Add `COMMENT` for `ALTER ADD` and format jjt file * Closed #1906, Add `COMMENT` for `ALTER ADD` and format jjt file * Fix --- .../statement/create/table/Index.java | 14 ++ .../create/table/NamedConstraint.java | 13 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 138 ++++++++++-------- .../jsqlparser/statement/alter/AlterTest.java | 17 +++ 4 files changed, 116 insertions(+), 66 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java index 93b672c96..651bbe66c 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java @@ -26,6 +26,7 @@ public class Index implements Serializable { private List columns; private final List name = new ArrayList<>(); private List idxSpec; + private String commentText; public List getColumnsNames() { return columns.stream() @@ -142,6 +143,11 @@ public String toString() { return head; } + // MYSQL: ALTER TABLE ADD INDEX COMMENT 'comment' + if (getCommentText() != null) { + return head + " " + tail + " COMMENT " + getCommentText(); + } + return head + " " + tail; } @@ -192,4 +198,12 @@ public String toString() { return columnName + (params != null ? " " + String.join(" ", params) : ""); } } + + public String getCommentText() { + return commentText; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java index 8f188ebc8..b44b7a114 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java @@ -18,9 +18,16 @@ public class NamedConstraint extends Index { @Override public String toString() { String idxSpecText = PlainSelect.getStringList(getIndexSpec(), false, false); - return (getName() != null ? "CONSTRAINT " + getName() + " " : "") - + getType() + " " + PlainSelect.getStringList(getColumnsNames(), true, true) + (!"". - equals(idxSpecText) ? " " + idxSpecText : ""); + String head = getName() != null ? "CONSTRAINT " + getName() + " " : ""; + String tail = getType() + " " + PlainSelect.getStringList(getColumnsNames(), true, true) + + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); + + // MYSQL: ALTER TABLE ADD CONSTRAINT COMMENT 'comment' + if (getCommentText() != null) { + return head + tail + " COMMENT " + getCommentText(); + } + + return head + tail; } @Override diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 824f6d6ce..a8cda9de0 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -6160,6 +6160,18 @@ List AlterExpressionConstraintState(): } } +Index IndexWithComment(Index index): +{ + Token tk = null; +} +{ + tk= { + index.setCommentText(tk.image); + } + { + return index; + } +} /** * This production needs refactoring to multiple smaller productions. The target class should @@ -6191,17 +6203,15 @@ AlterExpression AlterExpression(): ( ( ( - { alterExp.setOperation(AlterOperation.ADD); } + { alterExp.setOperation(AlterOperation.ADD); } | - { alterExp.setOperation(AlterOperation.ALTER); } + { alterExp.setOperation(AlterOperation.ALTER); } | { alterExp.setOperation(AlterOperation.MODIFY); } ) ( - LOOKAHEAD(2) ( - columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); } - ) + LOOKAHEAD(2) ( columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); }) constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] @@ -6214,6 +6224,7 @@ AlterExpression AlterExpression(): index = new Index().withType(tk.image).withName(sk3).withColumnsNames(columnNames); alterExp.setIndex(index); } + [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] ) @@ -6223,47 +6234,50 @@ AlterExpression AlterExpression(): ( LOOKAHEAD(4) ( - "(" { alterExp.useBrackets(true);} + "(" + { alterExp.useBrackets(true);} alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } ( "," alterExpressionColumnDataType = AlterExpressionColumnDataType() { - alterExp.addColDataType(alterExpressionColumnDataType); - } + alterExp.addColDataType(alterExpressionColumnDataType); + } )* - ")" - ) + ")" + ) | - LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType() { - alterExp.addColDataType(alterExpressionColumnDataType); - } + LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType() + { alterExp.addColDataType(alterExpressionColumnDataType); } | - LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull() { - alterExp.addColDropNotNull( alterExpressionColumnDropNotNull); - } + LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull() + { alterExp.addColDropNotNull( alterExpressionColumnDropNotNull);} | - alterExpressionColumnDropDefault = AlterExpressionColumnDropDefault() { - alterExp.addColDropDefault( alterExpressionColumnDropDefault); - } + alterExpressionColumnDropDefault = AlterExpressionColumnDropDefault() + { alterExp.addColDropDefault( alterExpressionColumnDropDefault); } ) ) | ( "(" alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } - ("," alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } )* ")" + ("," + alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } + )* + ")" ) | ( (( { alterExp.setUk(true); } | ) (tk= | tk=) { alterExp.setUkName(tk.image); } )? columnNames=ColumnsNamesList() { alterExp.setUkColumns(columnNames); } - [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }]) + [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] + ) | //following two choices regarding foreign keys should be merged ( columnNames=ColumnsNamesList() { alterExp.setFkColumns(columnNames); columnNames = null; } /* tk= [ columnNames=ColumnsNamesList() ] - { alterExp.setFkSourceTable(tk.image); alterExp.setFkSourceColumns(columnNames); } + { alterExp.setFkSourceTable(tk.image); alterExp.setFkSourceColumns(columnNames); } */ fkTable=Table() [ columnNames=ColumnsNamesList() ] { @@ -6273,17 +6287,17 @@ AlterExpression AlterExpression(): } [LOOKAHEAD(2) ( (tk= | tk=) action = Action() - { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } - )] + { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] [LOOKAHEAD(2) ( (tk= | tk=) action = Action() - { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } - )] + { alterExp.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] ) | ( sk3=RelObjectName() - - ( ( tk= tk2= + ( + ( tk= tk2= columnNames=ColumnsNamesList() { fkIndex = new ForeignKeyIndex() @@ -6298,12 +6312,12 @@ AlterExpression AlterExpression(): alterExp.setIndex(fkIndex); } - [LOOKAHEAD(2) ( (tk= | tk=) action = Action() - { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } - )] - [LOOKAHEAD(2) ( (tk= | tk=) action = Action() - { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } - )] + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] + [LOOKAHEAD(2) ( (tk= | tk=) action = Action() + { fkIndex.setReferentialAction(ReferentialAction.Type.from(tk.image), action); } + )] constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } ) | @@ -6318,13 +6332,14 @@ AlterExpression AlterExpression(): } constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] ) | ( {Expression exp = null;} ("(" exp = Expression() ")")* { - CheckConstraint checkCs = new CheckConstraint().withName(sk3).withExpression(exp); - alterExp.setIndex(checkCs); - } + CheckConstraint checkCs = new CheckConstraint().withName(sk3).withExpression(exp); + alterExp.setIndex(checkCs); + } ) | ( @@ -6339,28 +6354,25 @@ AlterExpression AlterExpression(): } constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] ) | ( tk= - columnNames=ColumnsNamesList() - { - index = new NamedConstraint() - .withName(sk3) - .withType(tk.image) - .withColumnsNames(columnNames); - alterExp.setIndex(index); - } - constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + columnNames=ColumnsNamesList() + { + index = new NamedConstraint() + .withName(sk3) + .withType(tk.image) + .withColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } ) - ) + ) ) | - ( sk3=RelObjectName() - tk= { - alterExp.withColumnName(sk3).withCommentText(tk.image); - } - ) + ( sk3=RelObjectName() tk= { alterExp.withColumnName(sk3).withCommentText(tk.image); } ) ) ) | @@ -6373,7 +6385,7 @@ AlterExpression AlterExpression(): ) ) | - { alterExp.setOperation(AlterOperation.DROP); } + { alterExp.setOperation(AlterOperation.DROP); } ( ( ( @@ -6406,9 +6418,9 @@ AlterExpression AlterExpression(): ( ( tk= | tk= ) ( tk2= | tk2= ) { - index = new Index().withType(tk.image).withName(tk2.image); - alterExp.setIndex(index); - } + index = new Index().withType(tk.image).withName(tk2.image); + alterExp.setIndex(index); + } ) | ( @@ -6435,18 +6447,18 @@ AlterExpression AlterExpression(): ) ) | - ( - { + ( + { alterExp.setOperation(AlterOperation.ALGORITHM); } ["=" { alterExp.setUseEqual(true);} ] sk3 = RelObjectName() {alterExp.addParameters(sk3); } - ) + ) | - LOOKAHEAD(2) { alterExp.setOperation(AlterOperation.RENAME); } [ { alterExp.hasColumn(true);} ] - ( tk= | tk= ) { alterExp.setColOldName(tk.image); } + LOOKAHEAD(2) { alterExp.setOperation(AlterOperation.RENAME); } [ { alterExp.hasColumn(true);} ] + ( tk= | tk= ) { alterExp.setColOldName(tk.image); } - (tk2= | tk2=) { alterExp.setColumnName(tk2.image); } + (tk2= | tk2=) { alterExp.setColumnName(tk2.image); } | ( {alterExp.setOperation(AlterOperation.RENAME_TABLE);} @@ -6471,7 +6483,7 @@ AlterExpression AlterExpression(): } alterExp.setOptionalSpecifier( optionalSpecifier.toString() ); - } + } ) { diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 0a6dc77fd..881af7c2e 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -873,6 +873,23 @@ public void testAlterTableDropMultipleColumnsIfExists() throws JSQLParserExcepti "ALTER TABLE test DROP COLUMN IF EXISTS name, DROP COLUMN IF EXISTS surname"); } + @Test + public void testAlterTableAddIndexWithComment1906() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE `student` ADD KEY `idx_name` (`name`) COMMENT 'name'"); + } + + @Test + public void testAlterTableAddIndexWithComment2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key UNIQUE (id) COMMENT 'name'"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key UNIQUE KEY (c1, c2) COMMENT 'name'"); + + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE team_phases ADD CONSTRAINT team_phases_id_key PRIMARY KEY (id) COMMENT 'name'"); + } + @Test public void testAlterTableDropMultipleColumnsIfExistsWithParams() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( From 989a84bb215283b1d0775dac1b3e95346bf87126 Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Fri, 15 Dec 2023 22:58:02 +0800 Subject: [PATCH 43/77] feat: Support `RENAME INDEX` for MySQL, `RENAME CONSTRAINT` for PostgreSQL (#1920) * feat: Support `RENAME INDEX` for MySQL, `RENAME CONSTRAINT` for PostgreSQL * Fix --- .../statement/alter/AlterExpression.java | 1410 +++++++++-------- .../statement/alter/AlterOperation.java | 2 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 19 +- .../jsqlparser/statement/alter/AlterTest.java | 12 + 4 files changed, 751 insertions(+), 692 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index 2902c0833..084749e42 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -12,14 +12,12 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; - import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; - import net.sf.jsqlparser.statement.ReferentialAction; import net.sf.jsqlparser.statement.ReferentialAction.Action; import net.sf.jsqlparser.statement.ReferentialAction.Type; @@ -31,743 +29,777 @@ @SuppressWarnings({"PMD.CyclomaticComplexity"}) public class AlterExpression implements Serializable { - private AlterOperation operation; - private String optionalSpecifier; - private String newTableName; - private String columnName; - private String columnOldName; - // private ColDataType dataType; + private AlterOperation operation; + private String optionalSpecifier; + private String newTableName; + private String columnName; + private String columnOldName; + // private ColDataType dataType; - private List colDataTypeList; - private List columnDropNotNullList; + private List colDataTypeList; + private List columnDropNotNullList; - private List columnDropDefaultList; - - private List pkColumns; - private List ukColumns; - private String ukName; - private Index index = null; - private String constraintName; - private boolean usingIfExists; - - private Set referentialActions = new LinkedHashSet<>(2); - - private List fkColumns; - private String fkSourceSchema; - - private String fkSourceTable; - private List fkSourceColumns; - private boolean uk; - private boolean useEqual; - - private List constraints; - private List parameters; - private String commentText; - - private boolean hasColumn = false; - - - private boolean useBrackets=false; - - public boolean hasColumn() { - return hasColumn; - } - - public boolean useBrackets() { - return useBrackets; - } - - public void useBrackets(boolean useBrackets) { - this.useBrackets = useBrackets; - } - - public void hasColumn(boolean hasColumn) { - this.hasColumn = hasColumn; - } - - public String getFkSourceSchema() { - return fkSourceSchema; - } - - public void setFkSourceSchema(String fkSourceSchema) { - this.fkSourceSchema = fkSourceSchema; - } - - public String getCommentText() { - return commentText; - } - - public void setCommentText(String commentText) { - this.commentText = commentText; - } - - public AlterOperation getOperation() { - return operation; - } - - public void setOperation(AlterOperation operation) { - this.operation = operation; - } - - public String getOptionalSpecifier() { - return optionalSpecifier; - } - - public void setOptionalSpecifier(String optionalSpecifier) { - this.optionalSpecifier = optionalSpecifier; - } - - /** - * @param type - * @param action - */ - public void setReferentialAction(Type type, Action action) { - setReferentialAction(type, action, true); - } - - public AlterExpression withReferentialAction(Type type, Action action) { - setReferentialAction(type, action); - return this; - } - - /** @param type */ - public void removeReferentialAction(Type type) { - setReferentialAction(type, null, false); - } - - /** - * @param type - * @return - */ - public ReferentialAction getReferentialAction(Type type) { - return referentialActions.stream() - .filter(ra -> type.equals(ra.getType())) - .findFirst() - .orElse(null); - } - - private void setReferentialAction(Type type, Action action, boolean set) { - ReferentialAction found = getReferentialAction(type); - if (set) { - if (found == null) { - referentialActions.add(new ReferentialAction(type, action)); - } else { - found.setAction(action); - } - } else if (found != null) { - referentialActions.remove(found); - } - } - /** - * @return - * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} - */ - @Deprecated - public boolean isOnDeleteCascade() { - ReferentialAction found = getReferentialAction(Type.DELETE); - return found != null && Action.CASCADE.equals(found.getAction()); - } - - /** - * @param onDeleteCascade - * @deprecated use {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} - */ - @Deprecated - public void setOnDeleteCascade(boolean onDeleteCascade) { - setReferentialAction(Type.DELETE, Action.CASCADE, onDeleteCascade); - } - - /** - * @return - * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} - */ - @Deprecated - public boolean isOnDeleteRestrict() { - ReferentialAction found = getReferentialAction(Type.DELETE); - return found != null && Action.RESTRICT.equals(found.getAction()); - } - - /** - * @param onDeleteRestrict - * @deprecated use {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} - */ - @Deprecated - public void setOnDeleteRestrict(boolean onDeleteRestrict) { - setReferentialAction(Type.DELETE, Action.RESTRICT, onDeleteRestrict); - } - - /** - * @return - * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} - */ - @Deprecated - public boolean isOnDeleteSetNull() { - ReferentialAction found = getReferentialAction(Type.DELETE); - return found != null && Action.SET_NULL.equals(found.getAction()); - } - - /** - * @param onDeleteSetNull - * @deprecated use {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} - */ - @Deprecated - public void setOnDeleteSetNull(boolean onDeleteSetNull) { - setReferentialAction(Type.DELETE, Action.SET_NULL, onDeleteSetNull); - } - - public List getFkColumns() { - return fkColumns; - } - - public void setFkColumns(List fkColumns) { - this.fkColumns = fkColumns; - } - - public String getFkSourceTable() { - return fkSourceTable; - } - - public void setFkSourceTable(String fkSourceTable) { - this.fkSourceTable = fkSourceTable; - } - - public List getColDataTypeList() { - return colDataTypeList; - } - - public void addColDataType(String columnName, ColDataType colDataType) { - addColDataType(new ColumnDataType(columnName, false, colDataType, null)); - } - - public void addColDataType(ColumnDataType columnDataType) { - if (colDataTypeList == null) { - colDataTypeList = new ArrayList<>(); - } - colDataTypeList.add(columnDataType); - } - - public void addColDropNotNull(ColumnDropNotNull columnDropNotNull) { - if (columnDropNotNullList == null) { - columnDropNotNullList = new ArrayList<>(); - } - columnDropNotNullList.add(columnDropNotNull); - } - - public void addColDropDefault(ColumnDropDefault columnDropDefault) { - if (columnDropDefaultList == null) { - columnDropDefaultList = new ArrayList<>(); - } - columnDropDefaultList.add(columnDropDefault); - } - - public List getFkSourceColumns() { - return fkSourceColumns; - } - - public void setFkSourceColumns(List fkSourceColumns) { - this.fkSourceColumns = fkSourceColumns; - } - - public String getNewTableName() { - return newTableName; - } - - public void setNewTableName(String newTableName) { - this.newTableName = newTableName; - } - - public String getColumnName() { - return columnName; - } - - public void setColumnName(String columnName) { - this.columnName = columnName; - } - - @Deprecated - public String getColOldName() { - return getColumnOldName(); - } - - @Deprecated - public void setColOldName(String columnOldName) { - setColumnOldName(columnOldName); - } - - public String getColumnOldName() { - return columnOldName; - } - - public void setColumnOldName(String columnOldName) { - this.columnOldName = columnOldName; - } + private List columnDropDefaultList; - public String getConstraintName() { - return this.constraintName; - } + private List pkColumns; + private List ukColumns; + private String ukName; + private Index index = null; + private Index oldIndex = null; + private String constraintName; + private boolean usingIfExists; - public void setConstraintName(final String constraintName) { - this.constraintName = constraintName; - } + private Set referentialActions = new LinkedHashSet<>(2); - public boolean isUsingIfExists() { - return usingIfExists; - } + private List fkColumns; + private String fkSourceSchema; - public void setUsingIfExists(boolean usingIfExists) { - this.usingIfExists = usingIfExists; - } + private String fkSourceTable; + private List fkSourceColumns; + private boolean uk; + private boolean useEqual; - public List getPkColumns() { - return pkColumns; - } + private List constraints; + private List parameters; + private String commentText; - public void setPkColumns(List pkColumns) { - this.pkColumns = pkColumns; - } - - public List getUkColumns() { - return ukColumns; - } - - public void setUkColumns(List ukColumns) { - this.ukColumns = ukColumns; - } - - public String getUkName() { - return ukName; - } - - public void setUkName(String ukName) { - this.ukName = ukName; - } - - public Index getIndex() { - return index; - } - - public void setIndex(Index index) { - this.index = index; - } - - public List getConstraints() { - return constraints; - } - - public void setConstraints(List constraints) { - this.constraints = constraints; - } - - public List getColumnDropNotNullList() { - return columnDropNotNullList; - } - - public void addParameters(String... params) { - if (parameters == null) { - parameters = new ArrayList<>(); - } - parameters.addAll(Arrays.asList(params)); - } - - public List getParameters() { - return parameters; - } - - public boolean getUseEqual() { - return useEqual; - } - - public void setUseEqual(boolean useEqual) { - this.useEqual = useEqual; - } - - public boolean getUk() { - return uk; - } - - public void setUk(boolean uk) { - this.uk = uk; - } - - @Override - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", "PMD.ExcessiveMethodLength"}) - public String toString() { - - StringBuilder b = new StringBuilder(); - - if (operation== AlterOperation.UNSPECIFIC) { - b.append(optionalSpecifier); - } else if (operation== AlterOperation.RENAME_TABLE) { - - b.append("RENAME TO ").append(newTableName); - } else if (operation== AlterOperation.DROP_PRIMARY_KEY) { - - b.append("DROP PRIMARY KEY "); - } else if (operation== AlterOperation.DROP_UNIQUE) { - - b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')'); - } else if (operation== AlterOperation.DROP_FOREIGN_KEY) { - - b.append("DROP FOREIGN KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); - } else if (operation== AlterOperation.DROP && columnName==null && pkColumns!=null && pkColumns.size()>0) { - // Oracle Multi Column Drop - b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')'); - } else { - b.append(operation).append(" "); - - if (commentText != null) { - if (columnName != null) { - b.append(columnName).append(" COMMENT "); - } - b.append(commentText); - } else if (columnName != null) { - if (hasColumn) { - b.append("COLUMN "); - } - if (usingIfExists) { - b.append("IF EXISTS "); - } - if (operation == AlterOperation.RENAME) { - b.append(columnOldName).append(" TO "); - } - b.append(columnName); - } else if (getColDataTypeList() != null) { - if (operation == AlterOperation.CHANGE) { - if (optionalSpecifier != null) { - b.append(optionalSpecifier).append(" "); - } - b.append(columnOldName).append(" "); - } else if (colDataTypeList.size() > 1) { - b.append("("); - } else { - if (hasColumn) { - b.append("COLUMN "); - } - } - if (useBrackets && colDataTypeList.size() == 1){ - b.append(" ( "); - } - b.append(PlainSelect.getStringList(colDataTypeList)); - if (useBrackets && colDataTypeList.size() == 1 ){ - b.append(" ) "); - } - if (colDataTypeList.size() > 1) { - b.append(")"); - } - } else if (getColumnDropNotNullList() != null) { - b.append("COLUMN "); - b.append(PlainSelect.getStringList(columnDropNotNullList)); - } else if ( columnDropDefaultList != null && !columnDropDefaultList.isEmpty() ) { - b.append("COLUMN "); - b.append(PlainSelect.getStringList(columnDropDefaultList)); - } else if (constraintName != null) { - b.append("CONSTRAINT "); - if (usingIfExists) { - b.append("IF EXISTS "); - } - b.append(constraintName); - } else if (pkColumns != null) { - b.append("PRIMARY KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); - } else if (ukColumns != null) { - b.append("UNIQUE"); - if (ukName != null) { - if (getUk()) { - b.append(" KEY "); + private boolean hasColumn = false; + + + private boolean useBrackets = false; + + public Index getOldIndex() { + return oldIndex; + } + + public void setOldIndex(Index oldIndex) { + this.oldIndex = oldIndex; + } + + public boolean hasColumn() { + return hasColumn; + } + + public boolean useBrackets() { + return useBrackets; + } + + public void useBrackets(boolean useBrackets) { + this.useBrackets = useBrackets; + } + + public void hasColumn(boolean hasColumn) { + this.hasColumn = hasColumn; + } + + public String getFkSourceSchema() { + return fkSourceSchema; + } + + public void setFkSourceSchema(String fkSourceSchema) { + this.fkSourceSchema = fkSourceSchema; + } + + public String getCommentText() { + return commentText; + } + + public void setCommentText(String commentText) { + this.commentText = commentText; + } + + public AlterOperation getOperation() { + return operation; + } + + public void setOperation(AlterOperation operation) { + this.operation = operation; + } + + public String getOptionalSpecifier() { + return optionalSpecifier; + } + + public void setOptionalSpecifier(String optionalSpecifier) { + this.optionalSpecifier = optionalSpecifier; + } + + /** + * @param type + * @param action + */ + public void setReferentialAction(Type type, Action action) { + setReferentialAction(type, action, true); + } + + public AlterExpression withReferentialAction(Type type, Action action) { + setReferentialAction(type, action); + return this; + } + + /** + * @param type + */ + public void removeReferentialAction(Type type) { + setReferentialAction(type, null, false); + } + + /** + * @param type + * @return + */ + public ReferentialAction getReferentialAction(Type type) { + return referentialActions.stream() + .filter(ra -> type.equals(ra.getType())) + .findFirst() + .orElse(null); + } + + private void setReferentialAction(Type type, Action action, boolean set) { + ReferentialAction found = getReferentialAction(type); + if (set) { + if (found == null) { + referentialActions.add(new ReferentialAction(type, action)); } else { - b.append(" INDEX "); + found.setAction(action); } - b.append(ukName); - } - b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")"); - } else if (fkColumns != null) { - b.append("FOREIGN KEY (") - .append(PlainSelect.getStringList(fkColumns)) - .append(") REFERENCES ") - .append( - fkSourceSchema != null && fkSourceSchema.trim().length() > 0 - ? fkSourceSchema + "." - : "") - .append(fkSourceTable) - .append(" (") - .append(PlainSelect.getStringList(fkSourceColumns)) - .append(")"); - referentialActions.forEach(b::append); - } else if (index != null) { - b.append(index); + } else if (found != null) { + referentialActions.remove(found); } + } + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteCascade() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.CASCADE.equals(found.getAction()); + } + + /** + * @param onDeleteCascade + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteCascade(boolean onDeleteCascade) { + setReferentialAction(Type.DELETE, Action.CASCADE, onDeleteCascade); + } + + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteRestrict() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.RESTRICT.equals(found.getAction()); + } + + /** + * @param onDeleteRestrict + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteRestrict(boolean onDeleteRestrict) { + setReferentialAction(Type.DELETE, Action.RESTRICT, onDeleteRestrict); + } - if (getConstraints() != null && !getConstraints().isEmpty()) { - b.append(' ').append(PlainSelect.getStringList(constraints, false, false)); + /** + * @return + * @deprecated use {@link #getReferentialAction(ReferentialAction.Type)} + */ + @Deprecated + public boolean isOnDeleteSetNull() { + ReferentialAction found = getReferentialAction(Type.DELETE); + return found != null && Action.SET_NULL.equals(found.getAction()); + } + + /** + * @param onDeleteSetNull + * @deprecated use + * {@link #setReferentialAction(ReferentialAction.Type, ReferentialAction.Action, boolean)} + */ + @Deprecated + public void setOnDeleteSetNull(boolean onDeleteSetNull) { + setReferentialAction(Type.DELETE, Action.SET_NULL, onDeleteSetNull); + } + + public List getFkColumns() { + return fkColumns; + } + + public void setFkColumns(List fkColumns) { + this.fkColumns = fkColumns; + } + + public String getFkSourceTable() { + return fkSourceTable; + } + + public void setFkSourceTable(String fkSourceTable) { + this.fkSourceTable = fkSourceTable; + } + + public List getColDataTypeList() { + return colDataTypeList; + } + + public void addColDataType(String columnName, ColDataType colDataType) { + addColDataType(new ColumnDataType(columnName, false, colDataType, null)); + } + + public void addColDataType(ColumnDataType columnDataType) { + if (colDataTypeList == null) { + colDataTypeList = new ArrayList<>(); } - if (getUseEqual()) { - b.append('='); + colDataTypeList.add(columnDataType); + } + + public void addColDropNotNull(ColumnDropNotNull columnDropNotNull) { + if (columnDropNotNullList == null) { + columnDropNotNullList = new ArrayList<>(); } + columnDropNotNullList.add(columnDropNotNull); } - if (parameters != null && !parameters.isEmpty()) { - b.append(' ').append(PlainSelect.getStringList(parameters, false, false)); - } - - return b.toString(); - } - - public AlterExpression withOperation(AlterOperation operation) { - this.setOperation(operation); - return this; - } - - public AlterExpression withOptionalSpecifier(String optionalSpecifier) { - this.setOptionalSpecifier(optionalSpecifier); - return this; - } - - public AlterExpression withColumnName(String columnName) { - this.setColumnName(columnName); - return this; - } - - public AlterExpression withPkColumns(List pkColumns) { - this.setPkColumns(pkColumns); - return this; - } - - public AlterExpression withUkColumns(List ukColumns) { - this.setUkColumns(ukColumns); - return this; - } - - public AlterExpression withUkName(String ukName) { - this.setUkName(ukName); - return this; - } - - public AlterExpression withIndex(Index index) { - this.setIndex(index); - return this; - } - - public AlterExpression withConstraintName(String constraintName) { - this.setConstraintName(constraintName); - return this; - } - - public AlterExpression withUsingIfExists(boolean usingIfExists) { - this.setUsingIfExists(usingIfExists); - return this; - } - - public AlterExpression withOnDeleteRestrict(boolean onDeleteRestrict) { - this.setOnDeleteRestrict(onDeleteRestrict); - return this; - } - - public AlterExpression withOnDeleteSetNull(boolean onDeleteSetNull) { - this.setOnDeleteSetNull(onDeleteSetNull); - return this; - } - - public AlterExpression withOnDeleteCascade(boolean onDeleteCascade) { - this.setOnDeleteCascade(onDeleteCascade); - return this; - } - - public AlterExpression withFkColumns(List fkColumns) { - this.setFkColumns(fkColumns); - return this; - } - - public AlterExpression withFkSourceSchema(String fkSourceSchema) { - this.setFkSourceTable(fkSourceSchema); - return this; - } - - public AlterExpression withFkSourceTable(String fkSourceTable) { - this.setFkSourceTable(fkSourceTable); - return this; - } - - public AlterExpression withFkSourceColumns(List fkSourceColumns) { - this.setFkSourceColumns(fkSourceColumns); - return this; - } - - public AlterExpression withUk(boolean uk) { - this.setUk(uk); - return this; - } - - public AlterExpression withUseEqual(boolean useEqual) { - this.setUseEqual(useEqual); - return this; - } - - public AlterExpression withConstraints(List constraints) { - this.setConstraints(constraints); - return this; - } - - public AlterExpression withCommentText(String commentText) { - this.setCommentText(commentText); - return this; - } - - public AlterExpression withColumnOldName(String columnOldName) { - setColumnOldName(columnOldName); - return this; - } - - public AlterExpression addPkColumns(String... pkColumns) { - List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); - Collections.addAll(collection, pkColumns); - return this.withPkColumns(collection); - } - - public AlterExpression addPkColumns(Collection pkColumns) { - List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); - collection.addAll(pkColumns); - return this.withPkColumns(collection); - } - - public AlterExpression addUkColumns(String... ukColumns) { - List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); - Collections.addAll(collection, ukColumns); - return this.withUkColumns(collection); - } - - public AlterExpression addUkColumns(Collection ukColumns) { - List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); - collection.addAll(ukColumns); - return this.withUkColumns(collection); - } - - public AlterExpression addFkColumns(String... fkColumns) { - List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); - Collections.addAll(collection, fkColumns); - return this.withFkColumns(collection); - } - - public AlterExpression addFkColumns(Collection fkColumns) { - List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); - collection.addAll(fkColumns); - return this.withFkColumns(collection); - } - - public AlterExpression addFkSourceColumns(String... fkSourceColumns) { - List collection = Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); - Collections.addAll(collection, fkSourceColumns); - return this.withFkSourceColumns(collection); - } - - public AlterExpression addFkSourceColumns(Collection fkSourceColumns) { - List collection = Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); - collection.addAll(fkSourceColumns); - return this.withFkSourceColumns(collection); - } - - public AlterExpression addConstraints(ConstraintState... constraints) { - List collection = - Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); - Collections.addAll(collection, constraints); - return this.withConstraints(collection); - } - - public AlterExpression addConstraints(Collection constraints) { - List collection = - Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); - collection.addAll(constraints); - return this.withConstraints(collection); - } - - public static final class ColumnDataType extends ColumnDefinition { - - private final boolean withType; - - public ColumnDataType(boolean withType) { - super(); - this.withType = withType; - } - - public ColumnDataType( - String columnName, boolean withType, ColDataType colDataType, List columnSpecs) { - super(columnName, colDataType, columnSpecs); - this.withType = withType; + public void addColDropDefault(ColumnDropDefault columnDropDefault) { + if (columnDropDefaultList == null) { + columnDropDefaultList = new ArrayList<>(); + } + columnDropDefaultList.add(columnDropDefault); } - @Override - public String toString() { - return getColumnName() + (withType ? " TYPE " : " ") + toStringDataTypeAndSpec(); + public List getFkSourceColumns() { + return fkSourceColumns; } - @Override - public ColumnDataType withColDataType(ColDataType colDataType) { - return (ColumnDataType) super.withColDataType(colDataType); + public void setFkSourceColumns(List fkSourceColumns) { + this.fkSourceColumns = fkSourceColumns; } - @Override - public ColumnDataType withColumnName(String columnName) { - return (ColumnDataType) super.withColumnName(columnName); + public String getNewTableName() { + return newTableName; } - @Override - public ColumnDataType addColumnSpecs(String... columnSpecs) { - return (ColumnDataType) super.addColumnSpecs(columnSpecs); + public void setNewTableName(String newTableName) { + this.newTableName = newTableName; } - @Override - public ColumnDataType addColumnSpecs(Collection columnSpecs) { - return (ColumnDataType) super.addColumnSpecs(columnSpecs); + public String getColumnName() { + return columnName; } - @Override - public ColumnDataType withColumnSpecs(List columnSpecs) { - return (ColumnDataType) super.withColumnSpecs(columnSpecs); + public void setColumnName(String columnName) { + this.columnName = columnName; } - } - public static final class ColumnDropNotNull implements Serializable { + @Deprecated + public String getColOldName() { + return getColumnOldName(); + } - private final String columnName; - private final boolean withNot; + @Deprecated + public void setColOldName(String columnOldName) { + setColumnOldName(columnOldName); + } - public ColumnDropNotNull(String columnName) { - this(columnName, false); + public String getColumnOldName() { + return columnOldName; } - public ColumnDropNotNull(String columnName, boolean withNot) { - this.columnName = columnName; - this.withNot = withNot; + public void setColumnOldName(String columnOldName) { + this.columnOldName = columnOldName; } - public String getColumnName() { - return columnName; + public String getConstraintName() { + return this.constraintName; } - public boolean isWithNot() { - return withNot; + public void setConstraintName(final String constraintName) { + this.constraintName = constraintName; } - @Override - public String toString() { - return columnName + " DROP" + (withNot ? " NOT " : " ") + "NULL"; + public boolean isUsingIfExists() { + return usingIfExists; } - } - public static final class ColumnDropDefault implements Serializable { + public void setUsingIfExists(boolean usingIfExists) { + this.usingIfExists = usingIfExists; + } - private final String columnName; + public List getPkColumns() { + return pkColumns; + } - public ColumnDropDefault(String columnName) { - this.columnName = columnName; + public void setPkColumns(List pkColumns) { + this.pkColumns = pkColumns; } - public String getColumnName() { - return columnName; + public List getUkColumns() { + return ukColumns; + } + + public void setUkColumns(List ukColumns) { + this.ukColumns = ukColumns; + } + + public String getUkName() { + return ukName; + } + + public void setUkName(String ukName) { + this.ukName = ukName; + } + + public Index getIndex() { + return index; + } + + public void setIndex(Index index) { + this.index = index; + } + + public List getConstraints() { + return constraints; + } + + public void setConstraints(List constraints) { + this.constraints = constraints; + } + + public List getColumnDropNotNullList() { + return columnDropNotNullList; + } + + public void addParameters(String... params) { + if (parameters == null) { + parameters = new ArrayList<>(); + } + parameters.addAll(Arrays.asList(params)); + } + + public List getParameters() { + return parameters; + } + + public boolean getUseEqual() { + return useEqual; + } + + public void setUseEqual(boolean useEqual) { + this.useEqual = useEqual; + } + + public boolean getUk() { + return uk; + } + + public void setUk(boolean uk) { + this.uk = uk; } @Override + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", + "PMD.ExcessiveMethodLength", "PMD.SwitchStmtsShouldHaveDefault"}) public String toString() { - return columnName + " DROP DEFAULT"; + + StringBuilder b = new StringBuilder(); + + if (operation == AlterOperation.UNSPECIFIC) { + b.append(optionalSpecifier); + } else if (getOldIndex() != null) { + b.append("RENAME"); + switch (operation) { + case RENAME_KEY: + b.append(" KEY "); + break; + case RENAME_INDEX: + b.append(" INDEX "); + break; + case RENAME_CONSTRAINT: + b.append(" CONSTRAINT "); + break; + } + b.append(getOldIndex().getName()).append(" TO ").append(getIndex().getName()); + } else if (operation == AlterOperation.RENAME_TABLE) { + + b.append("RENAME TO ").append(newTableName); + } else if (operation == AlterOperation.DROP_PRIMARY_KEY) { + + b.append("DROP PRIMARY KEY "); + } else if (operation == AlterOperation.DROP_UNIQUE) { + + b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (operation == AlterOperation.DROP_FOREIGN_KEY) { + + b.append("DROP FOREIGN KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (operation == AlterOperation.DROP && columnName == null && pkColumns != null + && !pkColumns.isEmpty()) { + // Oracle Multi Column Drop + b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else { + b.append(operation).append(" "); + + if (commentText != null) { + if (columnName != null) { + b.append(columnName).append(" COMMENT "); + } + b.append(commentText); + } else if (columnName != null) { + if (hasColumn) { + b.append("COLUMN "); + } + if (usingIfExists) { + b.append("IF EXISTS "); + } + if (operation == AlterOperation.RENAME) { + b.append(columnOldName).append(" TO "); + } + b.append(columnName); + } else if (getColDataTypeList() != null) { + if (operation == AlterOperation.CHANGE) { + if (optionalSpecifier != null) { + b.append(optionalSpecifier).append(" "); + } + b.append(columnOldName).append(" "); + } else if (colDataTypeList.size() > 1) { + b.append("("); + } else { + if (hasColumn) { + b.append("COLUMN "); + } + } + if (useBrackets && colDataTypeList.size() == 1) { + b.append(" ( "); + } + b.append(PlainSelect.getStringList(colDataTypeList)); + if (useBrackets && colDataTypeList.size() == 1) { + b.append(" ) "); + } + if (colDataTypeList.size() > 1) { + b.append(")"); + } + } else if (getColumnDropNotNullList() != null) { + b.append("COLUMN "); + b.append(PlainSelect.getStringList(columnDropNotNullList)); + } else if (columnDropDefaultList != null && !columnDropDefaultList.isEmpty()) { + b.append("COLUMN "); + b.append(PlainSelect.getStringList(columnDropDefaultList)); + } else if (constraintName != null) { + b.append("CONSTRAINT "); + if (usingIfExists) { + b.append("IF EXISTS "); + } + b.append(constraintName); + } else if (pkColumns != null) { + b.append("PRIMARY KEY (").append(PlainSelect.getStringList(pkColumns)).append(')'); + } else if (ukColumns != null) { + b.append("UNIQUE"); + if (ukName != null) { + if (getUk()) { + b.append(" KEY "); + } else { + b.append(" INDEX "); + } + b.append(ukName); + } + b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")"); + } else if (fkColumns != null) { + b.append("FOREIGN KEY (") + .append(PlainSelect.getStringList(fkColumns)) + .append(") REFERENCES ") + .append( + fkSourceSchema != null && fkSourceSchema.trim().length() > 0 + ? fkSourceSchema + "." + : "") + .append(fkSourceTable) + .append(" (") + .append(PlainSelect.getStringList(fkSourceColumns)) + .append(")"); + referentialActions.forEach(b::append); + } else if (index != null) { + b.append(index); + } + + + if (getConstraints() != null && !getConstraints().isEmpty()) { + b.append(' ').append(PlainSelect.getStringList(constraints, false, false)); + } + if (getUseEqual()) { + b.append('='); + } + } + + if (parameters != null && !parameters.isEmpty()) { + b.append(' ').append(PlainSelect.getStringList(parameters, false, false)); + } + + return b.toString(); + } + + public AlterExpression withOperation(AlterOperation operation) { + this.setOperation(operation); + return this; + } + + public AlterExpression withOptionalSpecifier(String optionalSpecifier) { + this.setOptionalSpecifier(optionalSpecifier); + return this; + } + + public AlterExpression withColumnName(String columnName) { + this.setColumnName(columnName); + return this; + } + + public AlterExpression withPkColumns(List pkColumns) { + this.setPkColumns(pkColumns); + return this; + } + + public AlterExpression withUkColumns(List ukColumns) { + this.setUkColumns(ukColumns); + return this; + } + + public AlterExpression withUkName(String ukName) { + this.setUkName(ukName); + return this; + } + + public AlterExpression withIndex(Index index) { + this.setIndex(index); + return this; + } + + public AlterExpression withConstraintName(String constraintName) { + this.setConstraintName(constraintName); + return this; + } + + public AlterExpression withUsingIfExists(boolean usingIfExists) { + this.setUsingIfExists(usingIfExists); + return this; + } + + public AlterExpression withOnDeleteRestrict(boolean onDeleteRestrict) { + this.setOnDeleteRestrict(onDeleteRestrict); + return this; + } + + public AlterExpression withOnDeleteSetNull(boolean onDeleteSetNull) { + this.setOnDeleteSetNull(onDeleteSetNull); + return this; + } + + public AlterExpression withOnDeleteCascade(boolean onDeleteCascade) { + this.setOnDeleteCascade(onDeleteCascade); + return this; + } + + public AlterExpression withFkColumns(List fkColumns) { + this.setFkColumns(fkColumns); + return this; + } + + public AlterExpression withFkSourceSchema(String fkSourceSchema) { + this.setFkSourceTable(fkSourceSchema); + return this; + } + + public AlterExpression withFkSourceTable(String fkSourceTable) { + this.setFkSourceTable(fkSourceTable); + return this; + } + + public AlterExpression withFkSourceColumns(List fkSourceColumns) { + this.setFkSourceColumns(fkSourceColumns); + return this; + } + + public AlterExpression withUk(boolean uk) { + this.setUk(uk); + return this; + } + + public AlterExpression withUseEqual(boolean useEqual) { + this.setUseEqual(useEqual); + return this; + } + + public AlterExpression withConstraints(List constraints) { + this.setConstraints(constraints); + return this; + } + + public AlterExpression withCommentText(String commentText) { + this.setCommentText(commentText); + return this; + } + + public AlterExpression withColumnOldName(String columnOldName) { + setColumnOldName(columnOldName); + return this; + } + + public AlterExpression addPkColumns(String... pkColumns) { + List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, pkColumns); + return this.withPkColumns(collection); + } + + public AlterExpression addPkColumns(Collection pkColumns) { + List collection = Optional.ofNullable(getPkColumns()).orElseGet(ArrayList::new); + collection.addAll(pkColumns); + return this.withPkColumns(collection); + } + + public AlterExpression addUkColumns(String... ukColumns) { + List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, ukColumns); + return this.withUkColumns(collection); + } + + public AlterExpression addUkColumns(Collection ukColumns) { + List collection = Optional.ofNullable(getUkColumns()).orElseGet(ArrayList::new); + collection.addAll(ukColumns); + return this.withUkColumns(collection); + } + + public AlterExpression addFkColumns(String... fkColumns) { + List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, fkColumns); + return this.withFkColumns(collection); + } + + public AlterExpression addFkColumns(Collection fkColumns) { + List collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new); + collection.addAll(fkColumns); + return this.withFkColumns(collection); + } + + public AlterExpression addFkSourceColumns(String... fkSourceColumns) { + List collection = + Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); + Collections.addAll(collection, fkSourceColumns); + return this.withFkSourceColumns(collection); + } + + public AlterExpression addFkSourceColumns(Collection fkSourceColumns) { + List collection = + Optional.ofNullable(getFkSourceColumns()).orElseGet(ArrayList::new); + collection.addAll(fkSourceColumns); + return this.withFkSourceColumns(collection); + } + + public AlterExpression addConstraints(ConstraintState... constraints) { + List collection = + Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); + Collections.addAll(collection, constraints); + return this.withConstraints(collection); + } + + public AlterExpression addConstraints(Collection constraints) { + List collection = + Optional.ofNullable(getConstraints()).orElseGet(ArrayList::new); + collection.addAll(constraints); + return this.withConstraints(collection); + } + + public static final class ColumnDataType extends ColumnDefinition { + + private final boolean withType; + + public ColumnDataType(boolean withType) { + super(); + this.withType = withType; + } + + public ColumnDataType( + String columnName, boolean withType, ColDataType colDataType, + List columnSpecs) { + super(columnName, colDataType, columnSpecs); + this.withType = withType; + } + + @Override + public String toString() { + return getColumnName() + (withType ? " TYPE " : " ") + toStringDataTypeAndSpec(); + } + + @Override + public ColumnDataType withColDataType(ColDataType colDataType) { + return (ColumnDataType) super.withColDataType(colDataType); + } + + @Override + public ColumnDataType withColumnName(String columnName) { + return (ColumnDataType) super.withColumnName(columnName); + } + + @Override + public ColumnDataType addColumnSpecs(String... columnSpecs) { + return (ColumnDataType) super.addColumnSpecs(columnSpecs); + } + + @Override + public ColumnDataType addColumnSpecs(Collection columnSpecs) { + return (ColumnDataType) super.addColumnSpecs(columnSpecs); + } + + @Override + public ColumnDataType withColumnSpecs(List columnSpecs) { + return (ColumnDataType) super.withColumnSpecs(columnSpecs); + } + } + + public static final class ColumnDropNotNull implements Serializable { + + private final String columnName; + private final boolean withNot; + + public ColumnDropNotNull(String columnName) { + this(columnName, false); + } + + public ColumnDropNotNull(String columnName, boolean withNot) { + this.columnName = columnName; + this.withNot = withNot; + } + + public String getColumnName() { + return columnName; + } + + public boolean isWithNot() { + return withNot; + } + + @Override + public String toString() { + return columnName + " DROP" + (withNot ? " NOT " : " ") + "NULL"; + } + } + + public static final class ColumnDropDefault implements Serializable { + + private final String columnName; + + public ColumnDropDefault(String columnName) { + this.columnName = columnName; + } + + public String getColumnName() { + return columnName; + } + + @Override + public String toString() { + return columnName + " DROP DEFAULT"; + } } - } } diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java index bb13956f4..d1791cc8b 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java @@ -10,7 +10,7 @@ package net.sf.jsqlparser.statement.alter; public enum AlterOperation { - ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, COMMENT, UNSPECIFIC; + ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, UNSPECIFIC; public static AlterOperation from(String operation) { return Enum.valueOf(AlterOperation.class, operation.toUpperCase()); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index a8cda9de0..6e6cce16b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -6460,15 +6460,30 @@ AlterExpression AlterExpression(): (tk2= | tk2=) { alterExp.setColumnName(tk2.image); } | - ( + LOOKAHEAD(2)( {alterExp.setOperation(AlterOperation.RENAME_TABLE);} - (tk2= | tk2=) { alterExp.setNewTableName(tk2.image);} + (tk2= | tk2=) { alterExp.setNewTableName(tk2.image);} ) | ( {alterExp.setOperation(AlterOperation.COMMENT);} tk= { alterExp.setCommentText(tk.image); } ) | + LOOKAHEAD(2) + ( (( {alterExp.setOperation(AlterOperation.RENAME_INDEX);} + | {alterExp.setOperation(AlterOperation.RENAME_KEY);}) + | { alterExp.setOperation(AlterOperation.RENAME_CONSTRAINT); } + ) + (tk= | tk=){ + alterExp.setOldIndex(new Index().withName(tk.image)); + } + + (tk2= | tk2=){ + index = new Index().withName(tk2.image); + alterExp.setIndex(index); + } + ) + | tokens = captureRest() { alterExp.setOperation(AlterOperation.UNSPECIFIC); StringBuilder optionalSpecifier = new StringBuilder(); diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 881af7c2e..0821224f8 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -83,6 +83,18 @@ public void testAlterTableBackBrackets() throws JSQLParserException { } + @Test + public void testAlterTableIssue1815() throws JSQLParserException { + // MySQL: see https://dev.mysql.com/doc/refman/8.0/en/alter-table.html + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE cers_record_10 RENAME INDEX idx_cers_record_1_gmtcreate TO idx_cers_record_10_gmtcreate"); + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE cers_record_10 RENAME KEY k_cers_record_1_gmtcreate TO k_cers_record_10_gmtcreate"); + // PostgreSQL: see https://www.postgresql.org/docs/current/sql-altertable.html + assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE cers_record_10 RENAME CONSTRAINT cst_cers_record_1_gmtcreate TO cst_cers_record_10_gmtcreate"); + } + @Test public void testAlterTablePrimaryKey() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE animals ADD PRIMARY KEY (id)"); From a35fbe77b33a07b2378fc4cbca7af53f50ce1cbc Mon Sep 17 00:00:00 2001 From: manticore-projects Date: Sat, 16 Dec 2023 11:42:01 +0700 Subject: [PATCH 44/77] Update sphinx.yml --- .github/workflows/sphinx.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index 1840bbe95..a97cdbc9b 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -10,8 +10,7 @@ on: permissions: write-all jobs: docs: - if_merged: - if: github.event.pull_request.merged == true + if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - uses: actions/setup-python@v4 From 313a4b42444b2d251f5b8c54f951dbfbbb32be6f Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Sat, 16 Dec 2023 15:07:49 +0800 Subject: [PATCH 45/77] feat: [MySQL] Support `TABLE STATEMENT` (#1921) * feat: Support `TABLE STATEMENT` * feat: Support `TABLE STATEMENT` * fmt code * Fix --- .../sf/jsqlparser/parser/feature/Feature.java | 5 + .../statement/select/SelectVisitor.java | 2 + .../select/SelectVisitorAdapter.java | 5 + .../statement/select/TableStatement.java | 59 +++++++++++ .../sf/jsqlparser/util/AddAliasesVisitor.java | 11 ++- .../util/ConnectExpressionsVisitor.java | 10 +- .../sf/jsqlparser/util/TablesNamesFinder.java | 6 ++ .../util/deparser/SelectDeParser.java | 15 ++- .../util/deparser/TableStatementDeParser.java | 98 +++++++++++++++++++ .../validation/feature/FeaturesAllowed.java | 83 +++++++++------- .../util/validation/feature/MySqlVersion.java | 1 + .../validation/validator/SelectValidator.java | 9 +- .../validator/TableStatementValidator.java | 27 +++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 20 ++++ .../parser/CCJSqlParserUtilTest.java | 22 +++-- .../statement/select/SelectTest.java | 59 ++++++----- .../util/deparser/StatementDeParserTest.java | 27 +++-- .../TableStatementValidatorTest.java | 38 +++++++ 18 files changed, 412 insertions(+), 85 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/select/TableStatement.java create mode 100644 src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java create mode 100644 src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java create mode 100644 src/test/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidatorTest.java diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index b59191844..956d636b5 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -306,6 +306,11 @@ public enum Feature { */ values, + /** + * SQL "TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]“ + */ + tableStatement, + /** * SQL "UPDATE" statement is allowed * diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java index 1d3643f50..04a6b9d9e 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitor.java @@ -22,4 +22,6 @@ public interface SelectVisitor { void visit(Values aThis); void visit(LateralSubSelect lateralSubSelect); + + void visit(TableStatement tableStatement); } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java index 91c6f32c2..a349a9005 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/SelectVisitorAdapter.java @@ -41,4 +41,9 @@ public void visit(Values aThis) { public void visit(LateralSubSelect lateralSubSelect) { } + + @Override + public void visit(TableStatement tableStatement) { + + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/TableStatement.java b/src/main/java/net/sf/jsqlparser/statement/select/TableStatement.java new file mode 100644 index 000000000..b665b16a6 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/TableStatement.java @@ -0,0 +1,59 @@ +/*- + * #%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.statement.select; + +import net.sf.jsqlparser.schema.Table; + +/** + * @see `TABLE table_name [ORDER + * BY column_name] [LIMIT number [OFFSET number]]` Union not currently supported + * + * @author jxnu-liguobin + */ +public class TableStatement extends Select { + + private Table table; + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + @Override + public StringBuilder appendSelectBodyTo(StringBuilder builder) { + builder.append("TABLE ").append(table.getName()); + return builder; + } + + @SuppressWarnings({"PMD.CyclomaticComplexity"}) + @Override + public StringBuilder appendTo(StringBuilder builder) { + + appendSelectBodyTo(builder); + + builder.append(orderByToString(false, orderByElements)); + + if (limit != null) { + builder.append(limit); + } + if (offset != null) { + builder.append(offset); + } + return builder; + } + + @Override + public void accept(SelectVisitor selectVisitor) { + selectVisitor.visit(this); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/AddAliasesVisitor.java b/src/main/java/net/sf/jsqlparser/util/AddAliasesVisitor.java index 3303c091f..58ca4326f 100644 --- a/src/main/java/net/sf/jsqlparser/util/AddAliasesVisitor.java +++ b/src/main/java/net/sf/jsqlparser/util/AddAliasesVisitor.java @@ -9,6 +9,8 @@ */ package net.sf.jsqlparser.util; +import java.util.LinkedList; +import java.util.List; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.statement.select.LateralSubSelect; import net.sf.jsqlparser.statement.select.ParenthesedSelect; @@ -18,12 +20,10 @@ import net.sf.jsqlparser.statement.select.SelectItemVisitor; import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.Values; import net.sf.jsqlparser.statement.select.WithItem; -import java.util.LinkedList; -import java.util.List; - /** * Add aliases to every column and expression selected by a select - statement. Existing aliases are * recognized and preserved. This class standard uses a prefix of A and a counter to generate new @@ -112,4 +112,9 @@ public void visit(Values aThis) { public void visit(LateralSubSelect lateralSubSelect) { lateralSubSelect.getSelect().accept(this); } + + @Override + public void visit(TableStatement tableStatement) { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/src/main/java/net/sf/jsqlparser/util/ConnectExpressionsVisitor.java b/src/main/java/net/sf/jsqlparser/util/ConnectExpressionsVisitor.java index 479bc3c3e..46b746f0a 100644 --- a/src/main/java/net/sf/jsqlparser/util/ConnectExpressionsVisitor.java +++ b/src/main/java/net/sf/jsqlparser/util/ConnectExpressionsVisitor.java @@ -9,6 +9,8 @@ */ package net.sf.jsqlparser.util; +import java.util.LinkedList; +import java.util.List; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.BinaryExpression; import net.sf.jsqlparser.statement.select.LateralSubSelect; @@ -19,12 +21,10 @@ import net.sf.jsqlparser.statement.select.SelectItemVisitor; import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.Values; import net.sf.jsqlparser.statement.select.WithItem; -import java.util.LinkedList; -import java.util.List; - /** * Connect all selected expressions with a binary expression. Out of select a,b from table one gets * select a || b as expr from table. The type of binary expression is set by overwriting this class @@ -103,4 +103,8 @@ public void visit(Values aThis) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public void visit(TableStatement tableStatement) { + throw new UnsupportedOperationException("Not supported yet."); + } } diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 44f0859a5..b3b697e4d 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -167,6 +167,7 @@ import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.TableFunction; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.Values; import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.show.ShowIndexStatement; @@ -691,6 +692,11 @@ public void visit(LateralSubSelect lateralSubSelect) { lateralSubSelect.getSelect().accept((SelectVisitor) this); } + @Override + public void visit(TableStatement tableStatement) { + tableStatement.getTable().accept(this); + } + /** * Initializes table names collector. Important is the usage of Column instances to find table * names. This is only allowed for expression parsing, where a better place for tablenames could 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 75aac7a9e..7c4c0e1fb 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -9,6 +9,10 @@ */ package net.sf.jsqlparser.util.deparser; +import static java.util.stream.Collectors.joining; + +import java.util.Iterator; +import java.util.List; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; @@ -40,16 +44,12 @@ import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.Skip; import net.sf.jsqlparser.statement.select.TableFunction; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.Top; import net.sf.jsqlparser.statement.select.UnPivot; import net.sf.jsqlparser.statement.select.Values; import net.sf.jsqlparser.statement.select.WithItem; -import java.util.Iterator; -import java.util.List; - -import static java.util.stream.Collectors.joining; - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public class SelectDeParser extends AbstractDeParser implements SelectVisitor, SelectItemVisitor, FromItemVisitor, PivotVisitor { @@ -589,6 +589,11 @@ public void visit(LateralSubSelect lateralSubSelect) { visit((ParenthesedSelect) lateralSubSelect); } + @Override + public void visit(TableStatement tableStatement) { + new TableStatementDeParser(expressionVisitor, buffer).deParse(tableStatement); + } + @Override public void visit(TableFunction tableFunction) { buffer.append(tableFunction.toString()); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java new file mode 100644 index 000000000..962849c6e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/deparser/TableStatementDeParser.java @@ -0,0 +1,98 @@ +/*- + * #%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.util.deparser; + +import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.statement.select.LateralSubSelect; +import net.sf.jsqlparser.statement.select.Offset; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.select.TableStatement; +import net.sf.jsqlparser.statement.select.Values; +import net.sf.jsqlparser.statement.select.WithItem; + +/** + * @author jxnu-liguobin + */ +public class TableStatementDeParser extends AbstractDeParser + implements SelectVisitor { + + private final ExpressionVisitor expressionVisitor; + + public TableStatementDeParser(ExpressionVisitor expressionVisitor, StringBuilder buffer) { + super(buffer); + this.expressionVisitor = expressionVisitor; + } + + @Override + public void deParse(TableStatement tableStatement) { + tableStatement.accept(this); + } + + public void visit(Offset offset) { + buffer.append(" OFFSET "); + offset.getOffset().accept(expressionVisitor); + if (offset.getOffsetParam() != null) { + buffer.append(" ").append(offset.getOffsetParam()); + } + + } + + @Override + public void visit(ParenthesedSelect parenthesedSelect) { + + } + + @Override + public void visit(PlainSelect plainSelect) { + + } + + @Override + public void visit(SetOperationList setOpList) { + + } + + @Override + public void visit(WithItem withItem) { + + } + + @Override + public void visit(Values aThis) { + + } + + @Override + public void visit(LateralSubSelect lateralSubSelect) { + + } + + @Override + public void visit(TableStatement tableStatement) { + buffer.append("TABLE "); + buffer.append(tableStatement.getTable()); + if (tableStatement.getOrderByElements() != null) { + new OrderByDeParser(expressionVisitor, buffer) + .deParse(tableStatement.getOrderByElements()); + } + + if (tableStatement.getLimit() != null) { + new LimitDeparser(expressionVisitor, buffer).deParse(tableStatement.getLimit()); + } + if (tableStatement.getOffset() != null) { + visit(tableStatement.getOffset()); + } + + // TODO UNION + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java index 04c9a1277..3ada0ec28 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/FeaturesAllowed.java @@ -38,13 +38,14 @@ public class FeaturesAllowed implements FeatureSetValidation, ModifyableFeatureS Feature.jdbcParameter, Feature.jdbcNamedParameter).unmodifyable(); - public static final FeaturesAllowed EXPRESSIONS = new FeaturesAllowed("EXPRESSIONS", Feature.exprLike, - Feature.exprSimilarTo); + public static final FeaturesAllowed EXPRESSIONS = + new FeaturesAllowed("EXPRESSIONS", Feature.exprLike, + Feature.exprSimilarTo); /** * all {@link Feature}' within SQL SELECT without modification features like - * {@link Feature#selectInto}, but jdbc-features like - * {@link Feature#jdbcParameter} and {@link Feature#jdbcNamedParameter} + * {@link Feature#selectInto}, but jdbc-features like {@link Feature#jdbcParameter} and + * {@link Feature#jdbcNamedParameter} */ public static final FeaturesAllowed SELECT = new FeaturesAllowed("SELECT", // select features @@ -86,22 +87,25 @@ public class FeaturesAllowed implements FeatureSetValidation, ModifyableFeatureS Feature.distinctOn, Feature.orderBy, Feature.orderByNullOrdering, + Feature.tableStatement, Feature.function).unmodifyable(); /** - * all {@link Feature}' for SQL INSERT including {@link #SELECT} and - * {@link Feature#selectInto} + * all {@link Feature}' for SQL INSERT including {@link #SELECT} and {@link Feature#selectInto} */ - public static final FeaturesAllowed INSERT = new FeaturesAllowed("INSERT", Feature.insert, Feature.insertFromSelect, - Feature.insertModifierIgnore, Feature.insertModifierPriority, Feature.insertReturningAll, - Feature.insertReturningExpressionList, Feature.insertUseSet, - Feature.insertValues, Feature.selectInto).add(SELECT).unmodifyable(); + public static final FeaturesAllowed INSERT = + new FeaturesAllowed("INSERT", Feature.insert, Feature.insertFromSelect, + Feature.insertModifierIgnore, Feature.insertModifierPriority, + Feature.insertReturningAll, + Feature.insertReturningExpressionList, Feature.insertUseSet, + Feature.insertValues, Feature.selectInto).add(SELECT).unmodifyable(); /** * all {@link Feature}' for SQL UPDATE including {@link #SELECT} */ - public static final FeaturesAllowed UPDATE = new FeaturesAllowed("UPDATE", Feature.update, Feature.updateJoins, + public static final FeaturesAllowed UPDATE = new FeaturesAllowed("UPDATE", Feature.update, + Feature.updateJoins, Feature.updateFrom, Feature.updateLimit, Feature.updateOrderBy, Feature.updateReturning, Feature.updateUseSelect) .add(SELECT).unmodifyable(); @@ -109,55 +113,66 @@ public class FeaturesAllowed implements FeatureSetValidation, ModifyableFeatureS /** * all {@link Feature}' for SQL UPDATE including {@link #SELECT} */ - public static final FeaturesAllowed DELETE = new FeaturesAllowed("DELETE", Feature.delete, Feature.deleteJoin, - Feature.deleteLimit, Feature.deleteOrderBy, Feature.deleteTables, Feature.deleteReturningExpressionList, - Feature.truncate) - .add(SELECT).unmodifyable(); + public static final FeaturesAllowed DELETE = + new FeaturesAllowed("DELETE", Feature.delete, Feature.deleteJoin, + Feature.deleteLimit, Feature.deleteOrderBy, Feature.deleteTables, + Feature.deleteReturningExpressionList, + Feature.truncate) + .add(SELECT).unmodifyable(); /** * all {@link Feature}' for SQL MERGE other similar commands */ - public static final FeaturesAllowed MERGE = new FeaturesAllowed("MERGE", Feature.merge, Feature.upsert, - Feature.insertUseDuplicateKeyUpdate).unmodifyable(); + public static final FeaturesAllowed MERGE = + new FeaturesAllowed("MERGE", Feature.merge, Feature.upsert, + Feature.insertUseDuplicateKeyUpdate).unmodifyable(); /** * all DML {@link Feature}'s */ - public static final FeaturesAllowed DML = new FeaturesAllowed("DML").add(SELECT, INSERT, UPDATE, DELETE, MERGE) - .unmodifyable(); + public static final FeaturesAllowed DML = + new FeaturesAllowed("DML").add(SELECT, INSERT, UPDATE, DELETE, MERGE) + .unmodifyable(); - public static final FeaturesAllowed EXECUTE = new FeaturesAllowed("EXECUTE", Feature.execute).unmodifyable(); + public static final FeaturesAllowed EXECUTE = + new FeaturesAllowed("EXECUTE", Feature.execute).unmodifyable(); /** * all "CREATE" {@link Feature}'s */ public static final FeaturesAllowed CREATE = new FeaturesAllowed("CREATE", Feature.createIndex, - Feature.createSchema, Feature.createSequence, Feature.createTable, Feature.createTableUnlogged, + Feature.createSchema, Feature.createSequence, Feature.createTable, + Feature.createTableUnlogged, Feature.createTableCreateOptionStrings, Feature.createTableTableOptionStrings, - Feature.createTableIfNotExists, Feature.createTableRowMovement, Feature.createTableFromSelect, + Feature.createTableIfNotExists, Feature.createTableRowMovement, + Feature.createTableFromSelect, Feature.createTrigger, Feature.createView).unmodifyable(); /** * all "ALTER" {@link Feature}'s */ - public static final FeaturesAllowed ALTER = new FeaturesAllowed("ALTER", Feature.alterTable, Feature.alterSequence, - Feature.alterView, Feature.alterIndex) - .unmodifyable(); + public static final FeaturesAllowed ALTER = + new FeaturesAllowed("ALTER", Feature.alterTable, Feature.alterSequence, + Feature.alterView, Feature.alterIndex) + .unmodifyable(); /** * all "DROP" {@link Feature}'s */ - public static final FeaturesAllowed DROP = new FeaturesAllowed("DROP", Feature.drop, Feature.dropTable, - Feature.dropIndex, Feature.dropView, Feature.dropSchema, Feature.dropSequence, Feature.dropTableIfExists, - Feature.dropIndexIfExists, Feature.dropViewIfExists, Feature.dropSchemaIfExists, - Feature.dropSequenceIfExists) - .unmodifyable(); + public static final FeaturesAllowed DROP = + new FeaturesAllowed("DROP", Feature.drop, Feature.dropTable, + Feature.dropIndex, Feature.dropView, Feature.dropSchema, Feature.dropSequence, + Feature.dropTableIfExists, + Feature.dropIndexIfExists, Feature.dropViewIfExists, Feature.dropSchemaIfExists, + Feature.dropSequenceIfExists) + .unmodifyable(); /** * all DDL {@link Feature}'s */ - public static final FeaturesAllowed DDL = new FeaturesAllowed("DDL").add(CREATE, ALTER, DROP).unmodifyable(); + public static final FeaturesAllowed DDL = + new FeaturesAllowed("DDL").add(CREATE, ALTER, DROP).unmodifyable(); private Set names = new LinkedHashSet<>(); private Set features = new HashSet<>(); @@ -278,7 +293,8 @@ public ValidationException getMessage(Feature feature) { @Override public String getName() { - return names.isEmpty() ? FeatureSetValidation.super.getName() : names.stream().collect(Collectors.joining(SEPERATOR)); + return names.isEmpty() ? FeatureSetValidation.super.getName() + : names.stream().collect(Collectors.joining(SEPERATOR)); } @@ -289,7 +305,8 @@ public Set getFeatures() { private List collectNames(FeatureSetValidation fs) { String name = fs.getName(); - return Stream.of(name.split(SEPERATOR_REGEX)).map(String::trim).collect(Collectors.toList()); + return Stream.of(name.split(SEPERATOR_REGEX)).map(String::trim) + .collect(Collectors.toList()); } } diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java index d631344d8..3486261ed 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java @@ -60,6 +60,7 @@ public enum MySqlVersion implements Version { Feature.insert, Feature.insertValues, Feature.values, + Feature.tableStatement, Feature.insertFromSelect, Feature.insertUseSet, Feature.insertModifierPriority, Feature.insertModifierIgnore, Feature.insertUseDuplicateKeyUpdate, 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 88166c1b5..3cba4a988 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 @@ -9,6 +9,7 @@ */ package net.sf.jsqlparser.util.validation.validator; +import java.util.List; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.MySQLIndexHint; import net.sf.jsqlparser.expression.SQLServerHints; @@ -33,6 +34,7 @@ import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.TableFunction; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.UnPivot; import net.sf.jsqlparser.statement.select.UnionOp; import net.sf.jsqlparser.statement.select.Values; @@ -41,8 +43,6 @@ import net.sf.jsqlparser.util.validation.ValidationUtil; import net.sf.jsqlparser.util.validation.metadata.NamedObject; -import java.util.List; - /** * @author gitmotte */ @@ -306,6 +306,11 @@ public void visit(LateralSubSelect lateralSubSelect) { validateOptional(lateralSubSelect.getSelect(), e -> e.accept(this)); } + @Override + public void visit(TableStatement tableStatement) { + getValidator(TableStatementValidator.class).validate(tableStatement); + } + @Override public void visit(TableFunction tableFunction) { validateFeature(Feature.tableFunction); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java new file mode 100644 index 000000000..4b954126e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidator.java @@ -0,0 +1,27 @@ +/*- + * #%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.util.validation.validator; + +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.statement.select.TableStatement; +import net.sf.jsqlparser.util.validation.ValidationCapability; + +/** + * @author jxnu-liguobin + */ +public class TableStatementValidator extends AbstractValidator { + + @Override + public void validate(TableStatement statement) { + for (ValidationCapability c : getCapabilities()) { + validateFeature(c, Feature.tableStatement); + } + } +} diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 6e6cce16b..547324944 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -676,6 +676,8 @@ Statement SingleStatement() : | stm = Select() | + stm = TableStatement() + | LOOKAHEAD(3) stm = Upsert() | LOOKAHEAD(2) stm = Alter() @@ -2002,6 +2004,24 @@ Select Select() #Select: } } +TableStatement TableStatement(): +{ + Table table = null; + List orderByElements = null; + Limit limit = null; + Offset offset = null; + TableStatement tableStatement = new TableStatement(); +}{ + + table = Table() + { tableStatement.setTable(table); } + [ LOOKAHEAD( ) orderByElements = OrderByElements() { tableStatement.setOrderByElements(orderByElements); } ] + [ LOOKAHEAD() limit=LimitWithOffset() { tableStatement.setLimit(limit);} ] + [ LOOKAHEAD() offset = Offset() { tableStatement.setOffset(offset);} ] + { return tableStatement; } + /* Support operationList */ +} + ParenthesedSelect ParenthesedSelect() #ParenthesedSelect: { ParenthesedSelect parenthesedSelect = new ParenthesedSelect(); diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java index 0b78f60d0..f5010099e 100644 --- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java @@ -9,6 +9,12 @@ */ package net.sf.jsqlparser.parser; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.ByteArrayInputStream; import java.io.StringReader; import java.nio.charset.StandardCharsets; @@ -19,7 +25,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; - import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; @@ -29,15 +34,9 @@ import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.Statements; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - import net.sf.jsqlparser.statement.UnsupportedStatement; import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.test.MemoryLeakVerifier; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; @@ -336,6 +335,13 @@ public void testCondExpressionIssue1482() throws JSQLParserException { assertEquals("test_table_enum.f1_enum IN ('TEST2'::test.test_enum)", expr.toString()); } + @Test + public void testTableStatementIssue1836() throws JSQLParserException { + TableStatement expr = (TableStatement) CCJSqlParserUtil + .parse("TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10"); + assertEquals("TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10", expr.toString()); + } + @Test public void testCondExpressionIssue1482_2() throws JSQLParserException { Expression expr = CCJSqlParserUtil.parseCondExpression( 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 f4a0b77f6..5fb760b93 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -9,6 +9,28 @@ */ package net.sf.jsqlparser.statement.select; +import static net.sf.jsqlparser.test.TestUtils.assertDeparse; +import static net.sf.jsqlparser.test.TestUtils.assertExpressionCanBeDeparsedAs; +import static net.sf.jsqlparser.test.TestUtils.assertExpressionCanBeParsedAndDeparsed; +import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static net.sf.jsqlparser.test.TestUtils.assertStatementCanBeDeparsedAs; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.AllValue; @@ -56,29 +78,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import static net.sf.jsqlparser.test.TestUtils.assertDeparse; -import static net.sf.jsqlparser.test.TestUtils.assertExpressionCanBeDeparsedAs; -import static net.sf.jsqlparser.test.TestUtils.assertExpressionCanBeParsedAndDeparsed; -import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; -import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; -import static net.sf.jsqlparser.test.TestUtils.assertStatementCanBeDeparsedAs; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - @Execution(ExecutionMode.CONCURRENT) public class SelectTest { @@ -4615,6 +4614,20 @@ public void testMultiColumnAliasIssue849_2() throws JSQLParserException { "SELECT * FROM crosstab('select rowid, attribute, value from ct where attribute = ''att2'' or attribute = ''att3'' order by 1,2') AS ct(row_name text, category_1 text, category_2 text, category_3 text)"); } + @Test + public void testTableStatementIssue1836() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10"); + assertSqlCanBeParsedAndDeparsed( + "TABLE columns ORDER BY column_name LIMIT 10"); + assertSqlCanBeParsedAndDeparsed( + "TABLE columns ORDER BY column_name"); + assertSqlCanBeParsedAndDeparsed( + "TABLE columns LIMIT 10 OFFSET 10"); + assertSqlCanBeParsedAndDeparsed( + "TABLE columns LIMIT 10"); + } + @Test public void testLimitClauseDroppedIssue845() throws JSQLParserException { assertEquals("SELECT * FROM employee ORDER BY emp_id LIMIT 10 OFFSET 2", CCJSqlParserUtil diff --git a/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java b/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java index e0f2d0b97..4248ef494 100644 --- a/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java +++ b/src/test/java/net/sf/jsqlparser/util/deparser/StatementDeParserTest.java @@ -9,6 +9,13 @@ */ package net.sf.jsqlparser.util.deparser; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; @@ -26,6 +33,7 @@ import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.SelectVisitor; +import net.sf.jsqlparser.statement.select.TableStatement; import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.update.UpdateSet; @@ -37,14 +45,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; - @ExtendWith(MockitoExtension.class) public class StatementDeParserTest { @@ -56,8 +56,12 @@ public class StatementDeParserTest { private StatementDeParser statementDeParser; + private TableStatementDeParser tableStatementDeParser; + @BeforeEach public void setUp() { + tableStatementDeParser = + new TableStatementDeParser(expressionDeParser, new StringBuilder()); statementDeParser = new StatementDeParser(expressionDeParser, selectDeParser, new StringBuilder()); } @@ -326,6 +330,13 @@ public void testIssue1500AllColumns() throws JSQLParserException { selectBody.accept(new SelectDeParser()); } + @Test + public void testIssue1836() throws JSQLParserException { + String sqlStr = "TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10;"; + TableStatement tableStatement = (TableStatement) CCJSqlParserUtil.parse(sqlStr); + tableStatement.accept(tableStatementDeParser); + } + @Test public void testIssue1500AllTableColumns() throws JSQLParserException { String sqlStr = "select count(a.*) from some_table a"; diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidatorTest.java new file mode 100644 index 000000000..c71b8448e --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/TableStatementValidatorTest.java @@ -0,0 +1,38 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2020 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.validation.validator; + +import java.util.Arrays; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.feature.Feature; +import net.sf.jsqlparser.util.validation.ValidationTestAsserts; +import net.sf.jsqlparser.util.validation.feature.FeaturesAllowed; +import net.sf.jsqlparser.util.validation.feature.MySqlVersion; +import net.sf.jsqlparser.util.validation.feature.PostgresqlVersion; +import org.junit.jupiter.api.Test; + +public class TableStatementValidatorTest extends ValidationTestAsserts { + + @Test + public void testValidationSelectAllowed() throws JSQLParserException { + String sql = "TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10"; + validateNoErrors(sql, 1, MySqlVersion.V8_0); + } + + @Test + public void testValidationSelectNotAllowed() throws JSQLParserException { + String sql = "TABLE columns ORDER BY column_name LIMIT 10 OFFSET 10"; + validateNotAllowed(sql, 1, 1, FeaturesAllowed.DDL, Feature.select, Feature.tableStatement); + + validateNotSupported(sql, 1, 1, Arrays.asList( + PostgresqlVersion.V14), Feature.tableStatement); + } + +} From 2974f4d20e2d785e7bf045542f29c7d84df45d85 Mon Sep 17 00:00:00 2001 From: manticore-projects Date: Sat, 16 Dec 2023 14:13:01 +0700 Subject: [PATCH 46/77] Update sphinx.yml --- .github/workflows/sphinx.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml index a97cdbc9b..ad8bde188 100644 --- a/.github/workflows/sphinx.yml +++ b/.github/workflows/sphinx.yml @@ -2,15 +2,10 @@ name: Sphinx Pages on: push: branches: [ "master" ] - pull_request: - branches: [ "master" ] - types: - - closed permissions: write-all jobs: docs: - if: github.event.pull_request.merged == true runs-on: ubuntu-latest steps: - uses: actions/setup-python@v4 From 815f8753d552d89500359051428464f999502f11 Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Sat, 16 Dec 2023 19:28:05 +0800 Subject: [PATCH 47/77] feat: Support `FOR SHARE` (#1922) * feat: Support `FOR SHARE` * optimize use way * optimize use way * fix test * fix param name --- .../sf/jsqlparser/parser/feature/Feature.java | 16 ++++++ .../jsqlparser/statement/select/ForMode.java | 34 ++++++++++++ .../statement/select/PlainSelect.java | 34 ++++++------ .../util/deparser/SelectDeParser.java | 6 +- .../util/validation/feature/MySqlVersion.java | 1 + .../validation/feature/PostgresqlVersion.java | 3 + .../validation/validator/SelectValidator.java | 10 +++- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 13 ++++- .../statement/select/SelectTest.java | 28 +++++++++- .../validator/SelectValidatorTest.java | 55 ++++++++++++++----- 10 files changed, 160 insertions(+), 40 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/select/ForMode.java diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 956d636b5..9b8502094 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -244,6 +244,22 @@ public enum Feature { * "FOR UPDATE" */ selectForUpdate, + + /** + * "FOR SHARE" + */ + selectForShare, + + /** + * "FOR KEY SHARE" + */ + selectForKeyShare, + + /** + * "NO KEY UPDATE" + */ + selectForNoKeyUpdate, + /** * "FOR UPDATE OF table" */ diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java b/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java new file mode 100644 index 000000000..a58233ed1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ForMode.java @@ -0,0 +1,34 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +/** + * @author jxnu-liguobin + */ +public enum ForMode { + + UPDATE("UPDATE"), + + SHARE("SHARE"), + + NO_KEY_UPDATE("NO KEY UPDATE"), + + KEY_SHARE("KEY SHARE"); + + private final String value; + + public String getValue() { + return value; + } + + ForMode(String s) { + this.value = s; + } +} 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 5e322c52f..72a4e530c 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java @@ -9,12 +9,7 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.expression.Alias; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.OracleHierarchicalExpression; -import net.sf.jsqlparser.expression.OracleHint; -import net.sf.jsqlparser.expression.WindowDefinition; -import net.sf.jsqlparser.schema.Table; +import static java.util.stream.Collectors.joining; import java.util.ArrayList; import java.util.Arrays; @@ -23,8 +18,12 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; - -import static java.util.stream.Collectors.joining; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.OracleHierarchicalExpression; +import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.WindowDefinition; +import net.sf.jsqlparser.schema.Table; @SuppressWarnings({"PMD.CyclomaticComplexity"}) public class PlainSelect extends Select { @@ -46,7 +45,7 @@ public class PlainSelect extends Select { private Top top; private OracleHierarchicalExpression oracleHierarchical = null; private OracleHint oracleHint = null; - private boolean forUpdate = false; + private ForMode forMode = null; private Table forUpdateTable = null; private boolean skipLocked; private Wait wait; @@ -312,12 +311,12 @@ public void setOracleHierarchical(OracleHierarchicalExpression oracleHierarchica this.oracleHierarchical = oracleHierarchical; } - public boolean isForUpdate() { - return forUpdate; + public ForMode getForMode() { + return forMode; } - public void setForUpdate(boolean forUpdate) { - this.forUpdate = forUpdate; + public void setForMode(ForMode forMode) { + this.forMode = forMode; } public Table getForUpdateTable() { @@ -486,8 +485,9 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) { if (emitChanges) { builder.append(" EMIT CHANGES"); } - if (isForUpdate()) { - builder.append(" FOR UPDATE"); + if (getForMode() != null) { + builder.append(" FOR "); + builder.append(getForMode().getValue()); if (forUpdateTable != null) { builder.append(" OF ").append(forUpdateTable); @@ -620,8 +620,8 @@ public PlainSelect withOracleSiblings(boolean oracleSiblings) { return this; } - public PlainSelect withForUpdate(boolean forUpdate) { - this.setForUpdate(forUpdate); + public PlainSelect withForMode(ForMode forMode) { + this.setForMode(forMode); return this; } 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 7c4c0e1fb..a6b718baf 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -268,8 +268,10 @@ public void visit(PlainSelect plainSelect) { buffer.append(plainSelect.getWindowDefinitions().stream() .map(WindowDefinition::toString).collect(joining(", "))); } - if (plainSelect.isForUpdate()) { - buffer.append(" FOR UPDATE"); + if (plainSelect.getForMode() != null) { + buffer.append(" FOR "); + buffer.append(plainSelect.getForMode().getValue()); + if (plainSelect.getForUpdateTable() != null) { buffer.append(" OF ").append(plainSelect.getForUpdateTable()); } diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java index 3486261ed..a2397f9c5 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java @@ -38,6 +38,7 @@ public enum MySqlVersion implements Version { Feature.selectForUpdateOfTable, Feature.selectForUpdateNoWait, Feature.selectForUpdateSkipLocked, + Feature.selectForShare, Feature.distinct, Feature.setOperation, diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java index 21cec80d2..08bf3bf1a 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java @@ -73,6 +73,9 @@ public enum PostgresqlVersion implements Version { Feature.orderBy, Feature.orderByNullOrdering, + Feature.selectForNoKeyUpdate, + Feature.selectForKeyShare, + Feature.selectForShare, Feature.selectForUpdate, Feature.selectForUpdateOfTable, Feature.selectForUpdateNoWait, 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 3cba4a988..97fcd08a3 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 @@ -17,6 +17,7 @@ import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.ExceptOp; import net.sf.jsqlparser.statement.select.Fetch; +import net.sf.jsqlparser.statement.select.ForMode; import net.sf.jsqlparser.statement.select.FromItemVisitor; import net.sf.jsqlparser.statement.select.IntersectOp; import net.sf.jsqlparser.statement.select.Join; @@ -85,8 +86,15 @@ public void visit(PlainSelect plainSelect) { isNotEmpty(plainSelect.getOrderByElements()) && plainSelect.isOracleSiblings(), Feature.oracleOrderBySiblings); - if (plainSelect.isForUpdate()) { + if (plainSelect.getForMode() != null) { validateFeature(c, Feature.selectForUpdate); + validateFeature(c, plainSelect.getForMode() == ForMode.KEY_SHARE, + Feature.selectForKeyShare); + validateFeature(c, plainSelect.getForMode() == ForMode.NO_KEY_UPDATE, + Feature.selectForNoKeyUpdate); + validateFeature(c, plainSelect.getForMode() == ForMode.SHARE, + Feature.selectForShare); + validateOptionalFeature(c, plainSelect.getForUpdateTable(), Feature.selectForUpdateOfTable); validateOptionalFeature(c, plainSelect.getWait(), Feature.selectForUpdateWait); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 547324944..ad394b56b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -400,6 +400,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -1801,7 +1802,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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | 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" ) + | 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCK" | tk="LOCKED" | tk="LOG" | tk="LOOP" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODE" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | 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; } } @@ -2268,13 +2269,19 @@ PlainSelect PlainSelect() #PlainSelect: [ LOOKAHEAD(, { limit==null }) limit = LimitWithOffset() { plainSelect.setLimit(limit); } ] [ LOOKAHEAD() fetch = Fetch() { plainSelect.setFetch(fetch); } ] [ LOOKAHEAD( ) withIsolation = WithIsolation() { plainSelect.setIsolation(withIsolation); } ] - [ LOOKAHEAD(2) { plainSelect.setForUpdate(true); } + [ LOOKAHEAD(2) + + ( + { plainSelect.setForMode(ForMode.UPDATE); } + | { plainSelect.setForMode(ForMode.SHARE); } + | ( { plainSelect.setForMode(ForMode.NO_KEY_UPDATE); }) + | ( { plainSelect.setForMode(ForMode.KEY_SHARE); }) + ) [ LOOKAHEAD(2) updateTable = Table() { plainSelect.setForUpdateTable(updateTable); } ] [ LOOKAHEAD() wait = Wait() { plainSelect.setWait(wait); } ] [ LOOKAHEAD(2) ( { plainSelect.setNoWait(true); } | { plainSelect.setSkipLocked(true); }) ] ] - [ LOOKAHEAD() optimize = OptimizeFor() { plainSelect.setOptimizeFor(optimize); } ] { plainSelect.setSelectItems(selectItems); 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 5fb760b93..59f6022b8 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -4804,6 +4805,27 @@ public void testCurrentIssue940() throws JSQLParserException { "SELECT date(current) AS test_date FROM systables WHERE tabid = 1"); } + @Test + public void testIssue1878() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM MY_TABLE1 FOR SHARE"); + // PostgreSQL ONLY + assertSqlCanBeParsedAndDeparsed("SELECT * FROM MY_TABLE1 FOR NO KEY UPDATE"); + assertSqlCanBeParsedAndDeparsed("SELECT * FROM MY_TABLE1 FOR KEY SHARE"); + } + + @Test + public void testIssue1878ViaJava() throws JSQLParserException { + String expectedSQLStr = "SELECT * FROM MY_TABLE1 FOR SHARE"; + + // Step 1: generate the Java Object Hierarchy for + Table table = new Table().withName("MY_TABLE1"); + + PlainSelect select = new PlainSelect().addSelectItem(new AllColumns()) + .withFromItem(table).withForMode(ForMode.KEY_SHARE).withForMode(ForMode.SHARE); + + Assertions.assertEquals(expectedSQLStr, select.toString()); + } + @Test public void testKeyWordView() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( @@ -5564,7 +5586,7 @@ public void testSelectStatementWithForUpdateAndSkipLockedTokens() throws JSQLPar Select select = (Select) CCJSqlParserUtil.parse(sql); PlainSelect plainSelect = (PlainSelect) select; - assertTrue(plainSelect.isForUpdate()); + assertSame(plainSelect.getForMode(), ForMode.UPDATE); assertTrue(plainSelect.isSkipLocked()); } @@ -5576,7 +5598,7 @@ public void testSelectStatementWithForUpdateButWithoutSkipLockedTokens() Select select = (Select) CCJSqlParserUtil.parse(sql); PlainSelect plainSelect = (PlainSelect) select; - assertTrue(plainSelect.isForUpdate()); + assertSame(plainSelect.getForMode(), ForMode.UPDATE); assertFalse(plainSelect.isSkipLocked()); } @@ -5588,7 +5610,7 @@ public void testSelectStatementWithoutForUpdateAndSkipLockedTokens() Select select = (Select) CCJSqlParserUtil.parse(sql); PlainSelect plainSelect = (PlainSelect) select; - assertFalse(plainSelect.isForUpdate()); + assertNull(plainSelect.getForMode()); assertFalse(plainSelect.isSkipLocked()); } diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/SelectValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/SelectValidatorTest.java index a22b34156..52b7a5396 100644 --- a/src/test/java/net/sf/jsqlparser/util/validation/validator/SelectValidatorTest.java +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/SelectValidatorTest.java @@ -68,6 +68,25 @@ public void testValidationForUpdateWaitWithTimeout() throws JSQLParserException validateNoErrors(sql, 1, DatabaseType.ORACLE, DatabaseType.MARIADB); } + @Test + public void testValidationForShare() throws JSQLParserException { + String sql = "SELECT * FROM mytable FOR SHARE"; + validateNoErrors(sql, 1, DatabaseType.MYSQL, DatabaseType.POSTGRESQL); + } + + @Test + public void testValidationForPostgresShare() throws JSQLParserException { + String sql = "SELECT * FROM mytable FOR KEY SHARE"; + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL); + + String sql2 = "SELECT * FROM mytable FOR NO KEY UPDATE"; + validateNoErrors(sql2, 1, DatabaseType.POSTGRESQL); + + // Not familiar with oracle, please modify if supported. + validateNotSupported(sql2, 1, 1, DatabaseType.ORACLE, + Feature.selectForNoKeyUpdate); + } + @Test public void testValidationForUpdateNoWait() throws JSQLParserException { String sql = "SELECT * FROM mytable FOR UPDATE NOWAIT"; @@ -83,7 +102,8 @@ public void testValidationJoinOuterSimple() throws JSQLParserException { @Test public void testValidationJoin() throws JSQLParserException { - for (String sql : Arrays.asList("SELECT t1.col, t2.col, t1.id FROM tab1 t1, tab2 t2 WHERE t1.id = t2.id", + for (String sql : Arrays.asList( + "SELECT t1.col, t2.col, t1.id FROM tab1 t1, tab2 t2 WHERE t1.id = t2.id", "SELECT t1.col, t2.col, t1.id FROM tab1 t1 JOIN tab2 t2 ON t1.id = t2.id", "SELECT t1.col, t2.col, t1.id FROM tab1 t1 INNER JOIN tab2 t2 ON t1.id = t2.id")) { validateNoErrors(sql, 1, DatabaseType.DATABASES); @@ -92,7 +112,8 @@ public void testValidationJoin() throws JSQLParserException { @Test public void testOracleHierarchicalQuery() throws JSQLParserException { - String sql = "SELECT last_name, employee_id, manager_id, LEVEL FROM employees START WITH employee_id = 100 CONNECT BY PRIOR employee_id = manager_id ORDER SIBLINGS BY last_name"; + String sql = + "SELECT last_name, employee_id, manager_id, LEVEL FROM employees START WITH employee_id = 100 CONNECT BY PRIOR employee_id = manager_id ORDER SIBLINGS BY last_name"; validateNoErrors(sql, 1, DatabaseType.ORACLE); } @@ -127,7 +148,8 @@ public void testValidationWith() throws JSQLParserException { @Test public void testValidationWithRecursive() throws JSQLParserException { - String statement = "WITH RECURSIVE t (n) AS ((SELECT 1) UNION ALL (SELECT n + 1 FROM t WHERE n < 100)) SELECT sum(n) FROM t"; + String statement = + "WITH RECURSIVE t (n) AS ((SELECT 1) UNION ALL (SELECT n + 1 FROM t WHERE n < 100)) SELECT sum(n) FROM t"; validateNoErrors(statement, 1, DatabaseType.H2, DatabaseType.MARIADB, DatabaseType.MYSQL, DatabaseType.SQLSERVER, DatabaseType.POSTGRESQL); validateNotSupported(statement, 1, 1, DatabaseType.ORACLE, Feature.withItemRecursive); @@ -135,7 +157,8 @@ public void testValidationWithRecursive() throws JSQLParserException { @Test public void testSelectMulipleExpressionList() { - String sql = "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222'))"; + String sql = + "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222'))"; validateNoErrors(sql, 1, DatabaseType.DATABASES); } @@ -148,7 +171,8 @@ public void testValidatePivotWithAlias() throws JSQLParserException { @Test public void testValidatePivotXml() throws JSQLParserException { - validateNoErrors("SELECT * FROM mytable PIVOT XML (count(a) FOR b IN ('val1'))", 1, DatabaseType.SQLSERVER); + validateNoErrors("SELECT * FROM mytable PIVOT XML (count(a) FOR b IN ('val1'))", 1, + DatabaseType.SQLSERVER); } @Test @@ -160,14 +184,17 @@ public void testValidateUnPivot() throws JSQLParserException { @Test public void testValidateSubJoin() throws JSQLParserException { - validateNoErrors("SELECT * FROM ((tabc c INNER JOIN tabn n ON n.ref = c.id) INNER JOIN taba a ON a.REF = c.id)", + validateNoErrors( + "SELECT * FROM ((tabc c INNER JOIN tabn n ON n.ref = c.id) INNER JOIN taba a ON a.REF = c.id)", 1, DatabaseType.SQLSERVER); } @Test public void testValidateTableFunction() { - for (String sql : Arrays.asList("SELECT f2 FROM SOME_FUNCTION()", "SELECT f2 FROM SOME_FUNCTION(1, 'val')")) { - validateNoErrors(sql, 1, DatabaseType.POSTGRESQL, DatabaseType.H2, DatabaseType.SQLSERVER); + for (String sql : Arrays.asList("SELECT f2 FROM SOME_FUNCTION()", + "SELECT f2 FROM SOME_FUNCTION(1, 'val')")) { + validateNoErrors(sql, 1, DatabaseType.POSTGRESQL, DatabaseType.H2, + DatabaseType.SQLSERVER); } } @@ -182,11 +209,11 @@ public void testValidateLateral() throws JSQLParserException { public void testValidateIssue1502() throws JSQLParserException { validateNoErrors( "select b.id, name ,(select name from Blog where name = 'sadf') as name2 " - + ", category, owner, b.update_time " - + "from Blog as b " - + "left join Content " - + "ON b.id = Content.blog_id " - + "where name = 'sadf' order by Content.title desc", + + ", category, owner, b.update_time " + + "from Blog as b " + + "left join Content " + + "ON b.id = Content.blog_id " + + "where name = 'sadf' order by Content.title desc", 1, DatabaseType.POSTGRESQL); - } + } } From aaca05855f9a11bc997c5e5d146e29b5afe6005d Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 18 Dec 2023 17:45:43 +0700 Subject: [PATCH 48/77] fix: De-Parse Oracle Hints in UPDATE, INSERT, DELETE and MERGE - adds 2 more Special Oracle tests --- .../jsqlparser/statement/delete/Delete.java | 4 +- .../jsqlparser/statement/insert/Insert.java | 3 + .../sf/jsqlparser/statement/merge/Merge.java | 6 +- .../jsqlparser/statement/update/Update.java | 3 + .../util/deparser/DeleteDeParser.java | 3 + .../util/deparser/InsertDeParser.java | 3 + .../util/deparser/StatementDeParser.java | 13 ++- .../util/deparser/UpdateDeParser.java | 3 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../jsqlparser/expression/OracleHintTest.java | 40 ++++++++++ .../parser/ParserKeywordsUtilsTest.java | 79 ++++++++++--------- .../statement/select/SelectTest.java | 33 ++++---- .../statement/select/SpecialOracleTest.java | 40 ++++++---- .../statement/select/oracle-tests/merge03.sql | 3 +- .../statement/select/oracle-tests/merge04.sql | 3 +- 15 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java diff --git a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java index c28a4a0d8..3e5c171a3 100644 --- a/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java +++ b/src/main/java/net/sf/jsqlparser/statement/delete/Delete.java @@ -188,7 +188,9 @@ public String toString() { } b.append("DELETE"); - + if (oracleHint != null) { + b.append(oracleHint).append(" "); + } if (modifierPriority != null) { b.append(" ").append(modifierPriority.name()); } 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 ea3809ad6..bb5595a35 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -218,6 +218,9 @@ public String toString() { } } sql.append("INSERT "); + if (oracleHint != null) { + sql.append(oracleHint).append(" "); + } if (modifierPriority != null) { sql.append(modifierPriority.name()).append(" "); } diff --git a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java index 8d69bbce4..0be4d2528 100644 --- a/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java +++ b/src/main/java/net/sf/jsqlparser/statement/merge/Merge.java @@ -182,7 +182,11 @@ public String toString() { b.append(" "); } } - b.append("MERGE INTO "); + b.append("MERGE "); + if (oracleHint != null) { + b.append(oracleHint).append(" "); + } + b.append("INTO "); b.append(table); b.append(" USING "); b.append(fromItem); 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 c4975f503..0a6224d76 100644 --- a/src/main/java/net/sf/jsqlparser/statement/update/Update.java +++ b/src/main/java/net/sf/jsqlparser/statement/update/Update.java @@ -292,6 +292,9 @@ public String toString() { } } b.append("UPDATE "); + if (oracleHint != null) { + b.append(oracleHint).append(" "); + } if (modifierPriority != null) { b.append(modifierPriority.name()).append(" "); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java index b97794af4..ef4d0922d 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java @@ -48,6 +48,9 @@ public void deParse(Delete delete) { } } buffer.append("DELETE"); + if (delete.getOracleHint() != null) { + buffer.append(delete.getOracleHint()).append(" "); + } if (delete.getModifierPriority() != null) { buffer.append(" ").append(delete.getModifierPriority()); } 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 84d473f76..dec9f6c0a 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -54,6 +54,9 @@ public void deParse(Insert insert) { if (insert.getModifierPriority() != null) { buffer.append(insert.getModifierPriority()).append(" "); } + if (insert.getOracleHint() != null) { + buffer.append(insert.getOracleHint()).append(" "); + } if (insert.isModifierIgnore()) { buffer.append("IGNORE "); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index b6006255d..883652204 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -9,9 +9,6 @@ */ package net.sf.jsqlparser.util.deparser; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; import net.sf.jsqlparser.statement.Block; import net.sf.jsqlparser.statement.Commit; import net.sf.jsqlparser.statement.CreateFunctionalStatement; @@ -62,6 +59,10 @@ import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.upsert.Upsert; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + public class StatementDeParser extends AbstractDeParser implements StatementVisitor { private final ExpressionDeParser expressionDeParser; @@ -215,7 +216,11 @@ public void visit(Merge merge) { } } - buffer.append("MERGE INTO "); + buffer.append("MERGE "); + if (merge.getOracleHint() != null) { + buffer.append(merge.getOracleHint()).append(" "); + } + buffer.append("INTO "); merge.getTable().accept(selectDeParser); buffer.append(" USING "); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java index 4f8affbeb..0ba4003c4 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java @@ -48,6 +48,9 @@ public void deParse(Update update) { } } buffer.append("UPDATE "); + if (update.getOracleHint() != null) { + buffer.append(update.getOracleHint()).append(" "); + } if (update.getModifierPriority() != null) { buffer.append(update.getModifierPriority()).append(" "); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index ad394b56b..a2a6220fc 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1802,7 +1802,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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCK" | tk="LOCKED" | tk="LOG" | tk="LOOP" | tk="MATCH" | tk="MATCHED" | tk="MATERIALIZED" | tk="MAXVALUE" | tk="MEMBER" | tk="MERGE" | tk="MINVALUE" | tk="MODE" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | 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" ) + | 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | 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; } } diff --git a/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java new file mode 100644 index 000000000..2d76440b7 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java @@ -0,0 +1,40 @@ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + +class OracleHintTest { + + @Test + void testSelect() throws JSQLParserException { + String sqlString = "SELECT /*+parallel*/ * from dual"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); + } + + @Test + void testDelete() throws JSQLParserException { + String sqlString = "DELETE /*+parallel*/ from dual"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); + } + + @Test + void testInsert() throws JSQLParserException { + String sqlString = "INSERT /*+parallel*/ INTO dual VALUES(1)"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); + } + + @Test + void testUpdate() throws JSQLParserException { + String sqlString = "UPDATE /*+parallel*/ dual SET a=b"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); + } + + @Test + void testMerge() throws JSQLParserException { + String sqlString = + "MERGE /*+parallel*/ INTO dual USING z ON (a=b) WHEN MATCHED THEN UPDATE SET a=b"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlString, true); + } + +} diff --git a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java index c48b3ef11..d69a24155 100644 --- a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java @@ -23,7 +23,6 @@ import org.javacc.parser.RegularExpression; import org.javacc.parser.Semanticize; import org.javacc.parser.Token; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -40,7 +39,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.logging.Logger; -import org.junit.jupiter.api.Disabled; class ParserKeywordsUtilsTest { @@ -61,39 +59,41 @@ private static void addTokenImage(TreeSet allKeywords, Object o) throws if (o instanceof RStringLiteral) { RStringLiteral literal = (RStringLiteral) o; addTokenImage(allKeywords, literal); - } else if (o instanceof RChoice) { + } else if (o instanceof RChoice) { RChoice choice = (RChoice) o; addTokenImage(allKeywords, choice); } else if (o instanceof RSequence) { RSequence sequence1 = (RSequence) o; addTokenImage(allKeywords, sequence1); - } else if (o instanceof ROneOrMore) { - ROneOrMore oneOrMore = (ROneOrMore) o ; + } else if (o instanceof ROneOrMore) { + ROneOrMore oneOrMore = (ROneOrMore) o; addTokenImage(allKeywords, oneOrMore); - } else if (o instanceof RZeroOrMore) { - RZeroOrMore zeroOrMore = (RZeroOrMore) o ; + } else if (o instanceof RZeroOrMore) { + RZeroOrMore zeroOrMore = (RZeroOrMore) o; addTokenImage(allKeywords, zeroOrMore); - } else if (o instanceof RZeroOrOne) { - RZeroOrOne zeroOrOne = (RZeroOrOne) o ; + } else if (o instanceof RZeroOrOne) { + RZeroOrOne zeroOrOne = (RZeroOrOne) o; addTokenImage(allKeywords, zeroOrOne); - } else if (o instanceof RJustName) { - RJustName zeroOrOne = (RJustName) o ; + } else if (o instanceof RJustName) { + RJustName zeroOrOne = (RJustName) o; addTokenImage(allKeywords, zeroOrOne); - } else if (o instanceof RCharacterList) { + } else if (o instanceof RCharacterList) { // do nothing, we are not interested in those } else { - throw new InvalidClassException("Unknown Type: " + o.getClass().getName() + " " + o.toString()); + throw new InvalidClassException( + "Unknown Type: " + o.getClass().getName() + " " + o.toString()); } } - private static void addTokenImage(TreeSet allKeywords, RSequence sequence) throws Exception { - for (Object o: sequence.units) { + private static void addTokenImage(TreeSet allKeywords, RSequence sequence) + throws Exception { + for (Object o : sequence.units) { addTokenImage(allKeywords, o); } } private static void addTokenImage(TreeSet allKeywords, ROneOrMore oneOrMore) { - for (Token token: oneOrMore.lhsTokens) { + for (Token token : oneOrMore.lhsTokens) { if (CHARSET_ENCODER.canEncode(token.image)) { allKeywords.add(token.image); } @@ -101,7 +101,7 @@ private static void addTokenImage(TreeSet allKeywords, ROneOrMore oneOrM } private static void addTokenImage(TreeSet allKeywords, RZeroOrMore oneOrMore) { - for (Token token: oneOrMore.lhsTokens) { + for (Token token : oneOrMore.lhsTokens) { if (CHARSET_ENCODER.canEncode(token.image)) { allKeywords.add(token.image); } @@ -109,7 +109,7 @@ private static void addTokenImage(TreeSet allKeywords, RZeroOrMore oneOr } private static void addTokenImage(TreeSet allKeywords, RZeroOrOne oneOrMore) { - for (Token token: oneOrMore.lhsTokens) { + for (Token token : oneOrMore.lhsTokens) { if (CHARSET_ENCODER.canEncode(token.image)) { allKeywords.add(token.image); } @@ -117,15 +117,16 @@ private static void addTokenImage(TreeSet allKeywords, RZeroOrOne oneOrM } private static void addTokenImage(TreeSet allKeywords, RJustName oneOrMore) { - for (Token token: oneOrMore.lhsTokens) { + for (Token token : oneOrMore.lhsTokens) { if (CHARSET_ENCODER.canEncode(token.image)) { allKeywords.add(token.image); } } } - private static void addTokenImage(TreeSet allKeywords, RChoice choice) throws Exception { - for (Object o: choice.getChoices()) { + private static void addTokenImage(TreeSet allKeywords, RChoice choice) + throws Exception { + for (Object o : choice.getChoices()) { addTokenImage(allKeywords, o); } } @@ -136,7 +137,7 @@ public static TreeSet getAllKeywordsUsingJavaCC(File file) throws Except Path jjtGrammar = file.toPath(); Path jjGrammarOutputDir = Files.createTempDirectory("jjgrammer"); - new JJTree().main(new String[]{ + new JJTree().main(new String[] { "-JDK_VERSION=1.8", "-OUTPUT_DIRECTORY=" + jjGrammarOutputDir.toString(), jjtGrammar.toString() @@ -150,11 +151,12 @@ public static TreeSet getAllKeywordsUsingJavaCC(File file) throws Except Semanticize.start(); // read all the Token and get the String image - for (Map.Entry item : JavaCCGlobals.rexps_of_tokens.entrySet()) { + for (Map.Entry item : JavaCCGlobals.rexps_of_tokens + .entrySet()) { addTokenImage(allKeywords, item.getValue()); } - //clean up + // clean up if (jjGrammarOutputDir.toFile().exists()) { jjGrammarOutputDir.toFile().delete(); } @@ -164,38 +166,37 @@ public static TreeSet getAllKeywordsUsingJavaCC(File file) throws Except @Test void getAllKeywords() throws IOException { - Set allKeywords = ParserKeywordsUtils.getAllKeywordsUsingRegex(FILE); - Assertions.assertFalse( allKeywords.isEmpty(), "Keyword List must not be empty!" ); + Set allKeywords = ParserKeywordsUtils.getAllKeywordsUsingRegex(FILE); + Assertions.assertFalse(allKeywords.isEmpty(), "Keyword List must not be empty!"); } @Test - @Disabled void getAllKeywordsUsingJavaCC() throws Exception { - Set allKeywords = getAllKeywordsUsingJavaCC(FILE); - Assertions.assertFalse( allKeywords.isEmpty(), "Keyword List must not be empty!" ); + Set allKeywords = getAllKeywordsUsingJavaCC(FILE); + Assertions.assertFalse(allKeywords.isEmpty(), "Keyword List must not be empty!"); } // Test, if all Tokens found per RegEx are also found from the JavaCCParser @Test - @Disabled void compareKeywordLists() throws Exception { - Set allRegexKeywords = ParserKeywordsUtils.getAllKeywordsUsingRegex(FILE); - Set allJavaCCParserKeywords = getAllKeywordsUsingJavaCC(FILE); + Set allRegexKeywords = ParserKeywordsUtils.getAllKeywordsUsingRegex(FILE); + Set allJavaCCParserKeywords = getAllKeywordsUsingJavaCC(FILE); // Exceptions, which should not have been found from the RegEx List exceptions = Arrays.asList("0x"); // We expect all Keywords from the Regex to be found by the JavaCC Parser - for (String s:allRegexKeywords) { + for (String s : allRegexKeywords) { Assertions.assertTrue( - exceptions.contains(s) || allJavaCCParserKeywords.contains(s) - , "The Keywords from JavaCC do not contain Keyword: " + s); + exceptions.contains(s) || allJavaCCParserKeywords.contains(s), + "The Keywords from JavaCC do not contain Keyword: " + s); } - // The JavaCC Parser finds some more valid Keywords (where no explicit Token has been defined - for (String s:allJavaCCParserKeywords) { - if ( ! (exceptions.contains(s) || allRegexKeywords.contains(s)) ) { - LOGGER.fine ("Found Additional Keywords from Parser: " + s); + // The JavaCC Parser finds some more valid Keywords (where no explicit Token has been + // defined + for (String s : allJavaCCParserKeywords) { + if (!(exceptions.contains(s) || allRegexKeywords.contains(s))) { + LOGGER.fine("Found Additional Keywords from Parser: " + s); } } } 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 59f6022b8..e6f85d4b7 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -1478,24 +1478,21 @@ public void testCase() throws JSQLParserException { statement = "SELECT a FROM tab1 WHERE CASE b WHEN 1 THEN 2 + 3 ELSE 4 END > 34"; assertSqlCanBeParsedAndDeparsed(statement); - statement = - "SELECT a, (CASE " + "WHEN (CASE a WHEN 1 THEN 10 ELSE 20 END) > 15 THEN 'BBB' " + // "WHEN - // (SELECT - // c - // FROM - // tab2 - // WHERE - // d - // = - // 2) - // = - // 3 - // THEN - // 'AAA' - // " - // + - "END) FROM tab1"; - assertSqlCanBeParsedAndDeparsed(statement); + statement = "SELECT a\n" + + " , ( CASE\n" + + " WHEN ( CASE\n" + + " WHEN 1\n" + + " THEN 10\n" + + " ELSE 20\n" + + " END ) > 15\n" + + " THEN 'BBB'\n" + + " WHEN ( SELECT c\n" + + " FROM tab2\n" + + " WHERE d = 2 ) = 3\n" + + " THEN 'AAA'\n" + + " END )\n" + + "FROM tab1\n"; + assertSqlCanBeParsedAndDeparsed(statement, true); } @Test diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java index 6d2b4a1a8..d0bbed775 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java @@ -9,11 +9,18 @@ */ package net.sf.jsqlparser.statement.select; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.commons.io.FileUtils; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + import java.io.File; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; @@ -21,22 +28,17 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.parser.CCJSqlParserUtil; + import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; -import org.apache.commons.io.FileUtils; -import org.assertj.core.api.Assertions; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; /** - * Tries to parse and deparse all statments in net.sf.jsqlparser.test.oracle-tests. - * + * Tries to parse and de-parse all statements in net.sf.jsqlparser.test.oracle-tests. + *

    * As a matter of fact there are a lot of files that can still not processed. Here a step by step * improvement is the way to go. - * + *

    * The test ensures, that the successful parsed file count does not decrease. * * @author toben @@ -97,7 +99,8 @@ public class SpecialOracleTest { "join21.sql", "keywordasidentifier01.sql", "keywordasidentifier02.sql", "keywordasidentifier03.sql", "keywordasidentifier04.sql", "keywordasidentifier05.sql", "lexer02.sql", "lexer03.sql", "lexer04.sql", "lexer05.sql", "like01.sql", "merge01.sql", - "merge02.sql", "order_by01.sql", "order_by02.sql", "order_by03.sql", "order_by04.sql", + "merge02.sql", "merge03.sql", "merge04.sql", "order_by01.sql", "order_by02.sql", + "order_by03.sql", "order_by04.sql", "order_by05.sql", "order_by06.sql", "pivot01.sql", "pivot02.sql", "pivot03.sql", "pivot04.sql", "pivot05.sql", "pivot06.sql", "pivot07.sql", "pivot07_Parenthesis.sql", "pivot08.sql", "pivot09.sql", "pivot11.sql", "pivot12.sql", "query_factoring01.sql", @@ -118,10 +121,11 @@ public void testAllSqlsParseDeparse() throws IOException { boolean foundUnexpectedFailures = false; + assert sqlTestFiles != null; for (File file : sqlTestFiles) { if (file.isFile()) { count++; - String sql = FileUtils.readFileToString(file, Charset.forName("UTF-8")); + String sql = FileUtils.readFileToString(file, StandardCharsets.UTF_8); try { assertSqlCanBeParsedAndDeparsed(sql, true); success++; @@ -187,9 +191,10 @@ public boolean accept(File dir, String name) { } }); + assert sqlTestFiles != null; for (File file : sqlTestFiles) { if (file.isFile()) { - String sql = FileUtils.readFileToString(file, Charset.forName("UTF-8")); + String sql = FileUtils.readFileToString(file, StandardCharsets.UTF_8); assertSqlCanBeParsedAndDeparsed(sql, true); } } @@ -197,7 +202,7 @@ public boolean accept(File dir, String name) { public void recordSuccessOnSourceFile(File file) throws IOException { File sourceFile = new File(SQL_SOURCE_DIR, file.getName()); - String sourceSql = FileUtils.readFileToString(sourceFile, Charset.forName("UTF-8")); + String sourceSql = FileUtils.readFileToString(sourceFile, StandardCharsets.UTF_8); if (!sourceSql.contains("@SUCCESSFULLY_PARSED_AND_DEPARSED")) { LOG.log(Level.INFO, "NEW SUCCESS: {0}", file.getName()); if (sourceFile.exists() && sourceFile.canWrite()) { @@ -219,10 +224,10 @@ public void recordSuccessOnSourceFile(File file) throws IOException { public void recordFailureOnSourceFile(File file, String message) throws IOException { File sourceFile = new File(SQL_SOURCE_DIR, file.getName()); - String sourceSql = FileUtils.readFileToString(sourceFile, Charset.forName("UTF-8")); + String sourceSql = FileUtils.readFileToString(sourceFile, StandardCharsets.UTF_8); if (!sourceSql.contains("@FAILURE: " + message) && sourceFile.canWrite()) { try (FileWriter writer = new FileWriter(sourceFile, true)) { - writer.append("\n--@FAILURE: " + message + " recorded first on ") + writer.append("\n--@FAILURE: ").append(message).append(" recorded first on ") .append(DateFormat.getDateTimeInstance().format(new Date())); } } @@ -233,8 +238,9 @@ public void testAllSqlsOnlyParse() throws IOException { File[] sqlTestFiles = new File(SQLS_DIR, "only-parse-test").listFiles(); List regressionFiles = new LinkedList<>(); + assert sqlTestFiles != null; for (File file : sqlTestFiles) { - String sql = FileUtils.readFileToString(file, Charset.forName("UTF-8")); + String sql = FileUtils.readFileToString(file, StandardCharsets.UTF_8); try { CCJSqlParserUtil.parse(sql); LOG.log(Level.FINE, "EXPECTED SUCCESS: {0}", file.getName()); diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge03.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge03.sql index 4afaac31e..ca021f1c2 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge03.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge03.sql @@ -25,4 +25,5 @@ update set mm.inserts = mm.inserts + v.inserts, mm.updates = mm.updates + v.upda mm.flags = mm.flags + v.flags - bitand(mm.flags,v.flags) , mm.drop_segments = mm.drop_segments + v.drop_segments when not matched then insert values (v.obj#, v.inserts, v.updates, v.deletes, sysdate, v.flags, v.drop_segments) ---@FAILURE: merge into sys.mon_mods_all$ mm using(select decode(grouping_id(tp.bo#,tsp.pobj#,m.obj#),3,tp.bo#,1,tsp.pobj#,m.obj#)obj#,sum(m.inserts)inserts,sum(m.updates)updates,sum(m.deletes)deletes,decode(sum(bitand(m.flags,1)),0,0,1)+decode(sum(bitand(m.flags,2)),0,0,2)+decode(sum(bitand(m.flags,4)),0,0,4)flags,sum(m.drop_segments)drop_segments from sys.mon_mods$ m,sys.tabcompart$ tp,sys.tabsubpart$ tsp where m.obj#=tsp.obj# and tp.obj#=tsp.pobj# group by rollup(tp.bo#,tsp.pobj#,m.obj#)having grouping_id(tp.bo#,tsp.pobj#,m.obj#)<7)v on(mm.obj#=v.obj#)when matched then update set mm.inserts=mm.inserts+v.inserts,mm.updates=mm.updates+v.updates,mm.deletes=mm.deletes+v.deletes,mm.flags=mm.flags+v.flags-bitand(mm.flags,v.flags),mm.drop_segments=mm.drop_segments+v.drop_segments when not matched then insert values(v.obj#,v.inserts,v.updates,v.deletes,sysdate,v.flags,v.drop_segments) recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: merge into sys.mon_mods_all$ mm using(select decode(grouping_id(tp.bo#,tsp.pobj#,m.obj#),3,tp.bo#,1,tsp.pobj#,m.obj#)obj#,sum(m.inserts)inserts,sum(m.updates)updates,sum(m.deletes)deletes,decode(sum(bitand(m.flags,1)),0,0,1)+decode(sum(bitand(m.flags,2)),0,0,2)+decode(sum(bitand(m.flags,4)),0,0,4)flags,sum(m.drop_segments)drop_segments from sys.mon_mods$ m,sys.tabcompart$ tp,sys.tabsubpart$ tsp where m.obj#=tsp.obj# and tp.obj#=tsp.pobj# group by rollup(tp.bo#,tsp.pobj#,m.obj#)having grouping_id(tp.bo#,tsp.pobj#,m.obj#)<7)v on(mm.obj#=v.obj#)when matched then update set mm.inserts=mm.inserts+v.inserts,mm.updates=mm.updates+v.updates,mm.deletes=mm.deletes+v.deletes,mm.flags=mm.flags+v.flags-bitand(mm.flags,v.flags),mm.drop_segments=mm.drop_segments+v.drop_segments when not matched then insert values(v.obj#,v.inserts,v.updates,v.deletes,sysdate,v.flags,v.drop_segments) recorded first on Aug 3, 2021, 7:20:08 AM +--@SUCCESSFULLY_PARSED_AND_DEPARSED first on 18 Dec 2023, 17:18:40 \ No newline at end of file diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge04.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge04.sql index 403806403..a4b80ac0b 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge04.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/merge04.sql @@ -26,4 +26,5 @@ update set mm.inserts = mm.inserts + v.inserts, mm.updates = mm.updates + v.upda mm.flags = mm.flags + v.flags - bitand(mm.flags,v.flags) , mm.drop_segments = mm.drop_segments + v.drop_segments when not matched then insert values (v.obj#, v.inserts, v.updates, v.deletes, sysdate, v.flags, v.drop_segments) ---@FAILURE: merge into sys.mon_mods_all$ mm using(select decode(grouping_id(tp.bo#,tsp.pobj#,m.obj#),3,tp.bo#,1,tsp.pobj#,m.obj#)obj#,sum(m.inserts)inserts,sum(m.updates)updates,sum(m.deletes)deletes,decode(sum(bitand(m.flags,1)),0,0,1)+decode(sum(bitand(m.flags,2)),0,0,2)+decode(sum(bitand(m.flags,4)),0,0,4)flags,sum(m.drop_segments)drop_segments from sys.mon_mods$ m,sys.tabcompart$ tp,sys.tabsubpart$ tsp where m.obj#=tsp.obj# and tp.obj#=tsp.pobj# group by rollup(tp.bo#,tsp.pobj#,m.obj#)having grouping_id(tp.bo#,tsp.pobj#,m.obj#)<7 order by 1,2,3)v on(mm.obj#=v.obj#)when matched then update set mm.inserts=mm.inserts+v.inserts,mm.updates=mm.updates+v.updates,mm.deletes=mm.deletes+v.deletes,mm.flags=mm.flags+v.flags-bitand(mm.flags,v.flags),mm.drop_segments=mm.drop_segments+v.drop_segments when not matched then insert values(v.obj#,v.inserts,v.updates,v.deletes,sysdate,v.flags,v.drop_segments) recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: merge into sys.mon_mods_all$ mm using(select decode(grouping_id(tp.bo#,tsp.pobj#,m.obj#),3,tp.bo#,1,tsp.pobj#,m.obj#)obj#,sum(m.inserts)inserts,sum(m.updates)updates,sum(m.deletes)deletes,decode(sum(bitand(m.flags,1)),0,0,1)+decode(sum(bitand(m.flags,2)),0,0,2)+decode(sum(bitand(m.flags,4)),0,0,4)flags,sum(m.drop_segments)drop_segments from sys.mon_mods$ m,sys.tabcompart$ tp,sys.tabsubpart$ tsp where m.obj#=tsp.obj# and tp.obj#=tsp.pobj# group by rollup(tp.bo#,tsp.pobj#,m.obj#)having grouping_id(tp.bo#,tsp.pobj#,m.obj#)<7 order by 1,2,3)v on(mm.obj#=v.obj#)when matched then update set mm.inserts=mm.inserts+v.inserts,mm.updates=mm.updates+v.updates,mm.deletes=mm.deletes+v.deletes,mm.flags=mm.flags+v.flags-bitand(mm.flags,v.flags),mm.drop_segments=mm.drop_segments+v.drop_segments when not matched then insert values(v.obj#,v.inserts,v.updates,v.deletes,sysdate,v.flags,v.drop_segments) recorded first on Aug 3, 2021, 7:20:08 AM +--@SUCCESSFULLY_PARSED_AND_DEPARSED first on 18 Dec 2023, 17:18:40 \ No newline at end of file From 77f6fb8c92b33780fe1d3d6210089a362a643acf Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Tue, 19 Dec 2023 12:28:13 +0800 Subject: [PATCH 49/77] feat: support mysql with rollup (#1923) * feat: support mysql with rollup * feat: support mysql with rollup --- .../statement/select/GroupByElement.java | 22 +++++++++++++++---- .../statement/select/OrderByElement.java | 17 ++++++++++++-- .../util/deparser/GroupByDeParser.java | 4 ++++ .../util/deparser/OrderByDeParser.java | 7 ++++-- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 19 ++++++++++------ .../statement/select/SelectTest.java | 11 ++++++++++ 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java b/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java index 4aa45bbf0..d052ad465 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/GroupByElement.java @@ -9,10 +9,6 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.operators.relational.ExpressionList; -import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; - import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -20,10 +16,15 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; public class GroupByElement implements Serializable { private ExpressionList groupByExpressions = new ExpressionList(); private List groupingSets = new ArrayList<>(); + // postgres rollup is an ExpressionList + private boolean mysqlWithRollup = false; public boolean isUsingBrackets() { return groupByExpressions.isUsingBrackets(); @@ -90,6 +91,10 @@ public String toString() { b.append(")"); } + if (isMysqlWithRollup()) { + b.append(" WITH ROLLUP"); + } + return b.toString(); } @@ -126,4 +131,13 @@ public GroupByElement addGroupingSets(Collection groupingSets) collection.addAll(groupingSets); return this.withGroupingSets(collection); } + + public boolean isMysqlWithRollup() { + return mysqlWithRollup; + } + + public GroupByElement setMysqlWithRollup(boolean mysqlWithRollup) { + this.mysqlWithRollup = mysqlWithRollup; + return this; + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java b/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java index acba858fd..36597fde4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/OrderByElement.java @@ -9,9 +9,8 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.expression.Expression; - import java.io.Serializable; +import net.sf.jsqlparser.expression.Expression; public class OrderByElement implements Serializable { @@ -24,6 +23,8 @@ public static NullOrdering from(String ordering) { } private Expression expression; + // postgres rollup is an ExpressionList + private boolean mysqlWithRollup = false; private boolean asc = true; private boolean ascDescPresent = false; private NullOrdering nullOrdering; @@ -79,6 +80,9 @@ public String toString() { b.append(' '); b.append(nullOrdering == NullOrdering.NULLS_FIRST ? "NULLS FIRST" : "NULLS LAST"); } + if (isMysqlWithRollup()) { + b.append(" WITH ROLLUP"); + } return b.toString(); } @@ -106,4 +110,13 @@ public E getExpression(Class type) { return type.cast(getExpression()); } + public boolean isMysqlWithRollup() { + return mysqlWithRollup; + } + + public OrderByElement setMysqlWithRollup(boolean mysqlWithRollup) { + this.mysqlWithRollup = mysqlWithRollup; + return this; + } + } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/GroupByDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/GroupByDeParser.java index 3c5cedf10..694a97d38 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/GroupByDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/GroupByDeParser.java @@ -45,5 +45,9 @@ public void deParse(GroupByElement groupBy) { } buffer.append(")"); } + + if (groupBy.isMysqlWithRollup()) { + buffer.append(" WITH ROLLUP"); + } } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/OrderByDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/OrderByDeParser.java index 1586bc6ed..8eb137051 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/OrderByDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/OrderByDeParser.java @@ -11,7 +11,6 @@ import java.util.Iterator; import java.util.List; - import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.statement.select.OrderByElement; @@ -58,9 +57,13 @@ public void deParseElement(OrderByElement orderBy) { } if (orderBy.getNullOrdering() != null) { buffer.append(' '); - buffer.append(orderBy.getNullOrdering() == OrderByElement.NullOrdering.NULLS_FIRST ? "NULLS FIRST" + buffer.append(orderBy.getNullOrdering() == OrderByElement.NullOrdering.NULLS_FIRST + ? "NULLS FIRST" : "NULLS LAST"); } + if (orderBy.isMysqlWithRollup()) { + buffer.append(" WITH ROLLUP"); + } } void setExpressionVisitor(ExpressionVisitor expressionVisitor) { diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index a2a6220fc..37c48fd91 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -383,6 +383,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -1802,7 +1803,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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | 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" ) + | 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="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOCK" | 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="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="COSTS" | tk="CS" | tk="CYCLE" | tk="DATA" | 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="GRANT" | tk="GUARD" | tk="HASH" | tk="HISTORY" | tk="HOPPING" | tk="INCLUDE" | tk="INCREMENT" | tk="INDEX" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="ISNULL" | tk="JSON" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="LAST" | tk="LEADING" | tk="LINK" | tk="LOCAL" | tk="LOCKED" | tk="LOG" | tk="LOOP" | 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="REMOTE" | tk="RENAME" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | 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; } } @@ -2984,20 +2985,23 @@ GroupByElement GroupByColumnReferences(): ( LOOKAHEAD(2) ( - "(" - list = GroupingSet() { groupBy.addGroupingSet(list); } - ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* + + "(" + list = GroupingSet() { groupBy.addGroupingSet(list); } + ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* ")" ) | ( list = ExpressionList() { groupBy.setGroupByExpressions(list); } ( - LOOKAHEAD(2) "(" - list = GroupingSet() { groupBy.addGroupingSet(list); } - ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* + LOOKAHEAD(2) + "(" + list = GroupingSet() { groupBy.addGroupingSet(list); } + ( LOOKAHEAD(2) "," list = GroupingSet() { groupBy.addGroupingSet(list); })* ")" )? + [ LOOKAHEAD(2) { groupBy.setMysqlWithRollup(true); } ] ) ) { @@ -3072,6 +3076,7 @@ OrderByElement OrderByElement(): ) ] ] + [ LOOKAHEAD(2) { orderByElement.setMysqlWithRollup(true); } ] { orderByElement.setExpression(columnReference); return orderByElement; 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 e6f85d4b7..527313bfe 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -5795,4 +5795,15 @@ void testBackSlashQuotationIssue1812() throws JSQLParserException { sqlStr, parser -> parser .withBackslashEscapeCharacter(true)); } + + @Test + public void testIssue1907() throws JSQLParserException { + String stmt = "SELECT MAX(a, b, c), COUNT(*), D FROM tab1 GROUP BY D WITH ROLLUP"; + assertSqlCanBeParsedAndDeparsed(stmt); + + // since mysql 8.0.12 + String stmt2 = + "SELECT * FROM (SELECT year, person, SUM(amount) FROM rentals GROUP BY year, person) t1 ORDER BY year DESC WITH ROLLUP"; + assertSqlCanBeParsedAndDeparsed(stmt2); + } } From 39ff32785589d83f4ae7c97c3b435b973587da9e Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Thu, 21 Dec 2023 18:14:38 +0700 Subject: [PATCH 50/77] style: fix spelling and punctuation --- src/site/sphinx/migration.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/site/sphinx/migration.rst b/src/site/sphinx/migration.rst index b21a961c0..7fc503699 100644 --- a/src/site/sphinx/migration.rst +++ b/src/site/sphinx/migration.rst @@ -118,7 +118,7 @@ The ``ValuesStatement`` has been replaced by ``Values``, which implements ``Sele `Expression` Lists --------------------------------- -The class ``ExpressionList`` directly extends ``List`` directly and so ``ExpressionList.getExpressions()`` is obsolete. +The class ``ExpressionList`` 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. @@ -194,11 +194,11 @@ The class ``SelectItem`` is now generic and various deriva `Select` Statement --------------------------------- -``SelectBody`` has been removed and `PlainSelect` can be used directly +``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``) +``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 +``SubSelect`` has been removed and any instance of ``Select`` (``PlainSelect``, ``Values`` or ``SetOperationList``) can be used instead .. tab:: Select @@ -383,5 +383,5 @@ A ``List`` is used for any `Set` clause within `Insert`, `Update`, `U `Statements` collection --------------------------------- -The ``Statements`` class extends `List` directly and so ``Statements.getStatements()`` is obsolete. +The ``Statements`` class extends `List` directly and so `Statements.getStatements()` is obsolete. From 14637ce64763b424aa64deb5ba659c4d2ae69cfa Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Thu, 21 Dec 2023 21:19:15 +0700 Subject: [PATCH 51/77] fix: `GO` shall terminate statement only, when appearing alone on an empty line - fixes #1929 --- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../statement/StatementSeparatorTest.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 37c48fd91..26b950a98 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -486,7 +486,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ TOKEN : /* Statement Separators */ { - + } TOKEN : /* Operators */ diff --git a/src/test/java/net/sf/jsqlparser/statement/StatementSeparatorTest.java b/src/test/java/net/sf/jsqlparser/statement/StatementSeparatorTest.java index ce92cdd26..b99ee6f26 100644 --- a/src/test/java/net/sf/jsqlparser/statement/StatementSeparatorTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/StatementSeparatorTest.java @@ -21,7 +21,7 @@ void testDoubleNewLine() throws JSQLParserException { String sqlStr = "SELECT * FROM DUAL\n\n\nSELECT * FROM DUAL\n\n\n\nSELECT * FROM dual\n\n\n\n\nSELECT * FROM dual"; Statements statements = CCJSqlParserUtil.parseStatements(sqlStr); - Assertions.assertEquals(4, statements.getStatements().size()); + Assertions.assertEquals(4, statements.size()); } @Test @@ -29,7 +29,7 @@ void testNewLineSlash() throws JSQLParserException { String sqlStr = "SELECT * FROM DUAL\n\n\nSELECT * FROM DUAL\n/\nSELECT * FROM dual\n/\n\nSELECT * FROM dual"; Statements statements = CCJSqlParserUtil.parseStatements(sqlStr); - Assertions.assertEquals(4, statements.getStatements().size()); + Assertions.assertEquals(4, statements.size()); } @Test @@ -37,7 +37,15 @@ void testNewLineGo() throws JSQLParserException { String sqlStr = "SELECT * FROM DUAL\n\n\nSELECT * FROM DUAL\nGO\nSELECT * FROM dual\ngo\n\nSELECT * FROM dual\ngo"; Statements statements = CCJSqlParserUtil.parseStatements(sqlStr); - Assertions.assertEquals(4, statements.getStatements().size()); + Assertions.assertEquals(4, statements.size()); + } + + @Test + void testNewLineNotGoIssue() throws JSQLParserException { + String sqlStr = + "select name,\ngoods from test_table"; + Statements statements = CCJSqlParserUtil.parseStatements(sqlStr); + Assertions.assertEquals(1, statements.size()); } @Test @@ -52,6 +60,6 @@ void testMSSQLBlock() throws JSQLParserException { String sqlStr = "create view MyView1 as\n" + "select Id,Name from table1\n" + "go\n" + "create view MyView2 as\n" + "select Id,Name from table1\n" + "go"; Statements statements = CCJSqlParserUtil.parseStatements(sqlStr); - Assertions.assertEquals(2, statements.getStatements().size()); + Assertions.assertEquals(2, statements.size()); } } From dce6a205eee205fc3a0db144aea9b3bec12a28f5 Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Sat, 23 Dec 2023 00:53:04 +0800 Subject: [PATCH 52/77] Fix: ALTER INDEX COMMENT (#1932) * Fix: ALTER INDEX COMMENT * Fix: ALTER INDEX COMMENT --- .../statement/alter/AlterExpression.java | 5 +++++ .../statement/create/table/Index.java | 5 ----- .../statement/create/table/NamedConstraint.java | 6 ------ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- .../jsqlparser/expression/OracleHintTest.java | 9 +++++++++ .../jsqlparser/statement/alter/AlterTest.java | 17 +++++++++++++++++ 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index 084749e42..c5e0f5078 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -538,6 +538,11 @@ public String toString() { b.append(' ').append(PlainSelect.getStringList(parameters, false, false)); } + if (index != null && index.getCommentText() != null) { + // `USING` is a parameters + b.append(" COMMENT ").append(index.getCommentText()); + } + return b.toString(); } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java index 651bbe66c..720fa1abe 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/Index.java @@ -143,11 +143,6 @@ public String toString() { return head; } - // MYSQL: ALTER TABLE ADD INDEX COMMENT 'comment' - if (getCommentText() != null) { - return head + " " + tail + " COMMENT " + getCommentText(); - } - return head + " " + tail; } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java index b44b7a114..f9a7f391d 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java @@ -21,12 +21,6 @@ public String toString() { String head = getName() != null ? "CONSTRAINT " + getName() + " " : ""; String tail = getType() + " " + PlainSelect.getStringList(getColumnsNames(), true, true) + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); - - // MYSQL: ALTER TABLE ADD CONSTRAINT COMMENT 'comment' - if (getCommentText() != null) { - return head + tail + " COMMENT " + getCommentText(); - } - return head + tail; } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 26b950a98..dfe4f095b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -6256,9 +6256,9 @@ AlterExpression AlterExpression(): index = new Index().withType(tk.image).withName(sk3).withColumnsNames(columnNames); alterExp.setIndex(index); } - [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } [ sk4=RelObjectName() { alterExp.addParameters("USING", sk4); }] + [ index = IndexWithComment(index) { alterExp.setIndex(index); } ] ) | LOOKAHEAD(3) ( diff --git a/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java index 2d76440b7..2da69721c 100644 --- a/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/OracleHintTest.java @@ -1,3 +1,12 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2023 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.expression; import net.sf.jsqlparser.JSQLParserException; diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 0821224f8..c399bcb71 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -483,6 +483,23 @@ public void testIssue679() throws JSQLParserException { "ALTER TABLE tb_session_status ADD INDEX idx_user_id_name (user_id, user_name(10)), ADD INDEX idx_user_name (user_name)"); } + @Test + public void testAlterTableColumnCommentIssue1926() throws JSQLParserException { + String statement = + "ALTER TABLE `student` ADD INDEX `idx_age` (`age`) USING BTREE COMMENT 'index age'"; + assertSqlCanBeParsedAndDeparsed(statement); + + String stmt2 = + "ALTER TABLE `student` ADD INDEX `idx_name` (`name`) COMMENT 'index name', " + + "ADD INDEX `idx_age` (`age`) USING BTREE COMMENT 'index age'"; + assertSqlCanBeParsedAndDeparsed(stmt2); + + // TODO NOT SUPPORT MYSQL: ADD {INDEX | KEY} `idx_age` USING BTREE (`age`) + // String stmt3 = "ALTER TABLE `student` ADD INDEX `idx_age` USING BTREE (`age`) COMMENT + // 'index age'"; + // assertSqlCanBeParsedAndDeparsed(stmt3); + } + @Test public void testAlterTableIndex586() throws Exception { Statement result = From 45f98f807df27632f743fc37979225bfc86496ab Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Sat, 23 Dec 2023 16:33:05 +0800 Subject: [PATCH 53/77] Fix: DESC and EXPLAIN (#1933) * Fix: DESC and EXPLAIN * Fix: DESC and EXPLAIN * add test --- .../sf/jsqlparser/parser/feature/Feature.java | 8 ++++ .../statement/DescribeStatement.java | 12 +++++- .../statement/ExplainStatement.java | 31 ++++++++++---- .../sf/jsqlparser/util/TablesNamesFinder.java | 4 +- .../util/deparser/StatementDeParser.java | 19 +++++---- .../util/validation/feature/MySqlVersion.java | 1 + .../validator/StatementValidator.java | 5 ++- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 40 +++++++++++++------ .../sf/jsqlparser/statement/DescribeTest.java | 9 ++++- .../validator/StatementValidatorTest.java | 13 +++++- .../select/oracle-tests/explain01.sql | 3 +- 11 files changed, 111 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 9b8502094..011919338 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -593,6 +593,14 @@ public enum Feature { * @see DescribeStatement */ describe, + + /** + * SQL "DESC" statement is allowed + * + * @see DescribeStatement + */ + desc, + /** * SQL "EXPLAIN" statement is allowed * diff --git a/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java b/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java index 09d84c2e7..8a2ab01d4 100644 --- a/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java @@ -14,6 +14,7 @@ public class DescribeStatement implements Statement { private Table table; + private String describeType; public DescribeStatement() { // empty constructor @@ -33,7 +34,7 @@ public void setTable(Table table) { @Override public String toString() { - return "DESCRIBE " + table.getFullyQualifiedName(); + return this.describeType + " " + table.getFullyQualifiedName(); } @Override @@ -45,4 +46,13 @@ public DescribeStatement withTable(Table table) { this.setTable(table); return this; } + + public String getDescribeType() { + return describeType; + } + + public DescribeStatement setDescribeType(String describeType) { + this.describeType = describeType; + return this; + } } diff --git a/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java b/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java index 2957472db..01e7c2f18 100644 --- a/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java @@ -9,11 +9,11 @@ */ package net.sf.jsqlparser.statement; -import net.sf.jsqlparser.statement.select.Select; - import java.io.Serializable; import java.util.LinkedHashMap; import java.util.stream.Collectors; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.select.Select; /** * An {@code EXPLAIN} statement @@ -22,11 +22,21 @@ public class ExplainStatement implements Statement { private Select select; private LinkedHashMap options; + private Table table; public ExplainStatement() { // empty constructor } + public Table getTable() { + return table; + } + + public ExplainStatement setTable(Table table) { + this.table = table; + return this; + } + public ExplainStatement(Select select) { this.select = select; } @@ -68,14 +78,21 @@ public Option getOption(OptionType optionType) { @Override public String toString() { StringBuilder statementBuilder = new StringBuilder("EXPLAIN"); - if (options != null) { + if (table != null) { + statementBuilder.append(" ").append(table); + } else { + if (options != null) { + statementBuilder.append(" "); + statementBuilder.append(options.values().stream().map(Option::formatOption) + .collect(Collectors.joining(" "))); + } + statementBuilder.append(" "); - statementBuilder.append(options.values().stream().map(Option::formatOption) - .collect(Collectors.joining(" "))); + if (select != null) { + statementBuilder.append(select.toString()); + } } - statementBuilder.append(" "); - statementBuilder.append(select.toString()); return statementBuilder.toString(); } diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index b3b697e4d..7a94ceff7 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -1067,7 +1067,9 @@ public void visit(DescribeStatement describe) { @Override public void visit(ExplainStatement explain) { - explain.getStatement().accept((StatementVisitor) this); + if (explain.getStatement() != null) { + explain.getStatement().accept((StatementVisitor) this); + } } @Override diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index 883652204..f5326ae41 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -9,6 +9,9 @@ */ package net.sf.jsqlparser.util.deparser; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; import net.sf.jsqlparser.statement.Block; import net.sf.jsqlparser.statement.Commit; import net.sf.jsqlparser.statement.CreateFunctionalStatement; @@ -59,10 +62,6 @@ import net.sf.jsqlparser.statement.update.Update; import net.sf.jsqlparser.statement.upsert.Upsert; -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - public class StatementDeParser extends AbstractDeParser implements StatementVisitor { private final ExpressionDeParser expressionDeParser; @@ -347,19 +346,25 @@ public void visit(Comment comment) { @Override public void visit(DescribeStatement describe) { - buffer.append("DESCRIBE "); + buffer.append(describe.getDescribeType()); + buffer.append(" "); buffer.append(describe.getTable()); } @Override public void visit(ExplainStatement explain) { buffer.append("EXPLAIN "); - if (explain.getOptions() != null) { + if (explain.getTable() != null) { + buffer.append(explain.getTable()); + } else if (explain.getOptions() != null) { buffer.append(explain.getOptions().values().stream() .map(ExplainStatement.Option::formatOption).collect(Collectors.joining(" "))); buffer.append(" "); } - explain.getStatement().accept(this); + if (explain.getStatement() != null) { + explain.getStatement().accept(this); + + } } @Override diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java index a2397f9c5..a7aeeaae0 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/MySqlVersion.java @@ -115,6 +115,7 @@ public enum MySqlVersion implements Version { // https://dev.mysql.com/doc/refman/8.0/en/describe.html Feature.describe, + Feature.desc, // https://dev.mysql.com/doc/refman/8.0/en/explain.html Feature.explain, // https://dev.mysql.com/doc/refman/8.0/en/show.html diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java index 2f2932e78..ec9650105 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java @@ -209,13 +209,16 @@ public void visit(Comment comment) { @Override public void visit(DescribeStatement describe) { validateFeature(Feature.describe); + validateFeature(Feature.desc); validateOptionalFromItem(describe.getTable()); } @Override public void visit(ExplainStatement explain) { validateFeature(Feature.explain); - explain.getStatement().accept(this); + if (explain.getStatement() != null) { + explain.getStatement().accept(this); + } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index dfe4f095b..fc8d2fe47 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -1056,29 +1056,43 @@ PurgeStatement PurgeStatement(): { DescribeStatement Describe(): { Table table; + DescribeStatement stmt = new DescribeStatement(); + Token tk = null; } { - table = Table() + (tk= | tk=) + table = Table() { stmt.setDescribeType(tk.image).setTable(table); } { - return new DescribeStatement(table); + return stmt; } } ExplainStatement Explain(): { Select select; + Table table = null; List options = null; + ExplainStatement es = new ExplainStatement(); } { - options=ExplainStatementOptions() - select = Select( ) - { - ExplainStatement es = new ExplainStatement(select); - if(options != null && !options.isEmpty()) { - for(ExplainStatement.Option o : options) { - es.addOption(o); - } - } - return es; - } + ( + LOOKAHEAD(3)( + options=ExplainStatementOptions() + select = Select( ) + { + es.setStatement(select); + if (options != null && !options.isEmpty()) { + for(ExplainStatement.Option o : options) { + es.addOption(o); + } + } + } + ) + | + ( + table=Table( ) { es.setTable(table); } + ) + + ) + { return es; } } /** diff --git a/src/test/java/net/sf/jsqlparser/statement/DescribeTest.java b/src/test/java/net/sf/jsqlparser/statement/DescribeTest.java index 204b886c4..a81a368a5 100644 --- a/src/test/java/net/sf/jsqlparser/statement/DescribeTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/DescribeTest.java @@ -9,8 +9,9 @@ */ package net.sf.jsqlparser.statement; -import net.sf.jsqlparser.JSQLParserException; import static net.sf.jsqlparser.test.TestUtils.*; + +import net.sf.jsqlparser.JSQLParserException; import org.junit.jupiter.api.Test; public class DescribeTest { @@ -20,6 +21,12 @@ public void testDescribe() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("DESCRIBE foo.products"); } + @Test + public void testDescribeIssue1931() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("DESC table_name"); + assertSqlCanBeParsedAndDeparsed("EXPLAIN table_name"); + } + @Test public void testDescribeIssue1212() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("DESCRIBE file_azbs.productcategory.json"); diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/StatementValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/StatementValidatorTest.java index a17cb1087..7a7e52888 100644 --- a/src/test/java/net/sf/jsqlparser/util/validation/validator/StatementValidatorTest.java +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/StatementValidatorTest.java @@ -29,11 +29,19 @@ public void testValidateCreateSchema() throws JSQLParserException { @Test public void testValidateCreateSchemaNotAllowed() throws JSQLParserException { - for (String sql : Arrays.asList("CREATE SCHEMA my_schema", "CREATE SCHEMA myschema AUTHORIZATION myauth")) { + for (String sql : Arrays.asList("CREATE SCHEMA my_schema", + "CREATE SCHEMA myschema AUTHORIZATION myauth")) { validateNotAllowed(sql, 1, 1, FeaturesAllowed.DML, Feature.createSchema); } } + @Test + public void testValidateDescNoErrors() throws JSQLParserException { + for (String sql : Arrays.asList("DESC table_name", "EXPLAIN table_name")) { + validateNoErrors(sql, 1, DatabaseType.MYSQL); + } + } + @Test public void testValidateTruncate() throws JSQLParserException { validateNoErrors("TRUNCATE TABLE my_table", 1, DatabaseType.DATABASES); @@ -53,7 +61,8 @@ public void testValidateBlock() throws JSQLParserException { @Test public void testValidateComment() throws JSQLParserException { for (String sql : Arrays.asList("COMMENT ON VIEW myschema.myView IS 'myComment'", - "COMMENT ON COLUMN myTable.myColumn is 'Some comment'", "COMMENT ON TABLE table1 IS 'comment1'")) { + "COMMENT ON COLUMN myTable.myColumn is 'Some comment'", + "COMMENT ON TABLE table1 IS 'comment1'")) { validateNoErrors(sql, 1, DatabaseType.H2, DatabaseType.ORACLE, DatabaseType.POSTGRESQL); } } diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/explain01.sql b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/explain01.sql index d4fc556bd..c60faf151 100644 --- a/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/explain01.sql +++ b/src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/explain01.sql @@ -16,4 +16,5 @@ explain plan (select department_id from departments where location_id = 1700) ---@FAILURE: Encountered unexpected token: "plan" recorded first on Aug 3, 2021, 7:20:08 AM \ No newline at end of file +--@FAILURE: Encountered unexpected token: "plan" recorded first on Aug 3, 2021, 7:20:08 AM +--@FAILURE: Encountered unexpected token: "set" "SET" recorded first on 2023年12月23日 下午1:38:33 \ No newline at end of file From 56515aba6ca893f3977c1c4ea9f2de5d2314f179 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sat, 23 Dec 2023 18:07:48 +0700 Subject: [PATCH 54/77] fix: refactor `JsonExpression`, avoiding expensive semantic lookahead and improving performance --- .../jsqlparser/expression/JsonExpression.java | 57 ++++++-- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 128 ++++++++---------- .../select/NestedBracketsPerformanceTest.java | 127 +++++++++++++++++ .../statement/select/PostgresTest.java | 4 +- 4 files changed, 234 insertions(+), 82 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java index 5d6aa794c..9fe2811cf 100644 --- a/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/JsonExpression.java @@ -9,16 +9,31 @@ */ package net.sf.jsqlparser.expression; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; + +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collection; import java.util.List; - -import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import java.util.Map; public class JsonExpression extends ASTNodeAccessImpl implements Expression { private Expression expr; - private List idents = new ArrayList(); - private List operators = new ArrayList(); + private final List> idents = new ArrayList<>(); + + public JsonExpression() { + + } + + public JsonExpression(Expression expr) { + this.expr = expr; + } + + public JsonExpression(Expression expr, List> idents) { + this.expr = expr; + this.idents.addAll(idents); + } @Override public void accept(ExpressionVisitor expressionVisitor) { @@ -34,24 +49,46 @@ public void setExpression(Expression expr) { } public void addIdent(String ident, String operator) { - idents.add(ident); - operators.add(operator); + idents.add(new AbstractMap.SimpleEntry<>(ident, operator)); } - public List getIdents() { + public void addAllIdents(Collection> idents) { + this.idents.addAll(idents); + } + + public List> getIdentList() { return idents; } + public Map.Entry getIdent(int index) { + return idents.get(index); + } + + @Deprecated + public List getIdents() { + ArrayList l = new ArrayList<>(); + for (Map.Entry ident : idents) { + l.add(ident.getKey()); + } + + return l; + } + + @Deprecated public List getOperators() { - return operators; + ArrayList l = new ArrayList<>(); + for (Map.Entry ident : idents) { + l.add(ident.getValue()); + } + return l; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append(expr.toString()); - for (int i = 0; i < idents.size(); i++) { - b.append(operators.get(i)).append(idents.get(i)); + for (Map.Entry ident : idents) { + b.append(ident.getValue()).append(ident.getKey()); } return b.toString(); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index fc8d2fe47..37b7a329f 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3638,7 +3638,27 @@ Expression Between(Expression leftExpression) : } { [ { result.setNot(true); }] - betweenExpressionStart=SimpleExpression() betweenExpressionEnd=SimpleExpression() + + ( + LOOKAHEAD( 3 ) betweenExpressionStart = ParenthesedSelect() + | + LOOKAHEAD( SimpleFunction() ) betweenExpressionStart = SimpleFunction() + | + LOOKAHEAD( RegularCondition() ) betweenExpressionStart = RegularCondition() + | + betweenExpressionStart = SimpleExpression() + ) + + + ( + LOOKAHEAD( 3 ) betweenExpressionEnd = ParenthesedSelect() + | + LOOKAHEAD( SimpleFunction() ) betweenExpressionEnd = SimpleFunction() + | + LOOKAHEAD( RegularCondition() ) betweenExpressionEnd = RegularCondition() + | + betweenExpressionEnd = SimpleExpression() + ) { result.setLeftExpression(leftExpression); @@ -3985,7 +4005,10 @@ Expression SimpleExpression(): user = UserVariable() ( operation = "=" | operation = ":=" ) ] - retval=ConcatExpression() + + ( + retval=ConcatExpression() + ) ) { if (user != null) { @@ -4172,6 +4195,8 @@ Expression PrimaryExpression() #PrimaryExpression: boolean exclamationMarkNot = false; boolean dateExpressionAllowed = true; ExpressionList list; + + final List> jsonIdents = new ArrayList>(); } { [ { not=true; } | "!" { not=true; exclamationMarkNot=true; } ] @@ -4195,15 +4220,13 @@ Expression PrimaryExpression() #PrimaryExpression: | retval=XMLSerializeExpr() - | LOOKAHEAD(JsonExpression(), {!interrupted}) retval=JsonExpression() - - | LOOKAHEAD(JsonFunction(), {!interrupted}) retval = JsonFunction() + | LOOKAHEAD(3, { !interrupted}) retval = JsonFunction() - | LOOKAHEAD(JsonAggregateFunction(), {!interrupted}) retval = JsonAggregateFunction() + | LOOKAHEAD(3, { !interrupted}) retval = JsonAggregateFunction() - | LOOKAHEAD(FullTextSearch(), {!interrupted}) retval = FullTextSearch() + | LOOKAHEAD(3, { !interrupted}) retval = FullTextSearch() - | LOOKAHEAD(Function(), {!interrupted}) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ] + | LOOKAHEAD(Function(), { !interrupted}) retval=Function() [ LOOKAHEAD(2) retval = AnalyticExpression( (Function) retval ) ] | LOOKAHEAD(2, {!interrupted}) retval = IntervalExpression() { dateExpressionAllowed = false; } @@ -4267,12 +4290,28 @@ Expression PrimaryExpression() #PrimaryExpression: [ retval = ArrayExpression(retval) ] ( "::" type=ColDataType() { - castExpr = new CastExpression(); - castExpr.setUseCastKeyword(false); - castExpr.setLeftExpression(retval); - castExpr.setColDataType(type); - retval=castExpr; - } )* + castExpr = new CastExpression(); + castExpr.setUseCastKeyword(false); + castExpr.setLeftExpression(retval); + castExpr.setColDataType(type); + retval=castExpr; + } + )* + + // Check for JSON operands + [ + LOOKAHEAD(2) ( + "->" (token= | token=) { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"->")); } + | + "->>" (token= | token=) { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"->>")); } + | + "#>" token= { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"#>")); } + | + "#>>" token= { jsonIdents.add(new AbstractMap.SimpleEntry(token.image,"#>>")); } + )+ + retval = JsonExpression(retval, jsonIdents) + ] + ( LOOKAHEAD(2) timezoneRightExpr=PrimaryExpression() { if (timezoneExpr == null) @@ -4428,65 +4467,13 @@ Expression ParenthesedExpression(): } } -JsonExpression JsonExpression() : { - JsonExpression result = new JsonExpression(); - Expression expr; +JsonExpression JsonExpression(Expression expr, List> idents) : { + JsonExpression result = new JsonExpression(expr, idents); Token token; ColDataType type = null; CastExpression castExpr = null; } { - ( - LOOKAHEAD(3, {!interrupted}) expr=CaseWhenExpression() - | - expr = SimpleJdbcParameter() - | - LOOKAHEAD(2, {!interrupted}) expr=JdbcNamedParameter() - | - expr=UserVariable() - | - LOOKAHEAD(JsonFunction(), {getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expr = JsonFunction() - | - LOOKAHEAD(JsonAggregateFunction(), {getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expr = JsonAggregateFunction() - | - LOOKAHEAD(FullTextSearch(), {getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expr = FullTextSearch() - | - LOOKAHEAD( Function() , {getAsBoolean(Feature.allowComplexParsing) && !interrupted} ) expr=Function() - | - LOOKAHEAD( 2, {!interrupted} ) expr=Column() - | - token= { expr = new StringValue(token.image); } - | - LOOKAHEAD(ParenthesedExpression(), {getAsBoolean(Feature.allowComplexParsing)} ) expr = ParenthesedExpression() - | - LOOKAHEAD( 3, {getAsBoolean(Feature.allowComplexParsing) && !interrupted}) expr=ParenthesedSelect() - ) - - ( - "::" type=ColDataType() - { - castExpr = new CastExpression(); - castExpr.setUseCastKeyword(false); - castExpr.setLeftExpression(expr); - castExpr.setColDataType(type); - expr=castExpr; - } - )* - { - result.setExpression(expr); - } - - ( - LOOKAHEAD(2) ( - "->" (token= | token=) {result.addIdent(token.image,"->");} - | - "->>" (token= | token=) {result.addIdent(token.image,"->>");} - | - "#>" token= {result.addIdent(token.image,"#>");} - | - "#>>" token= {result.addIdent(token.image,"#>>");} - ) - )+ // chaining JSON Expressions, e.g. // '{"obj":{"field": "value"}}'::JSON -> 'obj'::TEXT ->> 'field'::TEXT @@ -4504,8 +4491,7 @@ JsonExpression JsonExpression() : { ) )+ { - result = new JsonExpression(); - result.setExpression(expr); + result = new JsonExpression(expr); } ( @@ -5162,6 +5148,8 @@ Function SimpleFunction(): | LOOKAHEAD( AllTableColumns() ) expr=AllTableColumns() | + LOOKAHEAD( 3 ) expr = ParenthesedSelect() + | LOOKAHEAD( SimpleFunction() ) expr = SimpleFunction() | LOOKAHEAD( RegularCondition() ) expr = RegularCondition() diff --git a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java index 6a85d65d2..569b88873 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/NestedBracketsPerformanceTest.java @@ -228,4 +228,131 @@ public void testIssue1103() throws JSQLParserException { + ",0),0),0),0),0),0),0),0)\n" + ",0),0),0),0),0),0),0),0)", true); } + + @Test + public void testDeepFunctionParameters() throws JSQLParserException { + String sqlStr = "SELECT a.*\n" + + " , To_Char( a.eingangsdat, 'MM.YY' ) AS eingmonat\n" + + " , ( SELECT Trim( b.atext )\n" + + " FROM masseinheiten x\n" + + " , a_lmt b\n" + + " WHERE x.a_text_id = b.a_text_id\n" + + " AND b.sprach_kz = sprache\n" + + " AND x.masseinh_id = a.masseinh_id ) AS reklamengesonst_bez\n" + + " , ( SELECT Trim( name ) || ' ' || Trim( vorname ) AS eingangerfasser_name\n" + + " FROM personal\n" + + " WHERE mandanten_id = m_personal\n" + + " AND personal_id = eingangerfasser ) AS eingangerfasser_name\n" + + " , Nvl( ( SELECT Max( change_date )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), sysdate ) AS abschlussdatum\n" + + " , a.sachstand\n" + + " , a.bewertung\n" + + " , a.massnahmen\n" + + " , ( Decode( Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " , - 1, Trunc( sysdate ) - Trunc( a.adate ) - ( SELECT Count()\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND Trunc( sysdate ) )\n" + + " , Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " - ( SELECT Count()\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND ( SELECT Max( Trunc( change_date ) )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ) ) ) + 1 ) AS laufzeit\n" + + " , Nvl( ( SELECT grenzwert\n" + + " FROM beschfehler\n" + + " WHERE beschfehler_id = a.beschwkat_id ), 0 ) AS grenzwert\n" + + " , Nvl( ( SELECT warnwert\n" + + " FROM beschfehler\n" + + " WHERE beschfehler_id = a.beschwkat_id ), 0 ) AS warnwert\n" + + " , a.beschstatus_id AS pruef_status\n" + + " , ( CASE\n" + + " WHEN ( ( Decode( Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " , - 1, Trunc( sysdate ) - Trunc( a.adate ) - ( SELECT Count()\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND Trunc( sysdate ) )\n" + + " , Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " - ( SELECT Count()\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND ( SELECT Max( Trunc( change_date ) )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ) ) ) + 1 ) - Nvl( ( SELECT grenzwert\n" + + " FROM beschfehler\n" + + " WHERE beschfehler_id = a.beschwkat_id ), 0 ) ) < 0\n" + + " THEN 0\n" + + " ELSE ( ( Decode( Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " , - 1, Trunc( sysdate ) - Trunc( a.adate ) - ( SELECT Count()\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND Trunc( sysdate ) )\n" + + " , Nvl( ( SELECT Max( Trunc( change_date ) ) - Trunc( a.adate )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ), - 1 )\n" + + " - ( SELECT Count( * )\n" + + " FROM firmenkalender\n" + + " WHERE firma_id = firmen_id\n" + + " AND Nvl( b_verkauf, 'F' ) = 'T'\n" + + " AND kal_datum BETWEEN Trunc( a.adate )\n" + + " AND ( SELECT Max( Trunc( change_date ) )\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id\n" + + " AND beschstatus_id = 9\n" + + " AND Nvl( inaktiv, 'F' ) != 'T' ) ) ) + 1 ) - Nvl( ( SELECT grenzwert\n" + + " FROM beschfehler\n" + + " WHERE beschfehler_id = a.beschwkat_id ), 0 ) )\n" + + " END ) AS grenz_ueber\n" + + "FROM beschwerden a\n" + + "WHERE a.mandanten_id = m_beschwerde\n" + + " AND a.rec_status <> '9'\n" + + " AND EXISTS ( SELECT 1\n" + + " FROM besch_statusaenderung\n" + + " WHERE beschwerden_id = a.beschwerden_id )\n" + + " AND Nvl( ( SELECT grenzwert\n" + + " FROM beschfehler\n" + + " WHERE beschfehler_id = a.beschwkat_id ), 0 ) > 0\n"; + + assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java index ffa2c8012..56d36d854 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/PostgresTest.java @@ -57,9 +57,9 @@ public void testJSonExpressionIssue1696() throws JSQLParserException { String sqlStr = "SELECT '{\"key\": \"value\"}'::json -> 'key' AS X"; PlainSelect plainSelect = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true); SelectItem selectExpressionItem = - (SelectItem) plainSelect.getSelectItems().get(0); + plainSelect.getSelectItems().get(0); Assertions.assertEquals("'key'", - selectExpressionItem.getExpression(JsonExpression.class).getIdents().get(0)); + selectExpressionItem.getExpression(JsonExpression.class).getIdent(0).getKey()); } @Test From 23ba326db05e863f49cdc678a2a5588da699ff6e Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 00:39:37 +0100 Subject: [PATCH 55/77] corrected license header of some files --- .../operators/relational/TSQLLeftJoin.java | 20 ++++++++----------- .../operators/relational/TSQLRightJoin.java | 20 ++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java index 42a315650..e654bab2d 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLLeftJoin.java @@ -1,15 +1,11 @@ -/* - * #%L JSQLParser library %% Copyright (C) 2004 - 2013 JSQLParser %% This program is free software: - * you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 2.1 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public License along with this program. - * If not, see . #L% +/*- + * #%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; diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java index c11a36c67..a38ad4a9a 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/TSQLRightJoin.java @@ -1,15 +1,11 @@ -/* - * #%L JSQLParser library %% Copyright (C) 2004 - 2013 JSQLParser %% This program is free software: - * you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 2.1 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public License along with this program. - * If not, see . #L% +/*- + * #%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; From b2bd025b424e472e6837ab86147eff270d262656 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 00:51:46 +0100 Subject: [PATCH 56/77] upgraded some plugins --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9067f410c..a17414709 100644 --- a/pom.xml +++ b/pom.xml @@ -371,7 +371,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.2.3 false @@ -379,7 +379,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.10 From 256a1eff904834b47b5ddff9cee520bbc650a0f6 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 00:53:18 +0100 Subject: [PATCH 57/77] upgraded some plugins --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a17414709..e0f3b9248 100644 --- a/pom.xml +++ b/pom.xml @@ -279,7 +279,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.0-M7 + 3.0.1 true false From 0244905959cbcd9debc1d6b48b606f1ccf8a3007 Mon Sep 17 00:00:00 2001 From: jxnu-liguobin Date: Tue, 26 Dec 2023 18:37:41 +0800 Subject: [PATCH 58/77] Fix: mysql comment with `=` (#1936) * Fix: mysql comment with `=` * Fix: mysql comment with `=` --- .../net/sf/jsqlparser/statement/alter/AlterExpression.java | 7 +++++-- .../net/sf/jsqlparser/statement/alter/AlterOperation.java | 2 +- src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 1 + .../java/net/sf/jsqlparser/statement/alter/AlterTest.java | 6 ++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index c5e0f5078..f31de0da3 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -442,8 +442,11 @@ public String toString() { // Oracle Multi Column Drop b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')'); } else { - b.append(operation).append(" "); - + if (operation == AlterOperation.COMMENT_WITH_EQUAL_SIGN) { + b.append("COMMENT =").append(" "); + } else { + b.append(operation).append(" "); + } if (commentText != null) { if (columnName != null) { b.append(columnName).append(" COMMENT "); diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java index d1791cc8b..adddaa14d 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java @@ -10,7 +10,7 @@ package net.sf.jsqlparser.statement.alter; public enum AlterOperation { - ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, UNSPECIFIC; + ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC; public static AlterOperation from(String operation) { return Enum.valueOf(AlterOperation.class, operation.toUpperCase()); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 37b7a329f..3144b3103 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -6500,6 +6500,7 @@ AlterExpression AlterExpression(): ) | ( {alterExp.setOperation(AlterOperation.COMMENT);} + ["=" {alterExp.setOperation(AlterOperation.COMMENT_WITH_EQUAL_SIGN);} ] tk= { alterExp.setCommentText(tk.image); } ) | diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index c399bcb71..ab07437a6 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -896,6 +896,12 @@ public void testAlterTableDropColumnIfExists() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("ALTER TABLE test DROP COLUMN IF EXISTS name"); } + @Test + public void testAlterTableCommentIssue1935() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("ALTER TABLE table_name COMMENT = 'New table comment'"); + assertSqlCanBeParsedAndDeparsed("ALTER TABLE table_name COMMENT 'New table comment'"); + } + @Test public void testAlterTableDropMultipleColumnsIfExists() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( From cff03ca200c674c0d049e3bb555bb8a2d7d30c72 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 13:59:38 +0100 Subject: [PATCH 59/77] upgraded some plugins --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e0f3b9248..081340628 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ net.java.dev.javacc javacc - 7.0.12 + 7.0.13 test From 806d3a39e8f093eea3fdfbe5f61753f4f613a3db Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 21:31:48 +0100 Subject: [PATCH 60/77] Allowed to build JSqlParser on slower computers by increasing a fixed timeout. This should take machine power into account. --- .../java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java index f5010099e..1ec07b531 100644 --- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java @@ -389,7 +389,7 @@ public void run() { assertDoesNotThrow(new Executable() { @Override public void execute() throws Throwable { - executorService.awaitTermination(10, TimeUnit.SECONDS); + executorService.awaitTermination(20, TimeUnit.SECONDS); } }); From 44274b252c21cd1ddee8272dabf36850ea7641ac Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Tue, 26 Dec 2023 23:53:28 +0100 Subject: [PATCH 61/77] allow reinitializing of javacc semanticize --- .../java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java index d69a24155..2a5d51495 100644 --- a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java @@ -39,6 +39,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.logging.Logger; +import org.javacc.parser.JavaCCErrors; class ParserKeywordsUtilsTest { @@ -144,6 +145,7 @@ public static TreeSet getAllKeywordsUsingJavaCC(File file) throws Except }); Path jjGrammarFile = jjGrammarOutputDir.resolve("JSqlParserCC.jj"); + JavaCCErrors.reInit(); JavaCCParser parser = new JavaCCParser(new java.io.FileInputStream(jjGrammarFile.toFile())); parser.javacc_input(); From 639555315180cbf9a714f5fdb8c612eff57380e3 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 00:01:20 +0100 Subject: [PATCH 62/77] npe in memory leak verifier --- src/test/java/net/sf/jsqlparser/test/MemoryLeakVerifier.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/sf/jsqlparser/test/MemoryLeakVerifier.java b/src/test/java/net/sf/jsqlparser/test/MemoryLeakVerifier.java index 5784e5d95..112b6896d 100644 --- a/src/test/java/net/sf/jsqlparser/test/MemoryLeakVerifier.java +++ b/src/test/java/net/sf/jsqlparser/test/MemoryLeakVerifier.java @@ -101,7 +101,7 @@ private static void assertGarbageCollected(WeakReference ref, int maxIte for (int i = 0; i < maxIterations; i++) { runtime.runFinalization(); runtime.gc(); - if (ref.get() == null) { + if (ref == null || ref.get() == null) { break; } From 6a327b186528d1a237bf3185e70a6f37dbef646a Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 00:15:57 +0100 Subject: [PATCH 63/77] --- .../java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java index 2a5d51495..b1e9ed937 100644 --- a/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/ParserKeywordsUtilsTest.java @@ -145,11 +145,11 @@ public static TreeSet getAllKeywordsUsingJavaCC(File file) throws Except }); Path jjGrammarFile = jjGrammarOutputDir.resolve("JSqlParserCC.jj"); - JavaCCErrors.reInit(); JavaCCParser parser = new JavaCCParser(new java.io.FileInputStream(jjGrammarFile.toFile())); parser.javacc_input(); // needed for filling JavaCCGlobals + JavaCCErrors.reInit(); Semanticize.start(); // read all the Token and get the String image From 0f31d4b3aae5e450eb2ecdfe6803170bb80c6231 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 00:29:51 +0100 Subject: [PATCH 64/77] [maven-release-plugin] prepare release jsqlparser-4.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 081340628..696f4201f 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8-SNAPSHOT + 4.8 JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - HEAD + jsqlparser-4.8 From e287696f7920393570f5640fc41118385fe8ff11 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 00:29:53 +0100 Subject: [PATCH 65/77] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 696f4201f..e16c52723 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8 + 4.9-SNAPSHOT JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - jsqlparser-4.8 + HEAD From 040176ed08bd47c4a01a3227f7db3faaec2c6503 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 00:44:50 +0100 Subject: [PATCH 66/77] [maven-release-plugin] rollback the release of jsqlparser-4.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e16c52723..081340628 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.9-SNAPSHOT + 4.8-SNAPSHOT JSQLParser library 2004 From 446ee6d90cd5706ecb843489f82d1e1f518bf56b Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 01:18:54 +0100 Subject: [PATCH 67/77] [maven-release-plugin] prepare release jsqlparser-4.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 081340628..696f4201f 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8-SNAPSHOT + 4.8 JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - HEAD + jsqlparser-4.8 From bc894d149e716ce8eb19d546c6723795e7a0ef1c Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 01:18:55 +0100 Subject: [PATCH 68/77] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 696f4201f..e16c52723 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8 + 4.9-SNAPSHOT JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - jsqlparser-4.8 + HEAD From 14577eb8aae585ad731d03060f8372313ff38f53 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 01:53:35 +0100 Subject: [PATCH 69/77] [maven-release-plugin] rollback the release of jsqlparser-4.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e16c52723..081340628 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.9-SNAPSHOT + 4.8-SNAPSHOT JSQLParser library 2004 From 5ff53e835e675ea1698298b5e6819fbd3553ba26 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 01:54:26 +0100 Subject: [PATCH 70/77] problem with old sonatype repo? --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 081340628..2cd7a57cc 100644 --- a/pom.xml +++ b/pom.xml @@ -95,11 +95,11 @@ sonatype-nexus-staging - https://oss.sonatype.org/service/local/staging/deploy/maven2 + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ sonatype-nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots/ From d6b4cc374db41976be7b3b68796255e1ce93c732 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 23:43:59 +0100 Subject: [PATCH 71/77] problem with old sonatype repo? --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2cd7a57cc..b60f6bb7d 100644 --- a/pom.xml +++ b/pom.xml @@ -95,11 +95,11 @@ sonatype-nexus-staging - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + https://oss.sonatype.org/service/local/staging/deploy/maven2/ sonatype-nexus-snapshots - https://s01.oss.sonatype.org/content/repositories/snapshots/ + https://oss.sonatype.org/content/repositories/snapshots/ From 19bdef65c0e04c32652cdc71f2762a55ef8ffd93 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Wed, 27 Dec 2023 23:50:54 +0100 Subject: [PATCH 72/77] problem with old sonatype repo? --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b60f6bb7d..25a4bcd37 100644 --- a/pom.xml +++ b/pom.xml @@ -230,7 +230,7 @@ net.java.dev.javacc javacc - 7.0.12 + 7.0.13 From 40b45a9cc7d62f3f0c37cc0fe628c43f1df76e43 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Thu, 28 Dec 2023 00:06:12 +0100 Subject: [PATCH 73/77] [maven-release-plugin] prepare release jsqlparser-4.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 25a4bcd37..c1975878d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8-SNAPSHOT + 4.8 JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - HEAD + jsqlparser-4.8 From 55aaf477178306476f36419ccdff4a6b1e6367e4 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Thu, 28 Dec 2023 00:06:14 +0100 Subject: [PATCH 74/77] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c1975878d..aee1031d4 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8 + 4.9-SNAPSHOT JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - jsqlparser-4.8 + HEAD From 1087fc3ad2ab85b2b95ec2a872b4303e9e756895 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Thu, 28 Dec 2023 01:07:15 +0100 Subject: [PATCH 75/77] [maven-release-plugin] rollback the release of jsqlparser-4.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aee1031d4..25a4bcd37 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.9-SNAPSHOT + 4.8-SNAPSHOT JSQLParser library 2004 From 66dd0cffad8255d24c30f7b14eb522efd4f1dc94 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Thu, 28 Dec 2023 01:10:08 +0100 Subject: [PATCH 76/77] problem with old sonatype repo? --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 25a4bcd37..9b65c78fc 100644 --- a/pom.xml +++ b/pom.xml @@ -279,7 +279,7 @@ org.apache.maven.plugins maven-release-plugin - 3.0.1 + 3.0.0-M7 true false From 703c8698a2e36e5f1d2ba62982608077fe857630 Mon Sep 17 00:00:00 2001 From: Tobias Warneke Date: Thu, 28 Dec 2023 01:17:14 +0100 Subject: [PATCH 77/77] [maven-release-plugin] prepare release jsqlparser-4.8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9b65c78fc..da8534c2e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.jsqlparser jsqlparser - 4.8-SNAPSHOT + 4.8 JSQLParser library 2004 @@ -107,7 +107,7 @@ scm:git:https://github.com/JSQLParser/JSqlParser.git scm:git:ssh://git@github.com:JSQLParser/JSqlParser.git https://github.com/JSQLParser/JSqlParser.git - HEAD + jsqlparser-4.8