support mysql procedure declare exception handler

This commit is contained in:
zhujun
2016-04-17 00:15:16 +08:00
parent d45dbb4d51
commit b2fa273279
12 changed files with 431 additions and 0 deletions
@@ -0,0 +1,50 @@
/*
* 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;
/**
*
* @author zhujun [455910092@qq.com]
* 2016-04-16
*/
public class ConditionValue {
// type for condition SQLSTATE | SELF | SYSTEM | mysql_error_code
private ConditionType type;
//value for condition condition_name | sqlstate | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code
private String value;
public ConditionType getType() {
return type;
}
public void setType(ConditionType type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public enum ConditionType{
SQLSTATE,
SELF,
SYSTEM,
MYSQL_ERROR_CODE
}
}
@@ -0,0 +1,80 @@
/*
* 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 java.util.ArrayList;
import java.util.List;
import com.alibaba.druid.sql.ast.SQLStatement;
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 MySqlDeclareHandlerStatement extends MySqlStatementImpl{
//DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
//handler type
private MySqlHandlerType handleType;
//sp statement
private SQLStatement spStatement;
private List<ConditionValue> conditionValues;
public MySqlDeclareHandlerStatement() {
conditionValues = new ArrayList<ConditionValue>();
}
public List<ConditionValue> getConditionValues() {
return conditionValues;
}
public void setConditionValues(List<ConditionValue> conditionValues) {
this.conditionValues = conditionValues;
}
public MySqlHandlerType getHandleType() {
return handleType;
}
public void setHandleType(MySqlHandlerType handleType) {
this.handleType = handleType;
}
public SQLStatement getSpStatement() {
return spStatement;
}
public void setSpStatement(SQLStatement spStatement) {
this.spStatement = spStatement;
}
@Override
public void accept0(MySqlASTVisitor visitor) {
// TODO Auto-generated method stub
if (visitor.visit(this)) {
acceptChild(visitor, spStatement);
}
visitor.endVisit(this);
}
}
@@ -0,0 +1,29 @@
/*
* 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;
/**
*
* @author zhujun [zhujunxxxxx@163.com]
* @date 2016-04-14
*/
public enum MySqlHandlerType {
//DECLARE处理程序handler_type
CONTINUE,
EXIT,
UNDO
}
@@ -18,6 +18,8 @@ package com.alibaba.druid.sql.dialect.mysql.parser;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.AbstractDocument.LeafElement;
import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
import com.alibaba.druid.sql.ast.SQLDeclareItem;
@@ -86,10 +88,14 @@ import com.alibaba.druid.sql.ast.statement.SQLTableSource;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType;
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.MySqlDeclareHandlerStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlHandlerType;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlRepeatStatement;
@@ -3243,10 +3249,15 @@ 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]
lexer.reset(markBp, markChar, Token.DECLARE);
statementList.add(this.parseDeclareHandler());
} else {
lexer.reset(markBp, markChar, Token.DECLARE);
statementList.add(this.parseDeclare());
@@ -3657,4 +3668,94 @@ public class MySqlStatementParser extends SQLStatementParser {
return stmt;
}
/**
* 定义异常处理程序
* @author zhujun [455910092@qq.com]
* 2016-04-16
* @return
*/
public MySqlDeclareHandlerStatement parseDeclareHandler() {
//DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
//handler_type 取值为 CONTINUE | EXIT | UNDO
//condition_value 取值为 SQLWARNING | NOT FOUND | SQLEXCEPTION | SQLSTATE value(异常码 e.g 1062)
MySqlDeclareHandlerStatement stmt = new MySqlDeclareHandlerStatement();
accept(Token.DECLARE);
//String handlerType = exprParser.name().getSimpleName();
if(lexer.token() == Token.CONTINUE) {
stmt.setHandleType(MySqlHandlerType.CONTINUE);
} else if(lexer.token() == Token.EXIT) {
stmt.setHandleType(MySqlHandlerType.CONTINUE);
} else if(lexer.token() == Token.UNDO) {
stmt.setHandleType(MySqlHandlerType.CONTINUE);
} else {
throw new ParserException("unkown handle type");
}
lexer.nextToken();
accept(Token.HANDLER);
accept(Token.FOR);
for (;;) {
String tokenName = lexer.stringVal();
ConditionValue condition = new ConditionValue();
if (tokenName.equalsIgnoreCase("NOT")) {//for 'NOT FOUND'
lexer.nextToken();
accept(Token.FOUND);
condition.setType(ConditionType.SYSTEM);
condition.setValue("NOT FOUND");
} else if(tokenName.equalsIgnoreCase("SQLSTATE")) { //for SQLSTATE (SQLSTATE '10001')
condition.setType(ConditionType.SQLSTATE);
condition.setValue(lexer.stringVal());
lexer.nextToken();
} else if(lexer.token() == Token.SQLEXCEPTION) { //for SQLEXCEPTION
condition.setType(ConditionType.SYSTEM);
condition.setValue(lexer.stringVal());
lexer.nextToken();
} else if(lexer.token() == Token.SQLWARNING) { //for SQLWARNING
condition.setType(ConditionType.SYSTEM);
condition.setValue(lexer.stringVal());
lexer.nextToken();
} else { //for condition_name or mysql_error_code
if (lexer.token() == Token.LITERAL_INT) {
condition.setType(ConditionType.MYSQL_ERROR_CODE);
condition.setValue(lexer.integerValue().toString());
} else {
condition.setType(ConditionType.SELF);
condition.setValue(tokenName);
}
lexer.nextToken();
}
stmt.getConditionValues().add(condition);
if (lexer.token() == Token.COMMA) {
accept(Token.COMMA);
continue;
} else if (lexer.token() != Token.EOF) {
//TODO
break;
} else {
throw new ParserException("declare handle not eof");
}
}
stmt.setSpStatement(parseBlock());
accept(Token.SEMI);
return stmt;
}
/**
* 定义异常
* @author zhujun [455910092@qq.com]
* 2016-04-16
* @return
*/
public MySqlDeclareHandlerStatement parseDeclareCondition() {
//TODO
return null;
}
}
@@ -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.MySqlDeclareHandlerStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
@@ -581,5 +582,9 @@ public interface MySqlASTVisitor extends SQLASTVisitor {
boolean visit(MySqlSubPartitionByList x);
void endVisit(MySqlSubPartitionByList x);
boolean visit(MySqlDeclareHandlerStatement x);
void endVisit(MySqlDeclareHandlerStatement x);
} //
@@ -22,9 +22,11 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint;
import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey;
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.MySqlDeclareHandlerStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
@@ -1278,4 +1280,16 @@ public class MySqlASTVisitorAdapter extends SQLASTVisitorAdapter implements MySq
public void endVisit(MySqlSubPartitionByList x) {
}
@Override
public boolean visit(MySqlDeclareHandlerStatement x) {
// TODO Auto-generated method stub
return false;
}
@Override
public void endVisit(MySqlDeclareHandlerStatement x) {
// TODO Auto-generated method stub
}
} //
@@ -54,9 +54,12 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUseIndexHint;
import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType;
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.MySqlDeclareHandlerStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
@@ -3422,4 +3425,40 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV
public void endVisit(MySqlSubPartitionByList x) {
}
@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());
print0(ucase ? " HANDLER FOR " : " handler for ");
for (int i = 0; i < x.getConditionValues().size(); i++) {
ConditionValue cv = x.getConditionValues().get(i);
if (cv.getType() == ConditionType.SQLSTATE) {
print0(ucase ? " SQLSTATE " : " sqlstate ");
print0(cv.getValue());
} else if (cv.getType() == ConditionType.MYSQL_ERROR_CODE) {
print0(cv.getValue());
} else if (cv.getType() == ConditionType.SELF) {
print0(cv.getValue());
} else if (cv.getType() == ConditionType.SYSTEM){
print0(ucase ? cv.getValue().toUpperCase() : cv.getValue().toLowerCase());
}
if (i != x.getConditionValues().size() - 1) {
print0(", ");
}
}
println();
return true;
}
@Override
public void endVisit(MySqlDeclareHandlerStatement x) {
// TODO Auto-generated method stub
}
} //
@@ -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.MySqlDeclareHandlerStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlDeclareStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlIterateStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlLeaveStatement;
@@ -1388,4 +1389,16 @@ public class MySqlSchemaStatVisitor extends SchemaStatVisitor implements MySqlAS
public void endVisit(MySqlSubPartitionByList x) {
}
@Override
public boolean visit(MySqlDeclareHandlerStatement x) {
// TODO Auto-generated method stub
return false;
}
@Override
public void endVisit(MySqlDeclareHandlerStatement x) {
// TODO Auto-generated method stub
}
}
@@ -150,6 +150,13 @@ public class Keywords {
map.put("FETCH", Token.FETCH);
map.put("OUT", Token.OUT);
map.put("INOUT", Token.INOUT);
map.put("HANDLER", Token.HANDLER);
map.put("CONTINUE", Token.CONTINUE);
map.put("UNDO", Token.UNDO);
map.put("SQLSTATE", Token.SQLSTATE);
map.put("SQLWARNING", Token.SQLWARNING);
map.put("SQLEXCEPTION", Token.SQLEXCEPTION);
map.put("FOUND", Token.FOUND);
DEFAULT_KEYWORDS = new Keywords(map);
}
@@ -138,6 +138,13 @@ public enum Token {
CLOSE("CLOSE"),
OUT("OUT"),
INOUT("INOUT"),
HANDLER("HANDLER"),
EXIT("EXIT"),
UNDO("UNDO"),
SQLSTATE("SQLSTATE"),
SQLWARNING("SQLWARNING"),
SQLEXCEPTION("SQLEXCEPTION"),
FOUND("FOUND"),
//postgresql
WINDOW("WINDOW"),
@@ -695,4 +695,5 @@ public interface SQLASTVisitor {
boolean visit(SQLAlterTableRepairPartition x);
void endVisit(SQLAlterTableRepairPartition x);
}
@@ -0,0 +1,85 @@
/*
* 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: begin-end within begin-end
* @author zhujun [455910092@qq.com]
* @date 2016-4-14
* @version V1.0
*/
public class MySqlCreateProcedureTest8 extends MysqlTest {
/**
* DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement handler_type: CONTINUE | EXIT condition_value: SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code
* @throws Exception
*/
public void test_0() 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 continue handler FOR SQLSTATE '02000' 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;";
sql="create or replace procedure sp_name(level int,age int)"+
" begin"+
" declare continue handler FOR SQLEXCEPTION,SQLWARNING begin SET done = 1; end;"+
" end";
MySqlStatementParser parser=new MySqlStatementParser(sql);
List<SQLStatement> 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());
}
}