From 2c87b27d0fbdb9a4fe9a01ad69ddcffff1f96585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=A9=E5=B0=91?= Date: Fri, 24 Feb 2012 22:19:40 +0800 Subject: [PATCH] sql parser support oracle merge --- .../oracle/ast/stmt/OracleMergeStatement.java | 230 ++++++++++++++++++ .../oracle/ast/stmt/OracleStatement.java | 7 + .../oracle/ast/stmt/OracleStatementImpl.java | 16 ++ .../oracle/parser/OracleExprParser.java | 9 + .../dialect/oracle/parser/OracleLexer.java | 7 + .../oracle/parser/OracleSelectParser.java | 5 +- .../oracle/parser/OracleStatementParser.java | 136 ++++++++++- .../visitor/OracleASTVIsitorAdapter.java | 43 ++++ .../oracle/visitor/OracleASTVisitor.java | 17 ++ .../oracle/visitor/OracleOutputVisitor.java | 129 ++++++++++ .../visitor/OracleSchemaStatVisitor.java | 44 ++++ .../druid/sql/parser/SQLStatementParser.java | 2 + .../com/alibaba/druid/sql/parser/Token.java | 6 + .../druid/bvt/sql/oracle/OracleMergeTest.java | 50 ++++ .../bvt/sql/oracle/OracleSelectTest2.java | 6 +- 15 files changed, 698 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatementImpl.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java new file mode 100644 index 0000000000..4a51c4754c --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleMergeStatement.java @@ -0,0 +1,230 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.stmt; + +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.statement.SQLTableSource; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +public class OracleMergeStatement extends OracleStatementImpl { + + private static final long serialVersionUID = 1L; + + private final List hints = new ArrayList(); + + private SQLName into; + private String alias; + private SQLTableSource using; + private SQLExpr on; + private MergeUpdateClause updateClause; + private MergeInsertClause insertClause; + private ErrorLoggingClause errorLoggingClause; + + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, into); + acceptChild(visitor, using); + acceptChild(visitor, on); + acceptChild(visitor, updateClause); + acceptChild(visitor, insertClause); + acceptChild(visitor, errorLoggingClause); + } + visitor.endVisit(this); + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public SQLName getInto() { + return into; + } + + public void setInto(SQLName into) { + this.into = into; + } + + public SQLTableSource getUsing() { + return using; + } + + public void setUsing(SQLTableSource using) { + this.using = using; + } + + public SQLExpr getOn() { + return on; + } + + public void setOn(SQLExpr on) { + this.on = on; + } + + public MergeUpdateClause getUpdateClause() { + return updateClause; + } + + public void setUpdateClause(MergeUpdateClause updateClause) { + this.updateClause = updateClause; + } + + public MergeInsertClause getInsertClause() { + return insertClause; + } + + public void setInsertClause(MergeInsertClause insertClause) { + this.insertClause = insertClause; + } + + public ErrorLoggingClause getErrorLoggingClause() { + return errorLoggingClause; + } + + public void setErrorLoggingClause(ErrorLoggingClause errorLoggingClause) { + this.errorLoggingClause = errorLoggingClause; + } + + public List getHints() { + return hints; + } + + public static class MergeUpdateClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + + private List items = new ArrayList(); + private SQLExpr where; + private SQLExpr deleteWhere; + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public SQLExpr getWhere() { + return where; + } + + public void setWhere(SQLExpr where) { + this.where = where; + } + + public SQLExpr getDeleteWhere() { + return deleteWhere; + } + + public void setDeleteWhere(SQLExpr deleteWhere) { + this.deleteWhere = deleteWhere; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, items); + acceptChild(visitor, where); + acceptChild(visitor, deleteWhere); + } + visitor.endVisit(this); + } + + } + + public static class MergeInsertClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + + private List columns = new ArrayList(); + private List values = new ArrayList(); + private SQLExpr where; + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, columns); + acceptChild(visitor, columns); + acceptChild(visitor, columns); + } + visitor.endVisit(this); + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + public SQLExpr getWhere() { + return where; + } + + public void setWhere(SQLExpr where) { + this.where = where; + } + + } + + public static class ErrorLoggingClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private SQLName into; + private SQLExpr simpleExpression; + private SQLExpr limit; + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, into); + acceptChild(visitor, simpleExpression); + acceptChild(visitor, limit); + } + visitor.endVisit(this); + } + + public SQLName getInto() { + return into; + } + + public void setInto(SQLName into) { + this.into = into; + } + + public SQLExpr getSimpleExpression() { + return simpleExpression; + } + + public void setSimpleExpression(SQLExpr simpleExpression) { + this.simpleExpression = simpleExpression; + } + + public SQLExpr getLimit() { + return limit; + } + + public void setLimit(SQLExpr limit) { + this.limit = limit; + } + + } +} 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 new file mode 100644 index 0000000000..f7fc0c4ee9 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatement.java @@ -0,0 +1,7 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; + +public interface OracleStatement extends OracleSQLObject { + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatementImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatementImpl.java new file mode 100644 index 0000000000..feb7a7add2 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleStatementImpl.java @@ -0,0 +1,16 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.ast.SQLStatementImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public abstract class OracleStatementImpl extends SQLStatementImpl implements OracleStatement { + + private static final long serialVersionUID = 1L; + + protected void accept0(SQLASTVisitor visitor) { + accept0((OracleASTVisitor) visitor); + } + + public abstract void accept0(OracleASTVisitor visitor); +} 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 03ff6f7c7f..9d7ea6146e 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 @@ -16,6 +16,7 @@ package com.alibaba.druid.sql.dialect.oracle.parser; import java.math.BigInteger; +import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; @@ -28,6 +29,7 @@ import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; 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; @@ -590,4 +592,11 @@ public class OracleExprParser extends SQLExprParser { } return super.relationalRest(expr); } + + public void parseHints(List hints) { + if (lexer.token() == Token.HINT) { + hints.add(new OracleHint(lexer.stringVal())); + lexer.nextToken(); + } + } } 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 d32eb5e23c..11d2e9ac66 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 @@ -104,6 +104,13 @@ public class OracleLexer extends Lexer { map.put("SECOND", Token.SECOND); map.put("TO", Token.TO); map.put("MODEL", Token.MODEL); + map.put("MERGE", Token.MERGE); + map.put("USING", Token.USING); + map.put("MATCHED", Token.MATCHED); + map.put("LOG", Token.LOG); + map.put("ERRORS", Token.ERRORS); + map.put("REJECT", Token.REJECT); + map.put("UNLIMITED", Token.UNLIMITED); DEFAULT_ORACLE_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index 26554922ec..5e4df905e8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -1002,9 +1002,6 @@ public class OracleSelectParser extends SQLSelectParser { } private void parseHints(OracleSelectQueryBlock queryBlock) { - if (lexer.token() == Token.HINT) { - queryBlock.getHints().add(new OracleHint(lexer.stringVal())); - lexer.nextToken(); - } + this.createExprParser().parseHints(queryBlock.getHints()); } } 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 96b315e91d..dce08c3cf7 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,10 +19,12 @@ import java.util.List; 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.stmt.OracleMergeStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; 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; import com.alibaba.druid.sql.parser.Token; @@ -37,7 +39,7 @@ public class OracleStatementParser extends SQLStatementParser { super(lexer); } - protected SQLExprParser createExprParser() { + protected OracleExprParser createExprParser() { return new OracleExprParser(lexer); } @@ -111,8 +113,138 @@ public class OracleStatementParser extends SQLStatementParser { continue; } + if (lexer.token() == Token.MERGE) { + statementList.add(this.parseMerge()); + continue; + } + throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); } } + public OracleMergeStatement parseMerge() throws ParserException { + accept(Token.MERGE); + + OracleMergeStatement stmt = new OracleMergeStatement(); + + OracleExprParser exprParser = this.createExprParser(); + exprParser.parseHints(stmt.getHints()); + + accept(Token.INTO); + stmt.setInto(exprParser.name()); + + 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(); + if (lexer.token() == Token.MATCHED) { + OracleMergeStatement.MergeUpdateClause updateClause = new OracleMergeStatement.MergeUpdateClause(); + lexer.nextToken(); + accept(Token.THEN); + accept(Token.UPDATE); + accept(Token.SET); + + for (;;) { + SQLUpdateSetItem item = new SQLUpdateSetItem(); + item.setColumn(this.exprParser.name()); + accept(Token.EQ); + item.setValue(this.exprParser.expr()); + + updateClause.getItems().add(item); + + if (lexer.token() == (Token.COMMA)) { + lexer.nextToken(); + continue; + } + + 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); + accept(Token.LPAREN); + exprParser.exprList(insertClause.getColumns()); + accept(Token.RPAREN); + accept(Token.VALUES); + accept(Token.LPAREN); + exprParser.exprList(insertClause.getValues()); + accept(Token.RPAREN); + + if (lexer.token() == Token.WHERE) { + lexer.nextToken(); + insertClause.setWhere(exprParser.expr()); + } + + stmt.setInsertClause(insertClause); + } + + if (lexer.token() == Token.LOG) { + OracleMergeStatement.ErrorLoggingClause errorClause = new OracleMergeStatement.ErrorLoggingClause(); + + 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; + } } 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 58455f8fab..2bfc3c15ce 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 @@ -36,6 +36,10 @@ 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.OracleMergeStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.ErrorLoggingClause; +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.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -649,4 +653,43 @@ public class OracleASTVIsitorAdapter extends SQLASTVisitorAdapter implements Ora } + @Override + public boolean visit(OracleMergeStatement x) { + return true; + } + + @Override + public void endVisit(OracleMergeStatement x) { + } + + @Override + public boolean visit(MergeUpdateClause x) { + return true; + } + + @Override + public void endVisit(MergeUpdateClause x) { + + } + + @Override + public boolean visit(MergeInsertClause x) { + return true; + } + + @Override + public void endVisit(MergeInsertClause x) { + + } + + @Override + public boolean visit(ErrorLoggingClause x) { + return true; + } + + @Override + public void endVisit(ErrorLoggingClause 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 934644fa80..bcd78bb71d 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 @@ -42,6 +42,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.OracleMergeStatement; 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; @@ -282,4 +283,20 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(ModelClause x); void endVisit(ModelClause x); + + boolean visit(OracleMergeStatement x); + + void endVisit(OracleMergeStatement x); + + boolean visit(OracleMergeStatement.MergeUpdateClause x); + + void endVisit(OracleMergeStatement.MergeUpdateClause x); + + boolean visit(OracleMergeStatement.MergeInsertClause x); + + void endVisit(OracleMergeStatement.MergeInsertClause x); + + boolean visit(OracleMergeStatement.ErrorLoggingClause x); + + void endVisit(OracleMergeStatement.ErrorLoggingClause 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 c03b5a77d6..27694111d5 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 @@ -67,6 +67,10 @@ 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.OracleMergeStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.ErrorLoggingClause; +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.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -1309,4 +1313,129 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public void endVisit(CellAssignment x) { } + + @Override + public boolean visit(OracleMergeStatement x) { + print("MERGE "); + if (x.getHints().size() > 0) { + 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 + public boolean visit(MergeUpdateClause x) { + print("WHEN MATCHED THEN UPDATE SET "); + printAndAccept(x.getItems(), ", "); + if (x.getWhere() != null) { + incrementIndent(); + println(); + print("WHERE "); + x.getWhere().accept(this); + decrementIndent(); + } + + if (x.getDeleteWhere() != null) { + incrementIndent(); + println(); + print("DELETE WHERE "); + x.getDeleteWhere().accept(this); + decrementIndent(); + } + + return false; + } + + @Override + public void endVisit(MergeUpdateClause x) { + + } + + @Override + public boolean visit(MergeInsertClause x) { + print("WHEN NOT MATCHED THEN INSERT "); + printAndAccept(x.getColumns(), ", "); + print(" VALUES ("); + printAndAccept(x.getValues(), ", "); + print(")"); + if (x.getWhere() != null) { + incrementIndent(); + println(); + print("WHERE "); + x.getWhere().accept(this); + decrementIndent(); + } + + return false; + } + + @Override + public void endVisit(MergeInsertClause x) { + + } + + @Override + public boolean visit(ErrorLoggingClause x) { + print("LOG ERRORS "); + if (x.getInto() != null) { + print("INTO "); + 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(ErrorLoggingClause 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 f4fe3975c7..d047798b01 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 @@ -48,6 +48,10 @@ 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.OracleMergeStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMergeStatement.ErrorLoggingClause; +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.OracleOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; @@ -820,4 +824,44 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle } + @Override + public boolean visit(OracleMergeStatement x) { + return true; + } + + @Override + public void endVisit(OracleMergeStatement x) { + + } + + @Override + public boolean visit(MergeUpdateClause x) { + return true; + } + + @Override + public void endVisit(MergeUpdateClause x) { + + } + + @Override + public boolean visit(MergeInsertClause x) { + return true; + } + + @Override + public void endVisit(MergeInsertClause x) { + + } + + @Override + public boolean visit(ErrorLoggingClause x) { + return true; + } + + @Override + public void endVisit(ErrorLoggingClause 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 b37623700a..52140be9a2 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -344,4 +344,6 @@ public class SQLStatementParser extends SQLParser { createView.setSubQuery(new SQLSelectParser(this.lexer).select()); return createView; } + + } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Token.java b/src/main/java/com/alibaba/druid/sql/parser/Token.java index 1d8f33d527..6088615b19 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -145,6 +145,12 @@ public enum Token { SECOND("SECOND"), TO("TO"), MODEL("MODEL"), + MERGE("MERGE"), + MATCHED("MATCHED"), + LOG("LOG"), + ERRORS("ERRORS"), + REJECT("REJECT"), + UNLIMITED("UNLIMITED"), // transact-sql TOP("TOP"), diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java new file mode 100644 index 0000000000..5f099f8075 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleMergeTest.java @@ -0,0 +1,50 @@ +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 OracleMergeTest extends OracleTest { + + public void test_0() throws Exception { + String sql = "MERGE INTO bonuses D" + // + " USING (SELECT employee_id, salary, department_id FROM employees" + // + " WHERE department_id = 80) S" + // + " ON (D.employee_id = S.employee_id)" + // + " WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*0.01" + // + " DELETE WHERE (S.salary > 8000)" + // + " WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)" + // + " VALUES (S.employee_id, S.salary*0.01)" + // + " WHERE (S.salary <= 8000);"; + + 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()); + + Assert.assertEquals(2, visitor.getTables().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments"))); + + Assert.assertEquals(4, visitor.getColumns().size()); + + // Assert.assertTrue(visitor.getFields().contains(new TableStat.Column("films", "producer_id"))); + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleSelectTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleSelectTest2.java index 026574cb11..9abafa7593 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleSelectTest2.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleSelectTest2.java @@ -15,10 +15,10 @@ public class OracleSelectTest2 extends OracleTest { public void test_0() throws Exception { String sql = "WITH " + // " dept_costs AS (" + // - " SELECT department_name, SUM(salary) dept_total" + // + " SELECT d.department_name, SUM(d.salary) dept_total" + // " FROM employees e, departments d" + // " WHERE e.department_id = d.department_id" + // - " GROUP BY department_name)," + // + " GROUP BY d.department_name)," + // " avg_cost AS (" + // " SELECT SUM(dept_total)/COUNT(*) avg" + // " FROM dept_costs)" + // @@ -46,7 +46,7 @@ public class OracleSelectTest2 extends OracleTest { Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments"))); - Assert.assertEquals(0, visitor.getColumns().size()); + Assert.assertEquals(4, visitor.getColumns().size()); // Assert.assertTrue(visitor.getFields().contains(new TableStat.Column("films", "producer_id"))); }