diff --git a/src/main/java/net/sf/jsqlparser/expression/AllArrayExpression.java b/src/main/java/net/sf/jsqlparser/expression/AllArrayExpression.java new file mode 100644 index 000000000..438e566f5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AllArrayExpression.java @@ -0,0 +1,45 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression; + +public class AllArrayExpression implements AllExpression { + + private final ArrayLiteral array; + + public AllArrayExpression(ArrayLiteral array) { + this.array = array; + } + + public ArrayLiteral getArray() { + return array; + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } + + @Override + public String toString() { + return "ALL (" + array.toString() + ")"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AllComparisonExpression.java b/src/main/java/net/sf/jsqlparser/expression/AllComparisonExpression.java index 7c5f7d02a..8544e74c5 100644 --- a/src/main/java/net/sf/jsqlparser/expression/AllComparisonExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/AllComparisonExpression.java @@ -23,7 +23,7 @@ import net.sf.jsqlparser.statement.select.SubSelect; -public class AllComparisonExpression implements Expression { +public class AllComparisonExpression implements AllExpression { private final SubSelect subSelect; diff --git a/src/main/java/net/sf/jsqlparser/expression/AllExpression.java b/src/main/java/net/sf/jsqlparser/expression/AllExpression.java new file mode 100644 index 000000000..b8624fbef --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AllExpression.java @@ -0,0 +1,27 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression; + +public interface AllExpression extends Expression { + + void accept(ExpressionVisitor ExpressionVisitor); +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnyArrayExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnyArrayExpression.java new file mode 100644 index 000000000..b767fa023 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnyArrayExpression.java @@ -0,0 +1,45 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression; + +public class AnyArrayExpression implements AnyExpression { + + private final ArrayLiteral array; + + public AnyArrayExpression(ArrayLiteral array) { + this.array = array; + } + + public ArrayLiteral getArray() { + return array; + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } + + @Override + public String toString() { + return "ANY (" + array.toString()+")"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java index ac6b6b781..6e2bbae93 100644 --- a/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/AnyComparisonExpression.java @@ -27,7 +27,7 @@ * Combines ANY and SOME expressions. * @author toben */ -public class AnyComparisonExpression implements Expression { +public class AnyComparisonExpression implements AnyExpression { private final SubSelect subSelect; private final AnyType anyType; diff --git a/src/main/java/net/sf/jsqlparser/expression/AnyExpression.java b/src/main/java/net/sf/jsqlparser/expression/AnyExpression.java new file mode 100644 index 000000000..d27774bc7 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/AnyExpression.java @@ -0,0 +1,27 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression; + +public interface AnyExpression extends Expression { + + void accept(ExpressionVisitor ExpressionVisitor); +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ArrayLiteral.java b/src/main/java/net/sf/jsqlparser/expression/ArrayLiteral.java new file mode 100644 index 000000000..ac57119d5 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/ArrayLiteral.java @@ -0,0 +1,58 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2016 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression; + +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; + +/** + * An array literal is a list of simple expressions + *

+ * This shows up in Postgres SQL as + *

+ * ARRAY[val1, val2, val3, ...]
+ * 
+ */ +public class ArrayLiteral implements Expression { + + private ExpressionList list; + + public ArrayLiteral() { + } + + @Override + public void accept(ExpressionVisitor expressionVisitor) { + expressionVisitor.visit(this); + } + + public ExpressionList getList() { + return list; + } + + public void setList(ExpressionList list) { + this.list = list; + } + + @Override + public String toString() { + return "ARRAY["+list.toStringNoBrackets()+"]"; + } +} diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index 250917085..0e5d16d2a 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -121,8 +121,12 @@ public interface ExpressionVisitor { void visit(AllComparisonExpression allComparisonExpression); + void visit(AllArrayExpression allArrayExpression); + void visit(AnyComparisonExpression anyComparisonExpression); + void visit(AnyArrayExpression anyArrayExpression); + void visit(Concat concat); void visit(Matches matches); @@ -169,4 +173,7 @@ public interface ExpressionVisitor { void visit(DateTimeLiteralExpression literal); + void visit(ArrayLiteral arrayLiteral); + + } diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 7266043f0..5d4905612 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -107,6 +107,11 @@ public void visit(TimestampValue value) { } + @Override + public void visit(ArrayLiteral array) { + + } + @Override public void visit(Parenthesis parenthesis) { parenthesis.getExpression().accept(this); @@ -247,11 +252,21 @@ public void visit(AllComparisonExpression expr) { } + @Override + public void visit(AllArrayExpression expr) { + + } + @Override public void visit(AnyComparisonExpression expr) { } + @Override + public void visit(AnyArrayExpression expr) { + + } + @Override public void visit(Concat expr) { visitBinaryExpression(expr); 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 f0649a7ff..285499d37 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 @@ -53,6 +53,10 @@ public void accept(ItemsListVisitor itemsListVisitor) { itemsListVisitor.visit(this); } + public String toStringNoBrackets() { + return PlainSelect.getStringList(expressions, true, false); + } + @Override public String toString() { return PlainSelect.getStringList(expressions, true, true); diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 74b7fd315..2822cc416 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -301,6 +301,11 @@ public void visit(TimestampValue timestampValue) { public void visit(TimeValue timeValue) { } + @Override + public void visit(ArrayLiteral array) { + array.getList().accept(this); + } + /* * (non-Javadoc) * @@ -324,11 +329,21 @@ public void visit(AllComparisonExpression allComparisonExpression) { allComparisonExpression.getSubSelect().getSelectBody().accept(this); } + @Override + public void visit(AllArrayExpression allArrayExpression) { + allArrayExpression.getArray().accept(this); + } + @Override public void visit(AnyComparisonExpression anyComparisonExpression) { anyComparisonExpression.getSubSelect().getSelectBody().accept(this); } + @Override + public void visit(AnyArrayExpression anyArrayExpression) { + anyArrayExpression.getArray().accept(this); + } + @Override public void visit(SubJoin subjoin) { subjoin.getLeft().accept(this); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index 48ef826a7..9d54a6777 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -388,6 +388,11 @@ public void visit(TimeValue timeValue) { buffer.append("{t '").append(timeValue.getValue().toString()).append("'}"); } + @Override + public void visit(ArrayLiteral array) { + buffer.append(array.toString()); + } + @Override public void visit(CaseExpression caseExpression) { buffer.append("CASE "); @@ -426,12 +431,27 @@ public void visit(AllComparisonExpression allComparisonExpression) { allComparisonExpression.getSubSelect().accept((ExpressionVisitor) this); } + @Override + public void visit(AllArrayExpression allArrayExpression) { + buffer.append("ALL ("); + allArrayExpression.getArray().accept((ExpressionVisitor) this); + buffer.append(")"); + } + @Override public void visit(AnyComparisonExpression anyComparisonExpression) { buffer.append(anyComparisonExpression.getAnyType().name()).append(" "); anyComparisonExpression.getSubSelect().accept((ExpressionVisitor) this); } + @Override + public void visit(AnyArrayExpression anyArrayExpression) { + + buffer.append("ANY ("); + anyArrayExpression.getArray().accept((ExpressionVisitor) this); + buffer.append(")"); + } + @Override public void visit(Concat concat) { visitBinaryExpression(concat, " || "); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index ed122a0eb..be45e57f4 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -236,6 +236,14 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | + | : START_ARRAY_LIST + | + | +} + + TOKEN: +{ + :DEFAULT } TOKEN : /* Operators */ @@ -703,7 +711,7 @@ String RelObjectNameExt(): String result=null; } { - ( result=RelObjectName() | tk= | tk= | tk=) + ( result=RelObjectName() | tk= | tk= | tk= | tk= ) { if (tk!=null) result=tk.image; return result; @@ -1834,7 +1842,7 @@ Expression ComparisonItem() : } { ( - retval=AllComparisonExpression() + retval=AllExpression() | LOOKAHEAD(AnyComparisonExpression()) retval=AnyComparisonExpression() | LOOKAHEAD(3) retval=SimpleExpression() | retval=RowConstructor() @@ -1845,13 +1853,19 @@ Expression ComparisonItem() : } } -Expression AllComparisonExpression() : +AllExpression AllExpression() : { - AllComparisonExpression retval = null; + AllExpression retval = null; SubSelect subselect = null; + ArrayLiteral array = null; } { - "(" subselect=SubSelect() ")" { retval = new AllComparisonExpression(subselect); } + "(" + ( + subselect=SubSelect() { retval = new AllComparisonExpression(subselect); } + | array = ArrayLiteral() { retval = new AllArrayExpression(array); } + ) + ")" { return retval; } @@ -1859,13 +1873,21 @@ Expression AllComparisonExpression() : Expression AnyComparisonExpression() : { - AnyComparisonExpression retval = null; + AnyExpression retval = null; AnyType anyType; SubSelect subselect = null; + ArrayLiteral array = null; } { ( { anyType = AnyType.ANY; } | { anyType = AnyType.SOME; } ) - "(" subselect=SubSelect() ")" { retval = new AnyComparisonExpression(anyType, subselect); } + "(" + (subselect=SubSelect() { retval = new AnyComparisonExpression(anyType, subselect); } + |array = ArrayLiteral() { + if (anyType == AnyType.SOME) + throw new IllegalStateException("SOME not allowed with ARRAY"); + retval = new AnyArrayExpression(array); + } + ) ")" { return retval; } @@ -2496,6 +2518,19 @@ SubSelect SubSelect(): } } +ArrayLiteral ArrayLiteral(): +{ + ArrayLiteral array = new ArrayLiteral(); + ExpressionList list = null; +} +{ + list=SimpleExpressionList() "]" + { + array.setList(list); + return array; + } +} + CreateIndex CreateIndex(): { CreateIndex createIndex = new CreateIndex(); @@ -2743,6 +2778,7 @@ ColDataType ColDataType(): } { ( tk= [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } + | tk= [tk2=] { colDataType.setDataType(tk.image + (tk2!=null?" " + tk2.image:"")); } | ( tk= | tk= ) { colDataType.setDataType(tk.image); } ) [LOOKAHEAD(2) "(" ( (tk= | tk= | tk=) { argumentsStringList.add(tk.image); } ["," {/*argumentsStringList.add(",");*/}] )* ")"] 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 661e55273..608764b7f 100644 --- a/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/test/select/SelectTest.java @@ -1934,6 +1934,14 @@ public void testRowConstructor2() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE ROW(col1, col2) = (SELECT col3, col4 FROM t2 WHERE id = 10)"); } + public void testArrayLiteral1() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE col1 = ANY (ARRAY[1, 2, 3])"); + } + + public void testArrayLiteral2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM t1 WHERE col1 = ALL (ARRAY[\"a\", \"b\", \"c\"])"); + } + public void testIssue154() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT d.id, d.uuid, d.name, d.amount, d.percentage, d.modified_time FROM discount d LEFT OUTER JOIN discount_category dc ON d.id = dc.discount_id WHERE merchant_id = ? AND deleted = ? AND dc.discount_id IS NULL AND modified_time < ? AND modified_time >= ? ORDER BY modified_time"); }