diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java new file mode 100644 index 0000000000..44433ec16d --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLOver; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataAnalytic extends SQLOver implements TeradataExpr { + + private TeradataAnalyticWindowing windowing; + + public TeradataAnalytic(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((TeradataASTVisitor) visitor); + } + + public void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.partitionBy); + acceptChild(visitor, this.orderBy); + acceptChild(visitor, this.windowing); + } + visitor.endVisit(this); + } + + public TeradataAnalyticWindowing getWindowing() { + return this.windowing; + } + + public void setWindowing(TeradataAnalyticWindowing windowing) { + this.windowing = windowing; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java new file mode 100644 index 0000000000..76c6658bef --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java @@ -0,0 +1,49 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLObjectImpl; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataAnalyticWindowing extends SQLObjectImpl implements TeradataExpr { + + private Type type; + private SQLExpr expr; + + public TeradataAnalyticWindowing(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((TeradataASTVisitor) visitor); + } + + public void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.expr); + } + visitor.endVisit(this); + } + + public SQLExpr getExpr() { + return this.expr; + } + + public void setExpr(SQLExpr expr) { + this.expr = expr; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public static enum Type { + ROWS, RANGE; + } +} + diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java new file mode 100644 index 0000000000..60fe89ea34 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java @@ -0,0 +1,8 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.teradata.ast.TeradataObject; + +public interface TeradataExpr extends SQLExpr, TeradataObject { + +} \ No newline at end of file diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java new file mode 100644 index 0000000000..706db3dc67 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java @@ -0,0 +1,44 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.stmt; + +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { + private SQLOrderBy orderBy; + + public TeradataSelectQueryBlock() { + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof TeradataASTVisitor) { + accept0((TeradataASTVisitor) visitor); + return; + } + + super.accept0(visitor); + } + + protected void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.selectList); + acceptChild(visitor, this.into); + acceptChild(visitor, this.from); + acceptChild(visitor, this.where); + acceptChild(visitor, this.groupBy); + acceptChild(visitor, this.orderBy); + } + visitor.endVisit(this); + } + + public SQLOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(SQLOrderBy orderBy) { + this.orderBy = orderBy; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java index 94eaeeb6fa..4e938a1ac6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java @@ -15,8 +15,24 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLKeep; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.SQLKeep.DenseRank; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; +import com.alibaba.druid.sql.ast.expr.SQLCastExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; 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.Token; import com.alibaba.druid.util.JdbcConstants; public class TeradataExprParser extends SQLExprParser { @@ -35,5 +51,211 @@ public class TeradataExprParser extends SQLExprParser { this.dbType = JdbcConstants.TERADATA; } + public SQLExpr primary() { + SQLExpr sqlExpr = null; + + final Token tok = lexer.token(); + + switch (tok){ + case SEL: + SQLQueryExpr queryExpr = new SQLQueryExpr(createSelectParser().select()); + sqlExpr = queryExpr; + return primaryRest(sqlExpr); + case CAST: + System.out.println("cast test!"); + lexer.nextToken(); + accept(Token.LPAREN); + SQLCastExpr cast = new SQLCastExpr(); + cast.setExpr(expr()); + accept(Token.AS); + cast.setDataType(parseDataType()); + // TODO: deal with FORMAT + // currently ignore FORMAT keywords + if(lexer.token() == Token.FORMAT) { + lexer.nextToken(); + lexer.nextToken(); + } + accept(Token.RPAREN); + return primaryRest(cast); + default: + return super.primary(); + } + } + + protected SQLAggregateExpr parseAggregateExpr(String methodName) { + methodName = methodName.toUpperCase(); + + SQLAggregateExpr aggregateExpr; + if (lexer.token() == Token.UNIQUE) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.UNIQUE); + lexer.nextToken(); + } else if (lexer.token() == (Token.ALL)) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.ALL); + lexer.nextToken(); + } else if (lexer.token() == (Token.DISTINCT)) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.DISTINCT); + lexer.nextToken(); + } else { + aggregateExpr = new SQLAggregateExpr(methodName); + } + exprList(aggregateExpr.getArguments(), aggregateExpr); + accept(Token.RPAREN); + + if (lexer.token() == Token.KEEP) { + lexer.nextToken(); + + SQLKeep keep = new SQLKeep(); + accept(Token.LPAREN); + acceptIdentifier("DENSE_RANK"); + if (identifierEquals("FIRST")) { + lexer.nextToken(); + keep.setDenseRank(DenseRank.FIRST); + } else { + acceptIdentifier("LAST"); + keep.setDenseRank(DenseRank.LAST); + } + + SQLOrderBy orderBy = this.parseOrderBy(); + keep.setOrderBy(orderBy); + + aggregateExpr.setKeep(keep); + + accept(Token.RPAREN); + } + + if (lexer.token() == Token.OVER) { + TeradataAnalytic over = new TeradataAnalytic(); + + lexer.nextToken(); + accept(Token.LPAREN); + + if (identifierEquals("PARTITION")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + exprList(over.getPartitionBy(), over); + accept(Token.RPAREN); + } else { + exprList(over.getPartitionBy(), over); + } + } + + over.setOrderBy(parseOrderBy()); + if (over.getOrderBy() != null) { + TeradataAnalyticWindowing windowing = null; + if (lexer.stringVal().equalsIgnoreCase("ROWS")) { + lexer.nextToken(); + windowing = new TeradataAnalyticWindowing(); + windowing.setType(TeradataAnalyticWindowing.Type.ROWS); + } else if (lexer.stringVal().equalsIgnoreCase("RANGE")) { + lexer.nextToken(); + windowing = new TeradataAnalyticWindowing(); + windowing.setType(TeradataAnalyticWindowing.Type.RANGE); + } + + if (windowing != null) { + if (lexer.stringVal().equalsIgnoreCase("BETWEEN")) { + String between = null; + String and = null; + lexer.nextToken(); + if(lexer.token() == Token.LITERAL_INT) { + int val_1 = lexer.integerValue().intValue(); + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + between = "BETWEEN " + String.valueOf(val_1) + " PRECEDING"; + } else if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + between = "BETWEEN " + String.valueOf(val_1) + " FOLLOWING"; + } else { + throw new ParserException("syntax error"); + } + } else if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + between = "BETWEEN CURRENT ROW"; + } + } else if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + between = "BETWEEN UNBOUNDED PRECEDING"; + } + } else { + throw new ParserException("syntax error"); + } + if (lexer.stringVal().equalsIgnoreCase("AND")) { + lexer.nextToken(); + if(lexer.token() == Token.LITERAL_INT) { + System.out.println("yes, inside and"); + int val_2 = lexer.integerValue().intValue(); + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + and = " AND " + String.valueOf(val_2) + " PRECEDING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } else if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + and = " AND " + String.valueOf(val_2) + " FOLLOWING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } else { + throw new ParserException("syntax error"); + } + } else if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + and = " AND CURRENT ROW"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } + } else if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + and = " AND UNBOUNDED FOLLOWING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } + } else { + throw new ParserException("syntax error"); + } + } else { + throw new ParserException("syntax error"); + } + } else { + if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + windowing.setExpr(new SQLIdentifierExpr("CURRENT ROW")); + over.setWindowing(windowing); + } else { + throw new ParserException("syntax error"); + } + } + if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + System.out.println("yes, inside!!"); + windowing.setExpr(new SQLIdentifierExpr("UNBOUNDED PRECEDING")); + } else { + throw new ParserException("syntax error"); + } + } + } + over.setWindowing(windowing); + } + } + + accept(Token.RPAREN); + + aggregateExpr.setOver(over); + } + return aggregateExpr; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java index 2fe2c5917e..1506862e0f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java @@ -15,8 +15,15 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.util.JdbcConstants; public class TeradataStatementParser extends SQLStatementParser { @@ -27,5 +34,58 @@ public class TeradataStatementParser extends SQLStatementParser { public TeradataStatementParser(Lexer lexer){ super(new TeradataExprParser(lexer)); } + + public TeradataExprParser getExprParser() { + return (TeradataExprParser) exprParser; + } + + public TeradataSelectParser createSQLSelectParser() { + return new TeradataSelectParser(this.exprParser); + } + + public SQLSelectStatement parseSelect() { + TeradataSelectParser selectParser = new TeradataSelectParser(this.exprParser); + return new SQLSelectStatement(selectParser.select(), JdbcConstants.TERADATA); + } + + public void parseStatementList(List statementList, int max) { + for (;;) { + if (max != -1) { + if (statementList.size() >= max) { + return; + } + } + + if (lexer.token() == Token.EOF) { + return; + } + if (lexer.token() == Token.END) { + return; + } + if (lexer.token() == Token.ELSE) { + return; + } + + if (lexer.token() == (Token.SEMI)) { + lexer.nextToken(); + continue; + } + + if (lexer.token() == (Token.SELECT)) { + statementList.add(parseSelect()); + continue; + } else { + super.parseStatementList(statementList, max); + } + } + } + + public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.SEL) { + statementList.add(parseSelect()); + return true; + } + return false; + } }