From 25511feeaa1b5ec88c173181120a381493260df2 Mon Sep 17 00:00:00 2001 From: wenshao Date: Thu, 13 Oct 2011 10:01:40 +0000 Subject: [PATCH] =?UTF-8?q?Oracle=20Parser=E6=94=AF=E6=8C=81Model=20Clause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://code.alibabatech.com/svn/druid/trunk@952 b9813039-fb51-4c41-a8b9-e21c2acb5095 --- .../oracle/ast/clause/ModelClause.java | 410 ++++++++++++++++++ .../ast/stmt/OracleSelectQueryBlock.java | 11 + .../dialect/oracle/parser/OracleLexer.java | 1 + .../oracle/parser/OracleSelectParser.java | 243 +++++++++++ .../visitor/OracleASTVIsitorAdapter.java | 104 ++++- .../oracle/visitor/OracleASTVisitor.java | 45 +- .../oracle/visitor/OracleOutputVisitor.java | 215 ++++++++- .../druid/sql/parser/SQLExprParser.java | 27 +- .../com/alibaba/druid/sql/parser/Token.java | 1 + .../druid/bvt/sql/oracle/OracleAnyTest.java | 31 ++ 10 files changed, 1067 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/ModelClause.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleAnyTest.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/ModelClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/ModelClause.java new file mode 100644 index 0000000000..7faf86fc91 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/ModelClause.java @@ -0,0 +1,410 @@ +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.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +public class ModelClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + + private final List cellReferenceOptions = new ArrayList(); + private ReturnRowsClause returnRowsClause; + private final List referenceModelClauses = new ArrayList(); + private MainModelClause mainModel; + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, returnRowsClause); + acceptChild(visitor, referenceModelClauses); + acceptChild(visitor, mainModel); + } + } + + public MainModelClause getMainModel() { + return mainModel; + } + + public void setMainModel(MainModelClause mainModel) { + this.mainModel = mainModel; + } + + public ReturnRowsClause getReturnRowsClause() { + return returnRowsClause; + } + + public void setReturnRowsClause(ReturnRowsClause returnRowsClause) { + this.returnRowsClause = returnRowsClause; + } + + public List getReferenceModelClauses() { + return referenceModelClauses; + } + + public List getCellReferenceOptions() { + return cellReferenceOptions; + } + + public static enum CellReferenceOption { + IgnoreNav("IGNORE NAV"), KeepNav("KEEP NAV"), UniqueDimension("UNIQUE DIMENSION"), + UniqueSingleReference("UNIQUE SINGLE REFERENCE") + + ; + + public final String name; + + CellReferenceOption(){ + this(null); + } + + CellReferenceOption(String name){ + this.name = name; + } + + public String toString() { + return name; + } + + } + + public static class ReturnRowsClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private boolean all = false; + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + visitor.visit(this); + } + } + + public static class ReferenceModelClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private SQLExpr name; + private SQLSelect subQuery; + private final List cellReferenceOptions = new ArrayList(); + + public List getCellReferenceOptions() { + return cellReferenceOptions; + } + + public SQLExpr getName() { + return name; + } + + public void setName(SQLExpr name) { + this.name = name; + } + + public SQLSelect getSubQuery() { + return subQuery; + } + + public void setSubQuery(SQLSelect subQuery) { + this.subQuery = subQuery; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + + } + } + + public static class ModelColumnClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private QueryPartitionClause queryPartitionClause; + private String alias; + private final List dimensionByColumns = new ArrayList(); + private final List measuresColumns = new ArrayList(); + + public List getDimensionByColumns() { + return dimensionByColumns; + } + + public List getMeasuresColumns() { + return measuresColumns; + } + + public QueryPartitionClause getQueryPartitionClause() { + return queryPartitionClause; + } + + public void setQueryPartitionClause(QueryPartitionClause queryPartitionClause) { + this.queryPartitionClause = queryPartitionClause; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, queryPartitionClause); + acceptChild(visitor, dimensionByColumns); + acceptChild(visitor, measuresColumns); + } + } + + } + + public static class ModelColumn extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + + private SQLExpr expr; + private String alias; + + public SQLExpr getExpr() { + return expr; + } + + public void setExpr(SQLExpr expr) { + this.expr = expr; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, expr); + } + } + + } + + public static class QueryPartitionClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private List exprList = new ArrayList(); + + public List getExprList() { + return exprList; + } + + public void setExprList(List exprList) { + this.exprList = exprList; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, exprList); + } + } + + } + + public static class MainModelClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private SQLExpr mainModelName; + private ModelColumnClause modelColumnClause; + + private final List cellReferenceOptions = new ArrayList(); + private ModelRulesClause modelRulesClause; + + public ModelRulesClause getModelRulesClause() { + return modelRulesClause; + } + + public void setModelRulesClause(ModelRulesClause modelRulesClause) { + this.modelRulesClause = modelRulesClause; + } + + public List getCellReferenceOptions() { + return cellReferenceOptions; + } + + public ModelColumnClause getModelColumnClause() { + return modelColumnClause; + } + + public void setModelColumnClause(ModelColumnClause modelColumnClause) { + this.modelColumnClause = modelColumnClause; + } + + public SQLExpr getMainModelName() { + return mainModelName; + } + + public void setMainModelName(SQLExpr mainModelName) { + this.mainModelName = mainModelName; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, mainModelName); + acceptChild(visitor, modelColumnClause); + acceptChild(visitor, modelRulesClause); + } + } + + } + + public static class ModelRulesClause extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private final List options = new ArrayList(); + private SQLExpr iterate; + private SQLExpr until; + private final List cellAssignmentItems = new ArrayList(); + + public SQLExpr getUntil() { + return until; + } + + public void setUntil(SQLExpr until) { + this.until = until; + } + + public SQLExpr getIterate() { + return iterate; + } + + public void setIterate(SQLExpr iterate) { + this.iterate = iterate; + } + + public List getOptions() { + return options; + } + + public List getCellAssignmentItems() { + return cellAssignmentItems; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, iterate); + acceptChild(visitor, until); + acceptChild(visitor, cellAssignmentItems); + } + } + + } + + public static enum ModelRuleOption { + UPSERT("UPSERT"), UPDATE("UPDATE"), AUTOMATIC_ORDER("AUTOMATIC ORDER"), SEQUENTIAL_ORDER("SEQUENTIAL ORDER"), + + ; + + public final String name; + + ModelRuleOption(String name){ + this.name = name; + } + + public String toString() { + return name; + } + } + + public static class CellAssignmentItem extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private ModelRuleOption option; + private CellAssignment cellAssignment; + private SQLOrderBy orderBy; + private SQLExpr expr; + + public ModelRuleOption getOption() { + return option; + } + + public void setOption(ModelRuleOption option) { + this.option = option; + } + + public CellAssignment getCellAssignment() { + return cellAssignment; + } + + public void setCellAssignment(CellAssignment cellAssignment) { + this.cellAssignment = cellAssignment; + } + + public SQLOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(SQLOrderBy orderBy) { + this.orderBy = orderBy; + } + + public SQLExpr getExpr() { + return expr; + } + + public void setExpr(SQLExpr expr) { + this.expr = expr; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, cellAssignment); + acceptChild(visitor, orderBy); + acceptChild(visitor, expr); + } + } + + } + + public static class CellAssignment extends OracleSQLObjectImpl { + + private static final long serialVersionUID = 1L; + private SQLExpr measureColumn; + private final List conditions = new ArrayList(); + + public List getConditions() { + return conditions; + } + + public SQLExpr getMeasureColumn() { + return measureColumn; + } + + public void setMeasureColumn(SQLExpr measureColumn) { + this.measureColumn = measureColumn; + } + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, measureColumn); + acceptChild(visitor, conditions); + } + } + + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index 04b8345ee0..f368d0693e 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.statement.SQLSelectItem; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -32,11 +33,20 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { private final List hints = new ArrayList(1); private OracleSelectHierachicalQueryClause hierachicalQueryClause; + private ModelClause modelClause; public OracleSelectQueryBlock(){ } + public ModelClause getModelClause() { + return modelClause; + } + + public void setModelClause(ModelClause modelClause) { + this.modelClause = modelClause; + } + public OracleSelectHierachicalQueryClause getHierachicalQueryClause() { return this.hierachicalQueryClause; } @@ -73,6 +83,7 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { acceptChild(visitor, this.where); acceptChild(visitor, this.hierachicalQueryClause); acceptChild(visitor, this.groupBy); + acceptChild(visitor, this.modelClause); } visitor.endVisit(this); } 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 b6778566b3..bf5ed181e3 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 @@ -101,6 +101,7 @@ public class OracleLexer extends Lexer { map.put("MONTH", Token.MONTH); map.put("SECOND", Token.SECOND); map.put("TO", Token.TO); + map.put("MODEL", Token.MODEL); 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 dcc7e49f8d..dd7dff65f8 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 @@ -15,6 +15,9 @@ */ 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.SQLSetQuantifier; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; @@ -29,6 +32,18 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; 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.ModelClause.CellAssignment; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.CellAssignmentItem; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.CellReferenceOption; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.MainModelClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumn; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumnClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRuleOption; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRulesClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.QueryPartitionClause; +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.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; @@ -241,6 +256,8 @@ public class OracleSelectParser extends SQLSelectParser { parseGroupBy(queryBlock); + parseModelClause(queryBlock); + return queryRest(queryBlock); } @@ -294,6 +311,232 @@ public class OracleSelectParser extends SQLSelectParser { return selectQuery; } + private void parseModelClause(OracleSelectQueryBlock queryBlock) { + if (lexer.token() != Token.MODEL) { + return; + } + + lexer.nextToken(); + + ModelClause model = new ModelClause(); + parseCellReferenceOptions(model.getCellReferenceOptions()); + + if (identifierEquals("RETURN")) { + lexer.nextToken(); + ReturnRowsClause returnRowsClause = new ReturnRowsClause(); + if (lexer.token() == Token.ALL) { + lexer.nextToken(); + returnRowsClause.setAll(true); + } else { + acceptIdentifier("UPDATED"); + } + acceptIdentifier("ROWS"); + + model.setReturnRowsClause(returnRowsClause); + } + + while (lexer.token() == Token.REFERENCE) { + ReferenceModelClause referenceModelClause = new ReferenceModelClause(); + lexer.nextToken(); + + SQLExpr name = expr(); + referenceModelClause.setName(name); + + accept(Token.ON); + accept(Token.LPAREN); + OracleSelect subQuery = this.select(); + accept(Token.RPAREN); + referenceModelClause.setSubQuery(subQuery); + + parseModelColumnClause(referenceModelClause); + + parseCellReferenceOptions(referenceModelClause.getCellReferenceOptions()); + + model.getReferenceModelClauses().add(referenceModelClause); + } + + parseMainModelClause(model); + + queryBlock.setModelClause(model); + } + + private void parseMainModelClause(ModelClause modelClause) { + MainModelClause mainModel = new MainModelClause(); + + if (identifierEquals("MAIN")) { + lexer.nextToken(); + mainModel.setMainModelName(expr()); + } + + ModelColumnClause modelColumnClause = new ModelColumnClause(); + parseQueryPartitionClause(modelColumnClause); + mainModel.setModelColumnClause(modelColumnClause); + + acceptIdentifier("DIMENSION"); + accept(Token.BY); + accept(Token.LPAREN); + for (;;) { + if (lexer.token() == Token.RPAREN) { + lexer.nextToken(); + break; + } + + ModelColumn column = new ModelColumn(); + column.setExpr(expr()); + column.setAlias(as()); + modelColumnClause.getDimensionByColumns().add(column); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + } + + acceptIdentifier("MEASURES"); + accept(Token.LPAREN); + for (;;) { + if (lexer.token() == Token.RPAREN) { + lexer.nextToken(); + break; + } + + ModelColumn column = new ModelColumn(); + column.setExpr(expr()); + column.setAlias(as()); + modelColumnClause.getMeasuresColumns().add(column); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + } + mainModel.setModelColumnClause(modelColumnClause); + + parseCellReferenceOptions(mainModel.getCellReferenceOptions()); + + parseModelRulesClause(mainModel); + + modelClause.setMainModel(mainModel); + } + + private void parseModelRulesClause(MainModelClause mainModel) { + ModelRulesClause modelRulesClause = new ModelRulesClause(); + if (identifierEquals("RULES")) { + lexer.nextToken(); + if (lexer.token() == Token.UPDATE) { + modelRulesClause.getOptions().add(ModelRuleOption.UPDATE); + lexer.nextToken(); + } else if (identifierEquals("UPSERT")) { + modelRulesClause.getOptions().add(ModelRuleOption.UPSERT); + lexer.nextToken(); + } + + if (identifierEquals("AUTOMATIC")) { + lexer.nextToken(); + accept(Token.ORDER); + modelRulesClause.getOptions().add(ModelRuleOption.AUTOMATIC_ORDER); + } else if (identifierEquals("SEQUENTIAL")) { + lexer.nextToken(); + accept(Token.ORDER); + modelRulesClause.getOptions().add(ModelRuleOption.SEQUENTIAL_ORDER); + } + } + + if (identifierEquals("ITERATE")) { + lexer.nextToken(); + accept(Token.LPAREN); + modelRulesClause.setIterate(expr()); + accept(Token.RPAREN); + + if (identifierEquals("UNTIL")) { + lexer.nextToken(); + accept(Token.LPAREN); + modelRulesClause.setUntil(expr()); + accept(Token.RPAREN); + } + } + + accept(Token.LPAREN); + for (;;) { + if (lexer.token() == Token.RPAREN) { + lexer.nextToken(); + break; + } + + CellAssignmentItem item = new CellAssignmentItem(); + if (lexer.token() == Token.UPDATE) { + item.setOption(ModelRuleOption.UPDATE); + } else if (identifierEquals("UPSERT")) { + item.setOption(ModelRuleOption.UPSERT); + } + + item.setCellAssignment(parseCellAssignment()); + item.setOrderBy(this.parseOrderBy()); + accept(Token.EQ); + item.setExpr(expr()); + + modelRulesClause.getCellAssignmentItems().add(item); + } + + mainModel.setModelRulesClause(modelRulesClause); + } + + private CellAssignment parseCellAssignment() { + CellAssignment cellAssignment = new CellAssignment(); + + cellAssignment.setMeasureColumn(expr()); + accept(Token.LBRACKET); + this.createExprParser().exprList(cellAssignment.getConditions()); + accept(Token.RBRACKET); + + return cellAssignment; + } + + private void parseQueryPartitionClause(ModelColumnClause modelColumnClause) { + if (identifierEquals("PARTITION")) { + QueryPartitionClause queryPartitionClause = new QueryPartitionClause(); + + lexer.nextToken(); + accept(Token.BY); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + createExprParser().exprList(queryPartitionClause.getExprList()); + accept(Token.RPAREN); + } else { + createExprParser().exprList(queryPartitionClause.getExprList()); + } + modelColumnClause.setQueryPartitionClause(queryPartitionClause); + } + } + + private void parseModelColumnClause(ReferenceModelClause referenceModelClause) { + throw new ParserException(); + } + + private void parseCellReferenceOptions(List options) { + if (identifierEquals("IGNORE")) { + lexer.nextToken(); + acceptIdentifier("NAV"); + options.add(CellReferenceOption.IgnoreNav); + } else if (identifierEquals("KEEP")) { + lexer.nextToken(); + acceptIdentifier("NAV"); + options.add(CellReferenceOption.KeepNav); + } + + if (lexer.token() == Token.UNIQUE) { + lexer.nextToken(); + if (identifierEquals("DIMENSION")) { + lexer.nextToken(); + options.add(CellReferenceOption.UniqueDimension); + } else { + acceptIdentifier("SINGLE"); + accept(Token.REFERENCE); + options.add(CellReferenceOption.UniqueDimension); + } + } + } + private void parseGroupBy(OracleSelectQueryBlock queryBlock) { if (lexer.token() == (Token.GROUP)) { lexer.nextToken(); 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 fcfb34b3b8..4def3204dd 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 @@ -7,6 +7,15 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; 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.ModelClause.CellAssignment; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.CellAssignmentItem; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.MainModelClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumn; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumnClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRulesClause; +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.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; @@ -597,17 +606,106 @@ public class OracleASTVIsitorAdapter extends SQLASTVisitorAdapter implements Ora @Override public void endVisit(OracleCursorExpr x) { - + } - + @Override public boolean visit(OracleIsSetExpr x) { return true; } - + @Override public void endVisit(OracleIsSetExpr x) { + + } + + @Override + public boolean visit(ReturnRowsClause x) { + return true; + } + + @Override + public void endVisit(ReturnRowsClause x) { + + } + + @Override + public boolean visit(ModelClause x) { + return true; + } + + @Override + public void endVisit(ModelClause x) { + + } + + @Override + public boolean visit(MainModelClause x) { + return true; + } + + @Override + public void endVisit(MainModelClause x) { + + } + + @Override + public boolean visit(ModelColumnClause x) { + return true; + } + + @Override + public void endVisit(ModelColumnClause x) { + + } + + @Override + public boolean visit(QueryPartitionClause x) { + return true; + } + + @Override + public void endVisit(QueryPartitionClause x) { } + @Override + public boolean visit(ModelColumn x) { + return true; + } + + @Override + public void endVisit(ModelColumn x) { + + } + + @Override + public boolean visit(ModelRulesClause x) { + return true; + } + + @Override + public void endVisit(ModelRulesClause x) { + + } + + @Override + public boolean visit(CellAssignmentItem x) { + return true; + } + + @Override + public void endVisit(CellAssignmentItem x) { + + } + + @Override + public boolean visit(CellAssignment x) { + return true; + } + + @Override + public void endVisit(CellAssignment 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 e83b361bc2..2b0bb4b053 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 @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; 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.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; @@ -262,12 +263,48 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleSelect x); void endVisit(OracleSelect x); - + boolean visit(OracleCursorExpr x); - + void endVisit(OracleCursorExpr x); - + boolean visit(OracleIsSetExpr x); - + void endVisit(OracleIsSetExpr x); + + boolean visit(ModelClause.ReturnRowsClause x); + + void endVisit(ModelClause.ReturnRowsClause x); + + boolean visit(ModelClause.MainModelClause x); + + void endVisit(ModelClause.MainModelClause x); + + boolean visit(ModelClause.ModelColumnClause x); + + void endVisit(ModelClause.ModelColumnClause x); + + boolean visit(ModelClause.QueryPartitionClause x); + + void endVisit(ModelClause.QueryPartitionClause x); + + boolean visit(ModelClause.ModelColumn x); + + void endVisit(ModelClause.ModelColumn x); + + boolean visit(ModelClause.ModelRulesClause x); + + void endVisit(ModelClause.ModelRulesClause x); + + boolean visit(ModelClause.CellAssignmentItem x); + + void endVisit(ModelClause.CellAssignmentItem x); + + boolean visit(ModelClause.CellAssignment x); + + void endVisit(ModelClause.CellAssignment x); + + boolean visit(ModelClause x); + + void endVisit(ModelClause 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 cd75882f46..ec6d6e08cd 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 @@ -36,6 +36,18 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.CycleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.AsOfFlashbackQueryClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause; 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.ModelClause.CellAssignment; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.CellAssignmentItem; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.CellReferenceOption; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.MainModelClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumn; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelColumnClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRuleOption; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.ModelRulesClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause.QueryPartitionClause; +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.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; @@ -498,6 +510,11 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS select.getGroupBy().accept(this); } + if (select.getModelClause() != null) { + println(); + select.getModelClause().accept(this); + } + return false; } @@ -1163,16 +1180,210 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS public void endVisit(OracleCursorExpr x) { } - + @Override public boolean visit(OracleIsSetExpr x) { x.getNestedTable().accept(this); print(" IS A SET"); return false; } - + @Override public void endVisit(OracleIsSetExpr x) { + + } + + @Override + public boolean visit(ReturnRowsClause x) { + if (x.isAll()) { + print("RETURN ALL ROWS"); + } else { + print("RETURN UPDATED ROWS"); + } + return false; + } + + @Override + public void endVisit(ReturnRowsClause x) { + + } + + @Override + public boolean visit(ModelClause x) { + print("MODEL"); + + for (CellReferenceOption opt : x.getCellReferenceOptions()) { + print(' '); + print(opt.name); + } + + if (x.getReturnRowsClause() != null) { + print(' '); + x.getReturnRowsClause().accept(this); + } + + for (ReferenceModelClause item : x.getReferenceModelClauses()) { + print(' '); + item.accept(this); + } + + x.getMainModel().accept(this); + + return false; + } + + @Override + public void endVisit(ModelClause x) { + + } + + @Override + public boolean visit(MainModelClause x) { + if (x.getMainModelName() != null) { + print(" MAIN "); + x.getMainModelName().accept(this); + } + + println(); + x.getModelColumnClause().accept(this); + + for (CellReferenceOption opt : x.getCellReferenceOptions()) { + println(); + print(opt.name); + } + + println(); + x.getModelRulesClause().accept(this); + + return false; + } + + @Override + public void endVisit(MainModelClause x) { + + } + + @Override + public boolean visit(ModelColumnClause x) { + if (x.getQueryPartitionClause() != null) { + x.getQueryPartitionClause().accept(this); + println(); + } + + print("DIMENSION BY ("); + printAndAccept(x.getDimensionByColumns(), ", "); + print(")"); + + println(); + print("MEASURES ("); + printAndAccept(x.getMeasuresColumns(), ", "); + print(")"); + return false; + } + + @Override + public void endVisit(ModelColumnClause x) { + + } + + @Override + public boolean visit(QueryPartitionClause x) { + print("PARTITION BY ("); + printAndAccept(x.getExprList(), ", "); + print(")"); + return false; + } + + @Override + public void endVisit(QueryPartitionClause x) { + + } + + @Override + public boolean visit(ModelColumn x) { + x.getExpr().accept(this); + if (x.getAlias() != null) { + print(" "); + print(x.getAlias()); + } + return false; + } + + @Override + public void endVisit(ModelColumn x) { + + } + + @Override + public boolean visit(ModelRulesClause x) { + if (x.getOptions().size() > 0) { + print("RULES"); + for (ModelRuleOption opt : x.getOptions()) { + print(" "); + print(opt.name); + } + } + + if (x.getIterate() != null) { + print(" ITERATE ("); + x.getIterate().accept(this); + print(")"); + + if (x.getUntil() != null) { + print(" UNTIL ("); + x.getUntil().accept(this); + print(")"); + } + } + + print(" ("); + printAndAccept(x.getCellAssignmentItems(), ", "); + print(")"); + return false; + + } + + @Override + public void endVisit(ModelRulesClause x) { + + } + + @Override + public boolean visit(CellAssignmentItem x) { + if (x.getOption() != null) { + print(x.getOption().name); + print(" "); + } + + x.getCellAssignment().accept(this); + + if (x.getOrderBy() != null) { + print(" "); + x.getOrderBy().accept(this); + } + + print(" = "); + x.getExpr().accept(this); + + return false; + } + + @Override + public void endVisit(CellAssignmentItem x) { + + } + + @Override + public boolean visit(CellAssignment x) { + x.getMeasureColumn().accept(this); + print("["); + printAndAccept(x.getConditions(), ", "); + print("]"); + return false; + } + + @Override + public void endVisit(CellAssignment x) { } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index 61af34ad86..374b653022 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -358,16 +358,20 @@ public class SQLExprParser extends SQLParser { break; case ANY: lexer.nextToken(); - SQLAnyExpr anyExpr = new SQLAnyExpr(); + if (lexer.token() == Token.LPAREN) { + SQLAnyExpr anyExpr = new SQLAnyExpr(); - accept(Token.LPAREN); - SQLSelect anySubQuery = createSelectParser().select(); - anyExpr.setSubQuery(anySubQuery); - accept(Token.RPAREN); + accept(Token.LPAREN); + SQLSelect anySubQuery = createSelectParser().select(); + anyExpr.setSubQuery(anySubQuery); + accept(Token.RPAREN); - anySubQuery.setParent(anyExpr); + anySubQuery.setParent(anyExpr); - sqlExpr = anyExpr; + sqlExpr = anyExpr; + } else { + sqlExpr = new SQLIdentifierExpr("ANY"); + } break; case SOME: lexer.nextToken(); @@ -383,12 +387,11 @@ public class SQLExprParser extends SQLParser { sqlExpr = someExpr; break; case KEY: - lexer.nextToken(); - sqlExpr = new SQLIdentifierExpr("KEY"); - break; case DISTINCT: + case YEAR: + case MONTH: lexer.nextToken(); - sqlExpr = new SQLIdentifierExpr("DISTINCT"); + sqlExpr = new SQLIdentifierExpr(tok.name()); break; case ALL: lexer.nextToken(); @@ -524,7 +527,7 @@ public class SQLExprParser extends SQLParser { } public final void exprList(Collection exprCol) throws ParserException { - if (lexer.token() == Token.RPAREN) { + if (lexer.token() == Token.RPAREN || lexer.token() == Token.RBRACKET) { return; } 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 e78c4fc5c7..61f155b47b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -112,6 +112,7 @@ public enum Token { MONTH("MONTH"), SECOND("SECOND"), TO("TO"), + MODEL("MODEL"), EOF, ERROR, diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleAnyTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleAnyTest.java new file mode 100644 index 0000000000..053564f1a7 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/OracleAnyTest.java @@ -0,0 +1,31 @@ +package com.alibaba.druid.bvt.sql.oracle; + +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; + +import junit.framework.Assert; +import junit.framework.TestCase; + +public class OracleAnyTest extends TestCase { + + public void test_any() throws Exception { + String sql = "SELECT country, prod, year, s FROM sales_view " + "MODEL PARTITION BY (country) " + + "DIMENSION BY (prod, year) MEASURES (sale s) " + "IGNORE NAV " + + "UNIQUE DIMENSION RULES UPSERT SEQUENTIAL ORDER (s[ANY, 2000] = 0) " + + "ORDER BY country, prod, year;"; + + String expect = "SELECT country, prod, YEAR, s\n" + "FROM sales_view\n" + "MODEL\n" + + "PARTITION BY (country)\n" + "DIMENSION BY (prod, YEAR)\n" + "MEASURES (sale s)\n" + + "IGNORE NAV\n" + "UNIQUE DIMENSION\n" + "RULES UPSERT SEQUENTIAL ORDER (s[ANY, 2000] = 0)\n" + + "ORDER BY country, prod, YEAR;\n"; + + OracleStatementParser parser = new OracleStatementParser(sql); + SQLSelectStatement stmt = (SQLSelectStatement) parser.parseStatementList().get(0); + + String text = TestUtils.output(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}