mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
#1645 - Added full support for MySQL EXPLAIN / DESCRIBE.
This commit is contained in:
@@ -18,13 +18,13 @@
|
||||
*/
|
||||
package com.alibaba.druid.bvt.sql.cobar;
|
||||
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLSetStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetCharSetStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetNamesStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetTransactionStatement;
|
||||
@@ -81,7 +81,7 @@ public class DALParserTest extends TestCase {
|
||||
public void testdesc() throws Exception {
|
||||
String sql = "desc tb1";
|
||||
MySqlStatementParser parser = new MySqlStatementParser(sql);
|
||||
MySqlDescribeStatement desc = parser.parseDescribe();
|
||||
MySqlExplainStatement desc = parser.parseDescribe();
|
||||
parser.match(Token.EOF);
|
||||
String output = SQLUtils.toMySqlString(desc);
|
||||
Assert.assertEquals("DESC tb1", output);
|
||||
@@ -90,7 +90,7 @@ public class DALParserTest extends TestCase {
|
||||
public void testdesc_1() throws Exception {
|
||||
String sql = "desc db.tb1";
|
||||
MySqlStatementParser parser = new MySqlStatementParser(sql);
|
||||
MySqlDescribeStatement desc = parser.parseDescribe();
|
||||
MySqlExplainStatement desc = parser.parseDescribe();
|
||||
parser.match(Token.EOF);
|
||||
String output = SQLUtils.toMySqlString(desc);
|
||||
Assert.assertEquals("DESC db.tb1", output);
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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.mysql;
|
||||
|
||||
import com.alibaba.druid.sql.MysqlTest;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
|
||||
import com.alibaba.druid.stat.TableStat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* See https://dev.mysql.com/doc/refman/5.7/en/explain.html for test cases.
|
||||
*/
|
||||
public class MySqlExplainTest extends MysqlTest {
|
||||
|
||||
public void testExplainTable() throws Exception {
|
||||
String sql = " EXPLAIN City";
|
||||
SQLStatement statement = testParseFormat(sql);
|
||||
|
||||
MySqlSchemaStatVisitor stats = schemaStats(statement);
|
||||
assertHasSeenXTables(stats, 1);
|
||||
assertHasSeenXColumns(stats, 0);
|
||||
assertHasSeenXConditions(stats, 0);
|
||||
assertHasSeenTable(stats, "City");
|
||||
}
|
||||
|
||||
public void testExplainTableWithColumn() throws Exception {
|
||||
String sql = " EXPLAIN City name";
|
||||
SQLStatement statement = testParseFormat(sql);
|
||||
|
||||
MySqlSchemaStatVisitor stats = schemaStats(statement);
|
||||
assertHasSeenXTables(stats, 1);
|
||||
assertHasSeenXColumns(stats, 1);
|
||||
assertHasSeenXConditions(stats, 0);
|
||||
assertHasSeenTable(stats, "City");
|
||||
assertHasSeenTableColumn(stats, "City", "name");
|
||||
}
|
||||
|
||||
public void testExplainTableWithWild() throws Exception {
|
||||
String sql = " EXPLAIN City '%ame'";
|
||||
SQLStatement statement = testParseFormat(sql);
|
||||
|
||||
MySqlSchemaStatVisitor stats = schemaStats(statement);
|
||||
assertHasSeenXTables(stats, 1);
|
||||
assertHasSeenXColumns(stats, 0);
|
||||
assertHasSeenXConditions(stats, 0);
|
||||
assertHasSeenTable(stats, "City");
|
||||
}
|
||||
|
||||
public void testExplainExplainableStatements() throws Exception {
|
||||
testExplainExplainableStatement("EXPLAIN SELECT * FROM City", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN DELETE FROM City", "City", 0);
|
||||
testExplainExplainableStatement("EXPLAIN INSERT INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN REPLACE INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN UPDATE City SET name = 'Beijing'", "City", 1);
|
||||
}
|
||||
|
||||
public void testExplainExtendedExplainableStatements() throws Exception {
|
||||
testExplainExplainableStatement("EXPLAIN EXTENDED SELECT * FROM City", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN EXTENDED DELETE FROM City", "City", 0);
|
||||
testExplainExplainableStatement("EXPLAIN EXTENDED INSERT INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN EXTENDED REPLACE INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN EXTENDED UPDATE City SET name = 'Beijing'", "City", 1);
|
||||
}
|
||||
|
||||
public void testExplainPartitionsExplainableStatements() throws Exception {
|
||||
testExplainExplainableStatement("EXPLAIN PARTITIONS SELECT * FROM City", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN PARTITIONS DELETE FROM City", "City", 0);
|
||||
testExplainExplainableStatement("EXPLAIN PARTITIONS INSERT INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN PARTITIONS REPLACE INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN PARTITIONS UPDATE City SET name = 'Beijing'", "City", 1);
|
||||
}
|
||||
|
||||
public void testExplainFormatTraditionalExplainableStatements() throws Exception {
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = TRADITIONAL SELECT * FROM City", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = TRADITIONAL DELETE FROM City", "City", 0);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = TRADITIONAL INSERT INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = TRADITIONAL REPLACE INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = TRADITIONAL UPDATE City SET name = 'Beijing'", "City", 1);
|
||||
}
|
||||
|
||||
public void testExplainFormatJsonExplainableStatements() throws Exception {
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = JSON SELECT * FROM City", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = JSON DELETE FROM City", "City", 0);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = JSON INSERT INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = JSON REPLACE INTO City (name) VALUES ('Beijing')", "City", 1);
|
||||
testExplainExplainableStatement("EXPLAIN FORMAT = JSON UPDATE City SET name = 'Beijing'", "City", 1);
|
||||
}
|
||||
|
||||
public void testExplainForConnections() throws Exception {
|
||||
testExplainForConnection("EXPLAIN FOR CONNECTION 1");
|
||||
testExplainForConnection("EXPLAIN EXTENDED FOR CONNECTION 1");
|
||||
testExplainForConnection("EXPLAIN PARTITIONS FOR CONNECTION 1");
|
||||
testExplainForConnection("EXPLAIN FORMAT = TRADITIONAL FOR CONNECTION 1");
|
||||
testExplainForConnection("EXPLAIN FORMAT = JSON FOR CONNECTION 1");
|
||||
}
|
||||
|
||||
public void testExplainForConnection(String sql) throws Exception {
|
||||
SQLStatement statement = testParseFormat(sql);
|
||||
|
||||
MySqlSchemaStatVisitor stats = schemaStats(statement);
|
||||
assertHasSeenXTables(stats, 0);
|
||||
assertHasSeenXColumns(stats, 0);
|
||||
assertHasSeenXConditions(stats, 0);
|
||||
}
|
||||
|
||||
private SQLStatement testParseFormat(String sql) {
|
||||
List<SQLStatement> statements = parse(sql);
|
||||
assertStatements(statements);
|
||||
|
||||
SQLStatement statement = statements.get(0);
|
||||
assertStatement(statement);
|
||||
|
||||
String output = format(statement);
|
||||
assertSqlEquals(sql, output);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
private void testExplainExplainableStatement(String sql, String tableName, int columns) {
|
||||
SQLStatement statement = testParseFormat(sql);
|
||||
|
||||
MySqlSchemaStatVisitor stats = schemaStats(statement);
|
||||
assertHasSeenXTables(stats, 1);
|
||||
assertHasSeenXColumns(stats, columns);
|
||||
assertHasSeenXConditions(stats, 0);
|
||||
|
||||
assertHasSeenTable(stats, tableName);
|
||||
}
|
||||
|
||||
private List<SQLStatement> parse(String sql) {
|
||||
MySqlStatementParser parser = new MySqlStatementParser(sql);
|
||||
return parser.parseStatementList();
|
||||
}
|
||||
|
||||
private String format(SQLStatement statement) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
MySqlOutputVisitor visitor = new MySqlOutputVisitor(out);
|
||||
statement.accept(visitor);
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private MySqlSchemaStatVisitor schemaStats(SQLStatement stmt) {
|
||||
MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
|
||||
stmt.accept(visitor);
|
||||
|
||||
return visitor;
|
||||
}
|
||||
|
||||
private void assertSqlEquals(String sql, String output) {
|
||||
assertEquals(sql.trim(), output.replaceAll("\\s+", " "));
|
||||
}
|
||||
|
||||
private void assertStatement(SQLStatement statement) {
|
||||
assertTrue(statement instanceof MySqlExplainStatement);
|
||||
}
|
||||
|
||||
private void assertStatements(List<SQLStatement> statements) {
|
||||
assertEquals(1, statements.size());
|
||||
}
|
||||
|
||||
private void assertHasSeenXTables(MySqlSchemaStatVisitor visitor, int x) {
|
||||
assertEquals(x, visitor.getTables().size());
|
||||
}
|
||||
|
||||
private void assertHasSeenXColumns(MySqlSchemaStatVisitor visitor, int x) {
|
||||
assertEquals(x, visitor.getColumns().size());
|
||||
}
|
||||
|
||||
private void assertHasSeenXConditions(MySqlSchemaStatVisitor visitor, int x) {
|
||||
assertEquals(x, visitor.getConditions().size());
|
||||
}
|
||||
|
||||
private static void assertHasSeenTable(MySqlSchemaStatVisitor visitor, String tableName) {
|
||||
assertTrue(visitor.getTables().containsKey(new TableStat.Name(tableName)));
|
||||
}
|
||||
|
||||
private static void assertHasSeenTableColumn(MySqlSchemaStatVisitor visitor, String tableName, String columnName) {
|
||||
assertTrue(visitor.getColumns().contains(new TableStat.Column(tableName, columnName)));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user