From 267b22a7c6080b00dd92f51891c09c9d6e23b21d Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sun, 8 May 2022 22:26:26 +0700 Subject: [PATCH 1/6] INSERT with SetOperations Simplify the INSERT production Use SetOperations for Select and Values Better Bracket handling for WITH ... SELECT ... Fixes #1491 --- .../jsqlparser/statement/insert/Insert.java | 93 ++++--- .../jsqlparser/statement/select/Select.java | 21 ++ .../util/deparser/InsertDeParser.java | 8 +- .../util/deparser/StatementDeParser.java | 6 + .../util/validation/feature/H2Version.java | 4 +- .../validation/feature/MariaDbVersion.java | 2 +- .../util/validation/feature/MySqlVersion.java | 1 + .../validation/feature/OracleVersion.java | 1 + .../validation/feature/PostgresqlVersion.java | 1 + .../util/validation/feature/SQLVersion.java | 3 + .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 237 +++++++++--------- .../statement/insert/InsertTest.java | 107 ++++++-- .../validator/InsertValidatorTest.java | 9 +- 13 files changed, 290 insertions(+), 203 deletions(-) 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 64be4e8ed..15a5439f7 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; @@ -24,8 +27,11 @@ 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.SelectBody; import net.sf.jsqlparser.statement.select.SelectExpressionItem; +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 { @@ -33,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; @@ -86,20 +89,46 @@ 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; + SelectBody selectBody = select.getSelectBody(); + if (selectBody instanceof ValuesStatement) { + return true; + } else { + return false; + } } public boolean isReturningAllColumns() { @@ -126,12 +155,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() { @@ -234,24 +260,10 @@ public String toString() { sql.append(PlainSelect.getStringList(columns, true, true)).append(" "); } - if (useValues) { - sql.append("VALUES "); + if (select != null) { + sql.append(select); } - 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++) { @@ -289,21 +301,11 @@ public Insert withWithItemsList(List 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; @@ -369,11 +371,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 5cb975ee6..4bba513b6 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -77,13 +77,9 @@ public void deParse(Insert insert) { buffer.append(")"); } - 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) { @@ -94,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 81412eb4a..947ab6a09 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -142,6 +142,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(); @@ -153,6 +156,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 d313e5266..d83ccbab3 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -541,8 +541,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 ) | @@ -555,86 +576,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) { @@ -1107,7 +1128,7 @@ ShowStatement Show(): { ValuesStatement Values(): { ItemsList itemsList; } { - + ( | ) itemsList = SimpleExpressionList(false) @@ -1288,14 +1309,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; Select select = null; - boolean useValues = true; - boolean useSelectBrackets = false; boolean useDuplicate = false; List duplicateUpdateColumns = null; List duplicateUpdateExpressionList = null; @@ -1321,38 +1337,8 @@ Insert Insert( List with ): [LOOKAHEAD(2) "(" tableColumn=Column() { columns.add(tableColumn); } ("," tableColumn=Column() { columns.add(tableColumn); } )* ")" ] ( - 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(); @@ -1364,6 +1350,8 @@ Insert Insert( List with ): { setColumns.add(tableColumn); setExpressionList.add(exp); } )* ) + | + select = SelectWithWithItems( ) ) [ @@ -1391,8 +1379,6 @@ Insert Insert( List with ): insert.setColumns(columns); } return insert.withWithItemsList(with) - .withItemsList(itemsList) - .withUseSelectBrackets(useSelectBrackets) .withSelect(select) .withTable(table) .withUseDuplicate(useDuplicate) @@ -1688,7 +1674,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= ) { @@ -1773,7 +1759,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 c7f25e940..d90d04bad 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(insert, 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 @@ -386,4 +405,46 @@ public void testInsertTableArrays4() throws JSQLParserException { public void testKeywordDefaultIssue1470() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("INSERT INTO mytable (col1, col2, col3) VALUES (?, 'sadfsd', default)"); } + + @Test + public void testInsertUnionSelectIssue1491() throws JSQLParserException { + //@todo: Make this work +// assertSqlCanBeParsedAndDeparsed( +// "insert into table1 (tf1,tf2,tf2)\n" + +// "select sf1,sf2,sf3 from s1" + +// "union " + +// "select rf1,rf2,rf2 from r1" +// , true +// ); + + //@todo: Make this work +// 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( + "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 + ); + } } 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 From 87d3e4f2cb88a966e06cd3340f22d2e27d126025 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sun, 8 May 2022 23:20:31 +0700 Subject: [PATCH 2/6] INSERT with SetOperations Appease Codazy/PMD --- .../java/net/sf/jsqlparser/statement/insert/Insert.java | 7 +------ .../net/sf/jsqlparser/statement/insert/InsertTest.java | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) 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 15a5439f7..cedfaa4c6 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -123,12 +123,7 @@ public ItemsList getItemsList() { @Deprecated public boolean isUseValues() { - SelectBody selectBody = select.getSelectBody(); - if (selectBody instanceof ValuesStatement) { - return true; - } else { - return false; - } + return (select!=null && select.getSelectBody() instanceof ValuesStatement); } public boolean isReturningAllColumns() { 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 d90d04bad..d15ec7e60 100644 --- a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java @@ -73,7 +73,7 @@ public void testRegularInsert() throws JSQLParserException { .withColumns(Arrays.asList(new Column("col1"), new Column("col2"), new Column("col3"))) .withSelect(select); - assertDeparse(insert, statement); + assertDeparse(insert2, statement); statement = "INSERT INTO myschema.mytable VALUES (?, ?, 2.3)"; insert = (Insert) parserManager.parse(new StringReader(statement)); From 5a2381fbce735de579511fd281386ce4b213c952 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Sun, 8 May 2022 23:41:16 +0700 Subject: [PATCH 3/6] INSERT with SetOperations Appease Codazy/PMD --- src/main/java/net/sf/jsqlparser/statement/insert/Insert.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cedfaa4c6..40932661c 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -123,7 +123,7 @@ public ItemsList getItemsList() { @Deprecated public boolean isUseValues() { - return (select!=null && select.getSelectBody() instanceof ValuesStatement); + return select!=null && select.getSelectBody() instanceof ValuesStatement; } public boolean isReturningAllColumns() { From 2ce4a7fe7986aa55bfc44774dd834f41a882c782 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 9 May 2022 17:10:22 +0700 Subject: [PATCH 4/6] Update Readme List the changes Minor rephrases Correct the Maven Artifact Example --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 36cfc0a84..6c70ef132 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,13 @@ 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 +- `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 + Additionally, we have fixed many errors and improved the code quality and the test coverage. ## Extensions of JSqlParser releases @@ -76,7 +79,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). @@ -106,7 +109,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: @@ -121,14 +124,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 ``` From ca82997afd1295fd99f3b1171a39a37772626b40 Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Mon, 9 May 2022 17:11:29 +0700 Subject: [PATCH 5/6] Fix the two test cases (missing white space) --- .../statement/insert/InsertTest.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) 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 d15ec7e60..e33c83e4b 100644 --- a/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/insert/InsertTest.java @@ -408,23 +408,21 @@ public void testKeywordDefaultIssue1470() throws JSQLParserException { @Test public void testInsertUnionSelectIssue1491() throws JSQLParserException { - //@todo: Make this work -// assertSqlCanBeParsedAndDeparsed( -// "insert into table1 (tf1,tf2,tf2)\n" + -// "select sf1,sf2,sf3 from s1" + -// "union " + -// "select rf1,rf2,rf2 from r1" -// , true -// ); - - //@todo: Make this work -// 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\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" + From 1372f8f87756dd8631cee39416c73deab65126fc Mon Sep 17 00:00:00 2001 From: Andreas Reichel Date: Fri, 13 May 2022 15:18:27 +0700 Subject: [PATCH 6/6] Remove unused import --- src/main/java/net/sf/jsqlparser/statement/insert/Insert.java | 1 - 1 file changed, 1 deletion(-) 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 961eb50f8..4e47ec0e1 100644 --- a/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java +++ b/src/main/java/net/sf/jsqlparser/statement/insert/Insert.java @@ -29,7 +29,6 @@ import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; -import net.sf.jsqlparser.statement.select.SelectExpressionItem; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.WithItem; import net.sf.jsqlparser.statement.values.ValuesStatement;