improved mysql parser support.

This commit is contained in:
wenshao
2018-06-21 09:54:08 +08:00
parent 3d7a66ff3f
commit 00a9b92ca1
9 changed files with 309 additions and 65 deletions
@@ -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;
}
}
}
@@ -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());
}
}
}