diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHelpStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHelpStatement.java new file mode 100644 index 0000000000..593312b1ba --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlHelpStatement.java @@ -0,0 +1,26 @@ +package com.alibaba.druid.sql.dialect.mysql.ast.statement; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlHelpStatement extends MySqlStatementImpl { + + private static final long serialVersionUID = 1L; + + private SQLExpr content; + + public SQLExpr getContent() { + return content; + } + + public void setContent(SQLExpr content) { + this.content = content; + } + + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, content); + } + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java index c34e49abec..d2a8fc0e39 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlSelectQueryBlock.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.dialect.mysql.ast.statement; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObjectImpl; @@ -30,29 +31,39 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; @SuppressWarnings("serial") public class MySqlSelectQueryBlock extends SQLSelectQueryBlock implements MySqlObject { - private boolean hignPriority; - private boolean straightJoin; + private boolean hignPriority; + private boolean straightJoin; - private boolean smallResult; - private boolean bigResult; - private boolean bufferResult; - private Boolean cache; - private boolean calcFoundRows; + private boolean smallResult; + private boolean bigResult; + private boolean bufferResult; + private Boolean cache; + private boolean calcFoundRows; - private SQLOrderBy orderBy; + private SQLOrderBy orderBy; - private Limit limit; + private Limit limit; - private SQLName procedureName; - private List procedureArgumentList = new ArrayList(); + private SQLName procedureName; + private List procedureArgumentList = new ArrayList(); - private boolean forUpdate = false; - private boolean lockInShareMode = false; + private boolean forUpdate = false; + private boolean lockInShareMode = false; + + private List hints = new ArrayList(); public MySqlSelectQueryBlock(){ } + public List getHints() { + return hints; + } + + public void setHints(List hints) { + this.hints = hints; + } + public boolean isForUpdate() { return forUpdate; } @@ -163,7 +174,7 @@ public class MySqlSelectQueryBlock extends SQLSelectQueryBlock implements MySqlO accept0((MySqlASTVisitor) visitor); return; } - + if (visitor.visit(this)) { acceptChild(visitor, this.selectList); acceptChild(visitor, this.from); @@ -178,7 +189,7 @@ public class MySqlSelectQueryBlock extends SQLSelectQueryBlock implements MySqlO visitor.endVisit(this); } - + @Override public void accept0(MySqlASTVisitor visitor) { if (visitor.visit(this)) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java index b9f218a543..c0acc9f5d3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java @@ -389,7 +389,7 @@ public class MySqlLexer extends Lexer { } if (isHint) { - stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 2).trim(); + stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 1); token = Token.HINT; } else { stringVal = new String(buf, np, sp); 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 40179873c6..6a0c6fa9a8 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 @@ -64,6 +64,10 @@ public class MySqlSelectParser extends SQLSelectParser { if (lexer.token() == Token.SELECT) { lexer.nextToken(); + + if (lexer.token() == Token.HINT) { + this.createExprParser().parseHints(queryBlock.getHints()); + } if (lexer.token() == (Token.DISTINCT)) { queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); 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 ef61a4258d..202bcaca6e 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 @@ -57,6 +57,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropTableStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropUser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropViewStatement; 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.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; @@ -506,6 +507,14 @@ public class MySqlStatementParser extends SQLStatementParser { statementList.add(stmt); return true; } + + if (identifierEquals("HELP")) { + lexer.nextToken(); + MySqlHelpStatement stmt = new MySqlHelpStatement(); + stmt.setContent(this.exprParser.primary()); + statementList.add(stmt); + return true; + } if (identifierEquals("DESC") || identifierEquals("DESCRIBE")) { SQLStatement stmt = parseDescribe(); 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 f48fc9bc90..069b2a7e4d 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 @@ -45,6 +45,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropTableStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropUser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropViewStatement; 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.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; @@ -500,4 +501,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlCreateTableStatement x); void endVisit(MySqlCreateTableStatement x); + + boolean visit(MySqlHelpStatement x); + + void endVisit(MySqlHelpStatement 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 32adaf97fb..18f8b5435d 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 @@ -31,6 +31,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropTableStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropUser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropViewStatement; 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.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; @@ -1090,4 +1091,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements public void endVisit(MySqlCreateTableStatement x) { } + + @Override + public boolean visit(MySqlHelpStatement x) { + return true; + } + + @Override + public void endVisit(MySqlHelpStatement 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 acb35ec2b9..97a1c2aa0b 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 @@ -64,6 +64,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropTableStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropUser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropViewStatement; 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.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; @@ -162,6 +163,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } print("SELECT "); + + + for (SQLCommentHint hint : x.getHints()) { + hint.accept(this); + print(' '); + } if (SQLSetQuantifier.ALL == x.getDistionOption()) print("ALL "); else if (SQLSetQuantifier.DISTINCT == x.getDistionOption()) print("DISTINCT "); @@ -2584,4 +2591,16 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public void endVisit(MySqlCreateTableStatement x) { } + + @Override + public boolean visit(MySqlHelpStatement x) { + print("HELP "); + x.getContent().accept(this); + return false; + } + + @Override + public void endVisit(MySqlHelpStatement 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 dd04eae6c5..7c6bf70f44 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 @@ -42,6 +42,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropTableStatement import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropUser; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDropViewStatement; 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.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlKillStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlLoadDataInFileStatement; @@ -1155,4 +1156,14 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS public void endVisit(MySqlCreateTableStatement x) { } + + @Override + public boolean visit(MySqlHelpStatement x) { + return false; + } + + @Override + public void endVisit(MySqlHelpStatement x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java index e0da7f3583..292686f47a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -239,7 +239,7 @@ public class OracleLexer extends Lexer { } if (isHint) { - stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 2).trim(); + stringVal = new String(buf, np + startHintSp, (sp - startHintSp) - 1); token = Token.HINT; } else { stringVal = new String(buf, np, sp); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java index 06ea421419..b0b6f519a3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java @@ -15,7 +15,6 @@ */ package com.alibaba.druid.sql.dialect.oracle.visitor; -import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; diff --git a/src/test/java/com/alibaba/druid/bvt/sql/cobar/HintsTest.java b/src/test/java/com/alibaba/druid/bvt/sql/cobar/HintsTest.java index ff983c9ac2..29bfdd12f5 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/cobar/HintsTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/cobar/HintsTest.java @@ -17,6 +17,15 @@ public class HintsTest extends TestCase { SQLStatement stmt = parser.parseStatementList().get(0); parser.match(Token.EOF); String output = SQLUtils.toMySqlString(stmt); - Assert.assertEquals("CREATE /*!32302 TEMPORARY*/ TABLE t (\n\ta INT\n)", output); + Assert.assertEquals("CREATE /*!32302 TEMPORARY */ TABLE t (\n\ta INT\n)", output); + } + + public void test_hints_1() throws Exception { + String sql = "SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT /*! STRAIGHT_JOIN */ col1\nFROM table1, table2", output); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/HelpTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/HelpTest.java new file mode 100644 index 0000000000..87a512d179 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/HelpTest.java @@ -0,0 +1,23 @@ +package com.alibaba.druid.bvt.sql.mysql; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.parser.Token; + +import junit.framework.TestCase; + +public class HelpTest extends TestCase { + + public void test_help_0() throws Exception { + String sql = "HELP 'contents'"; + MySqlStatementParser parser = new MySqlStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + parser.match(Token.EOF); + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("HELP 'contents'", output); + } + +}