diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlExplainType.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlExplainType.java new file mode 100644 index 0000000000..35355444d2 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlExplainType.java @@ -0,0 +1,23 @@ +/* + * 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.sql.dialect.mysql.ast.clause; + +public enum MySqlExplainType { + + EXTENDED, + PARTITIONS, + FORMAT +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlFormatName.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlFormatName.java new file mode 100644 index 0000000000..eb0f3fa3f4 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlFormatName.java @@ -0,0 +1,22 @@ +/* + * 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.sql.dialect.mysql.ast.clause; + +public enum MySqlFormatName { + + TRADITIONAL, + JSON +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDescribeStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDescribeStatement.java deleted file mode 100644 index b2f3cad783..0000000000 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlDescribeStatement.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.sql.dialect.mysql.ast.statement; - -import com.alibaba.druid.sql.ast.SQLName; -import com.alibaba.druid.sql.ast.statement.SQLDescribeStatement; -import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; -import com.alibaba.druid.sql.visitor.SQLASTVisitor; - -public class MySqlDescribeStatement extends SQLDescribeStatement implements MySqlStatement { - - private SQLName colName; - - public void accept0(MySqlASTVisitor visitor) { - if (visitor.visit(this)) { - acceptChild(visitor, object); - acceptChild(visitor, colName); - } - visitor.endVisit(this); - } - - @Override - public void accept0(SQLASTVisitor visitor) { - if (visitor instanceof MySqlASTVisitor) { - accept0((MySqlASTVisitor) visitor); - } else { - throw new IllegalArgumentException("not support visitor type : " + visitor.getClass().getName()); - } - } - - public SQLName getColName() { - return colName; - } - - public void setColName(SQLName colName) { - this.colName = colName; - } - -} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlExplainStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlExplainStatement.java new file mode 100644 index 0000000000..9f01794528 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlExplainStatement.java @@ -0,0 +1,133 @@ +/* + * 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.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.statement.SQLExplainStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlExplainType; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlFormatName; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import com.alibaba.druid.util.JdbcConstants; + +public class MySqlExplainStatement extends SQLExplainStatement implements MySqlStatement { + + private boolean describe; + + private SQLName tableName; + private SQLName columnName; + private SQLExpr wild; + + private MySqlExplainType type; + + private MySqlFormatName formatName; + + private SQLExpr connectionId; + + public MySqlExplainStatement() { + super (JdbcConstants.MYSQL); + } + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + // tbl_name [col_name | wild] + if (tableName != null) { + acceptChild(visitor, tableName); + if (columnName != null) { + acceptChild(visitor, columnName); + } else if (wild != null) { + acceptChild(visitor, wild); + } + } else { + // {explainable_stmt | FOR CONNECTION connection_id} + if (connectionId != null) { + acceptChild(visitor, connectionId); + } else { + acceptChild(visitor, statement); + } + } + } + + visitor.endVisit(this); + } + + protected void accept0(SQLASTVisitor visitor) { + accept0((MySqlASTVisitor) visitor); + } + + public String toString() { + return SQLUtils.toMySqlString(this); + } + + public boolean isDescribe() { + return describe; + } + + public void setDescribe(boolean describe) { + this.describe = describe; + } + + public SQLName getTableName() { + return tableName; + } + + public void setTableName(SQLName tableName) { + this.tableName = tableName; + } + + public SQLName getColumnName() { + return columnName; + } + + public void setColumnName(SQLName columnName) { + this.columnName = columnName; + } + + public SQLExpr getWild() { + return wild; + } + + public void setWild(SQLExpr wild) { + this.wild = wild; + } + + public MySqlExplainType getType() { + return type; + } + + public void setType(MySqlExplainType type) { + this.type = type; + } + + public MySqlFormatName getFormatName() { + return formatName; + } + + public void setFormatName(MySqlFormatName formatName) { + this.formatName = formatName; + } + + public SQLExpr getConnectionId() { + return connectionId; + } + + public void setConnectionId(SQLExpr connectionId) { + this.connectionId = connectionId; + } + +} 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 ec854ceb14..98ba833318 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 @@ -15,9 +15,6 @@ */ package com.alibaba.druid.sql.dialect.mysql.parser; -import java.util.ArrayList; -import java.util.List; - import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; import com.alibaba.druid.sql.ast.SQLDeclareItem; @@ -37,8 +34,56 @@ import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; -import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.ast.statement.SQLAlterDatabaseStatement; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddConstraint; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddIndex; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAnalyzePartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableCheckPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableCoalescePartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableConvertCharSet; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDisableConstraint; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDisableKeys; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDiscardPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropColumnItem; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropConstraint; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropForeignKey; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropIndex; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropKey; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableDropPrimaryKey; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableEnableConstraint; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableEnableKeys; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableImportPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableOptimizePartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableReOrganizePartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableRebuildPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableRepairPartition; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableTruncatePartition; +import com.alibaba.druid.sql.ast.statement.SQLBlockStatement; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLCreateDatabaseStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateIndexStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateProcedureStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLIfStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; +import com.alibaba.druid.sql.ast.statement.SQLLoopStatement; +import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLSetStatement; +import com.alibaba.druid.sql.ast.statement.SQLShowTablesStatement; +import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLUnique; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue; import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType; @@ -48,6 +93,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatemen import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlExplainType; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlFormatName; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlHandlerType; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement; @@ -69,8 +116,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHelpStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; @@ -85,7 +132,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStateme import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlResetStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRollbackStatement; -import com.alibaba.druid.sql.ast.SQLLimit; 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.MySqlSetPasswordStatement; @@ -142,41 +188,50 @@ import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.sql.parser.Token; import com.alibaba.druid.util.JdbcConstants; +import java.util.ArrayList; +import java.util.List; + public class MySqlStatementParser extends SQLStatementParser { private static final String AUTO_INCREMENT = "AUTO_INCREMENT"; - private static final String COLLATE2 = "COLLATE"; - private static final String CHAIN = "CHAIN"; - private static final String ENGINES = "ENGINES"; - private static final String ENGINE = "ENGINE"; - private static final String BINLOG = "BINLOG"; - private static final String EVENTS = "EVENTS"; - private static final String CHARACTER = "CHARACTER"; - private static final String SESSION = "SESSION"; - private static final String GLOBAL = "GLOBAL"; - private static final String VARIABLES = "VARIABLES"; - private static final String ERRORS = "ERRORS"; - private static final String STATUS = "STATUS"; - private static final String IGNORE = "IGNORE"; - private static final String RESET = "RESET"; - private static final String DESCRIBE = "DESCRIBE"; - private static final String WRITE = "WRITE"; - private static final String READ = "READ"; - private static final String LOCAL = "LOCAL"; - private static final String TABLES = "TABLES"; - private static final String TEMPORARY = "TEMPORARY"; - private static final String SPATIAL = "SPATIAL"; - private static final String FULLTEXT = "FULLTEXT"; - private static final String DELAYED = "DELAYED"; - private static final String LOW_PRIORITY = "LOW_PRIORITY"; + private static final String COLLATE2 = "COLLATE"; + private static final String CHAIN = "CHAIN"; + private static final String ENGINES = "ENGINES"; + private static final String ENGINE = "ENGINE"; + private static final String BINLOG = "BINLOG"; + private static final String EVENTS = "EVENTS"; + private static final String CHARACTER = "CHARACTER"; + private static final String SESSION = "SESSION"; + private static final String GLOBAL = "GLOBAL"; + private static final String VARIABLES = "VARIABLES"; + private static final String ERRORS = "ERRORS"; + private static final String STATUS = "STATUS"; + private static final String IGNORE = "IGNORE"; + private static final String RESET = "RESET"; + private static final String DESCRIBE = "DESCRIBE"; + private static final String WRITE = "WRITE"; + private static final String READ = "READ"; + private static final String LOCAL = "LOCAL"; + private static final String TABLES = "TABLES"; + private static final String TEMPORARY = "TEMPORARY"; + private static final String SPATIAL = "SPATIAL"; + private static final String FULLTEXT = "FULLTEXT"; + private static final String DELAYED = "DELAYED"; + private static final String LOW_PRIORITY = "LOW_PRIORITY"; + private static final String CONNECTION = "CONNECTION"; + private static final String EXTENDED = "EXTENDED"; + private static final String PARTITIONS = "PARTITIONS"; + private static final String FORMAT = "FORMAT"; + private static final String TRADITIONAL = "TRADITIONAL"; + private static final String JSON = "JSON"; private int maxIntoClause = -1; - public MySqlStatementParser(String sql){ + public MySqlStatementParser(String sql) { super(new MySqlExprParser(sql)); } - public MySqlStatementParser(Lexer lexer){ + public MySqlStatementParser(Lexer lexer) { super(new MySqlExprParser(lexer)); } @@ -195,13 +250,13 @@ public class MySqlStatementParser extends SQLStatementParser { public SQLStatement parseSelect() { MySqlSelectParser selectParser = new MySqlSelectParser(this.exprParser); - + SQLSelect select = selectParser.select(); - + if (selectParser.returningFlag) { return selectParser.updateStmt; } - + return new SQLSelectStatement(select, JdbcConstants.MYSQL); } @@ -314,7 +369,7 @@ public class MySqlStatementParser extends SQLStatementParser { } if (lexer.token() == Token.UNIQUE || lexer.token() == Token.INDEX || identifierEquals(FULLTEXT) - || identifierEquals(SPATIAL)) { + || identifierEquals(SPATIAL)) { if (replace) { lexer.reset(markBp, markChar, Token.CREATE); } @@ -384,7 +439,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.LPAREN); - for (;;) { + for (; ; ) { SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem(); stmt.addItem(item); if (lexer.token() == Token.COMMA) { @@ -425,7 +480,7 @@ public class MySqlStatementParser extends SQLStatementParser { MySqlCreateUserStatement stmt = new MySqlCreateUserStatement(); - for (;;) { + for (; ; ) { MySqlCreateUserStatement.UserSpecification userSpec = new MySqlCreateUserStatement.UserSpecification(); SQLExpr expr = exprParser.primary(); @@ -527,7 +582,7 @@ public class MySqlStatementParser extends SQLStatementParser { MySqlResetStatement stmt = new MySqlResetStatement(); - for (;;) { + for (; ; ) { if (lexer.token() == Token.IDENTIFIER) { if (identifierEquals("QUERY")) { lexer.nextToken(); @@ -567,11 +622,11 @@ public class MySqlStatementParser extends SQLStatementParser { statementList.add(stmt); return true; } - - if(identifierEquals("DEALLOCATE")) { - MysqlDeallocatePrepareStatement stmt = parseDeallocatePrepare(); - statementList.add(stmt); - return true; + + if (identifierEquals("DEALLOCATE")) { + MysqlDeallocatePrepareStatement stmt = parseDeallocatePrepare(); + statementList.add(stmt); + return true; } if (identifierEquals("LOAD")) { @@ -598,6 +653,13 @@ public class MySqlStatementParser extends SQLStatementParser { return true; } + if (lexer.token() == Token.EXPLAIN) { + MySqlExplainStatement stmt = this.parseExplain(); + statementList.add(stmt); + return true; + } + + if (identifierEquals(BINLOG)) { SQLStatement stmt = parseBinlog(); statementList.add(stmt); @@ -642,12 +704,12 @@ public class MySqlStatementParser extends SQLStatementParser { boolean isLockTables = TABLES.equalsIgnoreCase(val) && lexer.token() == Token.IDENTIFIER; boolean isLockTable = "TABLE".equalsIgnoreCase(val) && lexer.token() == Token.TABLE; if (isLockTables || isLockTable) { - lexer.nextToken(); + lexer.nextToken(); } else { - setErrorEndPos(lexer.pos()); + setErrorEndPos(lexer.pos()); throw new ParserException("syntax error, expect TABLES or TABLE, actual " + lexer.token()); } - + MySqlLockTableStatement stmt = new MySqlLockTableStatement(); stmt.setTableSource(this.exprParser.name()); @@ -666,7 +728,7 @@ public class MySqlStatementParser extends SQLStatementParser { acceptIdentifier(WRITE); stmt.setLockType(LockType.LOW_PRIORITY_WRITE); } else { - throw new ParserException("syntax error, expect READ or WRITE, actual " + lexer.token()); + throw new ParserException("syntax error, expect READ or WRITE, actual " + lexer.token()); } if (lexer.token() == Token.HINT) { @@ -683,9 +745,9 @@ public class MySqlStatementParser extends SQLStatementParser { boolean isUnLockTable = "TABLE".equalsIgnoreCase(val) && lexer.token() == Token.TABLE; statementList.add(new MySqlUnlockTablesStatement()); if (isUnLockTables || isUnLockTable) { - lexer.nextToken(); + lexer.nextToken(); } else { - setErrorEndPos(lexer.pos()); + setErrorEndPos(lexer.pos()); throw new ParserException("syntax error, expect TABLES or TABLE, actual " + lexer.token()); } return true; @@ -736,19 +798,84 @@ public class MySqlStatementParser extends SQLStatementParser { return block; } - public MySqlDescribeStatement parseDescribe() { + public MySqlExplainStatement parseDescribe() { + // see https://dev.mysql.com/doc/refman/5.7/en/explain.html + MySqlExplainStatement describe = new MySqlExplainStatement(); + + // {DESCRIBE | DESC} if (lexer.token() == Token.DESC || identifierEquals(DESCRIBE)) { lexer.nextToken(); + describe.setDescribe(true); } else { - throw new ParserException("expect DESC, actual " + lexer.token()); + throw new ParserException("expect one of {DESCRIBE | DESC} , actual " + lexer.token()); } - MySqlDescribeStatement stmt = new MySqlDescribeStatement(); - stmt.setObject(this.exprParser.name()); - if (lexer.token() == Token.IDENTIFIER) { - stmt.setColName(this.exprParser.name()); + return parseExplain(describe); + } + + public MySqlExplainStatement parseExplain() { + // see https://dev.mysql.com/doc/refman/5.7/en/explain.html + MySqlExplainStatement explain = new MySqlExplainStatement(); + + // {EXPLAIN} + if (lexer.token() == Token.EXPLAIN) { + lexer.nextToken(); + } else { + throw new ParserException("expect EXPLAIN , actual " + lexer.token()); } - return stmt; + + return parseExplain(explain); + } + + + private MySqlExplainStatement parseExplain(MySqlExplainStatement explain) { + // see https://dev.mysql.com/doc/refman/5.7/en/explain.html + + boolean extended = lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(EXTENDED); + boolean partitions = lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(PARTITIONS); + boolean format = lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(FORMAT); + boolean type = extended || partitions || format; + boolean forConnection = lexer.token() == Token.FOR; + boolean table = lexer.token() == Token.IDENTIFIER && !type && !forConnection; + + // tbl_name [col_name | wild] + if (table) { + explain.setTableName(exprParser.name()); + if (lexer.token() == Token.IDENTIFIER) { + explain.setColumnName(exprParser.name()); + } else if (lexer.token() == Token.LITERAL_CHARS) { + explain.setWild(exprParser.expr()); + } + } else if (type) { + // [explain_type] + if (extended) { + explain.setType(MySqlExplainType.EXTENDED); + } else if (partitions) { + explain.setType(MySqlExplainType.PARTITIONS); + } else if (format) { + explain.setType(MySqlExplainType.FORMAT); + lexer.nextToken(); + accept(Token.EQ); + if (lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(TRADITIONAL)) { + explain.setFormatName(MySqlFormatName.TRADITIONAL); + } else if (lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase(JSON)) { + explain.setFormatName(MySqlFormatName.JSON); + } + } + + lexer.nextToken(); + } + + // {explainable_stmt | FOR CONNECTION connection_id} + if (lexer.token() == Token.FOR) { + lexer.nextToken(); + acceptIdentifier(CONNECTION); + explain.setConnectionId(exprParser.expr()); + } else if (!table) { + explain.setStatement(this.parseStatement()); + } + + return explain; } public SQLStatement parseShow() { @@ -1269,7 +1396,7 @@ public class MySqlStatementParser extends SQLStatementParser { lexer.nextToken(); MySqlShowProfileStatement stmt = new MySqlShowProfileStatement(); - for (;;) { + for (; ; ) { if (lexer.token() == Token.ALL) { stmt.getTypes().add(MySqlShowProfileStatement.Type.ALL); lexer.nextToken(); @@ -1679,7 +1806,7 @@ public class MySqlStatementParser extends SQLStatementParser { SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); stmt.getValuesList().add(values); - for (;;) { + for (; ; ) { stmt.addColumn(this.exprParser.name()); if (lexer.token() == Token.COLONEQ) { lexer.nextToken(); @@ -1943,14 +2070,14 @@ public class MySqlStatementParser extends SQLStatementParser { } public MysqlDeallocatePrepareStatement parseDeallocatePrepare() { - acceptIdentifier("DEALLOCATE"); - acceptIdentifier("PREPARE"); - - MysqlDeallocatePrepareStatement stmt = new MysqlDeallocatePrepareStatement(); - SQLName statementName = exprParser.name(); - stmt.setStatementName(statementName); - - return stmt; + acceptIdentifier("DEALLOCATE"); + acceptIdentifier("PREPARE"); + + MysqlDeallocatePrepareStatement stmt = new MysqlDeallocatePrepareStatement(); + SQLName statementName = exprParser.name(); + stmt.setStatementName(statementName); + + return stmt; } public SQLInsertStatement parseInsert() { @@ -1959,7 +2086,7 @@ public class MySqlStatementParser extends SQLStatementParser { if (lexer.token() == Token.INSERT) { lexer.nextToken(); - for (;;) { + for (; ; ) { if (identifierEquals(LOW_PRIORITY)) { insertStatement.setLowPriority(true); lexer.nextToken(); @@ -1989,7 +2116,7 @@ public class MySqlStatementParser extends SQLStatementParser { lexer.nextToken(); continue; } - + break; } @@ -2030,7 +2157,7 @@ public class MySqlStatementParser extends SQLStatementParser { SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); insertStatement.getValuesList().add(values); - for (;;) { + for (; ; ) { SQLName name = this.exprParser.name(); insertStatement.addColumn(name); if (lexer.token() == Token.EQ) { @@ -2073,7 +2200,7 @@ public class MySqlStatementParser extends SQLStatementParser { } private void parseValueClause(List valueClauseList, int columnSize) { - for (int i = 0;;++i) { + for (int i = 0; ; ++i) { if (lexer.token() != Token.LPAREN) { throw new ParserException("syntax error, expect ')'"); } @@ -2087,7 +2214,7 @@ public class MySqlStatementParser extends SQLStatementParser { valueExprList = new ArrayList(); } - for (;;) { + for (; ; ) { SQLExpr expr; if (lexer.token() == Token.LITERAL_INT) { expr = new SQLIntegerExpr(lexer.integerValue()); @@ -2330,7 +2457,7 @@ public class MySqlStatementParser extends SQLStatementParser { stmt.setIgnore(ignore); stmt.setName(this.exprParser.name()); - for (;;) { + for (; ; ) { if (lexer.token() == Token.DROP) { parseAlterDrop(stmt); } else if (lexer.token() == Token.TRUNCATE) { @@ -2465,42 +2592,42 @@ public class MySqlStatementParser extends SQLStatementParser { stmt.addItem(item); } else if (identifierEquals("MODIFY")) { lexer.nextToken(); - + if (lexer.token() == Token.COLUMN) { lexer.nextToken(); } - + boolean paren = false; if (lexer.token() == Token.LPAREN) { - paren = true; - lexer.nextToken(); + paren = true; + lexer.nextToken(); } - - for (;;) { - MySqlAlterTableModifyColumn item = new MySqlAlterTableModifyColumn(); - item.setNewColumnDefinition(this.exprParser.parseColumn()); - if (identifierEquals("AFTER")) { - lexer.nextToken(); - item.setAfterColumn(this.exprParser.name()); - } else if (identifierEquals("FIRST")) { - lexer.nextToken(); - if (lexer.token() == Token.IDENTIFIER) { - item.setFirstColumn(this.exprParser.name()); - } else { - item.setFirst(true); - } - } - stmt.addItem(item); - - if (paren && lexer.token() == Token.COMMA) { - lexer.nextToken(); - continue; - } - break; + + for (; ; ) { + MySqlAlterTableModifyColumn item = new MySqlAlterTableModifyColumn(); + item.setNewColumnDefinition(this.exprParser.parseColumn()); + if (identifierEquals("AFTER")) { + lexer.nextToken(); + item.setAfterColumn(this.exprParser.name()); + } else if (identifierEquals("FIRST")) { + lexer.nextToken(); + if (lexer.token() == Token.IDENTIFIER) { + item.setFirstColumn(this.exprParser.name()); + } else { + item.setFirst(true); + } + } + stmt.addItem(item); + + if (paren && lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; } - + if (paren) { - accept(Token.RPAREN); + accept(Token.RPAREN); } } else if (lexer.token() == Token.DISABLE) { lexer.nextToken(); @@ -2648,7 +2775,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.INTO); accept(Token.LPAREN); - for (;;) { + for (; ; ) { SQLPartition partition = this.getExprParser().parsePartition(); item.addPartition(partition); @@ -2677,7 +2804,7 @@ public class MySqlStatementParser extends SQLStatementParser { } else { this.exprParser.names(item.getPartitions(), item); } - + stmt.addItem(item); } else if (identifierEquals("REBUILD")) { lexer.nextToken(); @@ -2692,7 +2819,7 @@ public class MySqlStatementParser extends SQLStatementParser { } else { this.exprParser.names(item.getPartitions(), item); } - + stmt.addItem(item); } else if (identifierEquals("REPAIR")) { lexer.nextToken(); @@ -2707,7 +2834,7 @@ public class MySqlStatementParser extends SQLStatementParser { } else { this.exprParser.names(item.getPartitions(), item); } - + stmt.addItem(item); } else if (identifierEquals("REMOVE")) { lexer.nextToken(); @@ -2815,7 +2942,7 @@ public class MySqlStatementParser extends SQLStatementParser { } SQLAlterTableAddColumn item = new SQLAlterTableAddColumn(); - for (;;) { + for (; ; ) { SQLColumnDefinition columnDef = this.exprParser.parseColumn(); item.addColumn(columnDef); @@ -2922,7 +3049,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.TABLE); - for (;;) { + for (; ; ) { MySqlRenameTableStatement.Item item = new MySqlRenameTableStatement.Item(); item.setName(this.exprParser.name()); accept(Token.TO); @@ -2970,7 +3097,7 @@ public class MySqlStatementParser extends SQLStatementParser { if (lexer.token() == Token.DEFAULT) { lexer.nextToken(); } - + if (identifierEquals("CHARACTER")) { lexer.nextToken(); accept(Token.SET); @@ -2986,7 +3113,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.IDENTIFIER); stmt.setCharacterSet(charset); } - + if (lexer.token() == Token.DEFAULT) { lexer.nextToken(); } @@ -3007,7 +3134,7 @@ public class MySqlStatementParser extends SQLStatementParser { protected void parseUpdateSet(SQLUpdateStatement update) { accept(Token.SET); - for (;;) { + for (; ; ) { SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem(); update.addItem(item); @@ -3042,7 +3169,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.USER); MySqlAlterUserStatement stmt = new MySqlAlterUserStatement(); - for (;;) { + for (; ; ) { SQLExpr user = this.exprParser.expr(); acceptIdentifier("PASSWORD"); acceptIdentifier("EXPIRE"); @@ -3104,7 +3231,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse create procedure parameters - * + * * @param parameters */ private void parserParameters(List parameters) { @@ -3112,7 +3239,7 @@ public class MySqlStatementParser extends SQLStatementParser { return; } - for (;;) { + for (; ; ) { SQLParameter parameter = new SQLParameter(); if (lexer.token() == Token.CURSOR) { @@ -3169,7 +3296,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse procedure statement block - * + * * @param statementList */ private void parseProcedureStatementList(List statementList) { @@ -3181,7 +3308,7 @@ public class MySqlStatementParser extends SQLStatementParser { */ private void parseProcedureStatementList(List statementList, int max) { - for (;;) { + for (; ; ) { if (max != -1) { if (statementList.size() >= max) { return; @@ -3255,7 +3382,7 @@ public class MySqlStatementParser extends SQLStatementParser { SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) variant; if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) { SQLSetStatement stmt = new SQLSetStatement(binaryOpExpr.getLeft(), binaryOpExpr.getRight(), - getDbType()); + getDbType()); statementList.add(stmt); continue; } @@ -3323,13 +3450,13 @@ public class MySqlStatementParser extends SQLStatementParser { lexer.reset(markBp, markChar, Token.DECLARE); statementList.add(this.parseCursorDeclare()); } else if (identifierEquals("HANDLER")) { - //DECLARE异常处理程序 [add by zhujun 2016-04-16] - lexer.reset(markBp, markChar, Token.DECLARE); - statementList.add(this.parseDeclareHandler()); + //DECLARE异常处理程序 [add by zhujun 2016-04-16] + lexer.reset(markBp, markChar, Token.DECLARE); + statementList.add(this.parseDeclareHandler()); } else if (lexer.token() == Token.CONDITION) { - //DECLARE异常 [add by zhujun 2016-04-17] - lexer.reset(markBp, markChar, Token.DECLARE); - statementList.add(this.parseDeclareCondition()); + //DECLARE异常 [add by zhujun 2016-04-17] + lexer.reset(markBp, markChar, Token.DECLARE); + statementList.add(this.parseDeclareCondition()); } else { lexer.reset(markBp, markChar, Token.DECLARE); statementList.add(this.parseDeclare()); @@ -3406,7 +3533,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse if statement - * + * * @return MySqlIfStatement */ public SQLIfStatement parseIf() { @@ -3451,7 +3578,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse while statement - * + * * @return MySqlWhileStatement */ public MySqlWhileStatement parseWhile() { @@ -3476,7 +3603,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse while statement with label - * + * * @return MySqlWhileStatement */ public MySqlWhileStatement parseWhile(String label) { @@ -3506,7 +3633,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse case statement - * + * * @return MySqlCaseStatement */ public MySqlCaseStatement parseCase() { @@ -3574,7 +3701,7 @@ public class MySqlStatementParser extends SQLStatementParser { MySqlDeclareStatement stmt = new MySqlDeclareStatement(); accept(Token.DECLARE); // lexer.nextToken(); - for (;;) { + for (; ; ) { SQLDeclareItem item = new SQLDeclareItem(); item.setName(exprParser.name()); @@ -3683,7 +3810,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse repeat statement - * + * * @return */ public MySqlRepeatStatement parseRepeat() { @@ -3700,7 +3827,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse repeat statement with label - * + * * @param label * @return */ @@ -3720,7 +3847,7 @@ public class MySqlStatementParser extends SQLStatementParser { /** * parse cursor declare statement - * + * * @return */ public MySqlCursorDeclareStatement parseCursorDeclare() { @@ -3733,7 +3860,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.FOR); - SQLSelectStatement selelctStmt = (SQLSelectStatement)parseSelect(); + SQLSelectStatement selelctStmt = (SQLSelectStatement) parseSelect(); stmt.setSelect(selelctStmt); accept(Token.SEMI); @@ -3744,10 +3871,11 @@ public class MySqlStatementParser extends SQLStatementParser { /** * zhujun [455910092@qq.com] * parse spstatement + * * @return */ public SQLStatement parseSpStatement() { - + // update if (lexer.token() == (Token.UPDATE)) { return parseUpdateStatement(); @@ -3793,71 +3921,72 @@ public class MySqlStatementParser extends SQLStatementParser { throw new ParserException("error sp_statement"); } - + /** * 定义异常处理程序 + * + * @return * @author zhujun [455910092@qq.com] * 2016-04-16 - * @return */ public MySqlDeclareHandlerStatement parseDeclareHandler() { - //DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement - //handler_type 取值为 CONTINUE | EXIT | UNDO - //condition_value 取值为 SQLWARNING | NOT FOUND | SQLEXCEPTION | SQLSTATE value(异常码 e.g 1062) - - MySqlDeclareHandlerStatement stmt = new MySqlDeclareHandlerStatement(); + //DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement + //handler_type 取值为 CONTINUE | EXIT | UNDO + //condition_value 取值为 SQLWARNING | NOT FOUND | SQLEXCEPTION | SQLSTATE value(异常码 e.g 1062) + + MySqlDeclareHandlerStatement stmt = new MySqlDeclareHandlerStatement(); accept(Token.DECLARE); //String handlerType = exprParser.name().getSimpleName(); - if(lexer.token() == Token.CONTINUE) { - stmt.setHandleType(MySqlHandlerType.CONTINUE); - } else if(lexer.token() == Token.EXIT) { - stmt.setHandleType(MySqlHandlerType.CONTINUE); - } else if(lexer.token() == Token.UNDO) { - stmt.setHandleType(MySqlHandlerType.CONTINUE); + if (lexer.token() == Token.CONTINUE) { + stmt.setHandleType(MySqlHandlerType.CONTINUE); + } else if (lexer.token() == Token.EXIT) { + stmt.setHandleType(MySqlHandlerType.CONTINUE); + } else if (lexer.token() == Token.UNDO) { + stmt.setHandleType(MySqlHandlerType.CONTINUE); } else { - throw new ParserException("unkown handle type"); + throw new ParserException("unkown handle type"); } lexer.nextToken(); - + acceptIdentifier("HANDLER"); accept(Token.FOR); - for (;;) { - String tokenName = lexer.stringVal(); - ConditionValue condition = new ConditionValue(); - - if (tokenName.equalsIgnoreCase("NOT")) {//for 'NOT FOUND' - lexer.nextToken(); - acceptIdentifier("HANDLE"); - condition.setType(ConditionType.SYSTEM); - condition.setValue("NOT FOUND"); - - } else if(tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001') - condition.setType(ConditionType.SQLSTATE); - lexer.nextToken(); - //condition.setValue(lexer.stringVal()); - //lexer.nextToken(); - condition.setValue(exprParser.name().toString()); - } else if(identifierEquals("SQLEXCEPTION")) { //for SQLEXCEPTION - condition.setType(ConditionType.SYSTEM); - condition.setValue(lexer.stringVal()); - lexer.nextToken(); - } else if(identifierEquals("SQLWARNING")) { //for SQLWARNING - condition.setType(ConditionType.SYSTEM); - condition.setValue(lexer.stringVal()); - lexer.nextToken(); - } else { //for condition_name or mysql_error_code - if (lexer.token() == Token.LITERAL_INT) { - condition.setType(ConditionType.MYSQL_ERROR_CODE); - condition.setValue(lexer.integerValue().toString()); - } else { - condition.setType(ConditionType.SELF); - condition.setValue(tokenName); - } - lexer.nextToken(); - } - stmt.getConditionValues().add(condition); + for (; ; ) { + String tokenName = lexer.stringVal(); + ConditionValue condition = new ConditionValue(); + + if (tokenName.equalsIgnoreCase("NOT")) {//for 'NOT FOUND' + lexer.nextToken(); + acceptIdentifier("HANDLE"); + condition.setType(ConditionType.SYSTEM); + condition.setValue("NOT FOUND"); + + } else if (tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001') + condition.setType(ConditionType.SQLSTATE); + lexer.nextToken(); + //condition.setValue(lexer.stringVal()); + //lexer.nextToken(); + condition.setValue(exprParser.name().toString()); + } else if (identifierEquals("SQLEXCEPTION")) { //for SQLEXCEPTION + condition.setType(ConditionType.SYSTEM); + condition.setValue(lexer.stringVal()); + lexer.nextToken(); + } else if (identifierEquals("SQLWARNING")) { //for SQLWARNING + condition.setType(ConditionType.SYSTEM); + condition.setValue(lexer.stringVal()); + lexer.nextToken(); + } else { //for condition_name or mysql_error_code + if (lexer.token() == Token.LITERAL_INT) { + condition.setType(ConditionType.MYSQL_ERROR_CODE); + condition.setValue(lexer.integerValue().toString()); + } else { + condition.setType(ConditionType.SELF); + condition.setValue(tokenName); + } + lexer.nextToken(); + } + stmt.getConditionValues().add(condition); if (lexer.token() == Token.COMMA) { accept(Token.COMMA); continue; @@ -3867,58 +3996,59 @@ public class MySqlStatementParser extends SQLStatementParser { throw new ParserException("declare handle not eof"); } } - + stmt.setSpStatement(parseSpStatement()); if (!(stmt.getSpStatement() instanceof SQLBlockStatement)) { - accept(Token.SEMI); - } - + accept(Token.SEMI); + } + return stmt; } - + /** * zhujun [455910092@qq.com] * 2016-04-17 * 定义条件 + * * @return */ public MySqlDeclareConditionStatement parseDeclareCondition() { - /* - DECLARE condition_name CONDITION FOR condition_value + /* + DECLARE condition_name CONDITION FOR condition_value condition_value: SQLSTATE [VALUE] sqlstate_value | mysql_error_code */ - MySqlDeclareConditionStatement stmt = new MySqlDeclareConditionStatement(); + MySqlDeclareConditionStatement stmt = new MySqlDeclareConditionStatement(); accept(Token.DECLARE); stmt.setConditionName(exprParser.name().toString()); - + accept(Token.CONDITION); accept(Token.FOR); - + String tokenName = lexer.stringVal(); ConditionValue condition = new ConditionValue(); - if(tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001') - condition.setType(ConditionType.SQLSTATE); - lexer.nextToken(); - condition.setValue(exprParser.name().toString()); - } else if (lexer.token() == Token.LITERAL_INT) { - condition.setType(ConditionType.MYSQL_ERROR_CODE); - condition.setValue(lexer.integerValue().toString()); - lexer.nextToken(); - } else { - throw new ParserException("declare condition grammer error."); - } - + if (tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001') + condition.setType(ConditionType.SQLSTATE); + lexer.nextToken(); + condition.setValue(exprParser.name().toString()); + } else if (lexer.token() == Token.LITERAL_INT) { + condition.setType(ConditionType.MYSQL_ERROR_CODE); + condition.setValue(lexer.integerValue().toString()); + lexer.nextToken(); + } else { + throw new ParserException("declare condition grammer error."); + } + stmt.setConditionValue(condition); accept(Token.SEMI); - - return stmt; + + return 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 2d14834bba..3fd4fcfaf4 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 @@ -55,8 +55,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHelpStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; @@ -118,7 +118,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTableStatusSta import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByKey; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByList; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; @@ -167,9 +166,9 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlPrepareStatement x); void endVisit(MySqlExecuteStatement x); - + boolean visit(MysqlDeallocatePrepareStatement x); - + void endVisit(MysqlDeallocatePrepareStatement x); boolean visit(MySqlExecuteStatement x); @@ -258,9 +257,9 @@ public interface MySqlASTVisitor extends SQLASTVisitor { void endVisit(MySqlOutFileExpr x); - boolean visit(MySqlDescribeStatement x); + boolean visit(MySqlExplainStatement x); - void endVisit(MySqlDescribeStatement x); + void endVisit(MySqlExplainStatement x); boolean visit(MySqlUpdateStatement x); @@ -517,74 +516,74 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlSetPasswordStatement x); void endVisit(MySqlSetPasswordStatement x); - + boolean visit(MySqlHintStatement x); void endVisit(MySqlHintStatement x); - + boolean visit(MySqlOrderingExpr x); void endVisit(MySqlOrderingExpr x); - - /** - * support procedure - */ + + /** + * support procedure + */ boolean visit(MySqlWhileStatement x); void endVisit(MySqlWhileStatement x); - + boolean visit(MySqlCaseStatement x); void endVisit(MySqlCaseStatement x); - + boolean visit(MySqlDeclareStatement x); void endVisit(MySqlDeclareStatement x); - + boolean visit(MySqlSelectIntoStatement x); void endVisit(MySqlSelectIntoStatement x); - + boolean visit(MySqlWhenStatement x); void endVisit(MySqlWhenStatement x); - + boolean visit(MySqlLeaveStatement x); void endVisit(MySqlLeaveStatement x); - + boolean visit(MySqlIterateStatement x); void endVisit(MySqlIterateStatement x); - + boolean visit(MySqlRepeatStatement x); void endVisit(MySqlRepeatStatement x); - + boolean visit(MySqlCursorDeclareStatement x); void endVisit(MySqlCursorDeclareStatement x); - + boolean visit(MySqlUpdateTableSource x); void endVisit(MySqlUpdateTableSource x); - + boolean visit(MySqlAlterTableAlterColumn x); - + void endVisit(MySqlAlterTableAlterColumn x); - + boolean visit(MySqlSubPartitionByKey x); - + void endVisit(MySqlSubPartitionByKey x); - + boolean visit(MySqlSubPartitionByList x); - + void endVisit(MySqlSubPartitionByList x); - + boolean visit(MySqlDeclareHandlerStatement x); void endVisit(MySqlDeclareHandlerStatement x); - + boolean visit(MySqlDeclareConditionStatement x); void endVisit(MySqlDeclareConditionStatement 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 c3b852855d..05bb60e546 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 @@ -15,14 +15,119 @@ */ package com.alibaba.druid.sql.dialect.mysql.visitor; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.*; -import com.alibaba.druid.sql.dialect.mysql.ast.clause.*; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlForceIndexHint; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlIgnoreIndexHint; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint; +import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.*; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.*; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlSelectIntoStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlWhileStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlExtractExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlIntervalExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlMatchAgainstExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.CobarShowStatus; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableAlterColumn; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableChangeColumn; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableCharacter; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableDiscardTablespace; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableImportTablespace; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableModifyColumn; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableOption; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement.UserSpecification; +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.MySqlExplainStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHelpStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; +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.MySqlLockTableStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlOptimizeStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPartitionByKey; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPrepareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlResetStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRollbackStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +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.MySqlSetPasswordStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSetTransactionStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowAuthorsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowBinLogEventsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowBinaryLogsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCharacterSetStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCollationStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowColumnsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowContributorsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateDatabaseStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateEventStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateFunctionStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateProcedureStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateTableStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateTriggerStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateViewStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowDatabasesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowEngineStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowEnginesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowErrorsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowEventsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowFunctionCodeStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowFunctionStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowGrantsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowIndexesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowKeysStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowMasterLogsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowMasterStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowOpenTablesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowPluginsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowPrivilegesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowProcedureCodeStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowProcedureStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowProcessListStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowProfileStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowProfilesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowRelayLogEventsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowSlaveHostsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowSlaveStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTableStatusStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByKey; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByList; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnlockTablesStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateTableSource; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MysqlDeallocatePrepareStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySqlASTVisitor { @@ -335,12 +440,12 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq } @Override - public boolean visit(MySqlDescribeStatement x) { + public boolean visit(MySqlExplainStatement x) { return true; } @Override - public void endVisit(MySqlDescribeStatement x) { + public void endVisit(MySqlExplainStatement x) { } 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 4e7ea72b4e..0ad0d655e6 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 @@ -15,12 +15,6 @@ */ package com.alibaba.druid.sql.dialect.mysql.visitor; -import java.security.AccessControlException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Properties; - import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLExpr; @@ -30,8 +24,27 @@ import com.alibaba.druid.sql.ast.SQLParameter; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.SQLSubPartitionBy; -import com.alibaba.druid.sql.ast.expr.*; -import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableAddColumn; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableItem; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLAssignItem; +import com.alibaba.druid.sql.ast.statement.SQLBlockStatement; +import com.alibaba.druid.sql.ast.statement.SQLCharacterDataType; +import com.alibaba.druid.sql.ast.statement.SQLColumnConstraint; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLCreateProcedureStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLIfStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.SQLLoopStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLShowTablesStatement; +import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlForceIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlIgnoreIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; @@ -47,6 +60,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatemen import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlExplainType; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement; @@ -76,8 +90,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStateme import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement.UserSpecification; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHelpStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; @@ -139,7 +153,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTableStatusSta import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByKey; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByList; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; @@ -154,6 +167,11 @@ import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import com.alibaba.druid.util.JdbcConstants; +import java.security.AccessControlException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTVisitor { { @@ -161,11 +179,11 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV this.shardingSupport = true; } - public MySqlOutputVisitor(Appendable appender){ + public MySqlOutputVisitor(Appendable appender) { super(appender); } - public MySqlOutputVisitor(Appendable appender, boolean parameterized){ + public MySqlOutputVisitor(Appendable appender, boolean parameterized) { super(appender, parameterized); try { @@ -306,7 +324,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV println(); print0(ucase ? "FOR UPDATE" : "for update"); if (x.isNoWait()) { - print0(ucase ? " NO_WAIT" : " no_wait"); + print0(ucase ? " NO_WAIT" : " no_wait"); } else if (x.getWaitTime() != null) { print0(ucase ? " WAIT " : " wait "); x.getWaitTime().accept(this); @@ -323,11 +341,11 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public boolean visit(SQLColumnDefinition x) { x.getName().accept(this); - + SQLDataType dataType = x.getDataType(); - if (dataType != null) { - print(' '); - dataType.accept(this); + if (dataType != null) { + print(' '); + dataType.accept(this); } if (x.getCharsetExpr() != null) { @@ -364,15 +382,15 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print0(ucase ? " COMMENT " : " comment "); x.getComment().accept(this); } - + if (x.getAsExpr() != null) { print0(ucase ? " AS (" : " as ("); x.getAsExpr().accept(this); print(')'); } - + if (x.isSorted()) { - print0(ucase ? " SORTED" : " sorted"); + print0(ucase ? " SORTED" : " sorted"); } return false; @@ -535,7 +553,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV println(); x.getPartitioning().accept(this); } - + if (x.getTableGroup() != null) { println(); print0(ucase ? "TABLEGROUP " : "tablegroup "); @@ -648,7 +666,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print('\''); String text = x.getText(); - + StringBuilder buf = new StringBuilder(text.length()); for (int i = 0; i < text.length(); ++i) { char ch = text.charAt(i); @@ -691,16 +709,16 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print0("@@global."); } else { if ((!varName.startsWith("@")) // / - && (!varName.equals("?")) // - && (!varName.startsWith("#")) // - && (!varName.startsWith("$")) // - && (!varName.startsWith(":"))) { - + && (!varName.equals("?")) // + && (!varName.startsWith("#")) // + && (!varName.startsWith("$")) // + && (!varName.startsWith(":"))) { + boolean subPartitionOption = false; if (x.getParent() != null) { subPartitionOption = x.getParent().getParent() instanceof SQLSubPartitionBy; } - + if (!subPartitionOption) { print0("@@"); } @@ -887,16 +905,16 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } return false; } - + @Override - public void endVisit(MysqlDeallocatePrepareStatement x) { - + public void endVisit(MysqlDeallocatePrepareStatement x) { + } - + public boolean visit(MysqlDeallocatePrepareStatement x) { - print0(ucase ? "DEALLOCATE PREPARE " : "deallocate prepare "); - x.getStatementName().accept(this); - return false; + print0(ucase ? "DEALLOCATE PREPARE " : "deallocate prepare "); + x.getStatementName().accept(this); + return false; } @Override @@ -907,7 +925,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public boolean visit(MySqlDeleteStatement x) { print0(ucase ? "DELETE " : "delete "); - + for (int i = 0, size = x.getHintsSize(); i < size; ++i) { SQLCommentHint hint = x.getHints().get(i); hint.accept(this); @@ -988,7 +1006,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.isIgnore()) { print0(ucase ? "IGNORE " : "ignore "); } - + if (x.isRollbackOnFail()) { print0(ucase ? "ROLLBACK_ON_FAIL " : "rollback_on_fail "); } @@ -1522,7 +1540,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } else { print0(ucase ? "KILL " : "kill "); } - + printAndAccept(x.getThreadIds(), ", "); return false; } @@ -1612,8 +1630,6 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } - - // @Override @@ -1674,18 +1690,47 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } @Override - public boolean visit(MySqlDescribeStatement x) { - print0(ucase ? "DESC " : "desc "); - x.getObject().accept(this); - if (x.getColName() != null) { - print(' '); - x.getColName().accept(this); + public boolean visit(MySqlExplainStatement x) { + String name = x.isDescribe() ? "desc" : "explain"; + print0(ucase ? name.toUpperCase() : name); + print(' '); + + // tbl_name [col_name | wild] + if (x.getTableName() != null) { + x.getTableName().accept(this); + if (x.getColumnName() != null) { + print(' '); + x.getColumnName().accept(this); + } else if (x.getWild() != null) { + print(' '); + x.getWild().accept(this); + } + } else { + // [explain_type] + if (x.getType() != null) { + print0(ucase ? x.getType().name() : x.getType().name().toLowerCase()); + print(' '); + if (x.getType() == MySqlExplainType.FORMAT) { + print0("= "); + print(ucase ? x.getFormatName().name() : x.getFormatName().name().toLowerCase()); + print(' '); + } + } + + // {explainable_stmt | FOR CONNECTION connection_id} + if (x.getConnectionId() != null) { + print0(ucase ? "FOR CONNECTION " : "for connection "); + x.getConnectionId().accept(this); + } else { + x.getStatement().accept(this); + } } + return false; } @Override - public void endVisit(MySqlDescribeStatement x) { + public void endVisit(MySqlExplainStatement x) { } @@ -1697,7 +1742,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV println(); print0(ucase ? "FROM " : "from "); } - + print0(ucase ? "UPDATE " : "update "); if (x.isLowPriority()) { @@ -1707,19 +1752,19 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.isIgnore()) { print0(ucase ? "IGNORE " : "ignore "); } - + if (x.isCommitOnSuccess()) { print0(ucase ? "COMMIT_ON_SUCCESS " : "commit_on_success "); } - + if (x.isRollBackOnFail()) { print0(ucase ? "ROLLBACK_ON_FAIL " : "rollback_on_fail "); } - + if (x.isQueryOnPk()) { print0(ucase ? "QUEUE_ON_PK " : "queue_on_pk "); } - + if (x.getTargetAffectRow() != null) { print0(ucase ? "TARGET_AFFECT_ROW " : "target_affect_row "); x.getTargetAffectRow().accept(this); @@ -2503,23 +2548,23 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV println(); item.accept(this); } - + if (x.isRemovePatiting()) { println(); print0(ucase ? "REMOVE PARTITIONING" : "remove partitioning"); } - + if (x.isUpgradePatiting()) { println(); print0(ucase ? "UPGRADE PARTITIONING" : "upgrade partitioning"); } - + if (x.getTableOptions().size() > 0) { println(); } - + decrementIndent(); - + int i = 0; for (Map.Entry option : x.getTableOptions().entrySet()) { String key = option.getKey(); @@ -2544,7 +2589,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV option.getValue().accept(this); i++; } - + return false; } @@ -2706,7 +2751,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print(' '); x.getHints().get(i).accept(this); } - + if (x.getPartitionSize() > 0) { print0(ucase ? " PARTITION (" : " partition ("); printlnAndAccept(x.getPartitions(), ", "); @@ -2949,7 +2994,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print0(ucase ? " ON DELETE " : " on delete "); print0(ucase ? x.getOnDelete().name : x.getOnDelete().name_lcase); } - + if (x.getOnDelete() != null) { print0(ucase ? " ON UPDATE " : " on update "); print0(ucase ? x.getOnDelete().name : x.getOnDelete().name_lcase); @@ -3537,7 +3582,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV printAndAccept(x.getColumns(), ", "); print(")"); } - + if (x.getOptions().size() != 0) { println(); print0(ucase ? "SUBPARTITION OPTIONS (" : "subpartition options ("); @@ -3554,58 +3599,58 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } - @Override - public boolean visit(MySqlDeclareHandlerStatement x) { - - print0(ucase ? "DECLARE " : "declare "); + @Override + public boolean visit(MySqlDeclareHandlerStatement x) { + + print0(ucase ? "DECLARE " : "declare "); print0(ucase ? x.getHandleType().toString().toUpperCase() : x.getHandleType().toString().toLowerCase()); print0(ucase ? " HANDLER FOR " : " handler for "); for (int i = 0; i < x.getConditionValues().size(); i++) { - ConditionValue cv = x.getConditionValues().get(i); - if (cv.getType() == ConditionType.SQLSTATE) { - print0(ucase ? " SQLSTATE " : " sqlstate "); - print0(cv.getValue()); - } else if (cv.getType() == ConditionType.MYSQL_ERROR_CODE) { - print0(cv.getValue()); - } else if (cv.getType() == ConditionType.SELF) { - print0(cv.getValue()); - } else if (cv.getType() == ConditionType.SYSTEM){ - print0(ucase ? cv.getValue().toUpperCase() : cv.getValue().toLowerCase()); - } - - if (i != x.getConditionValues().size() - 1) { - print0(", "); - } - - } + ConditionValue cv = x.getConditionValues().get(i); + if (cv.getType() == ConditionType.SQLSTATE) { + print0(ucase ? " SQLSTATE " : " sqlstate "); + print0(cv.getValue()); + } else if (cv.getType() == ConditionType.MYSQL_ERROR_CODE) { + print0(cv.getValue()); + } else if (cv.getType() == ConditionType.SELF) { + print0(cv.getValue()); + } else if (cv.getType() == ConditionType.SYSTEM) { + print0(ucase ? cv.getValue().toUpperCase() : cv.getValue().toLowerCase()); + } + + if (i != x.getConditionValues().size() - 1) { + print0(", "); + } + + } println(); return true; - } + } - @Override - public void endVisit(MySqlDeclareHandlerStatement x) { - - } + @Override + public void endVisit(MySqlDeclareHandlerStatement x) { - @Override - public boolean visit(MySqlDeclareConditionStatement x) { - print0(ucase ? "DECLARE " : "declare "); + } + + @Override + public boolean visit(MySqlDeclareConditionStatement x) { + print0(ucase ? "DECLARE " : "declare "); print0(x.getConditionName()); print0(ucase ? " CONDITION FOR " : " condition for "); - - if (x.getConditionValue().getType() == ConditionType.SQLSTATE) { - print0(ucase ? "SQLSTATE " : "sqlstate "); - print0(x.getConditionValue().getValue()); - } else { - print0(x.getConditionValue().getValue()); - } - - println(); - return false; - } - @Override - public void endVisit(MySqlDeclareConditionStatement x) { - - } + if (x.getConditionValue().getType() == ConditionType.SQLSTATE) { + print0(ucase ? "SQLSTATE " : "sqlstate "); + print0(x.getConditionValue().getValue()); + } else { + print0(x.getConditionValue().getValue()); + } + + println(); + return false; + } + + @Override + public void endVisit(MySqlDeclareConditionStatement x) { + + } } // diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java index 874a33a59c..db608d40da 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlSchemaStatVisitor.java @@ -15,8 +15,6 @@ */ package com.alibaba.druid.sql.dialect.mysql.visitor; -import java.util.Map; - import com.alibaba.druid.sql.ast.SQLDeclareItem; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; @@ -28,6 +26,7 @@ import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; @@ -73,8 +72,8 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStateme import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement.UserSpecification; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDescribeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExecuteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHelpStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; @@ -136,7 +135,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTableStatusSta import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByKey; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByList; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; @@ -150,6 +148,8 @@ import com.alibaba.druid.stat.TableStat; import com.alibaba.druid.stat.TableStat.Mode; import com.alibaba.druid.util.JdbcUtils; +import java.util.Map; + public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlASTVisitor { public boolean visit(SQLSelectStatement x) { @@ -566,17 +566,24 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS } @Override - public boolean visit(MySqlDescribeStatement x) { - String table = x.getObject().toString(); - getTableStat(table); - if (x.getColName() != null) { - addColumn(table, x.getColName().toString()); + public boolean visit(MySqlExplainStatement x) { + if (x.getTableName() != null) { + String table = x.getTableName().toString(); + getTableStat(table); + if (x.getColumnName() != null) { + addColumn(table, x.getColumnName().toString()); + } } + + if (x.getStatement() != null) { + accept(x.getStatement()); + } + return false; } @Override - public void endVisit(MySqlDescribeStatement x) { + public void endVisit(MySqlExplainStatement x) { } 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 81a3fbc844..1f63761589 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -126,7 +126,7 @@ public enum Token { BINARY("BINARY"), SHOW("SHOW"), REPLACE("REPLACE"), - + // MySql procedure add by zz WHILE("WHILE"), DO("DO"), diff --git a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java index 67dbce412a..fe5bd61153 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -15,23 +15,10 @@ */ package com.alibaba.druid.wall.spi; -import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Set; -import java.util.Stack; - import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLLimit; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; @@ -61,24 +48,59 @@ import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLUnaryExpr; import com.alibaba.druid.sql.ast.expr.SQLValuableExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; -import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLBlockStatement; +import com.alibaba.druid.sql.ast.statement.SQLCallStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateIndexStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateSequenceStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement; +import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement; +import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropIndexStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropProcedureStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropSequenceStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropTriggerStatement; +import com.alibaba.druid.sql.ast.statement.SQLDropViewStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLInsertInto; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLMergeStatement; +import com.alibaba.druid.sql.ast.statement.SQLRollbackStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLSetStatement; +import com.alibaba.druid.sql.ast.statement.SQLShowTablesStatement; +import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; +import com.alibaba.druid.sql.ast.statement.SQLUnionOperator; +import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.ast.statement.SQLUseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; 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.MySqlDescribeStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExplainStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLockTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; -import com.alibaba.druid.sql.ast.SQLLimit; 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.MySqlShowGrantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowStatement; -import com.alibaba.druid.sql.ast.statement.SQLStartTransactionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; @@ -107,6 +129,20 @@ import com.alibaba.druid.wall.WallVisitor; import com.alibaba.druid.wall.violation.ErrorCode; import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +import static com.alibaba.druid.sql.visitor.SQLEvalVisitor.EVAL_VALUE; + public class WallVisitorUtils { private final static Log LOG = LogFactory.getLog(WallVisitorUtils.class); @@ -2467,7 +2503,7 @@ public class WallVisitorUtils { allow = config.isReplaceAllow(); denyMessage = "replace not allow"; errorCode = ErrorCode.REPLACE_NOT_ALLOW; - } else if (x instanceof MySqlDescribeStatement) { + } else if (x instanceof MySqlExplainStatement && ((MySqlExplainStatement)x).isDescribe()) { allow = config.isDescribeAllow(); denyMessage = "describe not allow"; errorCode = ErrorCode.DESC_NOT_ALLOW; diff --git a/src/test/java/com/alibaba/druid/bvt/sql/cobar/DALParserTest.java b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DALParserTest.java index dd71e27618..d743e5b74a 100755 --- a/src/test/java/com/alibaba/druid/bvt/sql/cobar/DALParserTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DALParserTest.java @@ -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); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlExplainTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlExplainTest.java new file mode 100644 index 0000000000..cbcacb99b7 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlExplainTest.java @@ -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 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 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 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))); + } + +}