diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleReturningClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleReturningClause.java new file mode 100644 index 0000000000..9c08c8c197 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleReturningClause.java @@ -0,0 +1,42 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.clause; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +public class OracleReturningClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + + private List items = new ArrayList(); + private List values = new ArrayList(); + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, items); + acceptChild(visitor, values); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java new file mode 100644 index 0000000000..7561197aec --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleInsertStatement.java @@ -0,0 +1,49 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +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 OracleInsertStatement extends SQLInsertStatement implements OracleStatement { + + private static final long serialVersionUID = 1L; + + private OracleReturningClause returning; + private OracleErrorLoggingClause errorLogging; + + 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); + } + + 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/ast/stmt/OracleMultiInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java new file mode 100644 index 0000000000..aedd278d56 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMultiInsertStatement.java @@ -0,0 +1,15 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + + +public class OracleMultiInsertStatement extends OracleStatementImpl { + + private static final long serialVersionUID = 1L; + + @Override + public void accept0(OracleASTVisitor visitor) { + + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java index f7fc0c4ee9..ba36c84d18 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java @@ -1,7 +1,8 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; +import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; -public interface OracleStatement extends OracleSQLObject { +public interface OracleStatement extends SQLStatement, OracleSQLObject { } 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 9d7ea6146e..511eac757f 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 @@ -97,11 +97,11 @@ public class OracleExprParser extends SQLExprParser { return new SQLVariantRefExpr(name); } else if (lexer.token() == Token.IDENTIFIER) { String name = lexer.stringVal(); - if (name.startsWith("B")) { + if (name.charAt(0) == 'B' || name.charAt(0) == 'b') { lexer.nextToken(); return new SQLVariantRefExpr(":" + name); } - throw new ParserException("syntax error : " + lexer.token()); + throw new ParserException("syntax error : " + lexer.token() + " " + lexer.stringVal()); } else { throw new ParserException("syntax error : " + lexer.token()); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleInsertParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleInsertParser.java deleted file mode 100644 index aab39b1b4b..0000000000 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleInsertParser.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.parser; - -import com.alibaba.druid.sql.parser.Lexer; -import com.alibaba.druid.sql.parser.ParserException; -import com.alibaba.druid.sql.parser.SQLExprParser; -import com.alibaba.druid.sql.parser.SQLStatementParser; - -public class OracleInsertParser extends SQLStatementParser { - - public OracleInsertParser(String sql) throws ParserException{ - super(sql); - } - - public OracleInsertParser(Lexer lexer){ - super(lexer); - } - - protected SQLExprParser createExprParser() { - return new OracleExprParser(lexer); - } -} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java index 11d2e9ac66..8240194685 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -111,6 +111,8 @@ public class OracleLexer extends Lexer { map.put("ERRORS", Token.ERRORS); map.put("REJECT", Token.REJECT); map.put("UNLIMITED", Token.UNLIMITED); + map.put("RETURNING", Token.RETURNING); + map.put("LIMIT", Token.LIMIT); DEFAULT_ORACLE_KEYWORDS = new Keywords(map); } 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 c81ddc22fe..11875f73e3 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 @@ -17,13 +17,17 @@ package com.alibaba.druid.sql.dialect.oracle.parser; 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.SQLSelectStatement; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; 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.OraclePLSQLCommitStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleStatement; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLStatementParser; @@ -85,7 +89,7 @@ public class OracleStatementParser extends SQLStatementParser { } if (lexer.token() == Token.INSERT) { - statementList.add(new OracleInsertParser(this.lexer).parseInsert()); + statementList.add(parseInsert()); continue; } @@ -137,13 +141,13 @@ public class OracleStatementParser extends SQLStatementParser { stmt.setAlias(as()); accept(Token.USING); - + SQLTableSource using = this.createSQLSelectParser().parseTableSource(); stmt.setUsing(using); - + accept(Token.ON); stmt.setOn(exprParser.expr()); - + boolean insertFlag = false; if (lexer.token() == Token.WHEN) { lexer.nextToken(); @@ -153,7 +157,7 @@ public class OracleStatementParser extends SQLStatementParser { accept(Token.THEN); accept(Token.UPDATE); accept(Token.SET); - + for (;;) { SQLUpdateSetItem item = new SQLUpdateSetItem(); item.setColumn(this.exprParser.name()); @@ -169,39 +173,39 @@ public class OracleStatementParser extends SQLStatementParser { break; } - + if (lexer.token() == Token.WHERE) { lexer.nextToken(); updateClause.setWhere(exprParser.expr()); } - + if (lexer.token() == Token.DELETE) { lexer.nextToken(); accept(Token.WHERE); updateClause.setWhere(exprParser.expr()); } - + stmt.setUpdateClause(updateClause); } else if (lexer.token() == Token.NOT) { lexer.nextToken(); insertFlag = true; } } - + if (!insertFlag) { if (lexer.token() == Token.WHEN) { lexer.nextToken(); } - + if (lexer.token() == Token.NOT) { lexer.nextToken(); insertFlag = true; } } - + if (insertFlag) { OracleMergeStatement.MergeInsertClause insertClause = new OracleMergeStatement.MergeInsertClause(); - + accept(Token.MATCHED); accept(Token.THEN); accept(Token.INSERT); @@ -212,40 +216,90 @@ public class OracleStatementParser extends SQLStatementParser { accept(Token.LPAREN); exprParser.exprList(insertClause.getValues()); accept(Token.RPAREN); - + if (lexer.token() == Token.WHERE) { lexer.nextToken(); insertClause.setWhere(exprParser.expr()); } - + stmt.setInsertClause(insertClause); } - + + OracleErrorLoggingClause errorClause = parseErrorLoggingClause(); + stmt.setErrorLoggingClause(errorClause); + + return stmt; + } + + public OracleStatement parseInsert() { + accept(Token.INSERT); + accept(Token.INTO); + + OracleInsertStatement stmt = new OracleInsertStatement(); + + parseInsert0(stmt); + + stmt.setReturning(parseReturningClause()); + stmt.setErrorLogging(parseErrorLoggingClause()); + + return stmt; + } + + private OracleErrorLoggingClause parseErrorLoggingClause() { if (lexer.token() == Token.LOG) { OracleErrorLoggingClause errorClause = new OracleErrorLoggingClause(); - + lexer.nextToken(); accept(Token.ERRORS); if (lexer.token() == Token.INTO) { lexer.nextToken(); errorClause.setInto(exprParser.name()); } - + if (lexer.token() == Token.LPAREN) { lexer.nextToken(); errorClause.setSimpleExpression(exprParser.expr()); accept(Token.RPAREN); } - + if (lexer.token() == Token.REJECT) { lexer.nextToken(); accept(Token.LIMIT); errorClause.setLimit(exprParser.expr()); } - - stmt.setErrorLoggingClause(errorClause); - } - return stmt; + return errorClause; + } + return null; + } + + private OracleReturningClause parseReturningClause() { + OracleReturningClause clause = null; + + if (lexer.token() == Token.RETURNING) { + lexer.nextToken(); + clause = new OracleReturningClause(); + + for (;;) { + SQLExpr item = exprParser.expr(); + clause.getItems().add(item); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + accept(Token.INTO); + for (;;) { + SQLExpr item = exprParser.expr(); + clause.getValues().add(item); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + } + return clause; } } 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 bc55a23102..df908146de 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 @@ -17,6 +17,7 @@ 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.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; @@ -37,6 +38,7 @@ 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; @@ -691,5 +693,25 @@ public class OracleASTVIsitorAdapter extends SQLASTVisitorAdapter implements Ora public void endVisit(OracleErrorLoggingClause x) { } + + @Override + public boolean visit(OracleReturningClause x) { + return true; + } + + @Override + public void endVisit(OracleReturningClause x) { + + } + + @Override + public boolean visit(OracleInsertStatement x) { + return true; + } + + @Override + public void endVisit(OracleInsertStatement 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 08727efcd5..5b62cbf3a0 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 @@ -24,6 +24,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.Vers import com.alibaba.druid.sql.dialect.oracle.ast.clause.GroupingSetExpr; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; 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.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; @@ -43,6 +44,7 @@ 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.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; @@ -300,4 +302,12 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleErrorLoggingClause x); void endVisit(OracleErrorLoggingClause x); + + boolean visit(OracleReturningClause x); + + void endVisit(OracleReturningClause x); + + boolean visit(OracleInsertStatement x); + + void endVisit(OracleInsertStatement 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 b75758d216..0d42decb7e 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 @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; @@ -49,6 +50,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.QueryPartitio import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ReferenceModelClause; 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.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; @@ -68,6 +70,7 @@ 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; @@ -1438,4 +1441,40 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS 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; + } + + @Override + public void endVisit(OracleInsertStatement x) { + endVisit((SQLInsertStatement) 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 e28d454eec..e4fcead763 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 @@ -11,6 +11,7 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; @@ -30,6 +31,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.QueryPartitio 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.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SubqueryFactoringClause; @@ -63,6 +65,7 @@ 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; @@ -883,6 +886,26 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle public void endVisit(OracleErrorLoggingClause x) { + } + + @Override + public boolean visit(OracleReturningClause x) { + return true; + } + + @Override + public void endVisit(OracleReturningClause x) { + + } + @Override + public boolean visit(OracleInsertStatement x) { + return visit((SQLInsertStatement) x); + } + + @Override + public void endVisit(OracleInsertStatement x) { + endVisit((SQLInsertStatement) x); + } } 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 c762bde07c..523657f617 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -21,7 +21,6 @@ import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.parser.OracleDeleteParser; -import com.alibaba.druid.sql.dialect.oracle.parser.OracleInsertParser; import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; @@ -80,7 +79,7 @@ public class SQLServerStatementParser extends SQLStatementParser { } if (lexer.token() == Token.INSERT) { - statementList.add(new OracleInsertParser(this.lexer).parseInsert()); + statementList.add(parseInsert()); continue; } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index 5643d69875..2a9aa53051 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -144,6 +144,10 @@ public class Lexer { protected final void scanChar() { ch = buf[++bp]; } + + protected void unscan() { + ch = buf[--bp]; + } /** * Report an error at the given position using the provided arguments. @@ -253,6 +257,11 @@ public class Lexer { return; case '.': scanChar(); + if (isDigit(ch)) { + unscan(); + scanNumber(); + return; + } token = Token.DOT; return; case '\'': 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 0302c48446..8350e1b5c3 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -165,6 +165,11 @@ public class SQLStatementParser extends SQLParser { SQLInsertStatement insertStatement = new SQLInsertStatement(); + parseInsert0(insertStatement); + return insertStatement; + } + + protected void parseInsert0(SQLInsertStatement insertStatement) { SQLName tableName = this.exprParser.name(); insertStatement.setTableName(tableName); @@ -190,7 +195,6 @@ public class SQLStatementParser extends SQLParser { SQLQueryExpr queryExpr = (SQLQueryExpr) this.createExprParser().expr(); insertStatement.setQuery(queryExpr.getSubQuery()); } - return insertStatement; } public boolean parseStatementListDialect(List statementList) { diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 1f7af07501..a1f4596448 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -390,14 +390,19 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { } public boolean visit(SQLQueryExpr x) { - if (x.getParent() instanceof SQLStatement) { + SQLObject parent = x.getParent(); + if (parent instanceof SQLSelect) { + parent = parent.getParent(); + } + + if (parent instanceof SQLStatement) { incrementIndent(); println(); x.getSubQuery().accept(this); decrementIndent(); - } else if (x.getParent() instanceof ValuesClause) { + } else if (parent instanceof ValuesClause) { println(); x.getSubQuery().accept(this); println(); @@ -428,6 +433,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { } public boolean visit(SQLSelect x) { + x.getQuery().setParent(x); x.getQuery().accept(this); if (x.getOrderBy() != null) { @@ -609,6 +615,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { } else { if (x.getQuery() != null) { println(); + x.getQuery().setParent(x); x.getQuery().accept(this); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest2.java new file mode 100644 index 0000000000..8950f2aa5e --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest2.java @@ -0,0 +1,51 @@ +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 OracleInsertTest2 extends OracleTest { + + public void test_0() throws Exception { + String sql = "INSERT INTO employees " + // + " (employee_id, last_name, email, hire_date, job_id, salary)" + // + " VALUES " + // + " (employees_seq.nextval, 'Doe', 'john.doe@example.com', " + // + " SYSDATE, 'SH_CLERK', 2400) " + // + " RETURNING salary*12, job_id INTO :bnd1, :bnd2; "; + + 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("employees"))); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(6, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "employee_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "last_name"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "email"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "hire_date"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "job_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary"))); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest3.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest3.java new file mode 100644 index 0000000000..ac6eda25ea --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleInsertTest3.java @@ -0,0 +1,47 @@ +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 OracleInsertTest3 extends OracleTest { + + public void test_0() throws Exception { + String sql = "INSERT INTO raises" + // + " SELECT employee_id, salary*1.1 FROM employees" + // + " WHERE commission_pct > .2" + // + " LOG ERRORS INTO errlog ('my_bad') REJECT LIMIT 10;"; + + 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("raises"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(3, visitor.getColumns().size()); + + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "employee_id"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary"))); + Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "commission_pct"))); + } + +}