diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java index 409d5cf853..51f931b432 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLBinaryOperator.java @@ -28,6 +28,7 @@ public enum SQLBinaryOperator { Multiply("*", 60), Divide("/", 60), Modulus("%", 60), + Mod("MOD", 60), Add("+", 70), Subtract("-", 70), 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 index cefebc2ea8..2296c9314c 100644 --- 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 @@ -1,6 +1,7 @@ package com.alibaba.druid.sql.dialect.teradata.ast.stmt; import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; @@ -8,6 +9,7 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { private SQLOrderBy orderBy; + private SQLExpr qualifyClause; public TeradataSelectQueryBlock() { @@ -30,6 +32,7 @@ public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { acceptChild(visitor, this.from); acceptChild(visitor, this.where); acceptChild(visitor, this.groupBy); + acceptChild(visitor, this.qualifyClause); acceptChild(visitor, this.orderBy); } visitor.endVisit(this); @@ -43,6 +46,17 @@ public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { this.orderBy = orderBy; } + public SQLExpr getQualifyClause() { + return qualifyClause; + } + + public void setQualifyClause(SQLExpr qualify) { + if (qualify != null) { + qualify.setParent(this); + } + this.qualifyClause = qualify; + } + public String toString() { return SQLUtils.toTeradataString(this); } 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 93ca424523..56e44bdeb1 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 @@ -21,6 +21,10 @@ import com.alibaba.druid.sql.ast.SQLKeep.DenseRank; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; +import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; @@ -63,7 +67,6 @@ public class TeradataExprParser extends SQLExprParser { sqlExpr = queryExpr; return primaryRest(sqlExpr); case CAST: - System.out.println("cast test!"); lexer.nextToken(); accept(Token.LPAREN); SQLCastExpr cast = new SQLCastExpr(); @@ -87,6 +90,27 @@ public class TeradataExprParser extends SQLExprParser { return new TeradataSelectParser(this); } + // for cases like: between DATE '2010-01-01' and DATE '2011-01-01' + public SQLExpr relationalRest(SQLExpr expr) { + if (lexer.token() == Token.BETWEEN) { + lexer.nextToken(); + SQLExpr beginExpr = bitOr(); + SQLExpr endExpr; + if (lexer.token() != Token.AND) { + lexer.nextToken(); + accept(Token.AND); + endExpr = bitOr(); + lexer.nextToken(); + } else { + accept(Token.AND); + endExpr = bitOr(); + } + expr = new SQLBetweenExpr(expr, beginExpr, endExpr); + return expr; + } + return super.relationalRest(expr); + } + protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) { if (acceptLPAREN) { accept(Token.LPAREN); @@ -121,10 +145,25 @@ public class TeradataExprParser extends SQLExprParser { accept(Token.RPAREN); return primaryRest(methodExpr); } + } else if (expr instanceof SQLCaseExpr) { + lexer.nextToken(); + accept(Token.RPAREN); + return primaryRest(expr); } return super.methodRest(expr, false); } + public SQLExpr multiplicativeRest(SQLExpr expr) { + if (lexer.token() == Token.MOD) { + lexer.nextToken(); + SQLExpr rightExp = bitXor(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Mod, rightExp, getDbType()); + expr = multiplicativeRest(expr); + return expr; + } + return super.multiplicativeRest(expr); + } + protected SQLAggregateExpr parseAggregateExpr(String methodName) { methodName = methodName.toUpperCase(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java index 42bbd60dc1..4defb47f59 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java @@ -45,6 +45,10 @@ public class TeradataLexer extends Lexer { map.put("FORMAT", Token.FORMAT); + map.put("QUALIFY", Token.QUALIFY); + + map.put("MOD", Token.MOD); + DEFAULT_TD_KEYWORDS = new Keywords(map); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java index bdd0fbd204..e5c166e2c5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java @@ -1,9 +1,17 @@ package com.alibaba.druid.sql.dialect.teradata.parser; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLDefaultExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; +import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.Token; @@ -54,9 +62,49 @@ public class TeradataSelectParser extends SQLSelectParser{ parseGroupBy(queryBlock); + parserQualify(queryBlock); + queryBlock.setOrderBy(this.exprParser.parseOrderBy()); return queryRest(queryBlock); } + private void parserQualify(TeradataSelectQueryBlock queryBlock) { + if (lexer.token() != Token.QUALIFY) { + return; + } + + lexer.nextToken(); + if (lexer.token() == Token.LITERAL_INT) { + lexer.nextToken(); + // possibly =, >=, <= + // ignore this for now + lexer.nextToken(); + + if (lexer.token() == Token.IDENTIFIER) { + SQLExpr expr = new SQLIdentifierExpr(lexer.stringVal()); + lexer.nextToken(); + if (lexer.token() != Token.COMMA) { + expr = this.exprParser.primaryRest(expr); + } + // TODO: add qualify clause into queryBlock + queryBlock.setQualifyClause(expr); + } else { + throw new ParserException("not support token:" + lexer.token()); + } + } else if (lexer.token() == Token.IDENTIFIER) { + SQLExpr expr = new SQLIdentifierExpr(lexer.stringVal()); + lexer.nextToken(); + if (lexer.token() != Token.COMMA) { + expr = this.exprParser.primaryRest(expr); + } + lexer.nextToken(); + lexer.nextToken(); + // TODO: add qualify clause into queryBlock + queryBlock.setQualifyClause(expr); + } else { + throw new ParserException("not support token:" + lexer.token()); + } + } + } 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 5ca482bcfd..5a8354e1c3 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 @@ -118,7 +118,8 @@ public class TeradataStatementParser extends SQLStatementParser { break; } - } else if (lexer.token() == (Token.SELECT)) { + } else if (lexer.token() == (Token.SELECT) + || lexer.token() == (Token.SEL)) { SQLSelect select = this.exprParser.createSelectParser().select(); select.setParent(insertStatement); insertStatement.setQuery(select); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java index 34d49ffbed..3fb1d36b69 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.teradata.visitor; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; 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.dialect.teradata.ast.expr.TeradataIntervalExpr; @@ -37,4 +38,8 @@ public interface TeradataASTVisitor extends SQLASTVisitor { boolean visit(TeradataIntervalExpr x); void endVisit(TeradataIntervalExpr x); + + boolean visit(SQLSubqueryTableSource x); + + void endVisit(SQLSubqueryTableSource x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java index fffec46566..706eb38799 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java @@ -15,14 +15,28 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; 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.dialect.teradata.ast.expr.TeradataIntervalExpr; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.stat.TableStat.Column; import com.alibaba.druid.util.JdbcUtils; public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements TeradataASTVisitor { + protected final Map aliasQueryMap = new LinkedHashMap(); + @Override public String getDbType() { return JdbcUtils.TERADATA; @@ -54,5 +68,69 @@ public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements Tera public void endVisit(TeradataIntervalExpr x) { } + + @Override + public boolean visit(SQLSubqueryTableSource x) { + accept(x.getSelect()); + + String table = (String) x.getSelect().getAttribute(ATTR_TABLE); + if (aliasMap != null && x.getAlias() != null) { + if (table != null) { + this.aliasMap.put(x.getAlias(), table); + } + addSubQuery(x.getAlias(), x.getSelect()); + this.setCurrentTable(x.getAlias()); + } + + if (table != null) { + x.putAttribute(ATTR_TABLE, table); + } + + return false; + } + + @Override + public void endVisit(SQLSubqueryTableSource x) { + + } + + public boolean visit(SQLSelectItem x) { + x.getExpr().accept(this); + + String alias = x.getAlias(); + + Map aliasMap = this.getAliasMap(); + if (alias != null && (!alias.isEmpty()) && aliasMap != null) { + if (x.getExpr() instanceof SQLName) { + putAliasMap(aliasMap, alias, x.getExpr().toString()); + } else { + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + x.getExpr().accept(visitor); + putAliasMap(aliasMap, alias, null); + // Add expression alias into aliasQuery map + // in order to retrieve column info from aliasQueryMap + // if aliasMap value is null + String uniqueAliasName = this.getCurrentTable() == null ? + alias : this.getCurrentTable() + "." + alias; + addAliasQuery(uniqueAliasName, x.getExpr()); + } + } + + return false; + } + + protected void addAliasQuery(String alias, SQLObject query) { + String alias_lcase = alias.toLowerCase(); + aliasQueryMap.put(alias_lcase, query); + } + + public SQLObject getAliasQuery(String alias) { + String alias_lcase = alias.toLowerCase(); + return aliasQueryMap.get(alias_lcase); + } + + public Map getAliasQueryMap() { + return aliasQueryMap; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index ae8703e8bc..7a9031e8bd 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -382,7 +382,8 @@ public class SQLSelectParser extends SQLParser { if (lexer.token() == Token.LPAREN) { lexer.nextToken(); SQLTableSource tableSource; - if (lexer.token() == Token.SELECT || lexer.token() == Token.WITH) { + if (lexer.token() == Token.SELECT || lexer.token() == Token.WITH + || lexer.token == Token.SEL) { SQLSelect select = select(); accept(Token.RPAREN); SQLSelectQuery query = queryRest(select.getQuery()); 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 e327e59937..b3b8097e8b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -279,6 +279,8 @@ public enum Token { POSITION("POSITION"), RANGE_N("RANGE_N"), FORMAT("FORMAT"), + QUALIFY("QUALIFY"), + MOD("MOD"), LPAREN("("), RPAREN(")"), diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java index 9839bf9bc7..54dc56d25a 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SchemaStatVisitor.java @@ -760,12 +760,14 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter { Map aliasMap = getAliasMap(); if (aliasMap != null) { - if (aliasMap.containsKey(name)) { + if (aliasMap.containsKey(name) + && aliasMap.get(name) != null) { return aliasMap.get(name); } String name_lcase = name.toLowerCase(); - if (name_lcase != name && aliasMap.containsKey(name_lcase)) { + if (name_lcase != name && aliasMap.containsKey(name_lcase) + && aliasMap.get(name_lcase) != null) { return aliasMap.get(name_lcase); }