From 33e3a931106f5b6877422a9f926f56df0afd5da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Mon, 20 Feb 2012 14:37:15 +0800 Subject: [PATCH] mysql parser support KILL statement --- .../sql/dialect/hive/parser/HiveLexer.java | 95 +++++++++++++++++++ .../ast/statement/MySqlKillStatement.java | 36 +++++++ .../sql/dialect/mysql/parser/MySqlLexer.java | 1 + .../mysql/parser/MySqlStatementParser.java | 29 ++++++ .../mysql/visitor/MySqlASTVisitor.java | 5 + .../mysql/visitor/MySqlASTVisitorAdapter.java | 11 +++ .../mysql/visitor/MySqlOutputVisitor.java | 19 ++++ .../com/alibaba/druid/sql/parser/Token.java | 1 + .../druid/bvt/sql/mysql/Kill_Test.java | 62 ++++++++++++ .../visitor/MySqlSchemaStatVisitorTest5.java | 1 + .../performance/TestSelectPerformance.java | 14 ++- 11 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveLexer.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlKillStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/Kill_Test.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveLexer.java new file mode 100644 index 0000000000..7282508490 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveLexer.java @@ -0,0 +1,95 @@ +package com.alibaba.druid.sql.dialect.hive.parser; + +import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.Token; + +public class HiveLexer extends Lexer { + + public HiveLexer(char[] input, int inputLength, boolean skipComment) { + super(input, inputLength, skipComment); + } + + + public void scanComment() { + if (ch != '/') { + throw new IllegalStateException(); + } + + np = bp; + sp = 0; + scanChar(); + sp++; + + // /*+ */ + if (ch == '*') { + scanChar(); + sp++; + + while (ch == ' ') { + scanChar(); + sp++; + } + + boolean isHint = false; + int startHintSp = sp + 1; + if (ch == '+') { + isHint = true; + scanChar(); + sp++; + } + + for (;;) { + if (ch == '*' && buf[bp + 1] == '/') { + sp += 2; + scanChar(); + scanChar(); + break; + } + + scanChar(); + sp++; + } + + if (isHint) { + stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 2).trim(); + token = Token.HINT; + } else { + stringVal = new String(buf, np, sp); + token = Token.MULTI_LINE_COMMENT; + } + + return; + } + + if (ch == '/') { + scanChar(); + sp++; + + for (;;) { + if (ch == '\r') { + if (buf[bp + 1] == '\n') { + sp += 2; + scanChar(); + break; + } + sp++; + break; + } + + if (ch == '\r') { + scanChar(); + sp++; + break; + } + + scanChar(); + sp++; + } + + stringVal = new String(buf, np + 1, sp); + token = Token.LINE_COMMENT; + return; + } + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlKillStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlKillStatement.java new file mode 100644 index 0000000000..4f9e41ce8c --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlKillStatement.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlKillStatement extends MySqlStatementImpl { + private static final long serialVersionUID = 1L; + private Type type; + private SQLExpr threadId; + + public static enum Type { + CONNECTION, QUERY + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public SQLExpr getThreadId() { + return threadId; + } + + public void setThreadId(SQLExpr threadId) { + this.threadId = threadId; + } + + protected void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, threadId); + } + } +} 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 dd77f4f966..099681828d 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 @@ -107,6 +107,7 @@ public class MySqlLexer extends Lexer { map.put("TRUE", Token.TRUE); map.put("FALSE", Token.FALSE); map.put("LIMIT", Token.LIMIT); + map.put("KILL", Token.KILL); DEFAULT_MYSQL_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index 86feb9e6da..c862d66ee7 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.mysql.parser; import java.util.List; +import com.alibaba.druid.bvt.sql.mysql.Kill_Test; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderBy; @@ -35,6 +36,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableParser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadXmlStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPrepareStatement; @@ -153,8 +155,35 @@ public class MySqlStatementParser extends SQLStatementParser { throw new ParserException("TODO " + lexer.token()); } + + public SQLStatement parseKill() { + accept(Token.KILL); + + MySqlKillStatement stmt = new MySqlKillStatement(); + + if (identifierEquals("CONNECTION")) { + stmt.setType(MySqlKillStatement.Type.CONNECTION); + lexer.nextToken(); + } else if (identifierEquals("QUERY")) { + stmt.setType(MySqlKillStatement.Type.QUERY); + lexer.nextToken(); + } else { + throw new ParserException("not support kill type " + lexer.token()); + } + + SQLExpr threadId = this.exprParser.expr(); + stmt.setThreadId(threadId); + + return stmt; + } public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.KILL) { + SQLStatement stmt = parseKill(); + statementList.add(stmt); + return true; + } + if (identifierEquals("PREPARE")) { MySqlPrepareStatement stmt = parsePrepare(); statementList.add(stmt); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index 51bc25e734..a4b485b9d8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadXmlStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPrepareStatement; @@ -148,4 +149,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { void endVisit(CobarShowStatus x); boolean visit(CobarShowStatus x); + + void endVisit(MySqlKillStatement x); + + boolean visit(MySqlKillStatement x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java index 9900046561..2f637480ef 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitorAdapter.java @@ -12,6 +12,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadXmlStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPrepareStatement; @@ -311,4 +312,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq return true; } + @Override + public void endVisit(MySqlKillStatement x) { + + } + + @Override + public boolean visit(MySqlKillStatement x) { + return true; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 9d4df07315..eb4e8541b9 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -42,6 +42,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStateme import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadXmlStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPrepareStatement; @@ -1201,4 +1202,22 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print("SHOW COBAR_STATUS"); return false; } + + + @Override + public void endVisit(MySqlKillStatement x) { + + } + + @Override + public boolean visit(MySqlKillStatement x) { + if (MySqlKillStatement.Type.CONNECTION.equals(x.getType())) { + print("KILL CONNECTION "); + } else if (MySqlKillStatement.Type.QUERY.equals(x.getType())) { + print("KILL QUERY "); + } + x.getThreadId().accept(this); + return false; + } + } 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 9ff5432ba0..9e890aa604 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -97,6 +97,7 @@ public enum Token { TRUE("TRUE"), FALSE("FALSE"), LIMIT("LIMIT"), + KILL("KILL"), // oracle START("START"), diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/Kill_Test.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/Kill_Test.java new file mode 100644 index 0000000000..c554ff8a25 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/Kill_Test.java @@ -0,0 +1,62 @@ +/* + * Copyright 1999-2011 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; + +import java.util.List; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; + +public class Kill_Test extends TestCase { + + public void test_0() throws Exception { + String sql = "KILL QUERY 233"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + + String text = output(stmtList); + + Assert.assertEquals("KILL QUERY 233;", text); + } + + public void test_1() throws Exception { + String sql = "KILL CONNECTION 233"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + + String text = output(stmtList); + + Assert.assertEquals("KILL CONNECTION 233;", text); + } + + + private String output(List stmtList) { + StringBuilder out = new StringBuilder(); + + for (SQLStatement stmt : stmtList) { + stmt.accept(new MySqlOutputVisitor(out)); + out.append(";"); + } + + return out.toString(); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlSchemaStatVisitorTest5.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlSchemaStatVisitorTest5.java index 095efb5820..00711a7b4f 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlSchemaStatVisitorTest5.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/visitor/MySqlSchemaStatVisitorTest5.java @@ -18,6 +18,7 @@ public class MySqlSchemaStatVisitorTest5 extends TestCase { + " LEFT JOIN (select * from view_portal_panel) c ON a.panel_id = c.panel_id " + " WHERE b.user_id = ? and ((b.is_grid='y' and c.param_name='is_hidden' and c.param_value='false') or b.is_grid != 'y') and b.user_id in (select user_id from table1 where id = 1) ORDER BY a.col ASC, a.position ASC"; + sql = "select columnName from table1 where id in (select id from table3 where name = ?)"; MySqlStatementParser parser = new MySqlStatementParser(sql); List statementList = parser.parseStatementList(); SQLStatement statemen = statementList.get(0); diff --git a/src/test/java/com/alibaba/druid/sql/performance/TestSelectPerformance.java b/src/test/java/com/alibaba/druid/sql/performance/TestSelectPerformance.java index 368955df07..fa21670faa 100644 --- a/src/test/java/com/alibaba/druid/sql/performance/TestSelectPerformance.java +++ b/src/test/java/com/alibaba/druid/sql/performance/TestSelectPerformance.java @@ -16,18 +16,21 @@ package com.alibaba.druid.sql.performance; import java.text.NumberFormat; +import java.util.List; import junit.framework.TestCase; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; import com.alibaba.druid.sql.parser.SQLStatementParser; public class TestSelectPerformance extends TestCase { - private final int COUNT = 1000 * 1; - private String sql = "SELECT F100, F101, F102, F103, F104 FROM T_001 WHERE F100 = ?"; + private final int COUNT = 1000 * 1000; + private String sql = "SELECT distinct a.id \"id\", a.col \"col\", a.position \"position\", a.panel_id \"panelId\" FROM (select * from view_position_info) a LEFT JOIN db1.view_portal b ON a.panel_id = b.panel_id LEFT JOIN (select * from view_portal_panel) c ON a.panel_id = c.panel_id WHERE b.user_id = ? and ((b.is_grid='y' and c.param_name='is_hidden' and c.param_value='false') or b.is_grid != 'y') and b.user_id in (select user_id from table1 where id = 1) ORDER BY a.col ASC, a.position ASC"; public void test_simple() throws Exception { - for (int i = 0; i < 1; ++i) { + for (int i = 0; i < 5; ++i) { f(); } } @@ -35,7 +38,10 @@ public class TestSelectPerformance extends TestCase { private void f() throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < COUNT; ++i) { - new SQLStatementParser(sql).parseStatementList(); + List statementList = new SQLStatementParser(sql).parseStatementList(); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statementList.get(0).accept(visitor); // stmtList.toString(); } long time = System.currentTimeMillis() - start;