diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index 2c03a5b376..9ccf69b00d 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -198,11 +198,19 @@ public class SQLUtils { if (parameters != null) { visitor.setParameters(parameters); } - - for (SQLStatement stmt : statementList) { - stmt.accept(visitor); + + for(int i =0; i0) { + out.append(";\n"); + } + statementList.get(i).accept(visitor); } +// for (SQLStatement stmt : statementList) { +// stmt.accept(visitor); +// out.append(";\n"); +// } + return out.toString(); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHintStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHintStatement.java new file mode 100644 index 0000000000..8a4dc36ab8 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHintStatement.java @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLCommentHint; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlHintStatement extends MySqlStatementImpl { + + private List hints; + + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.hints); + } + visitor.endVisit(this); + } + + public List getHints() { + return hints; + } + + public void setHints(List hints) { + this.hints = hints; + } + +} 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 9dd503b1c8..2ad7da4443 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 @@ -24,10 +24,10 @@ import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; -import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLListExpr; 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.SQLAlterTableAddConstraint; @@ -78,6 +78,7 @@ 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.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; @@ -663,6 +664,11 @@ public class MySqlStatementParser extends SQLStatementParser { statementList.add(new MySqlUnlockTablesStatement()); return true; } + + if(lexer.token() == Token.HINT) { + statementList.add(this.parseHint()); + return true; + } return false; } @@ -2569,4 +2575,12 @@ public class MySqlStatementParser extends SQLStatementParser { public MySqlExprParser getExprParser() { return (MySqlExprParser) exprParser; } + + public MySqlHintStatement parseHint() { + //accept(Token.HINT); + MySqlHintStatement stmt = new MySqlHintStatement(); + stmt.setHints(this.exprParser.parseHints()); + + 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 e9f5e338fe..5a1cdfb7a5 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 @@ -49,6 +49,7 @@ 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.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; @@ -561,4 +562,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlSetPasswordStatement x); void endVisit(MySqlSetPasswordStatement x); + + boolean visit(MySqlHintStatement x); + + void endVisit(MySqlHintStatement 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 67da8b1226..0efc305c36 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 @@ -51,6 +51,7 @@ 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.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; @@ -1247,4 +1248,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq } + @Override + public boolean visit(MySqlHintStatement x) { + return true; + } + + @Override + public void endVisit(MySqlHintStatement 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 a26fb22fc9..f5f1f16e4a 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 @@ -73,6 +73,7 @@ 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.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; @@ -3090,4 +3091,19 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } + @Override + public boolean visit(MySqlHintStatement x) { + List hints = x.getHints(); + + for (SQLCommentHint hint : hints) { + hint.accept(this); + } + return false; + } + + @Override + public void endVisit(MySqlHintStatement 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 5f3eeabdd5..5febee2adb 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 @@ -65,6 +65,7 @@ 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.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; @@ -1333,4 +1334,14 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS } + @Override + public boolean visit(MySqlHintStatement x) { + return true; + } + + @Override + public void endVisit(MySqlHintStatement x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/wall/WallConfig.java b/src/main/java/com/alibaba/druid/wall/WallConfig.java index fe96a03fe7..68235d8972 100644 --- a/src/main/java/com/alibaba/druid/wall/WallConfig.java +++ b/src/main/java/com/alibaba/druid/wall/WallConfig.java @@ -40,6 +40,7 @@ public class WallConfig implements WallConfigMBean { private boolean dropTableAllow = true; private boolean alterTableAllow = true; private boolean renameTableAllow = true; + private boolean hintAllow = true; private boolean conditionAndAlwayTrueAllow = false; private boolean conditionAndAlwayFalseAllow = false; @@ -712,6 +713,14 @@ public class WallConfig implements WallConfigMBean { public void setCallAllow(boolean callAllow) { this.callAllow = callAllow; } + + public boolean isHintAllow() { + return hintAllow; + } + + public void setHintAllow(boolean hintAllow) { + this.hintAllow = hintAllow; + } public static abstract interface TenantCallBack { 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 942cacc63e..81cc8f77a2 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/wall/spi/WallVisitorUtils.java @@ -96,6 +96,7 @@ 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.MySqlHintStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlReplaceStatement; @@ -2372,12 +2373,16 @@ public class WallVisitorUtils { errorCode = ErrorCode.ROLLBACK_NOT_ALLOW; } else if (x instanceof SQLUseStatement) { allow = config.isUseAllow(); - denyMessage = "show not allow"; + denyMessage = "use not allow"; errorCode = ErrorCode.USE_NOT_ALLOW; } else if (x instanceof MySqlRenameTableStatement) { allow = config.isRenameTableAllow(); denyMessage = "rename table not allow"; errorCode = ErrorCode.RENAME_TABLE_NOT_ALLOW; + } else if (x instanceof MySqlHintStatement) { + allow = config.isHintAllow(); + denyMessage = "hint not allow"; + errorCode = ErrorCode.HINT_NOT_ALLOW; } else { allow = config.isNoneBaseStatementAllow(); errorCode = ErrorCode.NONE_BASE_STATEMENT_NOT_ALLOW; diff --git a/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java b/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java index d1598a9cf3..c603a4aabc 100644 --- a/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java +++ b/src/main/java/com/alibaba/druid/wall/violation/ErrorCode.java @@ -27,6 +27,8 @@ public interface ErrorCode { public final static int INTERSET_NOT_ALLOW = 1008; public final static int MERGE_NOT_ALLOW = 1009; public final static int REPLACE_NOT_ALLOW = 1010; + + public final static int HINT_NOT_ALLOW = 1400; public final static int CALL_NOT_ALLOW = 1300; public final static int COMMIT_NOT_ALLOW = 1301; diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlHintStatementTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlHintStatementTest.java new file mode 100644 index 0000000000..261186f324 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlHintStatementTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.SQLUtils; + +public class MySqlHintStatementTest extends TestCase { + + public void test() { + String sql = "DROP TABLE IF EXISTS `item_similarity`;"// + + "\n/*!40101 SET @saved_cs_client = @@character_set_client */;"// + + "\n/*!40101 SET character_set_client = utf8 */;" // + + "\nCREATE TABLE `item_similarity` ("// + + " `id` bigint(20) unsigned NOT NULL, "// + + " `sellerId` bigint(20) DEFAULT NULL,"// + + " PRIMARY KEY (`id`)" // + + " ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"// + + " \n/*!40101 SET character_set_client = @saved_cs_client */;"; + String rs = SQLUtils.formatMySql(sql); + System.out.println(rs); + } +}