diff --git a/README.md b/README.md index 27eb9e61a..31ddb6ebb 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Also I would like to know about needed examples or documentation stuff. ## Extensions in the latest SNAPSHOT version 4.1 +* API change in ValuesStatement: the expression list is now hold as a ItemList and not as a List * support for parser modification within **parseExpression** and **parseCondExpression** ' support for table schema for foreign keys * support for Oracle hints on **insert, update and merge** diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java index edf840430..7d8bd5b96 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ExpressionList.java @@ -24,6 +24,7 @@ public class ExpressionList implements ItemsList { private List expressions; + private boolean withBrackets = true; public ExpressionList() { } @@ -54,6 +55,11 @@ public ExpressionList withExpressions(List expressions) { public void setExpressions(List expressions) { this.expressions = expressions; } + + public ExpressionList withBrackets(boolean brackets) { + this.withBrackets = brackets; + return this; + } @Override public void accept(ItemsListVisitor itemsListVisitor) { @@ -62,7 +68,7 @@ public void accept(ItemsListVisitor itemsListVisitor) { @Override public String toString() { - return PlainSelect.getStringList(expressions, true, true); + return PlainSelect.getStringList(expressions, true, withBrackets); } public ExpressionList addExpressions(Collection expressions) { diff --git a/src/main/java/net/sf/jsqlparser/statement/values/ValuesStatement.java b/src/main/java/net/sf/jsqlparser/statement/values/ValuesStatement.java index 9be089393..39682793a 100644 --- a/src/main/java/net/sf/jsqlparser/statement/values/ValuesStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/values/ValuesStatement.java @@ -11,68 +11,62 @@ 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.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ItemsList; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; -import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SelectVisitor; public class ValuesStatement implements Statement, SelectBody { - - private List expressions; + + private ItemsList expressions; public ValuesStatement() { // empty constructor } - public ValuesStatement(List expressions) { + public ValuesStatement(ItemsList expressions) { this.expressions = expressions; } - + @Override public void accept(StatementVisitor statementVisitor) { statementVisitor.visit(this); } - - public List getExpressions() { + + public ItemsList getExpressions() { return expressions; } - - public void setExpressions(List expressions) { + + public void setExpressions(ItemsList expressions) { this.expressions = expressions; } - + @Override public String toString() { StringBuilder sql = new StringBuilder(); sql.append("VALUES "); - sql.append(PlainSelect.getStringList(expressions, true, true)); + sql.append(expressions.toString()); return sql.toString(); } - + @Override public void accept(SelectVisitor selectVisitor) { selectVisitor.visit(this); } - public ValuesStatement withExpressions(List expressions) { + public ValuesStatement withExpressions(ItemsList expressions) { this.setExpressions(expressions); return this; } public ValuesStatement addExpressions(Expression... expressions) { - List collection = Optional.ofNullable(getExpressions()).orElseGet(ArrayList::new); - Collections.addAll(collection, expressions); - return this.withExpressions(collection); + return this.withExpressions(new ExpressionList(expressions)); } public ValuesStatement addExpressions(Collection expressions) { - List collection = Optional.ofNullable(getExpressions()).orElseGet(ArrayList::new); - collection.addAll(expressions); - return this.withExpressions(collection); + return this.withExpressions(new ExpressionList(new ArrayList<>(expressions))); } } diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index ca97477c5..a777b4e75 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -829,9 +829,7 @@ public void visit(Comment comment) { @Override public void visit(ValuesStatement values) { - for (Expression expr : values.getExpressions()) { - expr.accept(this); - } + values.getExpressions().accept(this); } @Override 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 d7a464169..da88cc854 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -18,6 +18,10 @@ import net.sf.jsqlparser.expression.MySQLIndexHint; import net.sf.jsqlparser.expression.OracleHint; import net.sf.jsqlparser.expression.SQLServerHints; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; +import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.AllColumns; @@ -52,7 +56,7 @@ @SuppressWarnings({"PMD.CyclomaticComplexity"}) public class SelectDeParser extends AbstractDeParser - implements SelectVisitor, SelectItemVisitor, FromItemVisitor, PivotVisitor { + implements SelectVisitor, SelectItemVisitor, FromItemVisitor, PivotVisitor, ItemsListVisitor { private ExpressionVisitor expressionVisitor; @@ -517,7 +521,7 @@ public void visit(ParenthesisFromItem parenthesis) { @Override public void visit(ValuesStatement values) { - new ValuesStatementDeParser(expressionVisitor, buffer).deParse(values); + new ValuesStatementDeParser(this, buffer).deParse(values); } private void deparseOptimizeFor(OptimizeFor optimizeFor) { @@ -530,4 +534,19 @@ private void deparseOptimizeFor(OptimizeFor optimizeFor) { void deParse(PlainSelect statement) { statement.accept(this); } + + @Override + public void visit(ExpressionList expressionList) { + buffer.append(expressionList.toString()); + } + + @Override + public void visit(NamedExpressionList namedExpressionList) { + buffer.append(namedExpressionList.toString()); + } + + @Override + public void visit(MultiExpressionList multiExprList) { + buffer.append(multiExprList.toString()); + } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java index 245455b98..1ba5548b1 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ValuesStatementDeParser.java @@ -9,31 +9,21 @@ */ package net.sf.jsqlparser.util.deparser; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.statement.values.ValuesStatement; public class ValuesStatementDeParser extends AbstractDeParser { - private final ExpressionVisitor expressionVisitor; + private final ItemsListVisitor expressionVisitor; - public ValuesStatementDeParser(ExpressionVisitor expressionVisitor, StringBuilder buffer) { + public ValuesStatementDeParser(ItemsListVisitor expressionVisitor, StringBuilder buffer) { super(buffer); this.expressionVisitor = expressionVisitor; } @Override public void deParse(ValuesStatement values) { - boolean first = true; - buffer.append("VALUES ("); - for (Expression expr : values.getExpressions()) { - if (first) { - first = false; - } else { - buffer.append(", "); - } - expr.accept(expressionVisitor); - } - buffer.append(")"); + buffer.append("VALUES "); + values.getExpressions().accept(expressionVisitor); } } diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java index a1c51d66e..4b823d794 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ValuesStatementValidator.java @@ -20,6 +20,6 @@ public class ValuesStatementValidator extends AbstractValidator @Override public void validate(ValuesStatement values) { validateFeature(Feature.values); - validateOptionalExpressions(values.getExpressions()); + validateOptionalItemsList(values.getExpressions()); } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 14f211bc2..ff0de4ff2 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -847,16 +847,14 @@ ShowStatement Show(): { } ValuesStatement Values(): { - List expList = new ArrayList(); - Expression exp; + ItemsList itemsList; } { - "(" - exp=PrimaryExpression() { expList.add(exp); } - ("," exp=PrimaryExpression() { expList.add(exp); } )* - ")" + + itemsList = SimpleExpressionList(false) + { - return new ValuesStatement(expList); + return new ValuesStatement(itemsList); } } @@ -1891,7 +1889,7 @@ ExpressionListItem ExpressionListItem(): } { "(" - expressionList=SimpleExpressionList() { expressionListItem = new ExpressionListItem(); expressionListItem.setExpressionList(expressionList); } + expressionList=SimpleExpressionList(true) { expressionListItem = new ExpressionListItem(); expressionListItem.setExpressionList(expressionList); } ")" [alias=Alias() { expressionListItem.setAlias(alias); }] { return expressionListItem; } @@ -2330,11 +2328,11 @@ GroupByElement GroupByColumnReferences(): | "(" ( LOOKAHEAD(2) "(" ")" { groupBy.addGroupingSet(new ExpressionList()); } - | LOOKAHEAD(3) "(" list = SimpleExpressionList() ")" { groupBy.addGroupingSet(list); } + | LOOKAHEAD(3) "(" list = SimpleExpressionList(true) ")" { groupBy.addGroupingSet(list); } | expr = SimpleExpression() { groupBy.addGroupingSet(expr); } ) ( "," ( LOOKAHEAD(2) "(" ")" { groupBy.addGroupingSet(new ExpressionList()); } - | LOOKAHEAD(3) "(" list = SimpleExpressionList() ")" { groupBy.addGroupingSet(list); } + | LOOKAHEAD(3) "(" list = SimpleExpressionList(true) ")" { groupBy.addGroupingSet(list); } | expr = SimpleExpression() { groupBy.addGroupingSet(expr); } ) )* ")" ) @@ -2798,7 +2796,7 @@ Expression InExpression() #InExpression : } { ( LOOKAHEAD(3) "(" ( - LOOKAHEAD(SimpleExpressionList()) leftItemsList = SimpleExpressionList() { result.setLeftItemsList(leftItemsList); } + LOOKAHEAD(SimpleExpressionList(true)) leftItemsList = SimpleExpressionList(true) { result.setLeftItemsList(leftItemsList); } | leftExpression=SimpleExpression() [ "(" "+" ")" { result.setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); } ] @@ -2814,7 +2812,7 @@ Expression InExpression() #InExpression : LOOKAHEAD(3) multiExpressionList = MultiInExpressions() | LOOKAHEAD(3) rightExpression = Function() | LOOKAHEAD(2) token= { rightExpression = new StringValue(token.image); } - | LOOKAHEAD(3) "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList() )")" + | LOOKAHEAD(3) "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList(true) )")" | rightExpression = SimpleExpression() ) { @@ -2833,7 +2831,7 @@ MultiExpressionList MultiInExpressions(): } { "(" "(" - expressionList=SimpleExpressionList() { + expressionList=SimpleExpressionList(true) { if(multiExpressionList == null) { multiExpressionList = new MultiExpressionList(); } @@ -2841,7 +2839,7 @@ MultiExpressionList MultiInExpressions(): } // potentially additional expression lists ( LOOKAHEAD(3) - ")" "," "(" expressionList=SimpleExpressionList() + ")" "," "(" expressionList=SimpleExpressionList(true) { if(multiExpressionList == null) { multiExpressionList = new MultiExpressionList(); @@ -2962,9 +2960,9 @@ ExpressionList SQLExpressionList(): } } -ExpressionList SimpleExpressionList() #ExpressionList: +ExpressionList SimpleExpressionList(boolean outerBrackets) #ExpressionList: { - ExpressionList retval = new ExpressionList(); + ExpressionList retval = new ExpressionList().withBrackets(outerBrackets); List expressions = new ArrayList(); Expression expr = null; } @@ -3370,7 +3368,7 @@ Expression PrimaryExpression() #PrimaryExpression: | LOOKAHEAD("(" retval=SubSelect() ")") "(" retval=SubSelect() ")" - | "(" list = SimpleExpressionList() ")" + | "(" list = SimpleExpressionList(true) ")" { if (list.getExpressions().size() == 1) { retval = new Parenthesis(list.getExpressions().get(0)); @@ -3737,7 +3735,7 @@ RowConstructor RowConstructor(): { } { [ { rowConstructor.setName("ROW");} ] "(" - list = SimpleExpressionList() + list = SimpleExpressionList(true) ")" { @@ -3781,9 +3779,9 @@ Execute Execute(): { ( "," expr = VariableExpression() { namedExprList.add(expr); })* { expressionList = new ExpressionList(namedExprList); } ) | - LOOKAHEAD(3) expressionList=SimpleExpressionList() + LOOKAHEAD(3) expressionList=SimpleExpressionList(true) | - ("(" expressionList=SimpleExpressionList() ")" { execute.setParenthesis(true); }) + ("(" expressionList=SimpleExpressionList(true) ")" { execute.setParenthesis(true); }) )? { @@ -3882,7 +3880,7 @@ Function InternalFunction(Function retval) : | LOOKAHEAD(NamedExpressionListExprFirst()) namedExpressionList = NamedExpressionListExprFirst() | - LOOKAHEAD(3) (expressionList=SimpleExpressionList() [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ]) + LOOKAHEAD(3) (expressionList=SimpleExpressionList(true) [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ]) | expr = SubSelect() { expr.setUseBrackets(false); expressionList = new ExpressionList(expr); } @@ -3940,7 +3938,7 @@ MySQLGroupConcat MySQLGroupConcat():{ { "(" [ { retval.setDistinct(true); } ] - expressionList = SimpleExpressionList() + expressionList = SimpleExpressionList(true) [ orderByList = OrderByElements() { retval.setOrderByElements(orderByList); } ] [ t= { retval.setSeparator(t.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 1d245bb48..6069e50ff 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -4520,4 +4520,14 @@ public void testProblematicDeparsingIssue1183_2() throws JSQLParserException { public void testKeywordCostsIssue1185() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("WITH costs AS (SELECT * FROM MY_TABLE1 AS ALIAS_TABLE1) SELECT * FROM TESTSTMT"); } + + @Test + public void testKeywordCostsIssue1135() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values ((0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16))) select day, value from sample_data", true); + } + + @Test + public void testKeywordCostsIssue1135_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("with sample_data(day, value) as (values (0, 13), (1, 12), (2, 15), (3, 4), (4, 8), (5, 16)) select day, value from sample_data", true); + } }