From b3ca3e74b6e073dfb3b19117fdeae75a81a6a5ea Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 4 Dec 2016 21:04:06 +0800 Subject: [PATCH] mysql parser support DIV operator. issue #1526 --- .../druid/sql/ast/expr/SQLBinaryOperator.java | 3 +- .../dialect/mysql/parser/MySqlExprParser.java | 8 +- .../sql/dialect/mysql/parser/MySqlLexer.java | 1 + .../druid/sql/parser/SQLExprParser.java | 7 +- .../com/alibaba/druid/sql/parser/Token.java | 1 + .../sql/mysql/select/MySqlSelectTest_33.java | 83 +++++++++++++++++++ 6 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_33.java diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java index 144b00fbdf..907a1ae4c0 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java @@ -26,7 +26,8 @@ public enum SQLBinaryOperator { BitwiseXor("^", 50), Multiply("*", 60), - Divide("/", 60), + Divide("/", 60), + DIV("DIV", 60), // mysql integer division Modulus("%", 60), Mod("MOD", 60), diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index 97783e2c39..d754bfe64f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -87,13 +87,19 @@ public class MySqlExprParser extends SQLExprParser { } public SQLExpr multiplicativeRest(SQLExpr expr) { - if (lexer.token() == Token.IDENTIFIER && "MOD".equalsIgnoreCase(lexer.stringVal())) { + final Token token = lexer.token(); + if (token == Token.IDENTIFIER && "MOD".equalsIgnoreCase(lexer.stringVal())) { lexer.nextToken(); SQLExpr rightExp = primary(); rightExp = relationalRest(rightExp); return new SQLBinaryOpExpr(expr, SQLBinaryOperator.Modulus, rightExp, JdbcConstants.MYSQL); + } else if (token == Token.DIV) { + lexer.nextToken(); + SQLExpr rightExp = bitXor(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.DIV, rightExp, getDbType()); + expr = multiplicativeRest(expr); } return super.multiplicativeRest(expr); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java index 711f138ee4..097afd102c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java @@ -52,6 +52,7 @@ public class MySqlLexer extends Lexer { map.put("ROW", Token.ROW); map.put("BEGIN", Token.BEGIN); map.put("END", Token.END); + map.put("DIV", Token.DIV); // for oceanbase & mysql 5.7 map.put("PARTITION", Token.PARTITION); 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 ec62b89e24..2f0c80885f 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -159,17 +159,18 @@ public class SQLExprParser extends SQLParser { } public SQLExpr multiplicativeRest(SQLExpr expr) { - if (lexer.token() == Token.STAR) { + final Token token = lexer.token(); + if (token == Token.STAR) { lexer.nextToken(); SQLExpr rightExp = bitXor(); expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Multiply, rightExp, getDbType()); expr = multiplicativeRest(expr); - } else if (lexer.token() == Token.SLASH) { + } else if (token == Token.SLASH) { lexer.nextToken(); SQLExpr rightExp = bitXor(); expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Divide, rightExp, getDbType()); expr = multiplicativeRest(expr); - } else if (lexer.token() == Token.PERCENT) { + } else if (token == Token.PERCENT) { lexer.nextToken(); SQLExpr rightExp = bitXor(); expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Modulus, rightExp, getDbType()); diff --git a/src/main/java/com/alibaba/druid/sql/parser/Token.java b/src/main/java/com/alibaba/druid/sql/parser/Token.java index d1a02dbf0b..bbad08819f 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -142,6 +142,7 @@ public enum Token { UNDO("UNDO"), SQLSTATE("SQLSTATE"), CONDITION("CONDITION"), + DIV("DIV"), //postgresql WINDOW("WINDOW"), diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_33.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_33.java new file mode 100644 index 0000000000..5eea13d200 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_33.java @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2101 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.mysql.select; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import org.junit.Assert; + +import java.util.List; + +public class MySqlSelectTest_33 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "select COUNT() AS count,\n" + + "DATE_FORMAT(DATE(reg_time), '%Y-%m-%d') AS date,\n" + + "(HOUR(reg_time) DIV 2) as intervalTime\n" + + "FROM USER_RECOMMEND_INFO WHERE 1=1\n" + + "and reg_time >='2016-12-01 00:00:00'\n" + + "and reg_time <='2016-12-01 23:59:59'\n" + + "group by DATE(reg_time),HOUR(reg_time) DIV 2"; + + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); +// print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + 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(1, visitor.getColumns().size()); +// Assert.assertEquals(0, visitor.getConditions().size()); +// Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + { + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT COUNT() AS count, DATE_FORMAT(DATE(reg_time), '%Y-%m-%d') AS date, HOUR(reg_time) DIV 2 AS intervalTime\n" + + "FROM USER_RECOMMEND_INFO\n" + + "WHERE 1 = 1\n" + + "\tAND reg_time >= '2016-12-01 00:00:00'\n" + + "\tAND reg_time <= '2016-12-01 23:59:59'\n" + + "GROUP BY DATE(reg_time), HOUR(reg_time) DIV 2", // + output); + } + { + String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + Assert.assertEquals("select count() as count, DATE_FORMAT(DATE(reg_time), '%Y-%m-%d') as date, HOUR(reg_time) div 2 as intervalTime\n" + + "from USER_RECOMMEND_INFO\n" + + "where 1 = 1\n" + + "\tand reg_time >= '2016-12-01 00:00:00'\n" + + "\tand reg_time <= '2016-12-01 23:59:59'\n" + + "group by DATE(reg_time), HOUR(reg_time) div 2", // + output); + } + } + + + +}