diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index df5bcbc15..9d042118c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: SQL Parser Error about: Create a report to help us improve title: 'JSQLParser Version : RDBMS : failing feature description' -labels: '' +labels: 'Parser Error', 'Feature Request', 'Documentation', 'Java API', 'RDBMS support' assignees: '' --- @@ -12,7 +12,7 @@ assignees: '' - Example: `WITH ROLLUP` can't be parsed **SQL Example** -- Simplyfied Query Exmple, focusing on the failing feature +- Simplified Query Example, focusing on the failing feature ```sql -- Replace with your ACTUAL example select 1 @@ -23,6 +23,6 @@ from dual - JSqlParser version - Database (e. g. Oracle, MS SQL Server, H2, PostgreSQL, IBM DB2 ) -**Tipps** -Please write in English and avoid Screenshots (as we can't copy paste content from it). +**Tips** +Please write in English and avoid Screenshots (as we can't copy and paste content from it). [Try your example online with the latest JSQLParser](http://217.160.215.75:8080/jsqlformatter/demo.html) and share the link in the error report. diff --git a/README.md b/README.md index ebded4a70..7b5a8488f 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,10 @@ To help JSqlParser's development you are encouraged to provide **Please write in English, since it's the language most of the dev team knows.** -Also I would like to know about needed examples or documentation stuff. +Any requests for examples or any particular documentation will be most welcome. ## Extensions in the latest SNAPSHOT version 4.5 -- Add support for `... ALTER COLUMN ... DROP DEFAULT` - Additionally, we have fixed many errors and improved the code quality and the test coverage. ## Extensions of JSqlParser releases @@ -64,6 +62,9 @@ Additionally, we have fixed many errors and improved the code quality and the te * Modifications before GitHub's release tagging are listed in the [Older Releases](https://github.com/JSQLParser/JSqlParser/wiki/Older-Releases) page. * UnsupportedStatement support instead of throwing Exceptions * support for **RETURNING** clause of a **DELETE** statement +* Add support for `... ALTER COLUMN ... DROP DEFAULT` +* `INSERT` supports `SetOperations` (e. g. `INSERT INTO ... SELECT ... FROM ... UNION SELECT ... FROM ...`), those `SetOperations` are used both for `SELECT` and `VALUES` clauses (API change) in order to simplify the Grammar +* `(WITH ... SELECT ...)` statements within brackets are now supported ## Building from the sources @@ -80,7 +81,7 @@ gradle build The project requires the following to build: - Maven (or Gradle) -- JDK 8 or later. The jar will target JDK 8, but the version of the maven-compiler-plugin that JsqlParser uses requires JDK 8+ +- JDK 8 or later. The JAR will target JDK 8, but the version of the maven-compiler-plugin that JSqlParser uses requires JDK 8+ This will produce the jsqlparser-VERSION.jar file in the `target/` directory (`build/libs/jsqlparser-VERSION.jar` in case of Gradle). @@ -110,7 +111,7 @@ This is a valid piece of source code: ## Maven Repository -JSQLParser is deployed at sonatypes open source maven repository. +JSQLParser is deployed at Sonatype open source maven repository. Starting from now I will deploy there. The first snapshot version there will be 0.8.5-SNAPSHOT. To use it this is the repository configuration: @@ -125,14 +126,14 @@ To use it this is the repository configuration: ``` -This repositories releases will be synched to maven central. Snapshots remain at sonatype. +These repository releases will be synchronised to Maven Central. Snapshots remain at Sonatype. And this is the dependency declaration in your pom: ```xml com.github.jsqlparser jsqlparser - 4.2 + 4.4 ``` 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 df99ba12a..4e47ec0e1 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -15,8 +15,11 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; + import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.OracleHint; +import net.sf.jsqlparser.expression.RowConstructor; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.ItemsList; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; @@ -25,8 +28,10 @@ import net.sf.jsqlparser.statement.StatementVisitor; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; -import net.sf.jsqlparser.statement.select.SelectItem; +import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.values.ValuesStatement; @SuppressWarnings({"PMD.CyclomaticComplexity"}) public class Insert implements Statement { @@ -34,10 +39,7 @@ public class Insert implements Statement { private Table table; private OracleHint oracleHint = null; private List columns; - private ItemsList itemsList; - private boolean useValues = true; private Select select; - private boolean useSelectBrackets = true; private boolean useDuplicate = false; private List duplicateUpdateColumns; private List duplicateUpdateExpressionList; @@ -95,20 +97,41 @@ public void setColumns(List list) { * * @return the values of the insert */ + @Deprecated public ItemsList getItemsList() { - return itemsList; + if (select!=null) { + SelectBody selectBody = select.getSelectBody(); + if (selectBody instanceof SetOperationList) { + SetOperationList setOperationList = (SetOperationList) selectBody; + List selects = setOperationList.getSelects(); + + if (selects.size() == 1) { + SelectBody selectBody1 = selects.get(0); + if (selectBody1 instanceof ValuesStatement) { + ValuesStatement valuesStatement = (ValuesStatement) selectBody1; + if (valuesStatement.getExpressions() instanceof ExpressionList) { + ExpressionList expressionList = (ExpressionList) valuesStatement.getExpressions(); + + if (expressionList.getExpressions().size() == 1 && expressionList.getExpressions().get(0) instanceof RowConstructor) { + RowConstructor rowConstructor = (RowConstructor) expressionList.getExpressions().get(0); + return rowConstructor.getExprList(); + } else { + return expressionList; + } + } else { + return valuesStatement.getExpressions(); + } + } + } + } + } + return null; } - public void setItemsList(ItemsList list) { - itemsList = list; - } + @Deprecated public boolean isUseValues() { - return useValues; - } - - public void setUseValues(boolean useValues) { - this.useValues = useValues; + return select!=null && select.getSelectBody() instanceof ValuesStatement; } public List getReturningExpressionList() { @@ -127,12 +150,9 @@ public void setSelect(Select select) { this.select = select; } + @Deprecated public boolean isUseSelectBrackets() { - return useSelectBrackets; - } - - public void setUseSelectBrackets(boolean useSelectBrackets) { - this.useSelectBrackets = useSelectBrackets; + return false; } public boolean isUseDuplicate() { @@ -235,28 +255,10 @@ public String toString() { sql.append(PlainSelect.getStringList(columns, true, true)).append(" "); } - if (outputClause!=null) { - outputClause.appendTo(sql); + if (select != null) { + sql.append(select); } - if (useValues) { - sql.append("VALUES "); - } - - if (itemsList != null) { - sql.append(itemsList); - } else { - if (useSelectBrackets) { - sql.append("("); - } - if (select != null) { - sql.append(select); - } - if (useSelectBrackets) { - sql.append(")"); - } - } - if (useSet) { sql.append("SET "); for (int i = 0; i < getSetColumns().size(); i++) { @@ -291,22 +293,12 @@ public Insert withWithItemsList(List withList) { this.withItemsList = withList; return this; } - - public Insert withUseValues(boolean useValues) { - this.setUseValues(useValues); - return this; - } public Insert withSelect(Select select) { this.setSelect(select); return this; } - public Insert withUseSelectBrackets(boolean useSelectBrackets) { - this.setUseSelectBrackets(useSelectBrackets); - return this; - } - public Insert withUseDuplicate(boolean useDuplicate) { this.setUseDuplicate(useDuplicate); return this; @@ -367,11 +359,6 @@ public Insert withSetColumns(List columns) { return this; } - public Insert withItemsList(ItemsList itemsList) { - this.setItemsList(itemsList); - return this; - } - public Insert addColumns(Column... columns) { List collection = Optional.ofNullable(getColumns()).orElseGet(ArrayList::new); Collections.addAll(collection, columns); diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Select.java b/src/main/java/net/sf/jsqlparser/statement/select/Select.java index 7886f44f7..67a002777 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Select.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Select.java @@ -23,6 +23,8 @@ public class Select implements Statement { private SelectBody selectBody; private List withItemsList; + private boolean useWithBrackets = false; + @Override public void accept(StatementVisitor statementVisitor) { statementVisitor.visit(this); @@ -41,11 +43,27 @@ public void setSelectBody(SelectBody body) { selectBody = body; } + public void setUsingWithBrackets(boolean useWithBrackets) { + this.useWithBrackets = useWithBrackets; + } + + public Select withUsingWithBrackets(boolean useWithBrackets) { + this.useWithBrackets = useWithBrackets; + return this; + } + + public boolean isUsingWithBrackets() { + return this.useWithBrackets; + } + @Override @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public String toString() { StringBuilder retval = new StringBuilder(); if (withItemsList != null && !withItemsList.isEmpty()) { + if (useWithBrackets) { + retval.append("( "); + } retval.append("WITH "); for (Iterator iter = withItemsList.iterator(); iter.hasNext();) { WithItem withItem = iter.next(); @@ -57,6 +75,9 @@ public String toString() { } } retval.append(selectBody); + if (withItemsList != null && !withItemsList.isEmpty() && useWithBrackets) { + retval.append(" )"); + } return retval.toString(); } 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 78ac2c930..f68f059e2 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -77,17 +77,9 @@ public void deParse(Insert insert) { buffer.append(")"); } - if (insert.getOutputClause()!=null) { - insert.getOutputClause().appendTo(buffer); - } - - if (insert.getItemsList() != null) { - insert.getItemsList().accept(this); - } - if (insert.getSelect() != null) { buffer.append(" "); - if (insert.isUseSelectBrackets()) { + if (insert.getSelect().isUsingWithBrackets()) { buffer.append("("); } if (insert.getSelect().getWithItemsList() != null) { @@ -98,7 +90,7 @@ public void deParse(Insert insert) { buffer.append(" "); } insert.getSelect().getSelectBody().accept(selectVisitor); - if (insert.isUseSelectBrackets()) { + if (insert.getSelect().isUsingWithBrackets()) { buffer.append(")"); } } 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 5dc82d3ff..5e904dd0b 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -143,6 +143,9 @@ public void visit(Select select) { expressionDeParser.setBuffer(buffer); selectDeParser.setExpressionVisitor(expressionDeParser); if (select.getWithItemsList() != null && !select.getWithItemsList().isEmpty()) { + if (select.isUsingWithBrackets()) { + buffer.append("( "); + } buffer.append("WITH "); for (Iterator iter = select.getWithItemsList().iterator(); iter.hasNext();) { WithItem withItem = iter.next(); @@ -154,6 +157,9 @@ public void visit(Select select) { } } select.getSelectBody().accept(selectDeParser); + if (select.isUsingWithBrackets()) { + buffer.append(" )"); + } } @Override diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java index a5ff52606..9cd733acf 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/H2Version.java @@ -100,6 +100,7 @@ public enum H2Version implements Version { // http://h2database.com/html/commands.html#insert Feature.insert, Feature.insertValues, + Feature.values, Feature.insertFromSelect, // http://h2database.com/html/commands.html#update Feature.update, @@ -136,7 +137,8 @@ public enum H2Version implements Version { // http://www.h2database.com/html/commands.html#grant_role Feature.grant, // http://h2database.com/html/commands.html#commit - Feature.commit)); + Feature.commit + )); private Set features; private String versionString; 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 b241ffde5..6a1cba1a4 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 @@ -60,7 +60,7 @@ public enum MariaDbVersion implements Version { Feature.withItem, Feature.withItemRecursive, // https://mariadb.com/kb/en/insert/ - Feature.insert, Feature.insertValues, + Feature.insert, Feature.insertValues, Feature.values, Feature.insertFromSelect, Feature.insertModifierPriority, Feature.insertModifierIgnore, Feature.insertUseSet, Feature.insertUseDuplicateKeyUpdate, Feature.insertReturningExpressionList, 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 cf30655d4..c13abaa3e 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 @@ -54,6 +54,7 @@ public enum MySqlVersion implements Version { // https://dev.mysql.com/doc/refman/8.0/en/insert.html Feature.insert, Feature.insertValues, + Feature.values, Feature.insertFromSelect, Feature.insertUseSet, Feature.insertModifierPriority, Feature.insertModifierIgnore, Feature.insertUseDuplicateKeyUpdate, diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java index 62fe19bb6..3c970bf65 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/OracleVersion.java @@ -88,6 +88,7 @@ public enum OracleVersion implements Version { // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/INSERT.html Feature.insert, Feature.insertValues, + Feature.values, // https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/INSERT.html // see "single_table_insert" Feature.insertFromSelect, 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 3f439bd41..32e62cca7 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 @@ -109,6 +109,7 @@ public enum PostgresqlVersion implements Version { // https://www.postgresql.org/docs/current/sql-insert.html Feature.insert, Feature.insertValues, + Feature.values, Feature.insertFromSelect, Feature.insertReturningAll, Feature.insertReturningExpressionList, // https://www.postgresql.org/docs/current/sql-update.html diff --git a/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java b/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java index b6eda6455..3b8266c83 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/feature/SQLVersion.java @@ -29,10 +29,13 @@ public enum SQLVersion implements Version { Feature.jdbcParameter, Feature.jdbcNamedParameter, // common features + Feature.setOperation, Feature.select, Feature.selectGroupBy, Feature.function, Feature.insert, + Feature.insertFromSelect, Feature.insertValues, + Feature.values, Feature.update, Feature.delete, Feature.truncate, diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 15cc46c09..4f5c2bff0 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -544,8 +544,29 @@ Statement SingleStatement() : { try { ( + LOOKAHEAD(2) ( + "(" with=WithList() + ( + stm = Select( with ) { ( (Select) stm).setUsingWithBrackets(true); } + + /* @todo: unsure, if we need to implement those + since DMLs in brackets do not really make any sense + | + stm = Insert( with ) { ( (Insert) stm).setUsingWithBrackets(true); } + | + stm = Update( with ) { ( (Update) stm).setUsingWithBrackets(true); } + | + stm = Delete( with ) { ( (Delete) stm).setUsingWithBrackets(true); } + | + stm = Merge( with ) { ( (Merge) stm).setUsingWithBrackets(true); } + + */ + ) + ")" + ) + | ( - [ with=WithList() { } ] + [ with=WithList() ] ( stm = Select( with ) | @@ -558,86 +579,86 @@ Statement SingleStatement() : stm = Merge( with) ) ) - | - stm = Upsert() - | - LOOKAHEAD(3) - stm = Replace() - | - LOOKAHEAD(2) - stm = AlterTable() - | - LOOKAHEAD(2) - stm = AlterSession() - | - LOOKAHEAD(CreateFunctionStatement()) - stm = CreateFunctionStatement() - | - LOOKAHEAD(CreateIndex()) - stm = CreateIndex() - | - LOOKAHEAD(CreateSchema()) - stm = CreateSchema() - | - LOOKAHEAD(CreateSequence()) - stm = CreateSequence() - | - LOOKAHEAD(CreateSynonym()) - stm = CreateSynonym() - | - LOOKAHEAD(CreateTable()) - stm = CreateTable() - | - LOOKAHEAD(CreateView()) - stm = CreateView() - | - LOOKAHEAD(AlterView()) - stm = AlterView() - | - LOOKAHEAD(AlterSequence()) - stm = AlterSequence() - | - stm = Drop() - | - stm = Truncate() - | - stm = Execute() - | - stm = Set() - | - stm = RenameTableStatement() - | - stm = Reset() - | - LOOKAHEAD(ShowColumns()) - stm = ShowColumns() - | - LOOKAHEAD(ShowTables()) - stm = ShowTables() - | - stm = Show() - | - stm = Use() - | - stm = SavepointStatement() - | - stm = RollbackStatement() - | - stm = Commit() - | - stm = Comment() - | - stm = Describe() - | - stm = Explain() - | - stm = Declare() - | - stm = Grant() - | - stm = PurgeStatement() - | - stm = AlterSystemStatement() + | + stm = Upsert() + | + LOOKAHEAD(3) + stm = Replace() + | + LOOKAHEAD(2) + stm = AlterTable() + | + LOOKAHEAD(2) + stm = AlterSession() + | + LOOKAHEAD(CreateFunctionStatement()) + stm = CreateFunctionStatement() + | + LOOKAHEAD(CreateIndex()) + stm = CreateIndex() + | + LOOKAHEAD(CreateSchema()) + stm = CreateSchema() + | + LOOKAHEAD(CreateSequence()) + stm = CreateSequence() + | + LOOKAHEAD(CreateSynonym()) + stm = CreateSynonym() + | + LOOKAHEAD(CreateTable()) + stm = CreateTable() + | + LOOKAHEAD(CreateView()) + stm = CreateView() + | + LOOKAHEAD(AlterView()) + stm = AlterView() + | + LOOKAHEAD(AlterSequence()) + stm = AlterSequence() + | + stm = Drop() + | + stm = Truncate() + | + stm = Execute() + | + stm = Set() + | + stm = RenameTableStatement() + | + stm = Reset() + | + LOOKAHEAD(ShowColumns()) + stm = ShowColumns() + | + LOOKAHEAD(ShowTables()) + stm = ShowTables() + | + stm = Show() + | + stm = Use() + | + stm = SavepointStatement() + | + stm = RollbackStatement() + | + stm = Commit() + | + stm = Comment() + | + stm = Describe() + | + stm = Explain() + | + stm = Declare() + | + stm = Grant() + | + stm = PurgeStatement() + | + stm = AlterSystemStatement() ) { return stm; } } catch (ParseException e) { @@ -1136,7 +1157,7 @@ ShowStatement Show(): { ValuesStatement Values(): { ItemsList itemsList; } { - + ( | ) itemsList = SimpleExpressionList(false) @@ -1317,14 +1338,9 @@ Insert Insert( List with ): Table table = null; Column tableColumn = null; List columns = new ArrayList(); - List primaryExpList = new ArrayList(); - ItemsList itemsList = null; Expression exp = null; - MultiExpressionList multiExpr = null; - List returning = null; + List returning = null; Select select = null; - boolean useValues = true; - boolean useSelectBrackets = false; boolean useDuplicate = false; List duplicateUpdateColumns = null; List duplicateUpdateExpressionList = null; @@ -1354,38 +1370,8 @@ Insert Insert( List with ): [ outputClause = OutputClause() { insert.setOutputClause(outputClause); } ] ( - LOOKAHEAD(2) [ | ] "(" exp=SimpleExpression() { primaryExpList.add(exp); } - ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { itemsList = new ExpressionList(primaryExpList); } - ("," "(" exp=SimpleExpression() { - if (multiExpr==null) { - multiExpr=new MultiExpressionList(); - multiExpr.addExpressionList((ExpressionList)itemsList); - itemsList = multiExpr; - } - primaryExpList = new ArrayList(); - primaryExpList.add(exp); } - ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { multiExpr.addExpressionList(primaryExpList); } )* - - | - ( - LOOKAHEAD(2) "(" { useSelectBrackets = true; } - { insert.setUseValues(false); } - select = SelectWithWithItems( ) - ")" - | - { insert.setUseValues(false); } - select = SelectWithWithItems( ) - ) - - | - - - ( - { - useSet = true; - insert.setUseValues(false); - } + { useSet = true; } tableColumn=Column() "=" exp=SimpleExpression() { setColumns = new ArrayList(); @@ -1397,6 +1383,8 @@ Insert Insert( List with ): { setColumns.add(tableColumn); setExpressionList.add(exp); } )* ) + | + select = SelectWithWithItems( ) ) [ @@ -1420,8 +1408,6 @@ Insert Insert( List with ): insert.setColumns(columns); } return insert.withWithItemsList(with) - .withItemsList(itemsList) - .withUseSelectBrackets(useSelectBrackets) .withSelect(select) .withTable(table) .withUseDuplicate(useDuplicate) @@ -1750,7 +1736,7 @@ String RelObjectName() : { Token tk = null; String result = null; } { (result = RelObjectNameWithoutValue() - | tk= | tk= | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= ) { @@ -1835,7 +1821,14 @@ Select SelectWithWithItems( ): List with = null; } { - [ with=WithList() { } ] select = Select( with ) + LOOKAHEAD(2) ( + "(" with=WithList() select = Select( with ) ")" { return select.withUsingWithBrackets(true); } + ) + | + ( + [ with=WithList() ] select = Select( with ) + ) + { return select; } diff --git a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java index e131c9995..9315230b8 100644 --- a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java @@ -9,8 +9,6 @@ */ package net.sf.jsqlparser.statement.insert; -import java.io.StringReader; -import java.util.Arrays; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.DoubleValue; import net.sf.jsqlparser.expression.JdbcParameter; @@ -24,6 +22,13 @@ import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.values.ValuesStatement; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.StringReader; +import java.util.Arrays; + import static net.sf.jsqlparser.test.TestUtils.assertDeparse; import static net.sf.jsqlparser.test.TestUtils.assertOracleHintExists; import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; @@ -33,8 +38,6 @@ 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 org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; public class InsertTest { @@ -56,13 +59,21 @@ public void testRegularInsert() throws JSQLParserException { getValue()); assertEquals(234, ((LongValue) ((ExpressionList) insert.getItemsList()).getExpressions(). get(2)).getValue()); - assertEquals(statement, "" + insert); + assertEquals(statement, insert.toString()); - assertDeparse(new Insert().withTable(new Table("mytable")) - .addColumns(Arrays.asList(new Column("col1"), new Column("col2"), new Column("col3"))) - .withItemsList(new ExpressionList(new JdbcParameter(), new StringValue("sadfsd"), - new LongValue().withValue(234))), - statement); + ExpressionList expressionList =new ExpressionList( + new JdbcParameter() + , new StringValue("sadfsd") + , new LongValue().withValue(234) + ); + + Select select = new Select().withSelectBody(new ValuesStatement().withExpressions(expressionList)); + + Insert insert2 = new Insert().withTable(new Table("mytable")) + .withColumns(Arrays.asList(new Column("col1"), new Column("col2"), new Column("col3"))) + .withSelect(select); + + assertDeparse(insert2, statement); statement = "INSERT INTO myschema.mytable VALUES (?, ?, 2.3)"; insert = (Insert) parserManager.parse(new StringReader(statement)); @@ -82,9 +93,9 @@ public void testInsertWithKeywordValue() throws JSQLParserException { assertEquals("mytable", insert.getTable().getName()); assertEquals(1, insert.getColumns().size()); assertEquals("col1", insert.getColumns().get(0).getColumnName()); - assertEquals("val1", - ((StringValue) ((ExpressionList) insert.getItemsList()).getExpressions().get(0)). - getValue()); + assertEquals("('val1')", + (((ExpressionList) insert.getItemsList()).getExpressions().get(0)). + toString()); assertEquals("INSERT INTO mytable (col1) VALUES ('val1')", insert.toString()); } @@ -103,11 +114,11 @@ public void testInsertFromSelect() throws JSQLParserException { assertEquals("mytable2", ((Table) ((PlainSelect) insert.getSelect().getSelectBody()).getFromItem()).getName()); - // toString uses brakets + // toString uses brackets String statementToString = "INSERT INTO mytable (col1, col2, col3) SELECT * FROM mytable2"; assertEquals(statementToString, "" + insert); - assertDeparse(new Insert().withUseValues(false).withUseSelectBrackets(false).withTable(new Table("mytable")) + assertDeparse(new Insert().withTable(new Table("mytable")) .addColumns(new Column("col1"), new Column("col2"), new Column("col3")) .withSelect(new Select().withSelectBody( new PlainSelect().addSelectItems(new AllColumns()).withFromItem(new Table("mytable2")))), @@ -135,7 +146,6 @@ public void testInsertValuesWithDuplicateElimination() throws JSQLParserExceptio Insert insert = (Insert) parserManager.parse(new StringReader(statement)); assertEquals("TEST", insert.getTable().getName()); assertEquals(2, insert.getColumns().size()); - assertTrue(insert.isUseValues()); assertEquals("ID", insert.getColumns().get(0).getColumnName()); assertEquals("COUNTER", insert.getColumns().get(1).getColumnName()); assertEquals(2, ((ExpressionList) insert.getItemsList()).getExpressions().size()); @@ -175,15 +185,24 @@ public void testInsertFromSetWithDuplicateElimination() throws JSQLParserExcepti public void testInsertMultiRowValue() throws JSQLParserException { String statement = "INSERT INTO mytable (col1, col2) VALUES (a, b), (d, e)"; assertSqlCanBeParsedAndDeparsed(statement); - assertDeparse(new Insert().withTable(new Table("mytable")) + + MultiExpressionList multiExpressionList = new MultiExpressionList() + .addExpressionLists( new ExpressionList().addExpressions(new Column("a")).addExpressions(new Column("b"))) + .addExpressionLists( new ExpressionList().addExpressions(new Column("d")).addExpressions(new Column("e"))); + + Select select = new Select().withSelectBody(new ValuesStatement().withExpressions(multiExpressionList)); + + Insert insert = new Insert().withTable(new Table("mytable")) .withColumns(Arrays.asList(new Column("col1"), new Column("col2"))) - .withItemsList(new MultiExpressionList().addExpressionLists( - new ExpressionList().addExpressions(Arrays.asList(new Column("a"), new Column("b"))), - new ExpressionList(new Column("d"), new Column("e")))), - statement); + .withSelect(select); + + assertDeparse(insert, statement); } @Test + @Disabled + //@todo: Clarify, if and why this test is supposed to fail and if it is the Parser's job to decide + //What if col1 and col2 are Array Columns? public void testInsertMultiRowValueDifferent() throws JSQLParserException { try { assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (col1, col2) VALUES (a, b), (d, e, c)"); @@ -234,8 +253,8 @@ public void testInsertSelect() throws JSQLParserException { @Test public void testInsertWithSelect() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (mycolumn) WITH a AS (SELECT mycolumn FROM mytable) SELECT mycolumn FROM a"); - assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (mycolumn) (WITH a AS (SELECT mycolumn FROM mytable) SELECT mycolumn FROM a)"); + assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (mycolumn) WITH a AS (SELECT mycolumn FROM mytable) SELECT mycolumn FROM a", true); + assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (mycolumn) (WITH a AS (SELECT mycolumn FROM mytable) SELECT mycolumn FROM a)", true); } @Test @@ -387,6 +406,46 @@ public void testKeywordDefaultIssue1470() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (col1, col2, col3) VALUES (?, 'sadfsd', default)"); } + @Test + public void testInsertUnionSelectIssue1491() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed( + "insert into table1 (tf1,tf2,tf2)\n" + + "select sf1,sf2,sf3 from s1\n" + + "union\n" + + "select rf1,rf2,rf2 from r1\n" + , true + ); + + assertSqlCanBeParsedAndDeparsed( + "insert into table1 (tf1,tf2,tf2)\n" + + "( select sf1,sf2,sf3 from s1\n" + + "union\n" + + "select rf1,rf2,rf2 from r1\n)" + , true + ); + + assertSqlCanBeParsedAndDeparsed( + "insert into table1 (tf1,tf2,tf2)\n" + + "(select sf1,sf2,sf3 from s1)" + + "union " + + "(select rf1,rf2,rf2 from r1)" + , true + ); + + assertSqlCanBeParsedAndDeparsed( + "insert into table1 (tf1,tf2,tf2)\n" + + "((select sf1,sf2,sf3 from s1)" + + "union " + + "(select rf1,rf2,rf2 from r1))" + , true + ); + + assertSqlCanBeParsedAndDeparsed( + "(with a as (select * from dual) select * from a)" + , true + ); + } + @Test public void testInsertOutputClause() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed( diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/InsertValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/InsertValidatorTest.java index d9b74658e..f5fe80f79 100644 --- a/src/test/java/net/sf/jsqlparser/util/validation/validator/InsertValidatorTest.java +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/InsertValidatorTest.java @@ -28,8 +28,13 @@ public void testValidationInsert() throws JSQLParserException { @Test public void testValidationInsertNotAllowed() throws JSQLParserException { String sql = "INSERT INTO tab1 (a, b, c) VALUES (5, 'val', ?)"; - validateNotAllowed(sql, 1, 1, FeaturesAllowed.SELECT.copy().add(FeaturesAllowed.JDBC), Feature.insert, - Feature.insertValues); + validateNotAllowed(sql, 1, 1, FeaturesAllowed.SELECT.copy().add(FeaturesAllowed.JDBC) + , Feature.insertValues + , Feature.setOperation + , Feature.insertFromSelect + , Feature.values + , Feature.insert + ); } @Test