diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MultiExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MultiExpressionList.java index 2c4eaad8f..80b0545ff 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/MultiExpressionList.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/MultiExpressionList.java @@ -19,8 +19,12 @@ package net.sf.jsqlparser.expression.operators.relational; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.statement.select.FromItem; +import net.sf.jsqlparser.statement.select.FromItemVisitor; /** * A list of ExpressionList items. e.g. multi values of insert statements. This one allows @@ -28,7 +32,6 @@ * @author toben */ public class MultiExpressionList implements ItemsList { - List exprList; public MultiExpressionList() { @@ -53,6 +56,14 @@ public void addExpressionList(ExpressionList el) { exprList.add(el); } + public void addExpressionList(List list) { + addExpressionList(new ExpressionList(list)); + } + + public void addExpressionList(Expression expr) { + addExpressionList(new ExpressionList(Arrays.asList(expr))); + } + @Override public String toString() { StringBuilder b = new StringBuilder(); diff --git a/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java index 7ebf544e6..9234f15b8 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/FromItemVisitor.java @@ -31,4 +31,6 @@ public interface FromItemVisitor { public void visit(SubJoin subjoin); public void visit(LateralSubSelect lateralSubSelect); + + public void visit(ValuesList valuesList); } diff --git a/src/main/java/net/sf/jsqlparser/statement/select/ValuesList.java b/src/main/java/net/sf/jsqlparser/statement/select/ValuesList.java new file mode 100644 index 000000000..79423ebf7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/ValuesList.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013 toben. + * + * This library 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 library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package net.sf.jsqlparser.statement.select; + +import java.util.Iterator; +import java.util.List; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; + +/** + * This is a container for a values item within a select statement. It holds + * some syntactical stuff that differs from values within an insert statement. + * + * @author toben + */ +public class ValuesList implements FromItem { + + private String alias; + private MultiExpressionList multiExpressionList; + private boolean noBrackets = false; + private List columnNames; + + public ValuesList() { + } + + public ValuesList(MultiExpressionList multiExpressionList) { + this.multiExpressionList = multiExpressionList; + } + + @Override + public void accept(FromItemVisitor fromItemVisitor) { + fromItemVisitor.visit(this); + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public void setAlias(String alias) { + this.alias = alias; + } + + public MultiExpressionList getMultiExpressionList() { + return multiExpressionList; + } + + public void setMultiExpressionList(MultiExpressionList multiExpressionList) { + this.multiExpressionList = multiExpressionList; + } + + public boolean isNoBrackets() { + return noBrackets; + } + + public void setNoBrackets(boolean noBrackets) { + this.noBrackets = noBrackets; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + + b.append("(VALUES "); + for (Iterator it = getMultiExpressionList().getExprList().iterator(); it.hasNext();) { + b.append(PlainSelect.getStringList(it.next().getExpressions(), true, !isNoBrackets())); + if (it.hasNext()) { + b.append(", "); + } + } + b.append(")"); + if (alias != null) { + b.append(" AS ").append(alias); + + if (columnNames != null) { + b.append("("); + for (Iterator it = columnNames.iterator(); it.hasNext();) { + b.append(it.next()); + if (it.hasNext()) { + b.append(", "); + } + } + b.append(")"); + } + } + return b.toString(); + } + + public List getColumnNames() { + return columnNames; + } + + public void setColumnNames(List columnNames) { + this.columnNames = columnNames; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 52b2c241f..b68a8cbb2 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -61,6 +61,7 @@ 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.ValuesList; import net.sf.jsqlparser.statement.select.WithItem; /** @@ -285,7 +286,6 @@ public void visit(TimeValue timeValue) { */ @Override public void visit(CaseExpression caseExpression) { - // TODO Auto-generated method stub } /* @@ -295,7 +295,6 @@ public void visit(CaseExpression caseExpression) { */ @Override public void visit(WhenClause whenClause) { - // TODO Auto-generated method stub } @Override @@ -375,4 +374,8 @@ public void visit(MultiExpressionList multiExprList) { exprList.accept(this); } } + + @Override + public void visit(ValuesList valuesList) { + } } 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 9b5f1a80f..d2235c61a 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -5,6 +5,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; +import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.AllColumns; @@ -25,6 +26,7 @@ import net.sf.jsqlparser.statement.select.SubJoin; import net.sf.jsqlparser.statement.select.SubSelect; import net.sf.jsqlparser.statement.select.Top; +import net.sf.jsqlparser.statement.select.ValuesList; import net.sf.jsqlparser.statement.select.WithItem; /** @@ -306,4 +308,9 @@ public void visit(WithItem withItem) { public void visit(LateralSubSelect lateralSubSelect) { buffer.append(lateralSubSelect.toString()); } + + @Override + public void visit(ValuesList valuesList) { + buffer.append(valuesList.toString()); + } } diff --git a/src/main/javacc/net/sf/jsqlparser/parser/JSqlParserCC.jj b/src/main/javacc/net/sf/jsqlparser/parser/JSqlParserCC.jj index 4e44c012a..e80634e2d 100644 --- a/src/main/javacc/net/sf/jsqlparser/parser/JSqlParserCC.jj +++ b/src/main/javacc/net/sf/jsqlparser/parser/JSqlParserCC.jj @@ -140,6 +140,7 @@ import net.sf.jsqlparser.statement.select.MinusOp; import net.sf.jsqlparser.statement.select.ExceptOp; import net.sf.jsqlparser.statement.select.IntersectOp; import net.sf.jsqlparser.statement.select.WithItem; +import net.sf.jsqlparser.statement.select.ValuesList; import net.sf.jsqlparser.statement.truncate.Truncate; import net.sf.jsqlparser.statement.update.Update; @@ -413,7 +414,7 @@ Insert Insert(): } primaryExpList = new ArrayList(); primaryExpList.add(exp); } - ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { multiExpr.addExpressionList(new ExpressionList(primaryExpList)); } )* + ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { multiExpr.addExpressionList(primaryExpList); } )* | @@ -774,28 +775,75 @@ FromItem FromItem(): } { ( - ( - "(" - ( - LOOKAHEAD(SubJoin()) - fromItem=SubJoin() - | - fromItem=SubSelect() + LOOKAHEAD(ValuesList()) fromItem=ValuesList() + | + ( + ( + ( + "(" + ( + LOOKAHEAD(SubJoin()) + fromItem=SubJoin() + | + fromItem=SubSelect() + ) + ")" ) - ")" + | + fromItem=Table() + | + fromItem=LateralSubSelect() + ) + + [alias=Alias() { fromItem.setAlias(alias); } ] ) - | - fromItem=Table() - | - fromItem=LateralSubSelect() ) - - [alias=Alias() { fromItem.setAlias(alias); } ] { return fromItem; } } +FromItem ValuesList(): +{ + MultiExpressionList exprList = new MultiExpressionList(); + List primaryExpList = new ArrayList(); + ValuesList valuesList = new ValuesList(); + Expression exp = null; + List colNames = null; + String colName; + String alias; +} +{ + "(" + + (LOOKAHEAD(3) ("(" exp=SimpleExpression() { primaryExpList.add(exp); } + ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { exprList.addExpressionList(primaryExpList); } + + ("," "(" exp=SimpleExpression() { + primaryExpList = new ArrayList(); + primaryExpList.add(exp); } + ("," exp=SimpleExpression() { primaryExpList.add(exp); } )* ")" { exprList.addExpressionList(primaryExpList); } )*) + | + ( exp=SimpleExpression() { exprList.addExpressionList(exp); valuesList.setNoBrackets(true); } + ("," exp=SimpleExpression() { exprList.addExpressionList(exp);} )* + )) + ")" + + [alias=Alias() { valuesList.setAlias(alias); } + + [ "(" + colName = RelObjectName() { colNames = new ArrayList(); colNames.add(colName); } + ( "," colName = RelObjectName() { colNames.add(colName); } )* + ")" { valuesList.setColumnNames(colNames); } ] + + ] + + { + valuesList.setMultiExpressionList(exprList); + return valuesList; + } +} + LateralSubSelect LateralSubSelect(): { LateralSubSelect lateralSubSelect = new LateralSubSelect(); 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 f25568ddb..2d0a0a35a 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -765,6 +765,26 @@ public void testLateralComplex1() throws IOException, JSQLParserException { Select select = (Select) parserManager.parse(new StringReader(stmt)); assertEquals("SELECT O.ORDERID, O.CUSTNAME, OL.LINETOTAL, OC.ORDCHGTOTAL, OT.TAXTOTAL FROM ORDERS AS O, LATERAL(SELECT SUM(NETAMT) AS LINETOTAL FROM ORDERLINES AS LINES WHERE LINES.ORDERID = O.ORDERID) AS OL, LATERAL(SELECT SUM(CHGAMT) AS ORDCHGTOTAL FROM ORDERCHARGES AS CHARGES WHERE LINES.ORDERID = O.ORDERID) AS OC, LATERAL(SELECT SUM(TAXAMT) AS TAXTOTAL FROM ORDERTAXES AS 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); + } private void assertSqlCanBeParsedAndDeparsed(String statement) throws JSQLParserException { Statement parsed = parserManager.parse(new StringReader(statement));