diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLValuesExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLValuesExpr.java new file mode 100644 index 0000000000..32232017a2 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLValuesExpr.java @@ -0,0 +1,57 @@ +package com.alibaba.druid.sql.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLExprImpl; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +import java.util.ArrayList; +import java.util.List; + +public class SQLValuesExpr extends SQLExprImpl { + private List values = new ArrayList(); + + public List getValues() { + return values; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, values); + } + visitor.endVisit(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SQLValuesExpr that = (SQLValuesExpr) o; + + return values.equals(that.values); + } + + @Override + public int hashCode() { + return values.hashCode(); + } + + @Override + public SQLExpr clone() { + SQLValuesExpr x = new SQLValuesExpr(); + + for (SQLListExpr value : values) { + SQLListExpr value2 = value.clone(); + value2.setParent(x); + x.values.add(value2); + } + + return x; + } + + @Override + public List getChildren() { + return values; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java index 23cd0f807b..a4f822b77f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java @@ -117,6 +117,27 @@ public class PGExprParser extends SQLExprParser { SQLUnaryExpr expr = new SQLUnaryExpr(SQLUnaryOperator.Pound, value); return primaryRest(expr); } + } else if (lexer.token() == Token.VALUES) { + lexer.nextToken(); + + SQLValuesExpr values = new SQLValuesExpr(); + for (;;) { + accept(Token.LPAREN); + SQLListExpr listExpr = new SQLListExpr(); + exprList(listExpr.getItems(), listExpr); + accept(Token.RPAREN); + + listExpr.setParent(values); + + values.getValues().add(listExpr); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + return values; } return super.primary(); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index eece84a8bb..69940b94e2 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -6300,4 +6300,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet return false; } + + @Override + public boolean visit(SQLValuesExpr x) { + print0(ucase ? "VALUES (" : "values ("); + printAndAccept(x.getValues(), ", "); + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java index 39d36b2497..ba8b646acb 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java @@ -826,4 +826,7 @@ public interface SQLASTVisitor { boolean visit(SQLAlterTableExchangePartition x); void endVisit(SQLAlterTableExchangePartition x); + + boolean visit(SQLValuesExpr x); + void endVisit(SQLValuesExpr x); } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java index b6449bdbd8..b341bb681f 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java @@ -1954,6 +1954,16 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor { } + @Override + public boolean visit(SQLValuesExpr x) { + return true; + } + + @Override + public void endVisit(SQLValuesExpr x) { + + } + public final boolean isEnabled(VisitorFeature feature) { return VisitorFeature.isEnabled(this.features, feature); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest63.java b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest63.java new file mode 100644 index 0000000000..b1f60e1dce --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/postgresql/select/PGSelectTest63.java @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2017 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.postgresql.select; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import junit.framework.TestCase; + +import java.util.List; + +public class PGSelectTest63 extends TestCase { + public void test_0() throws Exception { + String sql = "select * from a where a.id in (VALUES (4096), (4097))"; + + List stmtList = SQLUtils.parseStatements(sql, JdbcConstants.POSTGRESQL); + SQLStatement stmt = stmtList.get(0); + + assertEquals("SELECT *\n" + + "FROM a\n" + + "WHERE a.id IN (VALUES ((4096), (4097))", SQLUtils.toPGString(stmt)); + + assertEquals("select *\n" + + "from a\n" + + "where a.id in (values ((4096), (4097))", SQLUtils.toPGString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + + assertEquals(1, stmtList.size()); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.POSTGRESQL); + stmt.accept(visitor); + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); + + assertEquals(2, visitor.getColumns().size()); + assertEquals(1, visitor.getTables().size()); + } +}