diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlForceIndexHint.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlForceIndexHint.java new file mode 100644 index 0000000000..c3ff626975 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlForceIndexHint.java @@ -0,0 +1,17 @@ +package com.alibaba.druid.sql.dialect.mysql.ast; + +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlForceIndexHint extends MySqlIndexHintImpl { + + private static final long serialVersionUID = 1L; + + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getIndexList()); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnlockTablesStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnlockTablesStatement.java new file mode 100644 index 0000000000..9958460f3e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnlockTablesStatement.java @@ -0,0 +1,14 @@ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + + +public class MySqlUnlockTablesStatement extends MySqlStatementImpl { + + private static final long serialVersionUID = 1L; + + public void accept0(MySqlASTVisitor visitor) { + visitor.visit(this); + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java index 07ff99633d..40179873c6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +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.MySqlIndexHint; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlIndexHintImpl; @@ -272,6 +273,13 @@ public class MySqlSelectParser extends SQLSelectParser { parseIndexHint(hint); tableSource.getHints().add(hint); } + + if (identifierEquals("FORCE")) { + lexer.nextToken(); + MySqlForceIndexHint hint = new MySqlForceIndexHint(); + parseIndexHint(hint); + tableSource.getHints().add(hint); + } return super.parseTableSourceRest(tableSource); } 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 c3305e1072..e464ce8938 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 @@ -110,6 +110,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatem 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.MySqlStartTransactionStatement; +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.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; @@ -528,6 +529,13 @@ public class MySqlStatementParser extends SQLStatementParser { statementList.add(stmt); return true; } + + if (identifierEquals("UNLOCK")) { + lexer.nextToken(); + acceptIdentifier("TABLES"); + statementList.add(new MySqlUnlockTablesStatement()); + return true; + } return false; } 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 03d19758df..5de98d2d75 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 @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.mysql.visitor; +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; @@ -101,6 +102,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; 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.visitor.SQLASTVisitor; @@ -465,4 +467,12 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlLockTableStatement x); void endVisit(MySqlLockTableStatement x); + + boolean visit(MySqlUnlockTablesStatement x); + + void endVisit(MySqlUnlockTablesStatement x); + + boolean visit(MySqlForceIndexHint x); + + void endVisit(MySqlForceIndexHint 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 998746518a..d983e73600 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 @@ -1,5 +1,6 @@ package com.alibaba.druid.sql.dialect.mysql.visitor; +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; @@ -88,6 +89,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; 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.visitor.SQLASTVisitorAdapter; @@ -1013,4 +1015,24 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements public void endVisit(MySqlLockTableStatement x) { } + + @Override + public boolean visit(MySqlUnlockTablesStatement x) { + return true; + } + + @Override + public void endVisit(MySqlUnlockTablesStatement x) { + + } + + @Override + public boolean visit(MySqlForceIndexHint x) { + return true; + } + + @Override + public void endVisit(MySqlForceIndexHint 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 db0448c2e0..088e91d3f7 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 @@ -31,6 +31,7 @@ import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; 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.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; @@ -122,6 +123,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; 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.visitor.SQLASTOutputVisitor; @@ -2458,4 +2460,34 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public void endVisit(MySqlLockTableStatement x) { } + + @Override + public boolean visit(MySqlUnlockTablesStatement x) { + print("UNLOCK TABLES"); + return false; + } + + @Override + public void endVisit(MySqlUnlockTablesStatement x) { + + } + + @Override + public boolean visit(MySqlForceIndexHint x) { + print("FORCE INDEX "); + if (x.getOption() != null) { + print("FOR "); + print(x.getOption().name); + print(' '); + } + print('('); + printAndAccept(x.getIndexList(), ", "); + print(')'); + return false; + } + + @Override + public void endVisit(MySqlForceIndexHint 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 3f31f0ec35..20c67bb9b2 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 @@ -10,6 +10,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +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; @@ -98,6 +99,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; 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.visitor.SchemaStatVisitor; import com.alibaba.druid.stat.TableStat; @@ -1077,4 +1079,24 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS public void endVisit(MySqlLockTableStatement x) { } + + @Override + public boolean visit(MySqlUnlockTablesStatement x) { + return false; + } + + @Override + public void endVisit(MySqlUnlockTablesStatement x) { + + } + + @Override + public boolean visit(MySqlForceIndexHint x) { + return false; + } + + @Override + public void endVisit(MySqlForceIndexHint x) { + + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java index 27fe83001c..a9866aa337 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/cobar/DMLSelectParserTest.java @@ -237,4 +237,32 @@ public class DMLSelectParserTest extends TestCase { String output = SQLUtils.toMySqlString(stmt); Assert.assertEquals("SELECT t1.id, t2.*\nFROM t1, test.t2\nWHERE test.t1.id = '中''‘文'\n\tAND t1.id = test.t2.id", output); } + + public void test_select_19() throws Exception { + String sql = "select * from offer a straight_join wp_image b force index for join(t1,t2) on a.member_id=b.member_id inner join product_visit c where a.member_id=c.member_id and c.member_id='abc' "; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT *\n" + + // + "FROM offer a STRAIGHT_JOIN wp_image b FORCE INDEX FOR JOIN (t1, t2) ON a.member_id = b.member_id INNER JOIN product_visit c\n" + + // + "WHERE a.member_id = c.member_id\n" + // + "\tAND c.member_id = 'abc'", output); + } + + public void test_select_20() throws Exception { + String sql = "select * from offer a straight_join wp_image b ignore index for join(t1,t2) on a.member_id=b.member_id inner join product_visit c where a.member_id=c.member_id and c.member_id='abc' "; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT *\n" + + // + "FROM offer a STRAIGHT_JOIN wp_image b IGNORE INDEX FOR JOIN (t1, t2) ON a.member_id = b.member_id INNER JOIN product_visit c\n" + + // + "WHERE a.member_id = c.member_id\n" + // + "\tAND c.member_id = 'abc'", output); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/cobar/MTSParserTest.java b/src/test/java/com/alibaba/druid/bvt/sql/cobar/MTSParserTest.java index c75185865b..69a7ee25f6 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/cobar/MTSParserTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/cobar/MTSParserTest.java @@ -90,4 +90,13 @@ public class MTSParserTest extends TestCase { String output = SQLUtils.toMySqlString(stmt); Assert.assertEquals("LOCK TABLES t2 READ LOCAL", output); } + + public void test_unlockTable() throws Exception { + String sql = "UNLOCK TABLES"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("UNLOCK TABLES", output); + } }