From 25b4f4582a92dd242cf74b5f5ab7729f258495be Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 29 Dec 2012 17:29:23 +0800 Subject: [PATCH 1/8] improve Oracle SQL Parser, support Timestamp & Interval --- .../oracle/ast/OracleDataTypeIntervalDay.java | 48 +++++++++++ .../ast/OracleDataTypeIntervalYear.java | 28 +++++++ .../oracle/ast/OracleDataTypeTimestamp.java | 47 +++++++++++ .../oracle/parser/OracleExprParser.java | 68 ++++++++++++++++ .../oracle/visitor/OracleASTVisitor.java | 16 ++++ .../visitor/OracleASTVisitorAdapter.java | 33 ++++++++ .../oracle/visitor/OracleOutputVisitor.java | 71 +++++++++++++++++ .../visitor/OracleSchemaStatVisitor.java | 33 ++++++++ .../sql/oracle/OracleCreateTableTest15.java | 79 +++++++++++++++++++ .../sql/oracle/OracleCreateTableTest16.java | 79 +++++++++++++++++++ 10 files changed, 502 insertions(+) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest15.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest16.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java new file mode 100644 index 0000000000..26c014b934 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java @@ -0,0 +1,48 @@ +package com.alibaba.druid.sql.dialect.oracle.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLDataTypeImpl; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OracleDataTypeIntervalDay extends SQLDataTypeImpl implements OracleSQLObject { + + private static final long serialVersionUID = 1L; + + private boolean toSecond = false; + + protected final List fractionalSeconds = new ArrayList(); + + public OracleDataTypeIntervalDay(){ + this.setName("INTERVAL DAY"); + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((OracleASTVisitor) visitor); + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getArguments()); + } + visitor.endVisit(this); + } + + public boolean isToSecond() { + return toSecond; + } + + public void setToSecond(boolean toSecond) { + this.toSecond = toSecond; + } + + public List getFractionalSeconds() { + return fractionalSeconds; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java new file mode 100644 index 0000000000..f0484ee139 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java @@ -0,0 +1,28 @@ +package com.alibaba.druid.sql.dialect.oracle.ast; + +import com.alibaba.druid.sql.ast.SQLDataTypeImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OracleDataTypeIntervalYear extends SQLDataTypeImpl implements OracleSQLObject { + + private static final long serialVersionUID = 1L; + + public OracleDataTypeIntervalYear(){ + this.setName("INTERVAL YEAR"); + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((OracleASTVisitor) visitor); + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getArguments()); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java new file mode 100644 index 0000000000..a8de670fb1 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java @@ -0,0 +1,47 @@ +package com.alibaba.druid.sql.dialect.oracle.ast; + +import com.alibaba.druid.sql.ast.SQLDataTypeImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OracleDataTypeTimestamp extends SQLDataTypeImpl implements OracleSQLObject { + + private static final long serialVersionUID = 1L; + + private boolean withTimeZone = false; + private boolean withLocalTimeZone = false; + + public OracleDataTypeTimestamp() { + this.setName("TIMESTAMP"); + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((OracleASTVisitor) visitor); + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, getArguments()); + } + visitor.endVisit(this); + } + + public boolean isWithTimeZone() { + return withTimeZone; + } + + public void setWithTimeZone(boolean withTimeZone) { + this.withTimeZone = withTimeZone; + } + + public boolean isWithLocalTimeZone() { + return withLocalTimeZone; + } + + public void setWithLocalTimeZone(boolean withLocalTimeZone) { + this.withLocalTimeZone = withLocalTimeZone; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index 635c91c47c..68ac72fbc4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -37,6 +37,9 @@ import com.alibaba.druid.sql.ast.expr.SQLUnaryOperator; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; import com.alibaba.druid.sql.ast.statement.SQLCharactorDataType; import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeTimestamp; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; @@ -75,6 +78,7 @@ public class OracleExprParser extends SQLExprParser { + public boolean allowStringAdditive = false; /** @@ -117,9 +121,73 @@ public class OracleExprParser extends SQLExprParser { return null; } + if (lexer.token() == Token.INTERVAL) { + lexer.nextToken(); + if (identifierEquals("YEAR")) { + lexer.nextToken(); + OracleDataTypeIntervalYear interval = new OracleDataTypeIntervalYear(); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + interval.getArguments().add(this.expr()); + accept(Token.RPAREN); + } + + accept(Token.TO); + acceptIdentifier("MONTH"); + + return interval; + } else { + acceptIdentifier("DAY"); + OracleDataTypeIntervalDay interval = new OracleDataTypeIntervalDay(); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + interval.getArguments().add(this.expr()); + accept(Token.RPAREN); + } + + accept(Token.TO); + acceptIdentifier("SECOND"); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + interval.getFractionalSeconds().add(this.expr()); + accept(Token.RPAREN); + } + + return interval; + } + } + SQLName typeExpr = name(); String typeName = typeExpr.toString(); + if ("TIMESTAMP".equalsIgnoreCase(typeName)) { + OracleDataTypeTimestamp timestamp = new OracleDataTypeTimestamp(); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + timestamp.getArguments().add(this.expr()); + accept(Token.RPAREN); + } + + if (lexer.token() == Token.WITH) { + lexer.nextToken(); + + if (identifierEquals("LOCAL")) { + lexer.nextToken(); + timestamp.setWithLocalTimeZone(true); + } else { + timestamp.setWithTimeZone(true); + } + + acceptIdentifier("TIME"); + acceptIdentifier("ZONE"); + } + + return timestamp; + } + if (isCharType(typeName)) { SQLCharactorDataType charType = new SQLCharactorDataType(typeName); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java index baa78a0f71..7246799850 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java @@ -16,6 +16,9 @@ package com.alibaba.druid.sql.dialect.oracle.visitor; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeTimestamp; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; @@ -611,4 +614,17 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleDropDatabaseLinkStatement x); void endVisit(OracleDropDatabaseLinkStatement x); + + boolean visit(OracleDataTypeTimestamp x); + + void endVisit(OracleDataTypeTimestamp x); + + boolean visit(OracleDataTypeIntervalYear x); + + void endVisit(OracleDataTypeIntervalYear x); + + boolean visit(OracleDataTypeIntervalDay x); + + void endVisit(OracleDataTypeIntervalDay x); + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java index 7122c89aa6..afe2ae8df9 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java @@ -16,6 +16,9 @@ package com.alibaba.druid.sql.dialect.oracle.visitor; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeTimestamp; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; @@ -1393,4 +1396,34 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora } + @Override + public boolean visit(OracleDataTypeTimestamp x) { + return true; + } + + @Override + public void endVisit(OracleDataTypeTimestamp x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalYear x) { + return true; + } + + @Override + public void endVisit(OracleDataTypeIntervalYear x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalDay x) { + return true; + } + + @Override + public void endVisit(OracleDataTypeIntervalDay x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 07a611c768..89c151d483 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -38,6 +38,9 @@ import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; import com.alibaba.druid.sql.ast.statement.SQLRollbackStatement; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeTimestamp; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; @@ -3107,4 +3110,72 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } return false; } + + @Override + public boolean visit(OracleDataTypeTimestamp x) { + print(x.getName()); + if (x.getArguments().size() > 0) { + print("("); + x.getArguments().get(0).accept(this); + print(")"); + } + + if (x.isWithTimeZone()) { + print(" WITH TIME ZONE"); + } else if (x.isWithLocalTimeZone()) { + print(" WITH LOCAL TIME ZONE"); + } + + return false; + } + + @Override + public void endVisit(OracleDataTypeTimestamp x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalYear x) { + print(x.getName()); + if (x.getArguments().size() > 0) { + print("("); + x.getArguments().get(0).accept(this); + print(")"); + } + + print(" TO MONTH"); + + return false; + } + + @Override + public void endVisit(OracleDataTypeIntervalYear x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalDay x) { + print(x.getName()); + if (x.getArguments().size() > 0) { + print("("); + x.getArguments().get(0).accept(this); + print(")"); + } + + print(" TO SECOND"); + + if (x.getFractionalSeconds().size() > 0) { + print("("); + x.getFractionalSeconds().get(0).accept(this); + print(")"); + } + + return false; + } + + @Override + public void endVisit(OracleDataTypeIntervalDay x) { + // TODO Auto-generated method stub + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java index cd06b63dc3..9daabe6a55 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java @@ -37,6 +37,9 @@ import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLTruncateStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalDay; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeIntervalYear; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeTimestamp; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; @@ -1822,4 +1825,34 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle public void endVisit(OracleDropDatabaseLinkStatement x) { } + + @Override + public boolean visit(OracleDataTypeTimestamp x) { + return false; + } + + @Override + public void endVisit(OracleDataTypeTimestamp x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalYear x) { + return false; + } + + @Override + public void endVisit(OracleDataTypeIntervalYear x) { + + } + + @Override + public boolean visit(OracleDataTypeIntervalDay x) { + return false; + } + + @Override + public void endVisit(OracleDataTypeIntervalDay x) { + + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest15.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest15.java new file mode 100644 index 0000000000..65b21b964d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest15.java @@ -0,0 +1,79 @@ +/* + * 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.oracle; + +import java.util.List; + +import junit.framework.Assert; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; + +public class OracleCreateTableTest15 extends OracleTest { + + public void test_types() throws Exception { + String sql = // + "create table T (" + // + "F1 TIMESTAMP," + // + "F2 TIMESTAMP(9), " + // + "F3 TIMESTAMP WITH TIME ZONE," + // + "F4 TIMESTAMP(9) WITH TIME ZONE, " + // + "F5 TIMESTAMP WITH TIME ZONE," + // + "F6 TIMESTAMP(9) WITH TIME ZONE" + // + ") "; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statement = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + Assert.assertEquals("CREATE TABLE T (" + // + "\n\tF1 TIMESTAMP," + // + "\n\tF2 TIMESTAMP(9)," + // + "\n\tF3 TIMESTAMP WITH TIME ZONE," + // + "\n\tF4 TIMESTAMP(9) WITH TIME ZONE," + // + "\n\tF5 TIMESTAMP WITH TIME ZONE," + // + "\n\tF6 TIMESTAMP(9) WITH TIME ZONE" + // + "\n)", SQLUtils.toSQLString(statement, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + statement.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + + Assert.assertEquals(6, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F1"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F2"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F3"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F4"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F5"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F6"))); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest16.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest16.java new file mode 100644 index 0000000000..2ffd095467 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleCreateTableTest16.java @@ -0,0 +1,79 @@ +/* + * 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.oracle; + +import java.util.List; + +import junit.framework.Assert; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; + +public class OracleCreateTableTest16 extends OracleTest { + + public void test_types() throws Exception { + String sql = // + "create table T (" + // + "F1 INTERVAL YEAR TO MONTH," + // + "F2 INTERVAL YEAR (3) TO MONTH," + // + "F3 INTERVAL DAY TO SECOND," + // + "F4 INTERVAL DAY (3) TO SECOND," + // + "F5 INTERVAL DAY TO SECOND (9)," + // + "F6 INTERVAL DAY (3) TO SECOND (9)" + // + ") "; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statement = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + Assert.assertEquals("CREATE TABLE T (" + // + "\n\tF1 INTERVAL YEAR TO MONTH," + // + "\n\tF2 INTERVAL YEAR(3) TO MONTH," + // + "\n\tF3 INTERVAL DAY TO SECOND," + // + "\n\tF4 INTERVAL DAY(3) TO SECOND," + // + "\n\tF5 INTERVAL DAY TO SECOND(9)," + // + "\n\tF6 INTERVAL DAY(3) TO SECOND(9)" + // + "\n)", SQLUtils.toSQLString(statement, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + statement.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + + Assert.assertEquals(6, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F1"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F2"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F3"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F4"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F5"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("T", "F6"))); + } +} From 19dfadd569391add5da5fb2da5b568eb1cbab9f7 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 29 Dec 2012 18:38:01 +0800 Subject: [PATCH 2/8] improve Oracle SQL Parser, support Timestamp & Interval --- .../oracle/ast/OracleDataTypeIntervalDay.java | 15 +++++++++++++++ .../oracle/ast/OracleDataTypeIntervalYear.java | 15 +++++++++++++++ .../oracle/ast/OracleDataTypeTimestamp.java | 15 +++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java index 26c014b934..024353909c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalDay.java @@ -1,3 +1,18 @@ +/* + * 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.oracle.ast; import java.util.ArrayList; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java index f0484ee139..6aac7f6c80 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeIntervalYear.java @@ -1,3 +1,18 @@ +/* + * 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.oracle.ast; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java index a8de670fb1..924633eadf 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleDataTypeTimestamp.java @@ -1,3 +1,18 @@ +/* + * 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.oracle.ast; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; From fed6181a8e880191c85dcbaa281bb3b98ca06ea8 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 29 Dec 2012 19:06:09 +0800 Subject: [PATCH 3/8] remove unused import --- src/main/java/com/alibaba/druid/sql/ast/SQLDataTypeImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLDataTypeImpl.java b/src/main/java/com/alibaba/druid/sql/ast/SQLDataTypeImpl.java index a9232ee75f..21c7fb62d9 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLDataTypeImpl.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLDataTypeImpl.java @@ -18,7 +18,6 @@ package com.alibaba.druid.sql.ast; import java.util.ArrayList; import java.util.List; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLDataTypeImpl extends SQLObjectImpl implements SQLDataType { From c6607da9df74c6905ddc67c8a572558a95579aaa Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 29 Dec 2012 19:28:24 +0800 Subject: [PATCH 4/8] improve Oracle SQL Parser, support drop sequence --- .../ast/stmt/OracleDropSequenceStatement.java | 43 ++++++++++++++ .../oracle/parser/OracleStatementParser.java | 13 +++++ .../oracle/visitor/OracleASTVisitor.java | 6 +- .../visitor/OracleASTVisitorAdapter.java | 11 ++++ .../oracle/visitor/OracleOutputVisitor.java | 14 ++++- .../visitor/OracleSchemaStatVisitor.java | 11 ++++ .../sql/oracle/OracleDropSequenceTest.java | 58 +++++++++++++++++++ 7 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDropSequenceStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleDropSequenceTest.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDropSequenceStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDropSequenceStatement.java new file mode 100644 index 0000000000..057302768e --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleDropSequenceStatement.java @@ -0,0 +1,43 @@ +/* + * 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.oracle.ast.stmt; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +public class OracleDropSequenceStatement extends OracleStatementImpl implements OracleDDLStatement { + + private static final long serialVersionUID = 1L; + + private SQLName name; + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, name); + } + visitor.endVisit(this); + } + + public SQLName getName() { + return name; + } + + public void setName(SQLName name) { + this.name = name; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index b1fa2ac68b..84d52d4b47 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -72,6 +72,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateProcedureStatem import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDatabaseLinkStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement; @@ -444,6 +445,18 @@ public class OracleStatementParser extends SQLStatementParser { continue; } } + + if (identifierEquals("SEQUENCE")) { + lexer.nextToken(); + + SQLName name = this.exprParser.name(); + + OracleDropSequenceStatement stmt = new OracleDropSequenceStatement(); + stmt.setName(name); + + statementList.add(stmt); + continue; + } throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java index 7246799850..ddcecf2a1a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java @@ -80,6 +80,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStateme import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDatabaseLinkStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement; @@ -626,5 +627,8 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleDataTypeIntervalDay x); void endVisit(OracleDataTypeIntervalDay x); - + + boolean visit(OracleDropSequenceStatement x); + + void endVisit(OracleDropSequenceStatement x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java index afe2ae8df9..32152d5101 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java @@ -88,6 +88,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStateme import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDatabaseLinkStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement; @@ -1426,4 +1427,14 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora } + @Override + public boolean visit(OracleDropSequenceStatement x) { + return true; + } + + @Override + public void endVisit(OracleDropSequenceStatement x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index 89c151d483..e004e07df2 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -117,6 +117,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStateme import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDatabaseLinkStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement; @@ -3175,7 +3176,18 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS @Override public void endVisit(OracleDataTypeIntervalDay x) { - // TODO Auto-generated method stub + + } + + @Override + public boolean visit(OracleDropSequenceStatement x) { + print("DROP SEQUENCE "); + x.getName().accept(this); + return false; + } + + @Override + public void endVisit(OracleDropSequenceStatement x) { } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java index 9daabe6a55..da30c98718 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java @@ -109,6 +109,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateSequenceStateme import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropDatabaseLinkStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDropSequenceStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExceptionStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleExplainStatement; @@ -1855,4 +1856,14 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle public void endVisit(OracleDataTypeIntervalDay x) { } + + @Override + public boolean visit(OracleDropSequenceStatement x) { + return false; + } + + @Override + public void endVisit(OracleDropSequenceStatement x) { + + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleDropSequenceTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleDropSequenceTest.java new file mode 100644 index 0000000000..31f581c4ed --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleDropSequenceTest.java @@ -0,0 +1,58 @@ +/* + * 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.oracle; + +import java.util.List; + +import junit.framework.Assert; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.util.JdbcConstants; + +public class OracleDropSequenceTest extends OracleTest { + + public void test_types() throws Exception { + String sql = // + "DROP SEQUENCE oe.customers_seq"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statement = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + Assert.assertEquals("DROP SEQUENCE oe.customers_seq", SQLUtils.toSQLString(statement, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + statement.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(0, visitor.getTables().size()); + + Assert.assertEquals(0, visitor.getColumns().size()); + } +} From 9ad2d6ad13c2dbe4a9de3f3f3955456489a36778 Mon Sep 17 00:00:00 2001 From: wenshao Date: Mon, 7 Jan 2013 07:46:41 +0800 Subject: [PATCH 5/8] improve sql parser, sql server support with --- .../sqlserver/parser/SQLServerLexer.java | 94 +++++++++++++++ .../parser/SQLServerSelectParser.java | 2 + .../parser/SQLServerStatementParser.java | 12 ++ .../druid/sql/parser/SQLStatementParser.java | 7 +- .../sql/sqlserver/SQLServerSelectTest6.java | 62 ++++++++++ .../sql/sqlserver/SQLServerSelectTest7.java | 109 ++++++++++++++++++ 6 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java 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 1d03d19c36..4cc3ad1a8a 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 @@ -15,11 +15,14 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import static com.alibaba.druid.sql.parser.LayoutCharacters.EOI; + import java.util.HashMap; import java.util.Map; import com.alibaba.druid.sql.parser.Keywords; import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.NotAllowCommentException; import com.alibaba.druid.sql.parser.Token; public class SQLServerLexer extends Lexer { @@ -48,4 +51,95 @@ public class SQLServerLexer extends Lexer { super(input); super.keywods = DEFAULT_SQL_SERVER_KEYWORDS; } + + public void scanComment() { + if (ch != '/' && ch != '-') { + throw new IllegalStateException(); + } + + mark = pos; + bufPos = 0; + scanChar(); + + // /*+ */ + if (ch == '*') { + scanChar(); + bufPos++; + + while (ch == ' ') { + scanChar(); + bufPos++; + } + + boolean isHint = false; + int startHintSp = bufPos + 1; + if (ch == '!') { + isHint = true; + scanChar(); + bufPos++; + } + + for (;;) { + if (ch == '*' && charAt(pos + 1) == '/') { + bufPos += 2; + scanChar(); + scanChar(); + break; + } + + scanChar(); + bufPos++; + } + + if (isHint) { + stringVal = subString(mark + startHintSp, (bufPos - startHintSp) - 1); + token = Token.HINT; + } else { + stringVal = subString(mark, bufPos); + token = Token.MULTI_LINE_COMMENT; + } + + if (token != Token.HINT && !isAllowComment()) { + throw new NotAllowCommentException(); + } + + return; + } + + if (!isAllowComment()) { + throw new NotAllowCommentException(); + } + + if (ch == '/' || ch == '-') { + scanChar(); + bufPos++; + + for (;;) { + if (ch == '\r') { + if (charAt(pos + 1) == '\n') { + bufPos += 2; + scanChar(); + break; + } + bufPos++; + break; + } else if (ch == EOI) { + break; + } + + if (ch == '\n') { + scanChar(); + bufPos++; + break; + } + + scanChar(); + bufPos++; + } + + stringVal = subString(mark + 1, bufPos); + token = Token.LINE_COMMENT; + return; + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java index 6fae0bb0ad..19908b74e4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerSelectParser.java @@ -37,6 +37,8 @@ public class SQLServerSelectParser extends SQLSelectParser { public SQLSelect select() { SQLServerSelect select = new SQLServerSelect(); + + withSubquery(select); select.setQuery(query()); select.setOrderBy(parseOrderBy()); 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 b29aa7d642..6b196127ec 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 @@ -15,6 +15,8 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import java.util.List; + import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; @@ -40,6 +42,16 @@ public class SQLServerStatementParser extends SQLStatementParser { super(new SQLServerExprParser(lexer)); } + public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.WITH) { + SQLStatement stmt = parseSelect(); + statementList.add(stmt); + return true; + } + + return false; + } + public SQLStatement parseInsert() { SQLServerInsertStatement insertStatement = new SQLServerInsertStatement(); 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 4837ed096d..a41e0ebd8b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -38,6 +38,7 @@ import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLReleaseSavePointStatement; import com.alibaba.druid.sql.ast.statement.SQLRollbackStatement; import com.alibaba.druid.sql.ast.statement.SQLSavePointStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLSetStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; @@ -80,7 +81,7 @@ public class SQLStatementParser extends SQLParser { continue; } - if (lexer.token() == (Token.SELECT)) { + if (lexer.token() == Token.SELECT) { statementList.add(parseSelect()); continue; } @@ -545,7 +546,9 @@ public class SQLStatementParser extends SQLParser { } public SQLSelectStatement parseSelect() { - return new SQLSelectStatement(createSQLSelectParser().select()); + SQLSelectParser selectParser = createSQLSelectParser(); + SQLSelect select = selectParser.select(); + return new SQLSelectStatement(select); } public SQLSelectParser createSQLSelectParser() { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java new file mode 100644 index 0000000000..270091be86 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest6.java @@ -0,0 +1,62 @@ +/* + * 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.sqlserver; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerSelectTest6 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "WITH DirReps(ManagerID, DirectReports) AS " + // + "(" + // + " SELECT ManagerID, COUNT(*) " + // + " FROM HumanResources.Employee AS e" + // + " WHERE ManagerID IS NOT NULL" + // + " GROUP BY ManagerID" + // + ")" + // + "SELECT ManagerID, DirectReports " + // + "FROM DirReps " + // + "ORDER BY ManagerID;"; + + String expect = "WITH" + + "\n\tDirReps (ManagerID, DirectReports)" + + "\n\tAS" + + "\n\t(" + + "\n\t\tSELECT ManagerID, COUNT(*)" + + "\n\t\tFROM HumanResources.Employee e" + + "\n\t\tWHERE ManagerID IS NOT NULL" + + "\n\t\tGROUP BY ManagerID" + + "\n\t)" + + "\nSELECT ManagerID, DirectReports" + + "\nFROM DirReps" + + "\nORDER BY ManagerID"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java new file mode 100644 index 0000000000..bd30f98742 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerSelectTest7.java @@ -0,0 +1,109 @@ +/* + * 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.sqlserver; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerSelectTest7 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "with menu_view as(" + + "\n select t.*,1 level from sec_portal_menu t where t.parent_id = ?" + + "\n union all" + + "\n select t.*,level + 1 from sec_portal_menu t , menu_view x where t.parent_id = x.menu_id" + + "\n )" + + "\n select t.menu_id \"id\"," + + "\n t.menu_name \"name\"," + + "\n t.parent_id \"pId\"," + + "\n case t.level when 1 then 'true' else 'false' end \"open\"," + + "\n t.link_type \"linkType\"" + + "\n from menu_view t" + + "\n where 1=1" + + "\n and t.deleted = 0" + + "\n --菜单权限控制" + + "\n AND t.link_type in ('simple','link')" + + "\n AND (" + + "\n EXISTS (" + + "\n select p.entity_code from sec_role_auth p where p.entity_code = t.menu_id" + + "\n and p.entity_type = 'menu'" + + "\n and p.role_id in (" + + "\n select r.role_code from sec_role_member rm ,sec_role r where rm.entity_type = 'user'" + + "\n and entity_code = ? --用户ID" + + "\n and r.role_id = rm.role_id" + + "\n and r.enabled = 1" + + "\n and r.deleted = 0" + + "\n )" + + "\n )" + + "\n or '1'= ? --超级管理员账户id" + + "\n or t.need_control = 0" + + "\n )" + + "\n AND (" + + "\n t.enabled = 1 or '1'= ? --超级管理员账户id" + + "\n or t.need_control = 0" + + "\n )" + + "\n order by t.sort_order"; + + String expect = "WITH" + + "\n\tmenu_view" + + "\n\tAS" + + "\n\t(" + + "\n\t\tSELECT t.*, 1 AS level" + + "\n\t\tFROM sec_portal_menu t" + + "\n\t\tWHERE t.parent_id = ?" + + "\n\t\tUNION ALL" + + "\n\t\tSELECT t.*, level + 1" + + "\n\t\tFROM sec_portal_menu t, menu_view x" + + "\n\t\tWHERE t.parent_id = x.menu_id" + + "\n\t)" + + "\nSELECT t.menu_id AS \"id\", t.menu_name AS \"name\", t.parent_id AS \"pId\", CASE t.level WHEN 1 THEN 'true' ELSE 'false' END AS \"open\", t.link_type AS \"linkType\"" + + "\nFROM menu_view t" + + "\nWHERE 1 = 1" + + "\n\tAND t.deleted = 0" + + "\n\tAND t.link_type IN ('simple', 'link')" + + "\n\tAND (EXISTS (SELECT p.entity_code" + + "\n\t\t\tFROM sec_role_auth p" + + "\n\t\t\tWHERE p.entity_code = t.menu_id" + + "\n\t\t\t\tAND p.entity_type = 'menu'" + + "\n\t\t\t\tAND p.role_id IN (SELECT r.role_code" + + "\n\t\t\t\t\tFROM sec_role_member rm, sec_role r" + + "\n\t\t\t\t\tWHERE rm.entity_type = 'user'" + + "\n\t\t\t\t\t\tAND entity_code = ?" + + "\n\t\t\t\t\t\tAND r.role_id = rm.role_id" + + "\n\t\t\t\t\t\tAND r.enabled = 1" + + "\n\t\t\t\t\t\tAND r.deleted = 0))" + + "\n\t\tOR '1' = ?" + + "\n\t\tOR t.need_control = 0)" + + "\n\tAND (t.enabled = 1" + + "\n\t\tOR '1' = ?" + + "\n\t\tOR t.need_control = 0)" + + "\nORDER BY t.sort_order"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +} From 304285c9f824754fabe876a6be052ea626bcfaa5 Mon Sep 17 00:00:00 2001 From: wenshao Date: Fri, 11 Jan 2013 20:22:36 +0800 Subject: [PATCH 6/8] add testcase --- .../bvt/filter/wall/SQLServerWallTest1.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java new file mode 100644 index 0000000000..6b2e1f6d29 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java @@ -0,0 +1,61 @@ +/* + * 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.filter.wall; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.wall.WallUtils; + +/** + * SQLServerWallTest + * + * @author RaymondXiu + * @version 1.0, 2012-3-18 + * @see + */ +public class SQLServerWallTest1 extends TestCase { + + /** + * @param name + */ + public SQLServerWallTest1(String name){ + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void test_stuff() throws Exception { + Assert.assertTrue(WallUtils.isValidateSqlServer( // + "select DATE_FORMAT(staydate,'%m月') as month,sum(a) as addnum,sum(q) as quitnum " + // + " from (" + // + " select staydate,1 as a,0 as q " + // + " from add_person" + // + " union all" + // + " select quitdate,0 as a,1 as q" + // + " from quit_person" + // + " ) t " + // + "where DATE_FORMAT(staydate,'%Y')= 2012 " + // + "group by DATE_FORMAT(staydate,'%Y-%m')")); + } +} From 60ea70b09de053d48948bd037ab0aab95f460e66 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 12 Jan 2013 14:59:01 +0800 Subject: [PATCH 7/8] bug fixed for WallFilter --- .../mysql/ast/statement/MySqlUnionQuery.java | 3 ++- .../druid/wall/spi/MySqlWallVisitor.java | 8 ++++++++ ...ServerWallTest1.java => MySqlWallTest1.java} | 17 ++++------------- 3 files changed, 14 insertions(+), 14 deletions(-) rename src/test/java/com/alibaba/druid/bvt/filter/wall/{SQLServerWallTest1.java => MySqlWallTest1.java} (59%) diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java index 94185435dd..e2eded664c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/statement/MySqlUnionQuery.java @@ -16,11 +16,12 @@ package com.alibaba.druid.sql.dialect.mysql.ast.statement; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlObject; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; -public class MySqlUnionQuery extends SQLUnionQuery implements MySqlStatement { +public class MySqlUnionQuery extends SQLUnionQuery implements MySqlObject { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java index 2df999daac..8a50af9d04 100644 --- a/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java +++ b/src/main/java/com/alibaba/druid/wall/spi/MySqlWallVisitor.java @@ -46,6 +46,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectGroupBy; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUnionQuery; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; @@ -222,6 +223,13 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi return true; } + + @Override + public boolean visit(MySqlUnionQuery x) { + WallVisitorUtils.checkUnion(this, x); + + return true; + } @Override public String toSQL(SQLObject obj) { diff --git a/src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest1.java similarity index 59% rename from src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java rename to src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest1.java index 6b2e1f6d29..7854aeedf6 100644 --- a/src/test/java/com/alibaba/druid/bvt/filter/wall/SQLServerWallTest1.java +++ b/src/test/java/com/alibaba/druid/bvt/filter/wall/MySqlWallTest1.java @@ -28,12 +28,12 @@ import com.alibaba.druid.wall.WallUtils; * @version 1.0, 2012-3-18 * @see */ -public class SQLServerWallTest1 extends TestCase { +public class MySqlWallTest1 extends TestCase { /** * @param name */ - public SQLServerWallTest1(String name){ + public MySqlWallTest1(String name){ super(name); } @@ -46,16 +46,7 @@ public class SQLServerWallTest1 extends TestCase { } public void test_stuff() throws Exception { - Assert.assertTrue(WallUtils.isValidateSqlServer( // - "select DATE_FORMAT(staydate,'%m月') as month,sum(a) as addnum,sum(q) as quitnum " + // - " from (" + // - " select staydate,1 as a,0 as q " + // - " from add_person" + // - " union all" + // - " select quitdate,0 as a,1 as q" + // - " from quit_person" + // - " ) t " + // - "where DATE_FORMAT(staydate,'%Y')= 2012 " + // - "group by DATE_FORMAT(staydate,'%Y-%m')")); + Assert.assertTrue(WallUtils.isValidateMySql(// + "select count(*) from (select DATE_FORMAT(staydate,'%m月') as month,sum(a) as addnum,sum(q) as quitnum from (select staydate,1 as a,0 as q from add_person union all select quitdate,0 as a,1 as q from quit_person) t where (DATE_FORMAT(staydate,'%Y')= ? ) group by DATE_FORMAT(staydate,'%Y-%m'))")); } } From b503104bec49a81a70a468fcdde1244d93ebf771 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 12 Jan 2013 16:03:15 +0800 Subject: [PATCH 8/8] sql parser improve sql server support --- .../mysql/parser/MySqlStatementParser.java | 54 +++++++++------- .../oracle/parser/OracleUpdateParser.java | 28 +-------- .../parser/PGSQLStatementParser.java | 18 +----- .../sqlserver/ast/SQLServerStatement.java | 2 +- .../ast/stmt/SQLServerUpdateStatement.java | 55 ++++++++++++++++ .../parser/SQLServerStatementParser.java | 34 ++++++++++ .../visitor/SQLServerASTVisitor.java | 5 ++ .../visitor/SQLServerASTVisitorAdapter.java | 11 ++++ .../visitor/SQLServerOutputVisitor.java | 37 +++++++++++ .../visitor/SQLServerSchemaStatVisitor.java | 29 +++++++++ .../druid/sql/parser/SQLStatementParser.java | 46 +++++++++----- .../sql/sqlserver/SQLServerUpdateTest.java | 62 +++++++++++++++++++ 12 files changed, 298 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerUpdateStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerUpdateTest.java 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 46ee5bb6d6..cc547f2e84 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 @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLListExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; @@ -199,27 +200,7 @@ public class MySqlStatementParser extends SQLStatementParser { stmt.setTableSource(tableSource); } - accept(Token.SET); - - for (;;) { - SQLUpdateSetItem item = new SQLUpdateSetItem(); - item.setColumn(this.exprParser.name()); - if (lexer.token() == Token.EQ) { - lexer.nextToken(); - } else { - accept(Token.COLONEQ); - } - item.setValue(this.exprParser.expr()); - - stmt.getItems().add(item); - - if (lexer.token() == (Token.COMMA)) { - lexer.nextToken(); - continue; - } - - break; - } + parseUpdateSet(stmt); if (lexer.token() == (Token.WHERE)) { lexer.nextToken(); @@ -2361,4 +2342,35 @@ public class MySqlStatementParser extends SQLStatementParser { return stmt; } + + protected void parseUpdateSet(SQLUpdateStatement update) { + accept(Token.SET); + + for (;;) { + SQLUpdateSetItem item = new SQLUpdateSetItem(); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + SQLListExpr list = new SQLListExpr(); + this.exprParser.exprList(list.getItems()); + accept(Token.RPAREN); + item.setColumn(list); + } else { + item.setColumn(this.exprParser.primary()); + } + if (lexer.token() == Token.COLONEQ) { + lexer.nextToken(); + } else { + accept(Token.EQ); + } + item.setValue(this.exprParser.expr()); + update.getItems().add(item); + + if (lexer.token() != Token.COMMA) { + break; + } + + lexer.nextToken(); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java index 2859c2801e..dcb6548b65 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleUpdateParser.java @@ -55,7 +55,7 @@ public class OracleUpdateParser extends SQLStatementParser { } } - parseSet(update); + parseUpdateSet(update); parseWhere(update); @@ -117,30 +117,4 @@ public class OracleUpdateParser extends SQLStatementParser { } } - private void parseSet(OracleUpdateStatement update) { - accept(Token.SET); - - for (;;) { - SQLUpdateSetItem item = new SQLUpdateSetItem(); - - if (lexer.token() == (Token.LPAREN)) { - lexer.nextToken(); - SQLListExpr list = new SQLListExpr(); - this.exprParser.exprList(list.getItems()); - accept(Token.RPAREN); - item.setColumn(list); - } else { - item.setColumn(this.exprParser.primary()); - } - accept(Token.EQ); - item.setValue(this.exprParser.expr()); - update.getItems().add(item); - - if (lexer.token() != Token.COMMA) { - break; - } - - lexer.nextToken(); - } - } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java index c0c3cd4c47..7d1a4c033b 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGSQLStatementParser.java @@ -60,23 +60,7 @@ public class PGSQLStatementParser extends SQLStatementParser { SQLTableSource tableSource = this.exprParser.createSelectParser().parseTableSource(); udpateStatement.setTableSource(tableSource); - accept(Token.SET); - - for (;;) { - SQLUpdateSetItem item = new SQLUpdateSetItem(); - item.setColumn(this.exprParser.name()); - accept(Token.EQ); - item.setValue(this.exprParser.expr()); - - udpateStatement.getItems().add(item); - - if (lexer.token() == (Token.COMMA)) { - lexer.nextToken(); - continue; - } - - break; - } + parseUpdateSet(udpateStatement); if (lexer.token() == (Token.WHERE)) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerStatement.java index ede91d3fb0..12e1962b6c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/SQLServerStatement.java @@ -17,6 +17,6 @@ package com.alibaba.druid.sql.dialect.sqlserver.ast; import com.alibaba.druid.sql.ast.SQLStatement; -public interface SQLServerStatement extends SQLStatement { +public interface SQLServerStatement extends SQLStatement, SQLServerObject { } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerUpdateStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerUpdateStatement.java new file mode 100644 index 0000000000..7fa22aea22 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/stmt/SQLServerUpdateStatement.java @@ -0,0 +1,55 @@ +/* + * 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.sqlserver.ast.stmt; + +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerStatement; +import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class SQLServerUpdateStatement extends SQLUpdateStatement implements SQLServerStatement { + private static final long serialVersionUID = 1L; + + private SQLTableSource from; + + + public SQLTableSource getFrom() { + return from; + } + + + public void setFrom(SQLTableSource from) { + this.from = from; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((SQLServerASTVisitor) visitor); + } + + @Override + public void accept0(SQLServerASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, tableSource); + acceptChild(visitor, items); + acceptChild(visitor, from); + acceptChild(visitor, where); + } + visitor.endVisit(this); + } + +} 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 6b196127ec..95671d475b 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 @@ -22,7 +22,11 @@ import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLInsertInto; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +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.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.SQLStatementParser; @@ -112,4 +116,34 @@ public class SQLServerStatementParser extends SQLStatementParser { insertStatement.setQuery(queryExpr.getSubQuery()); } } + + protected SQLServerUpdateStatement createUpdateStatement() { + return new SQLServerUpdateStatement(); + } + + public SQLUpdateStatement parseUpdateStatement() { + SQLServerUpdateStatement udpateStatement = createUpdateStatement(); + + if (lexer.token() == Token.UPDATE) { + lexer.nextToken(); + + SQLTableSource tableSource = this.exprParser.createSelectParser().parseTableSource(); + udpateStatement.setTableSource(tableSource); + } + + parseUpdateSet(udpateStatement); + + if (lexer.token() == Token.FROM) { + lexer.nextToken(); + SQLTableSource from = this.exprParser.createSelectParser().parseTableSource(); + udpateStatement.setFrom(from); + } + + if (lexer.token() == (Token.WHERE)) { + lexer.nextToken(); + udpateStatement.setWhere(this.exprParser.expr()); + } + + return udpateStatement; + } } 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 1f07cb136f..d61534be7c 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 @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface SQLServerASTVisitor extends SQLASTVisitor { @@ -38,4 +39,8 @@ public interface SQLServerASTVisitor extends SQLASTVisitor { boolean visit(SQLServerInsertStatement x); void endVisit(SQLServerInsertStatement x); + + boolean visit(SQLServerUpdateStatement x); + + void endVisit(SQLServerUpdateStatement 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 699a8abbb6..f26fe7b0be 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 @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class SQLServerASTVisitorAdapter extends SQLASTVisitorAdapter implements SQLServerASTVisitor { @@ -63,4 +64,14 @@ public class SQLServerASTVisitorAdapter extends SQLASTVisitorAdapter implements } + @Override + public boolean visit(SQLServerUpdateStatement x) { + return true; + } + + @Override + public void endVisit(SQLServerUpdateStatement 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 5f6bf013ad..a0169e8320 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 @@ -20,6 +20,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLServerASTVisitor { @@ -144,4 +145,40 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe } + @Override + public boolean visit(SQLServerUpdateStatement x) { + print("UPDATE "); + + x.getTableSource().accept(this); + + println(); + print("SET "); + for (int i = 0, size = x.getItems().size(); i < size; ++i) { + if (i != 0) { + print(", "); + } + x.getItems().get(i).accept(this); + } + + if (x.getFrom() != null) { + println(); + print("FROM "); + x.getFrom().setParent(x); + x.getFrom().accept(this); + } + + if (x.getWhere() != null) { + println(); + print("WHERE "); + x.getWhere().setParent(x); + x.getWhere().accept(this); + } + + return false; + } + + @Override + public void endVisit(SQLServerUpdateStatement 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 a56c5c5ecb..93069d1f32 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 @@ -15,13 +15,17 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.visitor; +import java.util.Map; + import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock; import com.alibaba.druid.sql.dialect.sqlserver.ast.Top; import com.alibaba.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr; import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; import com.alibaba.druid.util.JdbcUtils; @@ -73,5 +77,30 @@ public class SQLServerSchemaStatVisitor extends SchemaStatVisitor implements SQL this.endVisit((SQLInsertStatement) x); } + @Override + public boolean visit(SQLServerUpdateStatement x) { + setAliasMap(); + + String ident = x.getTableName().toString(); + setCurrentTable(ident); + + TableStat stat = getTableStat(ident); + stat.incrementUpdateCount(); + + Map aliasMap = getAliasMap(); + aliasMap.put(ident, ident); + + accept(x.getItems()); + accept(x.getFrom()); + accept(x.getWhere()); + + return false; + } + + @Override + public void endVisit(SQLServerUpdateStatement 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 a41e0ebd8b..779e9888ff 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -21,6 +21,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLListExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLAssignItem; import com.alibaba.druid.sql.ast.statement.SQLCallStatement; @@ -565,23 +566,7 @@ public class SQLStatementParser extends SQLParser { udpateStatement.setTableSource(tableSource); } - accept(Token.SET); - - for (;;) { - SQLUpdateSetItem item = new SQLUpdateSetItem(); - item.setColumn(this.exprParser.name()); - accept(Token.EQ); - item.setValue(this.exprParser.expr()); - - udpateStatement.getItems().add(item); - - if (lexer.token() == (Token.COMMA)) { - lexer.nextToken(); - continue; - } - - break; - } + parseUpdateSet(udpateStatement); if (lexer.token() == (Token.WHERE)) { lexer.nextToken(); @@ -590,6 +575,33 @@ public class SQLStatementParser extends SQLParser { return udpateStatement; } + + protected void parseUpdateSet(SQLUpdateStatement update) { + accept(Token.SET); + + for (;;) { + SQLUpdateSetItem item = new SQLUpdateSetItem(); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + SQLListExpr list = new SQLListExpr(); + this.exprParser.exprList(list.getItems()); + accept(Token.RPAREN); + item.setColumn(list); + } else { + item.setColumn(this.exprParser.primary()); + } + accept(Token.EQ); + item.setValue(this.exprParser.expr()); + update.getItems().add(item); + + if (lexer.token() != Token.COMMA) { + break; + } + + lexer.nextToken(); + } + } protected SQLUpdateStatement createUpdateStatement() { return new SQLUpdateStatement(); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerUpdateTest.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerUpdateTest.java new file mode 100644 index 0000000000..2d72014b41 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerUpdateTest.java @@ -0,0 +1,62 @@ +/* + * 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.sqlserver; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerUpdateTest extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "update reg_student_charge_item " + // + "set FAmountReceived = b.amount " + // + "from reg_student_charge_item a" + // + " ,(" + // + " select a.FId, " + // + " case when sum(b.FChargeAmount) is null then 0 " + // + " else sum(b.FChargeAmount)" + // + " end as amount " + // + " from reg_student_charge_item a " + // + " left join reg_student_charge_daybook b on a.FId = b.FChargeItemId" + // + " where a.FId=? group by a.FId" + // + " ) b " + // + "where a.FId = b.FId and a.FId = ?"; + + String expect = "UPDATE reg_student_charge_item" + + "\nSET FAmountReceived = b.amount" + + "\nFROM reg_student_charge_item a, (SELECT a.FId, CASE WHEN SUM(b.FChargeAmount) IS NULL THEN 0 ELSE SUM(b.FChargeAmount) END AS amount" + + "\n\t\tFROM reg_student_charge_item a" + + "\n\t\t\tLEFT JOIN reg_student_charge_daybook b ON a.FId = b.FChargeItemId" + + "\n\t\tWHERE a.FId = ? GROUP BY a.FId" + + "\n\t\t) b" + + "\nWHERE a.FId = b.FId" + + "\nAND a.FId = ?"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}