From abad78e4a3b8ecf939fa40fdbd437ebc538be25c Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 11 Oct 2017 10:09:50 +0800 Subject: [PATCH] improved db2 sql parser, support db2 interval expr. for issue #2037 --- pom.xml | 13 +- .../druid/sql/ast/expr/SQLAggregateExpr.java | 2 +- .../druid/sql/ast/expr/SQLIntervalExpr.java | 7 +- .../sql/ast/statement/SQLUnionQuery.java | 20 ++- .../druid/sql/dialect/db2/ast/DB2Object.java | 6 + .../sql/dialect/db2/parser/DB2ExprParser.java | 37 +++- .../dialect/db2/parser/DB2SelectParser.java | 3 + .../dialect/db2/visitor/DB2OutputVisitor.java | 16 ++ .../db2/visitor/DB2SchemaStatVisitor.java | 5 + .../druid/sql/parser/SQLExprParser.java | 22 ++- .../druid/sql/parser/SQLSelectParser.java | 4 +- .../SchemaResolveVisitorFactory.java | 10 +- .../java/com/alibaba/druid/util/FnvHash.java | 11 +- .../druid/bvt/sql/db2/DB2SelectTest_26.java | 170 ++++++++++++++++++ .../sql/db2/DB2SelectTest_27_interval.java | 72 ++++++++ 15 files changed, 384 insertions(+), 14 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_26.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_27_interval.java diff --git a/pom.xml b/pom.xml index e4f09f1748..e164e39b24 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.alibaba druid - 1.1.5-SNAPSHOT + 1.1.5-preview_05 jar druid @@ -279,6 +279,7 @@ 1.6.0 system ${java.home}/../Classes/jconsole.jar + true @@ -298,6 +299,7 @@ 1.8.0 system ${java.home}/../lib/jconsole.jar + true @@ -306,6 +308,7 @@ 1.8.0 system ${java.home}/../lib/tools.jar + true @@ -540,7 +543,7 @@ org.apache.calcite calcite-core - 1.13.0 + 1.14.0 provided @@ -706,6 +709,12 @@ 1.2.36 test + + org.antlr + antlr4 + 4.7 + test + diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java index 45e76bc842..dcbf499221 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLAggregateExpr.java @@ -248,7 +248,7 @@ public class SQLAggregateExpr extends SQLExprImpl implements Serializable, SQLRe } } - if (hash == FnvHash.Constants.WM_CONAT + if (hash == FnvHash.Constants.WM_CONCAT || hash == FnvHash.Constants.GROUP_CONCAT) { return SQLCharExpr.DEFAULT_DATA_TYPE; } diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIntervalExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIntervalExpr.java index d7be259c99..1a8d782370 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIntervalExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIntervalExpr.java @@ -25,11 +25,16 @@ import java.util.List; public class SQLIntervalExpr extends SQLExprImpl { private SQLExpr value; - private SQLIntervalUnit unit; + private SQLIntervalUnit unit; public SQLIntervalExpr(){ } + public SQLIntervalExpr(SQLExpr value, SQLIntervalUnit unit){ + setValue(value); + this.unit = unit; + } + public SQLIntervalExpr clone() { SQLIntervalExpr x = new SQLIntervalExpr(); if (value != null) { diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java index 7911aeb836..91b479a71d 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnionQuery.java @@ -15,9 +15,11 @@ */ package com.alibaba.druid.sql.ast.statement; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLLimit; import com.alibaba.druid.sql.ast.SQLObjectImpl; import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLUnionQuery extends SQLObjectImpl implements SQLSelectQuery { @@ -29,7 +31,8 @@ public class SQLUnionQuery extends SQLObjectImpl implements SQLSelectQuery { private SQLUnionOperator operator = SQLUnionOperator.UNION; private SQLOrderBy orderBy; - private SQLLimit limit; + private SQLLimit limit; + private String dbType; public SQLUnionOperator getOperator() { return operator; @@ -133,6 +136,8 @@ public class SQLUnionQuery extends SQLObjectImpl implements SQLSelectQuery { x.setLimit(limit.clone()); } + x.dbType = dbType; + return x; } @@ -147,4 +152,17 @@ public class SQLUnionQuery extends SQLObjectImpl implements SQLSelectQuery { return null; } + + public String getDbType() { + return dbType; + } + + public void setDbType(String dbType) { + this.dbType = dbType; + } + + public void output(StringBuffer buf) { + SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(buf, dbType); + this.accept(visitor); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/DB2Object.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/DB2Object.java index f787045b70..c6c8dcf055 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/DB2Object.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/DB2Object.java @@ -17,8 +17,14 @@ package com.alibaba.druid.sql.dialect.db2.ast; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitor; +import com.alibaba.druid.util.FnvHash; public interface DB2Object extends SQLObject { void accept0(DB2ASTVisitor visitor); + + interface Constants { + long CURRENT_DATE = FnvHash.fnv1a_64_lower("CURRENT DATE"); + long CURRENT_TIME = FnvHash.fnv1a_64_lower("CURRENT TIME"); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2ExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2ExprParser.java index 9b7f5d1cfe..69e9a44c33 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2ExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2ExprParser.java @@ -18,6 +18,8 @@ package com.alibaba.druid.sql.dialect.db2.parser; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntervalExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntervalUnit; import com.alibaba.druid.sql.ast.expr.SQLSequenceExpr; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLExprParser; @@ -93,9 +95,42 @@ public class DB2ExprParser extends SQLExprParser { if (identExpr.hashCode64() == FnvHash.Constants.CURRENT) { lexer.nextToken(); - expr = new SQLIdentifierExpr("CURRENT DATE"); + expr = new SQLIdentifierExpr("CURRENT TIMESTAMP"); } } + } else if (lexer.identifierEquals(FnvHash.Constants.TIME)) { + if (expr instanceof SQLIdentifierExpr) { + SQLIdentifierExpr identExpr = (SQLIdentifierExpr) expr; + if (identExpr.hashCode64() == FnvHash.Constants.CURRENT) { + lexer.nextToken(); + + expr = new SQLIdentifierExpr("CURRENT TIME"); + } + } + } else if (lexer.identifierEquals(FnvHash.Constants.MONTHS)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.MONTH); + lexer.nextToken(); + expr = intervalExpr; + } else if (lexer.identifierEquals(FnvHash.Constants.YEARS)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.YEAR); + lexer.nextToken(); + expr = intervalExpr; + } else if (lexer.identifierEquals(FnvHash.Constants.DAYS)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.DAY); + lexer.nextToken(); + expr = intervalExpr; + } else if (lexer.identifierEquals(FnvHash.Constants.HOURS)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.HOUR); + lexer.nextToken(); + expr = intervalExpr; + } else if (lexer.identifierEquals(FnvHash.Constants.MINUTES)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.MINUTE); + lexer.nextToken(); + expr = intervalExpr; + } else if (lexer.identifierEquals(FnvHash.Constants.SECONDS)) { + SQLIntervalExpr intervalExpr = new SQLIntervalExpr(expr, SQLIntervalUnit.SECOND); + lexer.nextToken(); + expr = intervalExpr; } return super.primaryRest(expr); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2SelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2SelectParser.java index 26a016eafc..b71c77d421 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2SelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2SelectParser.java @@ -22,15 +22,18 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock.Isolation; import com.alibaba.druid.sql.parser.*; +import com.alibaba.druid.util.JdbcConstants; public class DB2SelectParser extends SQLSelectParser { public DB2SelectParser(SQLExprParser exprParser){ super(exprParser); + dbType = JdbcConstants.DB2; } public DB2SelectParser(SQLExprParser exprParser, SQLSelectListCache selectListCache){ super(exprParser, selectListCache); + dbType = JdbcConstants.DB2; } public DB2SelectParser(String sql){ diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java index ddd07192bb..e591c4d62c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java @@ -15,9 +15,12 @@ */ package com.alibaba.druid.sql.dialect.db2.visitor; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLPartitionBy; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLIntervalExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntervalUnit; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2CreateTableStatement; @@ -143,4 +146,17 @@ public class DB2OutputVisitor extends SQLASTOutputVisitor implements DB2ASTVisit print0(ucase ? operator.name : operator.name_lcase); } } + + public boolean visit(SQLIntervalExpr x) { + SQLExpr value = x.getValue(); + value.accept(this); + + SQLIntervalUnit unit = x.getUnit(); + if (unit != null) { + print(' '); + print0(ucase ? unit.name() : unit.name_lcase); + print(ucase ? 'S' : 's'); + } + return false; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2SchemaStatVisitor.java index 12568e1c13..266f16e52f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2SchemaStatVisitor.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.db2.visitor; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.db2.ast.DB2Object; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2CreateTableStatement; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2ValuesStatement; @@ -57,4 +58,8 @@ public class DB2SchemaStatVisitor extends SchemaStatVisitor implements DB2ASTVis public void endVisit(DB2CreateTableStatement x) { } + + protected boolean isPseudoColumn(long hash64) { + return hash64 == DB2Object.Constants.CURRENT_DATE || hash64 == DB2Object.Constants.CURRENT_TIME; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index 8eebcb2cbb..acdd191592 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -1035,7 +1035,7 @@ public class SQLExprParser extends SQLParser { } if (lexer.token == Token.LPAREN) { - expr = methodRest(expr, name); + expr = methodRest(expr, name, false); } else { expr = new SQLPropertyExpr(expr, name, hash_lower); } @@ -1045,7 +1045,7 @@ public class SQLExprParser extends SQLParser { return expr; } - private SQLExpr methodRest(SQLExpr expr, String name) { + private SQLExpr methodRest(SQLExpr expr, String name, boolean aggregate) { lexer.nextToken(); if (lexer.token == Token.DISTINCT) { @@ -1066,6 +1066,21 @@ public class SQLExprParser extends SQLParser { accept(Token.RPAREN); } expr = aggregateExpr; + } else if (aggregate) { + SQLAggregateExpr methodInvokeExpr = new SQLAggregateExpr(name); + methodInvokeExpr.setMethodName(expr.toString() + "." + name); + if (lexer.token == Token.RPAREN) { + lexer.nextToken(); + } else { + if (lexer.token == Token.PLUS) { + methodInvokeExpr.addArgument(new SQLIdentifierExpr("+")); + lexer.nextToken(); + } else { + exprList(methodInvokeExpr.getArguments(), methodInvokeExpr); + } + accept(Token.RPAREN); + } + expr = methodInvokeExpr; } else { SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(name); methodInvokeExpr.setOwner(expr); @@ -2657,7 +2672,8 @@ public class SQLExprParser extends SQLParser { token = lexer.token; if (token == Token.LPAREN) { - expr = methodRest(expr, name); + boolean aggregate = hash_lower == FnvHash.Constants.WMSYS && name_hash_lower == FnvHash.Constants.WM_CONCAT; + expr = methodRest(expr, name, aggregate); token = lexer.token; } else { if (name_hash_lower == FnvHash.Constants.NEXTVAL) { diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index ff2125a599..a59abac197 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -84,7 +84,9 @@ public class SQLSelectParser extends SQLParser { } protected SQLUnionQuery createSQLUnionQuery() { - return new SQLUnionQuery(); + SQLUnionQuery union = new SQLUnionQuery(); + union.setDbType(getDbType()); + return union; } public SQLUnionQuery unionRest(SQLUnionQuery union) { diff --git a/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java b/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java index 4ca43aae91..4504318206 100644 --- a/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java +++ b/src/main/java/com/alibaba/druid/sql/repository/SchemaResolveVisitorFactory.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.*; import com.alibaba.druid.sql.ast.expr.*; import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.dialect.db2.ast.DB2Object; import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock; import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitorAdapter; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; @@ -230,8 +231,6 @@ class SchemaResolveVisitorFactory { } } - - static class DB2ResolveVisitor extends DB2ASTVisitorAdapter implements SchemaResolveVisitor { private SchemaRepository repository; private int options; @@ -283,6 +282,11 @@ class SchemaResolveVisitorFactory { } public boolean visit(SQLIdentifierExpr x) { + long hash64 = x.hashCode64(); + if (hash64 == DB2Object.Constants.CURRENT_DATE || hash64 == DB2Object.Constants.CURRENT_TIME) { + return false; + } + resolve(this, x); return true; } @@ -1684,7 +1688,7 @@ class SchemaResolveVisitorFactory { if (ctxTable instanceof SQLJoinTableSource) { String alias = tableSource.computeAlias(); - if (alias == null) { + if (alias == null || tableSource instanceof SQLWithSubqueryClause.Entry) { return; } diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index 1a187a6ad5..6e6a4424b2 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -350,6 +350,7 @@ public final class FnvHash { long DATE = fnv1a_64_lower("DATE"); long DATETIME = fnv1a_64_lower("DATETIME"); + long TIME = fnv1a_64_lower("TIME"); long TIMESTAMP = fnv1a_64_lower("TIMESTAMP"); long CLOB = fnv1a_64_lower("CLOB"); long NCLOB = fnv1a_64_lower("NCLOB"); @@ -371,7 +372,7 @@ public final class FnvHash { long CURRENT = fnv1a_64_lower("CURRENT"); long COUNT = fnv1a_64_lower("COUNT"); long ROW_NUMBER = fnv1a_64_lower("ROW_NUMBER"); - long WM_CONAT = fnv1a_64_lower("WM_CONAT"); + long WM_CONCAT = fnv1a_64_lower("WM_CONCAT"); long AVG = fnv1a_64_lower("AVG"); long MAX = fnv1a_64_lower("MAX"); long MIN = fnv1a_64_lower("MIN"); @@ -472,6 +473,13 @@ public final class FnvHash { long MINUTE = fnv1a_64_lower("MINUTE"); long SECOND = fnv1a_64_lower("SECOND"); + long SECONDS = fnv1a_64_lower("SECONDS"); + long MINUTES = fnv1a_64_lower("MINUTES"); + long HOURS = fnv1a_64_lower("HOURS"); + long DAYS = fnv1a_64_lower("DAYS"); + long MONTHS = fnv1a_64_lower("MONTHS"); + long YEARS = fnv1a_64_lower("YEARS"); + long BEFORE = fnv1a_64_lower("BEFORE"); long AFTER = fnv1a_64_lower("AFTER"); long INSTEAD = fnv1a_64_lower("INSTEAD"); @@ -515,5 +523,6 @@ public final class FnvHash { long VALIDPROC = fnv1a_64_lower("VALIDPROC"); long COMPRESS = fnv1a_64_lower("COMPRESS"); long YES = fnv1a_64_lower("YES"); + long WMSYS = fnv1a_64_lower("WMSYS"); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_26.java b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_26.java new file mode 100644 index 0000000000..a82e3824f9 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_26.java @@ -0,0 +1,170 @@ +/* + * 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.db2; + +import com.alibaba.druid.sql.DB2Test; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; +import com.alibaba.druid.sql.dialect.db2.visitor.DB2SchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class DB2SelectTest_26 extends DB2Test { + + public void test_0() throws Exception { + String sql = "SELECT LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END BUREAU_KEY,\n" + + "SUM(COALESCE(ADD_USER_CNT, 0)) ADD_SUM,\n" + + "0 USER_ACCT,\n" + + "0 USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE BETWEEN SUBSTR('{THISMONTH}', 1, 4) || '-01-01' AND '{THISMONTH}'\n" + + "GROUP BY LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END\n" + + "UNION ALL\n" + + "SELECT LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END BUREAU_KEY,\n" + + "0 ADD_SUM,\n" + + "SUM(COALESCE(BILL_USER_CNT, 0)) USER_ACCT,\n" + + "0 USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE = '{THISMONTH}'\n" + + "GROUP BY LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END\n" + + "UNION ALL\n" + + "SELECT LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END BUREAU_KEY,\n" + + "0 ADD_SUM,\n" + + "0 USER_ACCT,\n" + + "SUM(COALESCE(BILL_USER_CNT, 0)) USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE = DATE(SUBSTR('{THISMONTH}', 1, 4) || '-01-01') - 1 MONTHS\n" + + "GROUP BY LATN_ID,\n" + + "CASE WHEN BUREAU_KEY = 116 THEN 46 ELSE BUREAU_KEY END"; + + DB2StatementParser parser = new DB2StatementParser(sql); + List statementList = parser.parseStatementList(); + SQLSelectStatement stmt = (SQLSelectStatement) statementList.get(0); + System.out.println(stmt.getSelect().getQuery()); + + Assert.assertEquals(1, statementList.size()); + + DB2SchemaStatVisitor visitor = new DB2SchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(5, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("MK.M_BROAD_BAND_USER_FACT"))); + +// Assert.assertTrue(visitor.getColumns().contains(new Column("DSN8B10.EMP", "WORKDEPT"))); + // Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "first_name"))); + // Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "full_name"))); + + Assert.assertEquals("SELECT LATN_ID\n" + + "\t, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND AS BUREAU_KEY, SUM(COALESCE(ADD_USER_CNT, 0)) AS ADD_SUM\n" + + "\t, 0 AS USER_ACCT, 0 AS USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE BETWEEN SUBSTR('{THISMONTH}', 1, 4) CONCAT '-01-01' AND '{THISMONTH}'\n" + + "GROUP BY LATN_ID, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND\n" + + "UNION ALL\n" + + "SELECT LATN_ID\n" + + "\t, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND AS BUREAU_KEY, 0 AS ADD_SUM\n" + + "\t, SUM(COALESCE(BILL_USER_CNT, 0)) AS USER_ACCT\n" + + "\t, 0 AS USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE = '{THISMONTH}'\n" + + "GROUP BY LATN_ID, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND\n" + + "UNION ALL\n" + + "SELECT LATN_ID\n" + + "\t, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND AS BUREAU_KEY, 0 AS ADD_SUM, 0 AS USER_ACCT\n" + + "\t, SUM(COALESCE(BILL_USER_CNT, 0)) AS USER_ACCT_LY\n" + + "FROM MK.M_BROAD_BAND_USER_FACT\n" + + "WHERE THE_DATE = DATE(SUBSTR('{THISMONTH}', 1, 4) CONCAT '-01-01') - 1 MONTHS\n" + + "GROUP BY LATN_ID, CASE \n" + + "\t\tWHEN BUREAU_KEY = 116 THEN 46\n" + + "\t\tELSE BUREAU_KEY\n" + + "\tEND", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2)); + + Assert.assertEquals("select LATN_ID\n" + + "\t, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend as BUREAU_KEY, sum(COALESCE(ADD_USER_CNT, 0)) as ADD_SUM\n" + + "\t, 0 as USER_ACCT, 0 as USER_ACCT_LY\n" + + "from MK.M_BROAD_BAND_USER_FACT\n" + + "where THE_DATE between SUBSTR('{THISMONTH}', 1, 4) concat '-01-01' and '{THISMONTH}'\n" + + "group by LATN_ID, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend\n" + + "union all\n" + + "select LATN_ID\n" + + "\t, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend as BUREAU_KEY, 0 as ADD_SUM\n" + + "\t, sum(COALESCE(BILL_USER_CNT, 0)) as USER_ACCT\n" + + "\t, 0 as USER_ACCT_LY\n" + + "from MK.M_BROAD_BAND_USER_FACT\n" + + "where THE_DATE = '{THISMONTH}'\n" + + "group by LATN_ID, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend\n" + + "union all\n" + + "select LATN_ID\n" + + "\t, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend as BUREAU_KEY, 0 as ADD_SUM, 0 as USER_ACCT\n" + + "\t, sum(COALESCE(BILL_USER_CNT, 0)) as USER_ACCT_LY\n" + + "from MK.M_BROAD_BAND_USER_FACT\n" + + "where THE_DATE = DATE(SUBSTR('{THISMONTH}', 1, 4) concat '-01-01') - 1 months\n" + + "group by LATN_ID, case \n" + + "\t\twhen BUREAU_KEY = 116 then 46\n" + + "\t\telse BUREAU_KEY\n" + + "\tend", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_27_interval.java b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_27_interval.java new file mode 100644 index 0000000000..6c1e0629f4 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2SelectTest_27_interval.java @@ -0,0 +1,72 @@ +/* + * 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.db2; + +import com.alibaba.druid.sql.DB2Test; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; +import com.alibaba.druid.sql.dialect.db2.visitor.DB2SchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class DB2SelectTest_27_interval extends DB2Test { + + public void test_0() throws Exception { + String sql = "SELECT current date + 1 YEAR, current date + 3 YEARS + 2 MONTHS + 15 DAYS, current time + 5 HOURS - 3 MINUTES + 10 SECONDS from sysibm.sysdummy1"; + + DB2StatementParser parser = new DB2StatementParser(sql); + List statementList = parser.parseStatementList(); + SQLSelectStatement stmt = (SQLSelectStatement) statementList.get(0); + System.out.println(stmt.getSelect().getQuery()); + + Assert.assertEquals(1, statementList.size()); + + DB2SchemaStatVisitor visitor = new DB2SchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(0, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("sysibm.sysdummy1"))); + +// Assert.assertTrue(visitor.getColumns().contains(new Column("DSN8B10.EMP", "WORKDEPT"))); + // Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "first_name"))); + // Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "full_name"))); + + Assert.assertEquals("SELECT CURRENT DATE + 1 AS YEAR\n" + + "\t, CURRENT DATE + 3 YEARS + 2 MONTHS + 15 DAYS\n" + + "\t, CURRENT TIME + 5 HOURS - 3 MINUTES + 10 SECONDS\n" + + "FROM sysibm.sysdummy1", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2)); + + Assert.assertEquals("select CURRENT DATE + 1 as YEAR\n" + + "\t, CURRENT DATE + 3 years + 2 months + 15 days\n" + + "\t, CURRENT TIME + 5 hours - 3 minutes + 10 seconds\n" + + "from sysibm.sysdummy1", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + } +}