mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improved mysql parser support.
This commit is contained in:
+67
-34
@@ -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<SQLCommentHint> 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<Item> items = new ArrayList<Item>();
|
||||
|
||||
@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<SQLCommentHint> getHints() {
|
||||
return hints;
|
||||
public List<Item> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setHints(List<SQLCommentHint> hints) {
|
||||
this.hints = hints;
|
||||
public void setItems(List<Item> 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<SQLCommentHint> 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<SQLCommentHint> getHints() {
|
||||
return hints;
|
||||
}
|
||||
|
||||
public void setHints(List<SQLCommentHint> hints) {
|
||||
this.hints = hints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+91
-29
@@ -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<SQLCommentHint> 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<MySqlLockTableStatement.Item> 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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<SQLStatement> 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<SQLStatement> 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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user