diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerDeclareItem.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerDeclareItem.java new file mode 100644 index 0000000000..e7ceb4b371 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerDeclareItem.java @@ -0,0 +1,85 @@ +/* + * Copyright 2014 Alibaba.com All right reserved. This software is the + * confidential and proprietary information of Alibaba.com ("Confidential + * Information"). You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Alibaba.com. + */ +package com.alibaba.druid.sql.dialect.sqlserver.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLDataType; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor; + +public class SQLServerDeclareItem extends SQLServerObjectImpl { + + protected Type type; + + protected SQLExpr name; + + protected SQLDataType dataType; + + protected SQLExpr value; + + protected List tableElementList = new ArrayList(); + + @Override + public void accept0(SQLServerASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.name); + acceptChild(visitor, this.dataType); + acceptChild(visitor, this.value); + acceptChild(visitor, this.tableElementList); + } + visitor.endVisit(this); + } + + public SQLExpr getName() { + return name; + } + + public void setName(SQLExpr name) { + this.name = name; + } + + public SQLDataType getDataType() { + return dataType; + } + + public void setDataType(SQLDataType dataType) { + this.dataType = dataType; + } + + public SQLExpr getValue() { + return value; + } + + public void setValue(SQLExpr value) { + this.value = value; + } + + public List getTableElementList() { + return tableElementList; + } + + public void setTableElementList(List tableElementList) { + this.tableElementList = tableElementList; + } + + public enum Type { + TABLE, LOCAL, CURSOR; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerDeclareStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerDeclareStatement.java new file mode 100644 index 0000000000..50e140516e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerDeclareStatement.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Alibaba.com All right reserved. This software is the + * confidential and proprietary information of Alibaba.com ("Confidential + * Information"). You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Alibaba.com. + */ +package com.alibaba.druid.sql.dialect.sqlserver.ast.stmt; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerObjectImpl; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerStatement; +import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor; + +public class SQLServerDeclareStatement extends SQLServerObjectImpl implements SQLServerStatement { + + protected List items = new ArrayList(); + + @Override + public void accept0(SQLServerASTVisitor visitor) { + if (visitor.visit(this)) { + this.acceptChild(visitor, items); + } + visitor.endVisit(this); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java index 84abe762fd..c974625e8f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerLexer.java @@ -41,6 +41,7 @@ public class SQLServerLexer extends Lexer { map.put("WITH", Token.WITH); map.put("PERCENT", Token.PERCENT); map.put("IDENTITY", Token.IDENTITY); + map.put("DECLARE", Token.DECLARE); DEFAULT_SQL_SERVER_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java index da434554dd..a3abfa583c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -21,13 +21,18 @@ import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLConstraint; import com.alibaba.druid.sql.ast.statement.SQLInsertInto; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSetStatement; +import com.alibaba.druid.sql.ast.statement.SQLTableElement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeclareStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetStatement; @@ -77,9 +82,91 @@ public class SQLServerStatementParser extends SQLStatementParser { statementList.add(execStmt); return true; } + + if (lexer.token() == Token.DECLARE) { + statementList.add(this.parseDeclare()); + return true; + } return false; } + + public SQLStatement parseDeclare() { + this.accept(Token.DECLARE); + + SQLServerDeclareStatement declareStatement = new SQLServerDeclareStatement(); + + for (;;) { + SQLServerDeclareItem item = new SQLServerDeclareItem(); + declareStatement.getItems().add(item); + + item.setName(this.exprParser.name()); + + if (lexer.token() == Token.AS) { + lexer.nextToken(); + } + + if (lexer.token() == Token.TABLE) { + lexer.nextToken(); + item.setType(SQLServerDeclareItem.Type.TABLE); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + for (;;) { + if (lexer.token() == Token.IDENTIFIER // + || lexer.token() == Token.LITERAL_ALIAS) { + SQLColumnDefinition column = this.exprParser.parseColumn(); + item.getTableElementList().add(column); + } else if (lexer.token() == Token.PRIMARY // + || lexer.token() == Token.UNIQUE // + || lexer.token() == Token.CHECK // + || lexer.token() == Token.CONSTRAINT) { + SQLConstraint constraint = this.exprParser.parseConstaint(); + constraint.setParent(item); + item.getTableElementList().add((SQLTableElement) constraint); + } else if (lexer.token() == Token.TABLESPACE) { + throw new ParserException("TODO " + lexer.token()); + } else { + SQLColumnDefinition column = this.exprParser.parseColumn(); + item.getTableElementList().add(column); + } + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + + if (lexer.token() == Token.RPAREN) { + break; + } + continue; + } + + break; + } + accept(Token.RPAREN); + } + break; + } else if (lexer.token() == Token.CURSOR) { + item.setType(SQLServerDeclareItem.Type.CURSOR); + lexer.nextToken(); + } else { + item.setType(SQLServerDeclareItem.Type.LOCAL); + item.setDataType(this.exprParser.parseDataType()); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + item.setValue(this.exprParser.expr()); + } + } + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } else { + break; + } + } + return declareStatement; + } public SQLStatement parseInsert() { SQLServerInsertStatement insertStatement = new SQLServerInsertStatement(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java index b7db1ce651..8aa1dacdcd 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitor.java @@ -16,10 +16,12 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeclareStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetStatement; @@ -72,4 +74,13 @@ public interface SQLServerASTVisitor extends SQLASTVisitor { boolean visit(SQLServerOutput x); void endVisit(SQLServerOutput x); + + boolean visit(SQLServerDeclareItem x); + + void endVisit(SQLServerDeclareItem x); + + boolean visit(SQLServerDeclareStatement x); + + void endVisit(SQLServerDeclareStatement x); + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java index abeeb4eac7..e0c7799089 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerASTVisitorAdapter.java @@ -16,10 +16,12 @@ package com.alibaba.druid.sql.dialect.sqlserver.visitor; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeclareStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetStatement; @@ -136,7 +138,27 @@ public class SQLServerASTVisitorAdapter extends SQLASTVisitorAdapter implements @Override public void endVisit(SQLServerOutput x) { - + + } + + @Override + public boolean visit(SQLServerDeclareItem x) { + return true; + } + + @Override + public void endVisit(SQLServerDeclareItem x) { + + } + + @Override + public boolean visit(SQLServerDeclareStatement x) { + return true; + } + + @Override + public void endVisit(SQLServerDeclareStatement x) { + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java index 0c2979dd25..9868f724f5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java @@ -23,10 +23,12 @@ import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition.Identity; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeclareStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetStatement; @@ -409,4 +411,58 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe public void endVisit(SQLServerOutput x) { } + + @Override + public boolean visit(SQLServerDeclareItem x) { + x.getName().accept(this); + + if(x.getType() == SQLServerDeclareItem.Type.TABLE) { + print(" TABLE"); + int size = x.getTableElementList().size(); + + if (size > 0) { + print(" ("); + incrementIndent(); + println(); + for (int i = 0; i < size; ++i) { + if (i != 0) { + print(","); + println(); + } + x.getTableElementList().get(i).accept(this); + } + decrementIndent(); + println(); + print(")"); + } + } else if (x.getType() == SQLServerDeclareItem.Type.CURSOR) { + print(" CURSOR"); + } else { + print(" "); + x.getDataType().accept(this); + if (x.getValue() != null) { + print(" = "); + x.getValue().accept(this); + } + } + + return false; + } + + @Override + public void endVisit(SQLServerDeclareItem x) { + + } + + @Override + public boolean visit(SQLServerDeclareStatement x) { + print("DECLARE "); + this.printAndAccept(x.getItems(), ", "); + return false; + } + + @Override + public void endVisit(SQLServerDeclareStatement x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java index 275c5d1206..d6665ff303 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerSchemaStatVisitor.java @@ -22,10 +22,12 @@ import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerColumnDefinition.Identity; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerDeclareItem; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerOutput; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeclareStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetStatement; @@ -168,4 +170,24 @@ public class SQLServerSchemaStatVisitor extends SchemaStatVisitor implements SQL } + @Override + public boolean visit(SQLServerDeclareItem x) { + return false; + } + + @Override + public void endVisit(SQLServerDeclareItem x) { + + } + + @Override + public boolean visit(SQLServerDeclareStatement x) { + return false; + } + + @Override + public void endVisit(SQLServerDeclareStatement x) { + + } + } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerDeclareTest.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerDeclareTest.java new file mode 100644 index 0000000000..29eb22219a --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerDeclareTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014 Alibaba.com All right reserved. This software is the + * confidential and proprietary information of Alibaba.com ("Confidential + * Information"). You shall not disclose such Confidential Information and shall + * use it only in accordance with the terms of the license agreement you entered + * into with Alibaba.com. + */ +package com.alibaba.druid.bvt.sql.sqlserver; + +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.util.JdbcUtils; + +public class SQLServerDeclareTest extends TestCase { + + public void test_0() { + String sql = "declare @is_updated bit"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + List stmtList = parser.parseStatementList(); + + Assert.assertEquals(1, stmtList.size()); + + String text = SQLUtils.toSQLString(stmtList, JdbcUtils.SQL_SERVER); + + Assert.assertEquals("DECLARE @is_updated bit", text); + } + + public void test_1() { + String sql = "DECLARE @Group nvarchar(50), @Sales money=1;"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + List stmtList = parser.parseStatementList(); + + Assert.assertEquals(1, stmtList.size()); + + String text = SQLUtils.toSQLString(stmtList, JdbcUtils.SQL_SERVER); + + Assert.assertEquals("DECLARE @Group nvarchar(50), @Sales money = 1", text); + } + + public void test_2() { + String sql = "DECLARE @cursor CURSOR"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + List stmtList = parser.parseStatementList(); + + Assert.assertEquals(1, stmtList.size()); + + String text = SQLUtils.toSQLString(stmtList, JdbcUtils.SQL_SERVER); + + Assert.assertEquals("DECLARE @cursor CURSOR", text); + } + + public void test_3() { + String sql = "DECLARE @MyTableVar table( EmpID int NOT NULL, OldVacationHours int, NewVacationHours int, ModifiedDate datetime);"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + List stmtList = parser.parseStatementList(); + + Assert.assertEquals(1, stmtList.size()); + + String text = SQLUtils.toSQLString(stmtList, JdbcUtils.SQL_SERVER); + + String expected = "DECLARE @MyTableVar TABLE ("// + + "\n\tEmpID int NOT NULL,"// + + "\n\tOldVacationHours int,"// + + "\n\tNewVacationHours int,"// + + "\n\tModifiedDate datetime"// + + "\n)"; + Assert.assertEquals(expected, text); + } + +}