diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java index fc635e67be..8e91c3144c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java @@ -96,16 +96,20 @@ public class OracleMultiInsertStatement extends OracleStatementImpl { @Override public void accept0(OracleASTVisitor visitor) { - // TODO Auto-generated method stub - + if (visitor.visit(this)) { + acceptChild(visitor, items); + acceptChild(visitor, elseItem); + } + visitor.endVisit(this); } } - public static class ConditionalInsertClauseItem { + public static class ConditionalInsertClauseItem extends OracleSQLObjectImpl { - private SQLExpr when; - private InsertIntoClause then; + private static final long serialVersionUID = 1L; + private SQLExpr when; + private InsertIntoClause then; public SQLExpr getWhen() { return when; @@ -123,6 +127,15 @@ public class OracleMultiInsertStatement extends OracleStatementImpl { this.then = then; } + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, when); + acceptChild(visitor, then); + } + visitor.endVisit(this); + } + } public static class InsertIntoClause extends SQLInsertInto implements OracleSQLObject, Entry { 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 acd1ae3aeb..51248f9426 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 @@ -260,7 +260,7 @@ public class OracleStatementParser extends SQLStatementParser { lexer.nextToken(); stmt.setOption(OracleMultiInsertStatement.Option.FIRST); } - + while (lexer.token() == Token.INTO) { OracleMultiInsertStatement.InsertIntoClause clause = new OracleMultiInsertStatement.InsertIntoClause(); @@ -268,17 +268,39 @@ public class OracleStatementParser extends SQLStatementParser { clause.setReturning(parseReturningClause()); clause.setErrorLogging(parseErrorLoggingClause()); - + stmt.getEntries().add(clause); } - + if (lexer.token() == Token.WHEN) { - throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + OracleMultiInsertStatement.ConditionalInsertClause clause = new OracleMultiInsertStatement.ConditionalInsertClause(); + + while (lexer.token() == Token.WHEN) { + lexer.nextToken(); + + OracleMultiInsertStatement.ConditionalInsertClauseItem item = new OracleMultiInsertStatement.ConditionalInsertClauseItem(); + + item.setWhen(this.createExprParser().expr()); + accept(Token.THEN); + OracleMultiInsertStatement.InsertIntoClause insertInto = new OracleMultiInsertStatement.InsertIntoClause(); + parseInsert0(insertInto); + item.setThen(insertInto); + + clause.getItems().add(item); + } + + if (lexer.token() == Token.ELSE) { + lexer.nextToken(); + + OracleMultiInsertStatement.InsertIntoClause insertInto = new OracleMultiInsertStatement.InsertIntoClause(); + parseInsert0(insertInto, false); + clause.setElseItem(insertInto); + } } SQLSelect subQuery = this.createSQLSelectParser().select(); stmt.setSubQuery(subQuery); - + return stmt; } 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 6a85a6d8c9..6a67a14d59 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 @@ -43,6 +43,8 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeInsertClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeUpdateClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClause; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClauseItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.InsertIntoClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; @@ -735,5 +737,25 @@ public class OracleASTVIsitorAdapter extends SQLASTVisitorAdapter implements Ora public void endVisit(OracleMultiInsertStatement x) { } + + @Override + public boolean visit(ConditionalInsertClause x) { + return true; + } + + @Override + public void endVisit(ConditionalInsertClause x) { + + } + + @Override + public boolean visit(ConditionalInsertClauseItem x) { + return true; + } + + @Override + public void endVisit(ConditionalInsertClauseItem x) { + + } } 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 6ebd56de77..2232de37e5 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 @@ -47,6 +47,8 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleDeleteStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClause; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClauseItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.InsertIntoClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; @@ -320,4 +322,12 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleMultiInsertStatement x); void endVisit(OracleMultiInsertStatement x); + + boolean visit(ConditionalInsertClause x); + + void endVisit(ConditionalInsertClause x); + + boolean visit(ConditionalInsertClauseItem x); + + void endVisit(ConditionalInsertClauseItem 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 a2e66fab9f..f97651eb71 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 @@ -75,6 +75,8 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeInsertClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeUpdateClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClause; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClauseItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.InsertIntoClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; @@ -1543,7 +1545,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getEntries().get(i).accept(this); decrementIndent(); } - + println(); x.getSubQuery().accept(this); @@ -1554,4 +1556,50 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public void endVisit(OracleMultiInsertStatement x) { } + + @Override + public boolean visit(ConditionalInsertClause x) { + for (int i = 0, size = x.getItems().size(); i < size; ++i) { + if (i != 0) { + println(); + } + + ConditionalInsertClauseItem item = x.getItems().get(i); + + item.accept(this); + } + + if (x.getElseItem() != null) { + println(); + print("ELSE"); + incrementIndent(); + println(); + x.getElseItem().accept(this); + decrementIndent(); + } + + return false; + } + + @Override + public void endVisit(ConditionalInsertClause x) { + + } + + @Override + public boolean visit(ConditionalInsertClauseItem x) { + print("WHEN "); + x.getWhen().accept(this); + print(" THEN"); + incrementIndent(); + println(); + x.getThen().accept(this); + decrementIndent(); + return false; + } + + @Override + public void endVisit(ConditionalInsertClauseItem 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 792b7d2992..78501db171 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 @@ -5,6 +5,7 @@ import java.util.Map; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; @@ -56,6 +57,8 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeInsertClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.MergeUpdateClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClause; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.ConditionalInsertClauseItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement.InsertIntoClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; @@ -912,7 +915,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public boolean visit(InsertIntoClause x) { String originalTable = currentTableLocal.get(); - + if (x.getTableName() instanceof SQLName) { String ident = ((SQLName) x.getTableName()).toString(); currentTableLocal.set(ident); @@ -933,12 +936,12 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle aliasMap.put(ident, ident); } } - + accept(x.getColumns()); accept(x.getQuery()); accept(x.getReturning()); accept(x.getErrorLogging()); - + return false; } @@ -953,13 +956,13 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle modeLocal.set(Mode.Insert); aliasLocal.set(new HashMap()); - + accept(x.getSubQuery()); - + for (OracleMultiInsertStatement.Entry entry : x.getEntries()) { entry.setParent(x); } - + accept(x.getEntries()); return false; @@ -968,5 +971,47 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public void endVisit(OracleMultiInsertStatement x) { + } + + @Override + public boolean visit(ConditionalInsertClause x) { + for (ConditionalInsertClauseItem item : x.getItems()) { + item.setParent(x); + } + if (x.getElseItem() != null) { + x.getElseItem().setParent(x); + } + return true; + } + + @Override + public void endVisit(ConditionalInsertClause x) { + + } + + @Override + public boolean visit(ConditionalInsertClauseItem x) { + String originalTable = currentTableLocal.get(); + SQLObject parent = x.getParent(); + if (parent instanceof ConditionalInsertClause) { + parent = parent.getParent(); + } + if (parent instanceof OracleMultiInsertStatement) { + SQLSelect subQuery = ((OracleMultiInsertStatement) parent).getSubQuery(); + if (subQuery != null) { + String table = (String) subQuery.getAttribute("_table_"); + currentTableLocal.set(table); + } + } + x.getWhen().accept(this); + x.getThen().accept(this); + currentTableLocal.set(originalTable); + return false; + } + + @Override + public void endVisit(ConditionalInsertClauseItem 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 e6497c4490..eeb4fb76f4 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -168,8 +168,12 @@ public class SQLStatementParser extends SQLParser { parseInsert0(insertStatement); return insertStatement; } - + protected void parseInsert0(SQLInsertInto insertStatement) { + parseInsert0(insertStatement, true); + } + + protected void parseInsert0(SQLInsertInto insertStatement, boolean acceptSubQuery) { accept(Token.INTO); SQLName tableName = this.exprParser.name(); @@ -193,7 +197,7 @@ public class SQLStatementParser extends SQLParser { this.exprParser.exprList(values.getValues()); insertStatement.setValues(values); accept(Token.RPAREN); - } else if (lexer.token() == Token.SELECT || lexer.token() == Token.LPAREN) { + } else if (acceptSubQuery && (lexer.token() == Token.SELECT || lexer.token() == Token.LPAREN)) { SQLQueryExpr queryExpr = (SQLQueryExpr) this.createExprParser().expr(); insertStatement.setQuery(queryExpr.getSubQuery()); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest2.java new file mode 100644 index 0000000000..8730a2d3e6 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest2.java @@ -0,0 +1,56 @@ +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.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; + +public class OracleMultiInsertTest2 extends OracleTest { + + public void test_0() throws Exception { + String sql = "INSERT ALL" + // + " WHEN order_total < 100000 THEN" + // + " INTO small_orders" + // + " WHEN order_total > 100000 AND order_total < 200000 THEN" + // + " INTO medium_orders" + // + " ELSE" + // + " INTO large_orders" + // + " SELECT order_id, order_total, sales_rep_id, customer_id" + // + " FROM orders;"; // + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + System.out.println(output(statementList)); + + Assert.assertEquals(1, statementList.size()); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + statemen.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()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("orders"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("small_orders"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("medium_orders"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("large_orders"))); + + Assert.assertEquals(4, visitor.getTables().size()); + Assert.assertEquals(4, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("orders", "order_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("orders", "order_total"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("orders", "sales_rep_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("orders", "customer_id"))); + + } + +}