From 99fbfb364c6e8bb58508fcc1f35d9f09541dc361 Mon Sep 17 00:00:00 2001 From: Yako Date: Sun, 8 Feb 2015 22:44:58 +0800 Subject: [PATCH] support mysql create trigger/begin...end & fix issue #782 --- .../ast/statement/MySqlBlockStatement.java | 44 ++++++++++++++ .../sql/dialect/mysql/parser/MySqlLexer.java | 3 + .../mysql/parser/MySqlStatementParser.java | 27 ++++++++- .../mysql/visitor/MySqlASTVisitor.java | 5 ++ .../mysql/visitor/MySqlASTVisitorAdapter.java | 11 ++++ .../mysql/visitor/MySqlOutputVisitor.java | 27 +++++++++ .../mysql/visitor/MySqlSchemaStatVisitor.java | 11 ++++ .../druid/sql/parser/SQLStatementParser.java | 7 ++- .../bvt/sql/mysql/MySqlCreateTriggerTest.java | 57 +++++++++++++++++++ 9 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlBlockStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTriggerTest.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlBlockStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlBlockStatement.java new file mode 100644 index 0000000000..873137fa67 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlBlockStatement.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2011 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.sql.dialect.mysql.ast.statement; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; + +public class MySqlBlockStatement extends MySqlStatementImpl { + + private List statementList = new ArrayList(); + + public List getStatementList() { + return statementList; + } + + public void setStatementList(List statementList) { + this.statementList = statementList; + } + + @Override + public void accept0(MySqlASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, statementList); + } + visitor.endVisit(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 051a9bd3e7..d38b9b0f87 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 @@ -51,6 +51,9 @@ public class MySqlLexer extends Lexer { map.put("CACHE", Token.CACHE); map.put("ANALYZE", Token.ANALYZE); map.put("OPTIMIZE", Token.OPTIMIZE); + map.put("ROW", Token.ROW); + map.put("BEGIN", Token.BEGIN); + map.put("END", Token.END); DEFAULT_MYSQL_KEYWORDS = new Keywords(map); } 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 cb64014163..be7d736c70 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 @@ -70,6 +70,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatemen import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBlockStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateIndexStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; @@ -351,6 +352,13 @@ public class MySqlStatementParser extends SQLStatementParser { return parseCreateView(); } + + if (lexer.token() == Token.TRIGGER) { + if (replace) { + lexer.reset(markBp, markChar, Token.CREATE); + } + return parseCreateTrigger(); + } throw new ParserException("TODO " + lexer.token()); } @@ -673,9 +681,26 @@ public class MySqlStatementParser extends SQLStatementParser { statementList.add(this.parseHint()); return true; } + + if (lexer.token() == Token.BEGIN) { + statementList.add(this.parseBlock()); + return true; + } return false; } + + public MySqlBlockStatement parseBlock() { + MySqlBlockStatement block = new MySqlBlockStatement(); + + accept(Token.BEGIN); + + parseStatementList(block.getStatementList()); + + accept(Token.END); + + return block; + } public MySqlDescribeStatement parseDescribe() { if (lexer.token() == Token.DESC || identifierEquals(DESCRIBE)) { @@ -1492,7 +1517,7 @@ public class MySqlStatementParser extends SQLStatementParser { stmt.setConsistentSnapshot(true); } - if (identifierEquals("BEGIN")) { + if (lexer.token() == Token.BEGIN) { lexer.nextToken(); stmt.setBegin(true); if (identifierEquals("WORK")) { 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 f2260f392f..b9ed3ca8eb 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 @@ -41,6 +41,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatemen import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBlockStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateIndexStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; @@ -566,4 +567,8 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlSelectGroupByExpr x); void endVisit(MySqlSelectGroupByExpr x); + + boolean visit(MySqlBlockStatement x); + + void endVisit(MySqlBlockStatement 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 93d235dba0..26663ba6a2 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 @@ -41,6 +41,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatemen import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBlockStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateIndexStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; @@ -1257,4 +1258,14 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq } + @Override + public boolean visit(MySqlBlockStatement x) { + return true; + } + + @Override + public void endVisit(MySqlBlockStatement 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 361b995c08..356b2ee15c 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 @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; @@ -63,6 +64,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatemen import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBlockStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateIndexStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; @@ -3159,4 +3161,29 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } + @Override + public boolean visit(MySqlBlockStatement x) { + print("BEGIN"); + incrementIndent(); + println(); + for (int i = 0, size = x.getStatementList().size(); i < size; ++i) { + if (i != 0) { + println(); + } + SQLStatement stmt = x.getStatementList().get(i); + stmt.setParent(x); + stmt.accept(this); + print(";"); + } + decrementIndent(); + println(); + print("END"); + return false; + } + + @Override + public void endVisit(MySqlBlockStatement 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 b630810441..f48d132f9c 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 @@ -56,6 +56,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableStatemen import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterUserStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAnalyzeStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBinlogStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlBlockStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCommitStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateIndexStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; @@ -1352,4 +1353,14 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS } + @Override + public boolean visit(MySqlBlockStatement x) { + return true; + } + + @Override + public void endVisit(MySqlBlockStatement x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 322ecbaaf0..2c3841403d 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1409,8 +1409,11 @@ public class SQLStatementParser extends SQLParser { stmt.setForEachRow(true); } - SQLStatement body = this.parseBlock(); - stmt.setBody(body); + List body = this.parseStatementList(); + if(body == null || body.isEmpty()) { + throw new ParserException("syntax error"); + } + stmt.setBody(body.get(0)); return stmt; } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTriggerTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTriggerTest.java new file mode 100644 index 0000000000..3e481537f6 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateTriggerTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 1999-2011 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; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; + +public class MySqlCreateTriggerTest extends MysqlTest { + + public void test_0() throws Exception { + String sql = "CREATE TRIGGER ins_sum BEFORE INSERT ON account FOR EACH ROW SET @sum = @sum + NEW.amount;"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + } + + public void test_1() throws Exception { + String sql = "CREATE TRIGGER testref BEFORE INSERT ON test1" + + " FOR EACH ROW" + + " BEGIN" + + " INSERT INTO test2 SET a2 = NEW.a1;" + + " DELETE FROM test3 WHERE a3 = NEW.a1;" + + " UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;" + + " END;"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + } + + +}