diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlLockTableStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlLockTableStatement.java index 0e3ddbe078..724e14ce08 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlLockTableStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlLockTableStatement.java @@ -15,47 +15,22 @@ */ 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.SQLName; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlObjectImpl; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; public class MySqlLockTableStatement extends MySqlStatementImpl { - private SQLExprTableSource tableSource; - - private LockType lockType; - - private List hints; - - public SQLExprTableSource getTableSource() { - return tableSource; - } - - public void setTableSource(SQLExprTableSource tableSource) { - if (tableSource != null) { - tableSource.setParent(this); - } - this.tableSource = tableSource; - } - - public void setTableSource(SQLName name) { - setTableSource(new SQLExprTableSource(name)); - } - - public LockType getLockType() { - return lockType; - } - - public void setLockType(LockType lockType) { - this.lockType = lockType; - } + private List items = new ArrayList(); + @Override public void accept0(MySqlASTVisitor visitor) { if (visitor.visit(this)) { - acceptChild(visitor, tableSource); + acceptChild(visitor, items); } visitor.endVisit(this); } @@ -70,11 +45,69 @@ public class MySqlLockTableStatement extends MySqlStatementImpl { } } - public List getHints() { - return hints; + public List getItems() { + return items; } - public void setHints(List hints) { - this.hints = hints; + public void setItems(List items) { + this.items = items; + } + + public LockType getLockType() { + if (items.size() == 1) { + return items.get(0).lockType; + } + return null; + } + + public SQLExprTableSource getTableSource() { + if (items.size() == 1) { + return items.get(0).tableSource; + } + return null; + } + + public static class Item extends MySqlObjectImpl { + + private SQLExprTableSource tableSource = new SQLExprTableSource(); + + private LockType lockType; + + private List hints; + + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, tableSource); + } + visitor.endVisit(this); + } + + public SQLExprTableSource getTableSource() { + return tableSource; + } + + public void setTableSource(SQLExprTableSource tableSource) { + if (tableSource != null) { + tableSource.setParent(this); + } + this.tableSource = tableSource; + } + + public LockType getLockType() { + return lockType; + } + + public void setLockType(LockType lockType) { + this.lockType = lockType; + } + + 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 845b28cbd5..50afefc6b2 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 @@ -806,31 +806,62 @@ public class MySqlStatementParser extends SQLStatementParser { } MySqlLockTableStatement stmt = new MySqlLockTableStatement(); - stmt.setTableSource(this.exprParser.name()); - if (lexer.identifierEquals(READ)) { - lexer.nextToken(); - if (lexer.identifierEquals(LOCAL)) { + for(;;) { + MySqlLockTableStatement.Item item = new MySqlLockTableStatement.Item(); + + SQLExprTableSource tableSource = null; + SQLName tableName = this.exprParser.name(); + + + if (lexer.token() == Token.AS) { + lexer.nextToken(); + String as = lexer.stringVal(); + tableSource = new SQLExprTableSource(tableName, as); lexer.nextToken(); - stmt.setLockType(LockType.READ_LOCAL); } else { - stmt.setLockType(LockType.READ); + tableSource = new SQLExprTableSource(tableName); } - } else if (lexer.identifierEquals(WRITE)) { - lexer.nextToken(); - stmt.setLockType(LockType.WRITE); - } else if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) { - lexer.nextToken(); - acceptIdentifier(WRITE); - lexer.nextToken(); - stmt.setLockType(LockType.LOW_PRIORITY_WRITE); - } else { - throw new ParserException("syntax error, expect READ or WRITE, actual " + lexer.token() + ", " + lexer.info()); + item.setTableSource(tableSource); + stmt.getItems().add(item); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + + if (lexer.identifierEquals(READ)) { + lexer.nextToken(); + if (lexer.identifierEquals(LOCAL)) { + lexer.nextToken(); + item.setLockType(LockType.READ_LOCAL); + } else { + item.setLockType(LockType.READ); + } + } else if (lexer.identifierEquals(WRITE)) { + lexer.nextToken(); + item.setLockType(LockType.WRITE); + } else if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) { + lexer.nextToken(); + acceptIdentifier(WRITE); + lexer.nextToken(); + item.setLockType(LockType.LOW_PRIORITY_WRITE); + } else { + throw new ParserException( + "syntax error, expect READ or WRITE OR AS, actual " + lexer.token() + ", " + lexer.info()); + } + + if (lexer.token() == Token.HINT) { + item.setHints(this.exprParser.parseHints()); + } + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; } - if (lexer.token() == Token.HINT) { - stmt.setHints(this.exprParser.parseHints()); - } statementList.add(stmt); return true; } @@ -859,25 +890,56 @@ public class MySqlStatementParser extends SQLStatementParser { List hints = this.exprParser.parseHints(); boolean tddlHints = false; + boolean accept = false; - final Token token = lexer.token(); + + boolean acceptHint = false; + switch (lexer.token()) { + case SELECT: + case WITH: + case DELETE: + case UPDATE: + case INSERT: + case SHOW: + case REPLACE: + case TRUNCATE: + case DROP: + case ALTER: + case CREATE: + case CHECK: + case SET: + acceptHint = true; + break; + case IDENTIFIER: + acceptHint = lexer.hash_lower() == FnvHash.Constants.DUMP + || lexer.hash_lower() == FnvHash.Constants.RENAME; + break; + default: + break; + } if (hints.size() == 1 && statementList.size() == 0 - && (token == Token.SELECT || token == Token.DELETE)) { + && acceptHint) { SQLCommentHint hint = hints.get(0); - String hintText = hint.getText(); - if (hintText.startsWith("+TDDL")) { + String hintText = hint.getText().toUpperCase(); + if (hintText.startsWith("+TDDL") + || hintText.startsWith("+ TDDL") + || hintText.startsWith("TDDL") + || hintText.startsWith("!TDDL")) + { tddlHints = true; + } else if(hintText.startsWith("+")) { + accept = true; } } if (tddlHints) { - SQLStatementImpl stmt; - if (token == Token.SELECT) { - stmt = (SQLSelectStatement) this.parseStatement(); - } else { - stmt = (SQLDeleteStatement) this.parseStatement(); - } + SQLStatementImpl stmt = (SQLStatementImpl)this.parseStatement(); + stmt.setHeadHints(hints); + statementList.add(stmt); + return true; + } else if (accept) { + SQLStatementImpl stmt = (SQLStatementImpl) this.parseStatement(); stmt.setHeadHints(hints); statementList.add(stmt); return true; 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 340ff1b87c..3d83cc29df 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 @@ -340,6 +340,10 @@ public interface MySqlASTVisitor extends SQLASTVisitor { void endVisit(MySqlLockTableStatement x); + boolean visit(MySqlLockTableStatement.Item x); + + void endVisit(MySqlLockTableStatement.Item x); + boolean visit(MySqlUnlockTablesStatement x); void endVisit(MySqlUnlockTablesStatement 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 34ae78d4d4..8cfb832c80 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 @@ -788,6 +788,16 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq } + @Override + public boolean visit(MySqlLockTableStatement.Item x) { + return true; + } + + @Override + public void endVisit(MySqlLockTableStatement.Item x) { + + } + @Override public boolean visit(MySqlUnlockTablesStatement x) { return true; 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 0a62045fba..718daac486 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 @@ -2596,7 +2596,22 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public boolean visit(MySqlLockTableStatement x) { - print0(ucase ? "LOCK TABLES " : "lock tables "); + print0(ucase ? "LOCK TABLES" : "lock tables"); + List items = x.getItems(); + if(items.size() > 0) { + print(' '); + printAndAccept(items, ", "); + } + return false; + } + + @Override + public void endVisit(MySqlLockTableStatement x) { + + } + + @Override + public boolean visit(MySqlLockTableStatement.Item x) { x.getTableSource().accept(this); if (x.getLockType() != null) { print(' '); @@ -2611,7 +2626,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } @Override - public void endVisit(MySqlLockTableStatement x) { + public void endVisit(MySqlLockTableStatement.Item 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 4d5bfb4ffd..00c58814d1 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 @@ -886,6 +886,16 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS } + @Override + public boolean visit(MySqlLockTableStatement.Item x) { + return false; + } + + @Override + public void endVisit(MySqlLockTableStatement.Item x) { + + } + @Override public boolean visit(MySqlUnlockTablesStatement x) { return false; diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index 5be48ac625..406e0ceb0b 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -582,6 +582,7 @@ public final class FnvHash { long SCHEDULE = fnv1a_64_lower("SCHEDULE"); long COMPLETION = fnv1a_64_lower("COMPLETION"); long RENAME = fnv1a_64_lower("RENAME"); + long DUMP = fnv1a_64_lower("DUMP"); long AT = fnv1a_64_lower("AT"); long LANGUAGE = fnv1a_64_lower("LANGUAGE"); long LOGFILE = fnv1a_64_lower("LOGFILE"); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/AdsDumpTest_0.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/AdsDumpTest_0.java new file mode 100644 index 0000000000..351ec5c4ba --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/AdsDumpTest_0.java @@ -0,0 +1,32 @@ +package com.alibaba.druid.bvt.sql.mysql; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLDumpStatement; +import com.alibaba.druid.util.JdbcConstants; +import junit.framework.TestCase; + +import java.util.List; + +public class AdsDumpTest_0 extends TestCase { + + public void test_0() throws Exception { + String sql = "/*+dump-merge=true*/DUMP DATA SELECT amp.buyer_add_cart_info.buyer_id,amp.buyer_add_cart_info.pre_score,amp.buyer_add_cart_info.cart_price FROM amp.buyer_add_cart_info JOIN amp.crm_user_base_info ON amp.crm_user_base_info.user_id = amp.buyer_add_cart_info.buyer_id where (((amp.buyer_add_cart_info.seller_id=1921906956)) AND ((amp.buyer_add_cart_info.auction_id=562769960283)) AND ((amp.buyer_add_cart_info.show_price>=13300))) LIMIT 144800 "; + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL); + SQLDumpStatement stmt = (SQLDumpStatement)statementList.get(0); + + assertEquals(1, statementList.size()); + + assertEquals("/*+dump-merge=true*/\n" + + "DUMP DATA SELECT amp.buyer_add_cart_info.buyer_id, amp.buyer_add_cart_info.pre_score, amp.buyer_add_cart_info.cart_price\n" + + "FROM amp.buyer_add_cart_info\n" + + "\tJOIN amp.crm_user_base_info ON amp.crm_user_base_info.user_id = amp.buyer_add_cart_info.buyer_id\n" + + "WHERE amp.buyer_add_cart_info.seller_id = 1921906956\n" + + "\tAND amp.buyer_add_cart_info.auction_id = 562769960283\n" + + "\tAND amp.buyer_add_cart_info.show_price >= 13300\n" + + "LIMIT 144800", stmt.toString()); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/update/MySqlUpdateTest_17.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/update/MySqlUpdateTest_17.java new file mode 100644 index 0000000000..b256dbda0a --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/update/MySqlUpdateTest_17.java @@ -0,0 +1,77 @@ +/* + * 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.update; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat.Column; +import com.alibaba.druid.wall.WallUtils; + +import java.util.List; + +public class MySqlUpdateTest_17 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "update security_group_ip_count set ip_count=GREATEST(ip_count-?, 0), gmt_modified=now() where group_id=? "; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + + assertEquals(1, visitor.getTables().size()); + assertEquals(3, visitor.getColumns().size()); + // assertEquals(2, visitor.getConditions().size()); + + assertTrue(visitor.containsTable("security_group_ip_count")); + + assertTrue(visitor.getColumns().contains(new Column("security_group_ip_count", "ip_count"))); + + assertEquals("UPDATE security_group_ip_count\n" + + "SET ip_count = GREATEST(ip_count - ?, 0), gmt_modified = now()\n" + + "WHERE group_id = ?", // + stmt.toString()); + assertEquals("update security_group_ip_count\n" + + "set ip_count = GREATEST(ip_count - ?, 0), gmt_modified = now()\n" + + "where group_id = ?", // + stmt.toLowerCaseString()); + + + assertTrue(WallUtils.isValidateMySql(sql)); + + { + SQLUpdateStatement update = (SQLUpdateStatement) stmt; + SQLExpr where = update.getWhere(); + assertEquals("group_id = ?", where.toString()); + } + + } +}