diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java index fd9b51c591..49df34d4c9 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUpdateStatement.java @@ -36,13 +36,15 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab protected SQLTableSource tableSource; protected List returning; + protected List hints; + // for mysql protected SQLOrderBy orderBy; public SQLUpdateStatement(){ } - + public SQLUpdateStatement(String dbType){ super (dbType); } @@ -90,7 +92,7 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab public List getItems() { return items; } - + public void addItem(SQLUpdateSetItem item) { this.items.add(item); item.setParent(this); @@ -115,6 +117,25 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab this.from = from; } + public int getHintsSize() { + if (hints == null) { + return 0; + } + + return hints.size(); + } + + public List getHints() { + if (hints == null) { + hints = new ArrayList(2); + } + return hints; + } + + public void setHints(List hints) { + this.hints = hints; + } + @Override public void output(StringBuffer buf) { SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(buf, dbType); @@ -129,6 +150,7 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab acceptChild(visitor, items); acceptChild(visitor, where); acceptChild(visitor, orderBy); + acceptChild(visitor, hints); } visitor.endVisit(this); } @@ -260,4 +282,34 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab return false; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SQLUpdateStatement that = (SQLUpdateStatement) o; + + if (with != null ? !with.equals(that.with) : that.with != null) return false; + if (items != null ? !items.equals(that.items) : that.items != null) return false; + if (where != null ? !where.equals(that.where) : that.where != null) return false; + if (from != null ? !from.equals(that.from) : that.from != null) return false; + if (hints != null ? !hints.equals(that.hints) : that.hints != null) return false; + if (tableSource != null ? !tableSource.equals(that.tableSource) : that.tableSource != null) return false; + if (returning != null ? !returning.equals(that.returning) : that.returning != null) return false; + return orderBy != null ? orderBy.equals(that.orderBy) : that.orderBy == null; + } + + @Override + public int hashCode() { + int result = with != null ? with.hashCode() : 0; + result = 31 * result + (items != null ? items.hashCode() : 0); + result = 31 * result + (where != null ? where.hashCode() : 0); + result = 31 * result + (from != null ? from.hashCode() : 0); + result = 31 * result + (tableSource != null ? tableSource.hashCode() : 0); + result = 31 * result + (returning != null ? returning.hashCode() : 0); + result = 31 * result + (orderBy != null ? orderBy.hashCode() : 0); + result = 31 * result + (hints != null ? hints.hashCode() : 0); + return result; + } } 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 cf06f8b591..46f03c3fe5 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 @@ -323,6 +323,10 @@ public class MySqlSelectParser extends SQLSelectParser { } } + while (lexer.token() == Token.HINT) { + this.exprParser.parseHints(update.getHints()); + } + SQLSelectParser selectParser = this.exprParser.createSelectParser(); SQLTableSource updateTableSource = selectParser.parseTableSource(); update.setTableSource(updateTableSource); 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 9a6b8d4575..95674d0c19 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 @@ -1594,6 +1594,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV print0(ucase ? "IGNORE " : "ignore "); } + + if (x.getHints() != null && x.getHints().size() > 0) { + printAndAccept(x.getHints(), " "); + print0(" "); + } + if (x.isCommitOnSuccess()) { print0(ucase ? "COMMIT_ON_SUCCESS " : "commit_on_success "); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index b604f461dc..a6e76f4d3e 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -2818,7 +2818,13 @@ public class SQLExprParser extends SQLParser { @SuppressWarnings({ "unchecked", "rawtypes" }) public void parseHints(List hints) { if (lexer.token == Token.HINT) { - hints.add(new SQLCommentHint(lexer.stringVal())); + SQLCommentHint hint = new SQLCommentHint(lexer.stringVal()); + + if (lexer.commentCount > 0) { + hint.addBeforeComment(lexer.comments); + } + + hints.add(hint); lexer.nextToken(); } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 91cb73a7c5..4350ced860 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -3257,6 +3257,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } public boolean visit(SQLCommentHint x) { + if (x.hasBeforeComment()) { + printlnComment(x.getBeforeCommentsDirect()); + print0(" "); + } + print0("/*"); print0(x.getText()); print0("*/"); 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 index 47c432e230..ba85e09c1a 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlHintStatementTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlHintStatementTest.java @@ -48,4 +48,13 @@ public class MySqlHintStatementTest extends TestCase { + "\n" + "\n/*!40101 SET character_set_client = @saved_cs_client */;", rs); } + public void test_1() throws Exception { + String sql = "update " + + "/*MS-MRCHISHUB-MH-MESSAGE-UPDATE*/ " + + "/*+ INDEX(mh_message primary) */ " + + "mh_message set gmt_modified = now(),status= '099' where id = 1244918;"; + String rs = SQLUtils.formatMySql(sql); + assertEquals("UPDATE /*MS-MRCHISHUB-MH-MESSAGE-UPDATE*/ /*+ INDEX(mh_message primary) */ mh_message\n" + + "SET gmt_modified = now(), status = '099'\n" + "WHERE id = 1244918;", rs); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_49_for_update_no_wait.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_49_for_update_no_wait.java index 4c82b3f0d3..50a8818e89 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_49_for_update_no_wait.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_49_for_update_no_wait.java @@ -52,7 +52,7 @@ public class MySqlSelectTest_49_for_update_no_wait extends MysqlTest { { String output = SQLUtils.toMySqlString(stmt); - assertEquals("SELECT /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + + assertEquals("SELECT /*MS-ARCORE-AR-II-M-QUERY-IN-LOCK-BY-AR-NOS*/ /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + "FROM tb_001\n" + "WHERE tnt_inst_id = ?\n" + "\tAND ar_no IN (?)\n" + @@ -61,7 +61,7 @@ public class MySqlSelectTest_49_for_update_no_wait extends MysqlTest { } { String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); - assertEquals("select /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + + assertEquals("select /*MS-ARCORE-AR-II-M-QUERY-IN-LOCK-BY-AR-NOS*/ /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + "from tb_001\n" + "where tnt_inst_id = ?\n" + "\tand ar_no in (?)\n" + @@ -71,7 +71,7 @@ public class MySqlSelectTest_49_for_update_no_wait extends MysqlTest { { String output = SQLUtils.toMySqlString(stmt, new SQLUtils.FormatOption(true, true, true)); - assertEquals("SELECT /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + + assertEquals("SELECT /*MS-ARCORE-AR-II-M-QUERY-IN-LOCK-BY-AR-NOS*/ /*+INDEX(AR_II_M_004 PRIMARY) */ tnt_inst_id, ar_no, ar_nm, ar_tp_code, ar_entity_type\n" + "FROM tb\n" + "WHERE tnt_inst_id = ?\n" + "\tAND ar_no IN (?)\n" + diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest104.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest104.java index 72a91df7f2..9599a07684 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest104.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/select/OracleSelectTest104.java @@ -66,7 +66,7 @@ public class OracleSelectTest104 extends OracleTest { { String text = SQLUtils.toOracleString(stmt); - assertEquals("SELECT /*+ cursor_sharing_exact dynamic_sampling(0) no_sql_tune no_monitoring optimizer_features_enable(default) */ SUM(C1)\n" + + assertEquals("SELECT /* DS_SVC */ /*+ cursor_sharing_exact dynamic_sampling(0) no_sql_tune no_monitoring optimizer_features_enable(default) */ SUM(C1)\n" + "FROM (\n" + "\tSELECT /*+ qb_name(\"innerQuery\") */ 1 AS C1\n" + "\tFROM SYS.\"X$KZSPR\" \"X$KZSPR\"\n" +