From 6160de7e60f5f165143060c247ece6172eb34c0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Sat, 25 Feb 2012 17:03:35 +0800 Subject: [PATCH] oracle sql parser support multi insert --- .../sql/ast/statement/SQLInsertInto.java | 60 ++++++ .../sql/ast/statement/SQLInsertStatement.java | 49 +---- .../ast/stmt/OracleMultiInsertStatement.java | 176 +++++++++++++++++- .../oracle/parser/OracleStatementParser.java | 71 ++++++- .../visitor/OracleASTVIsitorAdapter.java | 44 +++++ .../oracle/visitor/OracleASTVisitor.java | 20 ++ .../oracle/visitor/OracleOutputVisitor.java | 173 ++++++++++++++--- .../visitor/OracleSchemaStatVisitor.java | 131 +++++++++++-- .../druid/sql/parser/SQLStatementParser.java | 12 +- .../druid/sql/visitor/SchemaStatVisitor.java | 4 + .../bvt/sql/oracle/OracleMultiInsertTest.java | 73 ++++++++ .../sql/oracle/OracleMultiInsertTest2.java | 56 ++++++ 12 files changed, 776 insertions(+), 93 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest2.java diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java new file mode 100644 index 0000000000..43c4ce6fe6 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java @@ -0,0 +1,60 @@ +package com.alibaba.druid.sql.ast.statement; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObjectImpl; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; + +public abstract class SQLInsertInto extends SQLObjectImpl { + + private static final long serialVersionUID = 1L; + protected SQLName tableName; + protected String alias; + + protected final List columns = new ArrayList(); + protected ValuesClause values; + protected SQLSelect query; + + public SQLInsertInto(){ + + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public SQLName getTableName() { + return tableName; + } + + public void setTableName(SQLName tableName) { + this.tableName = tableName; + } + + public SQLSelect getQuery() { + return query; + } + + public void setQuery(SQLSelect query) { + this.query = query; + } + + public List getColumns() { + return columns; + } + + public ValuesClause getValues() { + return values; + } + + public void setValues(ValuesClause values) { + this.values = values; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertStatement.java index 40c32d74eb..64bbac3037 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertStatement.java @@ -19,61 +19,18 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLObjectImpl; -import com.alibaba.druid.sql.ast.SQLStatementImpl; +import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.visitor.SQLASTVisitor; -public class SQLInsertStatement extends SQLStatementImpl { +public class SQLInsertStatement extends SQLInsertInto implements SQLStatement { - private static final long serialVersionUID = 1L; - protected SQLName tableName; - protected String alias; - - protected final List columns = new ArrayList(); - protected ValuesClause values; - protected SQLSelect query; + private static final long serialVersionUID = 1L; public SQLInsertStatement(){ } - public String getAlias() { - return alias; - } - - public void setAlias(String alias) { - this.alias = alias; - } - - public SQLName getTableName() { - return tableName; - } - - public void setTableName(SQLName tableName) { - this.tableName = tableName; - } - - public SQLSelect getQuery() { - return query; - } - - public void setQuery(SQLSelect query) { - this.query = query; - } - - public List getColumns() { - return columns; - } - - public ValuesClause getValues() { - return values; - } - - public void setValues(ValuesClause values) { - this.values = values; - } - @Override protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { 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 aedd278d56..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 @@ -1,15 +1,187 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import java.util.ArrayList; +import java.util.List; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertInto; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleMultiInsertStatement extends OracleStatementImpl { + public static enum Option { + ALL, FIRST + } + private static final long serialVersionUID = 1L; + private SQLSelect subQuery; + private Option option; + private List entries = new ArrayList(); + private List hints = new ArrayList(); + + public List getHints() { + return hints; + } + + public void setHints(List hints) { + this.hints = hints; + } + + public List getEntries() { + return entries; + } + + public void setEntries(List entries) { + this.entries = entries; + } + + public Option getOption() { + return option; + } + + public void setOption(Option option) { + this.option = option; + } + + public SQLSelect getSubQuery() { + return subQuery; + } + + public void setSubQuery(SQLSelect subQuery) { + this.subQuery = subQuery; + } + @Override public void accept0(OracleASTVisitor visitor) { - + if (visitor.visit(this)) { + acceptChild(visitor, this.entries); + acceptChild(visitor, this.subQuery); + } + visitor.endVisit(this); } + public static interface Entry extends OracleSQLObject { + + } + + public static class ConditionalInsertClause extends OracleSQLObjectImpl implements Entry { + + private static final long serialVersionUID = 1L; + private List items = new ArrayList(); + private InsertIntoClause elseItem; + + public InsertIntoClause getElseItem() { + return elseItem; + } + + public void setElseItem(InsertIntoClause elseItem) { + this.elseItem = elseItem; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, items); + acceptChild(visitor, elseItem); + } + visitor.endVisit(this); + } + + } + + public static class ConditionalInsertClauseItem extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private SQLExpr when; + private InsertIntoClause then; + + public SQLExpr getWhen() { + return when; + } + + public void setWhen(SQLExpr when) { + this.when = when; + } + + public InsertIntoClause getThen() { + return then; + } + + public void setThen(InsertIntoClause then) { + 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 { + + private static final long serialVersionUID = 1L; + + private OracleReturningClause returning; + private OracleErrorLoggingClause errorLogging; + + public InsertIntoClause(){ + + } + + public OracleReturningClause getReturning() { + return returning; + } + + public void setReturning(OracleReturningClause returning) { + this.returning = returning; + } + + public OracleErrorLoggingClause getErrorLogging() { + return errorLogging; + } + + public void setErrorLogging(OracleErrorLoggingClause errorLogging) { + this.errorLogging = errorLogging; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((OracleASTVisitor) visitor); + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + this.acceptChild(visitor, tableName); + this.acceptChild(visitor, columns); + this.acceptChild(visitor, values); + this.acceptChild(visitor, query); + this.acceptChild(visitor, returning); + this.acceptChild(visitor, errorLogging); + } + + visitor.endVisit(this); + } + } } 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 11875f73e3..4dcad99dae 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 @@ -19,6 +19,7 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; @@ -26,6 +27,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; 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.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleStatement; import com.alibaba.druid.sql.parser.Lexer; @@ -233,14 +235,73 @@ public class OracleStatementParser extends SQLStatementParser { public OracleStatement parseInsert() { accept(Token.INSERT); - accept(Token.INTO); - OracleInsertStatement stmt = new OracleInsertStatement(); + if (lexer.token() == Token.INTO) { + OracleInsertStatement stmt = new OracleInsertStatement(); - parseInsert0(stmt); + parseInsert0(stmt); - stmt.setReturning(parseReturningClause()); - stmt.setErrorLogging(parseErrorLoggingClause()); + stmt.setReturning(parseReturningClause()); + stmt.setErrorLogging(parseErrorLoggingClause()); + + return stmt; + } + + return parseMultiInsert(); + } + + public OracleStatement parseMultiInsert() { + OracleMultiInsertStatement stmt = new OracleMultiInsertStatement(); + + if (lexer.token() == Token.ALL) { + lexer.nextToken(); + stmt.setOption(OracleMultiInsertStatement.Option.ALL); + } else if (lexer.token() == Token.FIRST) { + lexer.nextToken(); + stmt.setOption(OracleMultiInsertStatement.Option.FIRST); + } + + while (lexer.token() == Token.INTO) { + OracleMultiInsertStatement.InsertIntoClause clause = new OracleMultiInsertStatement.InsertIntoClause(); + + parseInsert0(clause); + + clause.setReturning(parseReturningClause()); + clause.setErrorLogging(parseErrorLoggingClause()); + + stmt.getEntries().add(clause); + } + + if (lexer.token() == Token.WHEN) { + 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); + } + + stmt.getEntries().add(clause); + } + + 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 df908146de..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 @@ -42,6 +42,10 @@ 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.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; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -713,5 +717,45 @@ public class OracleASTVIsitorAdapter extends SQLASTVisitorAdapter implements Ora public void endVisit(OracleInsertStatement x) { } + + @Override + public boolean visit(InsertIntoClause x) { + return true; + } + + @Override + public void endVisit(InsertIntoClause x) { + + } + + @Override + public boolean visit(OracleMultiInsertStatement x) { + return true; + } + + @Override + 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 5b62cbf3a0..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 @@ -46,6 +46,10 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleConstraintState; 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; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -310,4 +314,20 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleInsertStatement x); void endVisit(OracleInsertStatement x); + + boolean visit(InsertIntoClause x); + + void endVisit(InsertIntoClause x); + + 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 0d42decb7e..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 @@ -74,6 +74,10 @@ 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.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; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -1324,43 +1328,43 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS printAndAccept(x.getHints(), ", "); print(" "); } - + print("INTO "); x.getInto().accept(this); - + if (x.getAlias() != null) { print(" "); print(x.getAlias()); } - + println(); print("USING "); x.getUsing().accept(this); - + print(" ON "); x.getOn().accept(this); - + if (x.getUpdateClause() != null) { println(); x.getUpdateClause().accept(this); } - + if (x.getInsertClause() != null) { println(); x.getInsertClause().accept(this); } - + if (x.getErrorLoggingClause() != null) { println(); x.getErrorLoggingClause().accept(this); } - + return false; } @Override public void endVisit(OracleMergeStatement x) { - + } @Override @@ -1374,7 +1378,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getWhere().accept(this); decrementIndent(); } - + if (x.getDeleteWhere() != null) { incrementIndent(); println(); @@ -1382,13 +1386,13 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getDeleteWhere().accept(this); decrementIndent(); } - + return false; } @Override public void endVisit(MergeUpdateClause x) { - + } @Override @@ -1405,13 +1409,13 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getWhere().accept(this); decrementIndent(); } - + return false; } @Override public void endVisit(MergeInsertClause x) { - + } @Override @@ -1422,54 +1426,55 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getInto().accept(this); print(" "); } - + if (x.getSimpleExpression() != null) { print("("); x.getSimpleExpression().accept(this); print(")"); } - + if (x.getLimit() != null) { print(" REJECT LIMIT "); x.getLimit().accept(this); } - + return false; } @Override public void endVisit(OracleErrorLoggingClause x) { - + } + @Override public boolean visit(OracleReturningClause x) { print("RETURNING "); printAndAccept(x.getItems(), ", "); print(" INTO "); printAndAccept(x.getValues(), ", "); - + return false; } - + @Override public void endVisit(OracleReturningClause x) { - + } @Override public boolean visit(OracleInsertStatement x) { visit((SQLInsertStatement) x); - + if (x.getReturning() != null) { println(); x.getReturning().accept(this); } - + if (x.getErrorLogging() != null) { println(); x.getErrorLogging().accept(this); } - + return false; } @@ -1477,4 +1482,124 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public void endVisit(OracleInsertStatement x) { endVisit((SQLInsertStatement) x); } + + @Override + public boolean visit(InsertIntoClause x) { + print("INTO "); + + x.getTableName().accept(this); + + if (x.getColumns().size() > 0) { + incrementIndent(); + println(); + print("("); + for (int i = 0, size = x.getColumns().size(); i < size; ++i) { + if (i != 0) { + if (i % 5 == 0) { + println(); + } + print(", "); + } + x.getColumns().get(i).accept(this); + } + print(")"); + decrementIndent(); + } + + if (x.getValues() != null) { + println(); + print("VALUES "); + x.getValues().accept(this); + } else { + if (x.getQuery() != null) { + println(); + x.getQuery().setParent(x); + x.getQuery().accept(this); + } + } + + return false; + } + + @Override + public void endVisit(InsertIntoClause x) { + + } + + @Override + public boolean visit(OracleMultiInsertStatement x) { + print("INSERT "); + + if (x.getHints().size() > 0) { + this.printHints(x.getHints()); + } + + if (x.getOption() != null) { + print(x.getOption().name()); + print(" "); + } + + for (int i = 0, size = x.getEntries().size(); i < size; ++i) { + incrementIndent(); + println(); + x.getEntries().get(i).accept(this); + decrementIndent(); + } + + println(); + x.getSubQuery().accept(this); + + return false; + } + + @Override + 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 e4fcead763..f11639ed87 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; @@ -30,8 +31,8 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRulesCla import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.QueryPartitionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ReturnRowsClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleErrorLoggingClause; -import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleReturningClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SubqueryFactoringClause; @@ -51,9 +52,14 @@ import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleOuterExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleTimestampExpr; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleConstraintState; 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.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; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -65,7 +71,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot.Item; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectRestriction.CheckOption; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectRestriction.ReadOnly; -import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleInsertStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectSubqueryTableSource; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectUnPivot; @@ -221,7 +226,7 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return false; } - return super.visit(x); + return super.visit(x); } public boolean visit(SQLIdentifierExpr x) { @@ -815,12 +820,12 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle x.putAttribute("_original_use_mode", modeLocal.get()); modeLocal.set(Mode.Merge); - + String originalTable = currentTableLocal.get(); x.getUsing().accept(this); - String ident = x.getInto().toString(); + String ident = x.getInto().toString(); currentTableLocal.set(ident); x.putAttribute("_old_local_", originalTable); @@ -838,13 +843,13 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle } aliasMap.put(ident, ident); } - + x.getOn().accept(this); - + if (x.getUpdateClause() != null) { x.getUpdateClause().accept(this); } - + if (x.getInsertClause() != null) { x.getInsertClause().accept(this); } @@ -885,18 +890,16 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle @Override public void endVisit(OracleErrorLoggingClause x) { - } - + @Override public boolean visit(OracleReturningClause x) { return true; } - + @Override public void endVisit(OracleReturningClause x) { - - + } @Override @@ -908,4 +911,106 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle public void endVisit(OracleInsertStatement x) { endVisit((SQLInsertStatement) x); } + + @Override + public boolean visit(InsertIntoClause x) { + String originalTable = currentTableLocal.get(); + + if (x.getTableName() instanceof SQLName) { + String ident = ((SQLName) x.getTableName()).toString(); + currentTableLocal.set(ident); + x.putAttribute("_old_local_", originalTable); + + TableStat stat = tableStats.get(ident); + if (stat == null) { + stat = new TableStat(); + tableStats.put(new TableStat.Name(ident), stat); + } + stat.incrementInsertCount(); + + Map aliasMap = aliasLocal.get(); + if (aliasMap != null) { + if (x.getAlias() != null) { + aliasMap.put(x.getAlias(), ident); + } + aliasMap.put(ident, ident); + } + } + + accept(x.getColumns()); + accept(x.getQuery()); + accept(x.getReturning()); + accept(x.getErrorLogging()); + + return false; + } + + @Override + public void endVisit(InsertIntoClause x) { + + } + + @Override + public boolean visit(OracleMultiInsertStatement x) { + x.putAttribute("_original_use_mode", modeLocal.get()); + 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; + } + + @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 8350e1b5c3..eeb4fb76f4 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLCreateViewStatement; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; import com.alibaba.druid.sql.ast.statement.SQLDropTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertInto; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLSetStatement; @@ -161,15 +162,20 @@ public class SQLStatementParser extends SQLParser { public SQLStatement parseInsert() { accept(Token.INSERT); - accept(Token.INTO); SQLInsertStatement insertStatement = new SQLInsertStatement(); parseInsert0(insertStatement); return insertStatement; } + + protected void parseInsert0(SQLInsertInto insertStatement) { + parseInsert0(insertStatement, true); + } - protected void parseInsert0(SQLInsertStatement insertStatement) { + protected void parseInsert0(SQLInsertInto insertStatement, boolean acceptSubQuery) { + accept(Token.INTO); + SQLName tableName = this.exprParser.name(); insertStatement.setTableName(tableName); @@ -191,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/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java index f32d91e206..2abe018b34 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java @@ -363,6 +363,10 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { currentTableLocal.set(null); } else { currentTableLocal.set(ident); + x.putAttribute("_table_", ident); + if (x.getParent() instanceof SQLSelect) { + x.getParent().putAttribute("_table_", ident); + } } x.putAttribute("_old_local_", originalTable); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest.java new file mode 100644 index 0000000000..992dd28a6f --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMultiInsertTest.java @@ -0,0 +1,73 @@ +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 OracleMultiInsertTest extends OracleTest { + + public void test_0() throws Exception { + String sql = "INSERT ALL" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date, sales_sun)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+1, sales_mon)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+2, sales_tue)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+3, sales_wed)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+4, sales_thu)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+5, sales_fri)" + // + " INTO sales (prod_id, cust_id, time_id, amount)" + // + " VALUES (product_id, customer_id, weekly_start_date+6, sales_sat)" + // + " SELECT product_id, customer_id, weekly_start_date, sales_sun," + // + " sales_mon, sales_tue, sales_wed, sales_thu, sales_fri, sales_sat" + // + " FROM sales_input_table;"; // + + 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("sales"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("sales_input_table"))); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(14, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales", "prod_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales", "cust_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales", "time_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales", "amount"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "product_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "customer_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "weekly_start_date"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_sun"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_mon"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_tue"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_wed"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_thu"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_fri"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("sales_input_table", "sales_sat"))); + + } + +} 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"))); + + } + +}