From 23ed0b6c693ec72ab89296a5849e7e418c7a6b56 Mon Sep 17 00:00:00 2001 From: zhujun Date: Fri, 22 Apr 2016 16:38:16 +0800 Subject: [PATCH] support mysql procedure declare exception (last time forget this part) --- .../MySqlDeclareConditionStatement.java | 66 +++++++++ .../mysql/parser/MySqlStatementParser.java | 54 +++++++- .../mysql/visitor/MySqlASTVisitor.java | 5 + .../mysql/visitor/MySqlASTVisitorAdapter.java | 13 ++ .../mysql/visitor/MySqlOutputVisitor.java | 25 +++- .../mysql/visitor/MySqlSchemaStatVisitor.java | 13 ++ .../alibaba/druid/sql/parser/Keywords.java | 1 + .../com/alibaba/druid/sql/parser/Token.java | 1 + .../sql/mysql/MySqlCreateProcedureTest9.java | 128 ++++++++++++++++++ 9 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlDeclareConditionStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateProcedureTest9.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlDeclareConditionStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlDeclareConditionStatement.java new file mode 100644 index 0000000000..6ba1b65560 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/clause/MySqlDeclareConditionStatement.java @@ -0,0 +1,66 @@ +/* + * Copyright 1999-2101 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.clause; + + +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStatementImpl; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; +/** + * + * @author zhujun [455910092@qq.com] + * @date 2016-04-14 + */ +public class MySqlDeclareConditionStatement extends MySqlStatementImpl{ + + /* + DECLARE condition_name CONDITION FOR condition_value + + condition_value: + SQLSTATE [VALUE] sqlstate_value + | mysql_error_code + */ + + //condition_name + private String conditionName; + //sp statement + private ConditionValue conditionValue; + + public String getConditionName() { + return conditionName; + } + + public void setConditionName(String conditionName) { + this.conditionName = conditionName; + } + + public ConditionValue getConditionValue() { + return conditionValue; + } + + public void setConditionValue(ConditionValue conditionValue) { + this.conditionValue = conditionValue; + } + + @Override + public void accept0(MySqlASTVisitor visitor) { + // TODO Auto-generated method stub + visitor.visit(this); + visitor.endVisit(this); + + } + +} + 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 620b0c5a5c..5998be5b3f 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 @@ -92,6 +92,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionTy import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlHandlerType; @@ -179,7 +180,6 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowTriggersStatem import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowVariantsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowWarningsStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStartTransactionStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnlockTablesStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.parser.Lexer; @@ -3249,15 +3249,18 @@ public class MySqlStatementParser extends SQLStatementParser { int markBp = lexer.bp(); lexer.nextToken(); lexer.nextToken(); - System.out.println(lexer.token()); if (lexer.token() == Token.CURSOR)// cursor declare statement { lexer.reset(markBp, markChar, Token.DECLARE); statementList.add(this.parseCursorDeclare()); } else if (lexer.token() == Token.HANDLER) { - //DECLARE处理程序 [add by zhujun 2016-04-16] + //DECLARE异常处理程序 [add by zhujun 2016-04-16] lexer.reset(markBp, markChar, Token.DECLARE); statementList.add(this.parseDeclareHandler()); + } else if (lexer.token() == Token.CONDITION) { + //DECLARE异常 [add by zhujun 2016-04-17] + lexer.reset(markBp, markChar, Token.DECLARE); + statementList.add(this.parseDeclareCondition()); } else { lexer.reset(markBp, markChar, Token.DECLARE); statementList.add(this.parseDeclare()); @@ -3789,7 +3792,6 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.COMMA); continue; } else if (lexer.token() != Token.EOF) { - //TODO break; } else { throw new ParserException("declare handle not eof"); @@ -3805,4 +3807,48 @@ public class MySqlStatementParser extends SQLStatementParser { return stmt; } + + /** + * zhujun [455910092@qq.com] + * 2016-04-17 + * 定义条件 + * @return + */ + public MySqlDeclareConditionStatement parseDeclareCondition() { + /* + DECLARE condition_name CONDITION FOR condition_value + + condition_value: + SQLSTATE [VALUE] sqlstate_value + | mysql_error_code + */ + MySqlDeclareConditionStatement stmt = new MySqlDeclareConditionStatement(); + accept(Token.DECLARE); + + stmt.setConditionName(exprParser.name().toString()); + + accept(Token.CONDITION); + + accept(Token.FOR); + + String tokenName = lexer.stringVal(); + ConditionValue condition = new ConditionValue(); + if(tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001') + condition.setType(ConditionType.SQLSTATE); + lexer.nextToken(); + condition.setValue(exprParser.name().toString()); + } else if (lexer.token() == Token.LITERAL_INT) { + condition.setType(ConditionType.MYSQL_ERROR_CODE); + condition.setValue(lexer.integerValue().toString()); + lexer.nextToken(); + } else { + throw new ParserException("declare condition grammer error."); + } + + stmt.setConditionValue(condition); + + accept(Token.SEMI); + + return stmt; + } } 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 6b4edd3e1d..2e5eff03e5 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 @@ -25,6 +25,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; @@ -586,5 +587,9 @@ public interface MySqlASTVisitor extends SQLASTVisitor { boolean visit(MySqlDeclareHandlerStatement x); void endVisit(MySqlDeclareHandlerStatement x); + + boolean visit(MySqlDeclareConditionStatement x); + + void endVisit(MySqlDeclareConditionStatement 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 3876360f3f..36e8e6f51f 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 @@ -26,6 +26,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; @@ -1292,4 +1293,16 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq } + @Override + public boolean visit(MySqlDeclareConditionStatement x) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void endVisit(MySqlDeclareConditionStatement x) { + // TODO Auto-generated method stub + + } + } // 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 df2efdd7d3..15df4346b9 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 @@ -59,6 +59,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionTy import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; @@ -3429,7 +3430,6 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public boolean visit(MySqlDeclareHandlerStatement x) { - // TODO Auto-generated method stub print0(ucase ? "DECLARE " : "declare "); print0(ucase ? x.getHandleType().toString().toUpperCase() : x.getHandleType().toString().toLowerCase()); @@ -3458,7 +3458,28 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV @Override public void endVisit(MySqlDeclareHandlerStatement x) { - // TODO Auto-generated method stub + + } + + @Override + public boolean visit(MySqlDeclareConditionStatement x) { + print0(ucase ? "DECLARE " : "declare "); + print0(x.getConditionName()); + print0(ucase ? " CONDITION FOR " : " condition for "); + + if (x.getConditionValue().getType() == ConditionType.SQLSTATE) { + print0(ucase ? "SQLSTATE " : "sqlstate "); + print0(x.getConditionValue().getValue()); + } else { + print0(x.getConditionValue().getValue()); + } + + println(); + return false; + } + + @Override + public void endVisit(MySqlDeclareConditionStatement 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 3c2e15e170..b81b2300b0 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 @@ -39,6 +39,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCursorDeclareStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareConditionStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareHandlerStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement; @@ -1401,4 +1402,16 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS // TODO Auto-generated method stub } + + @Override + public boolean visit(MySqlDeclareConditionStatement x) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void endVisit(MySqlDeclareConditionStatement x) { + // TODO Auto-generated method stub + + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Keywords.java b/src/main/java/com/alibaba/druid/sql/parser/Keywords.java index 2835265ba2..70be95c76c 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Keywords.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Keywords.java @@ -157,6 +157,7 @@ public class Keywords { map.put("SQLWARNING", Token.SQLWARNING); map.put("SQLEXCEPTION", Token.SQLEXCEPTION); map.put("FOUND", Token.FOUND); + map.put("CONDITION", Token.CONDITION); DEFAULT_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Token.java b/src/main/java/com/alibaba/druid/sql/parser/Token.java index 3e8dd49da7..f41f8b4b37 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -145,6 +145,7 @@ public enum Token { SQLWARNING("SQLWARNING"), SQLEXCEPTION("SQLEXCEPTION"), FOUND("FOUND"), + CONDITION("CONDITION"), //postgresql WINDOW("WINDOW"), diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateProcedureTest9.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateProcedureTest9.java new file mode 100644 index 0000000000..cc2d3ed1d4 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlCreateProcedureTest9.java @@ -0,0 +1,128 @@ +/* + * Copyright 1999-2101 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; +import com.alibaba.druid.stat.TableStat.Column; + +/** + * + * @Description: 测试异常声明 + * @author zhujun [455910092@qq.com] + * @date 2016-4-17 + * @version V1.0 + */ +public class MySqlCreateProcedureTest9 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "create or replace procedure sp_name(level int,age int)"+ + " begin"+ + " declare test1 CONDITION FOR SQLSTATE '02000';"+ + " end"; + + MySqlStatementParser parser=new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.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()); + + Assert.assertEquals(0, visitor.getTables().size()); + Assert.assertEquals(0, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + } + + public void test_2() throws Exception { + String sql = "create or replace procedure sp_name(level int,age int)"+ + " begin"+ + " declare condition_name CONDITION FOR 1002;"+ + " end"; + + MySqlStatementParser parser=new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.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()); + + Assert.assertEquals(0, visitor.getTables().size()); + Assert.assertEquals(0, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + } + + public void test_3() throws Exception { + String sql="create or replace procedure test_cursor (in param int(10),out result varchar(90)) " + +" begin" + +" declare name varchar(20);" + +" declare pass varchar(20);" + +" declare done int;" + +" declare cur_test CURSOR for select user_name,user_pass from test;" + +" declare condition_name CONDITION FOR 1002;" + +" declare continue handler FOR condition_name SET done = 1;" + +" if param then" + +" select concat_ws(',',user_name,user_pass) into result from test.users where id=param;" + +" else" + +" open cur_test;" + +" repeat" + +" fetch cur_test into name, pass;" + +" select concat_ws(',',result,name,pass) into result;" + +" until done end repeat;" + +" close cur_test;" + +" end if;" + +" end;"; + + MySqlStatementParser parser=new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + statemen.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()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(3, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + } + +}