Merge pull request #1149 from usakey/master

Add implementation for Teradata SQL parser SELECT
This commit is contained in:
wenshao
2016-04-01 08:50:27 +08:00
30 changed files with 2177 additions and 4 deletions
@@ -172,6 +172,14 @@ public class SQLUtils {
public static String toSQLServerString(SQLObject sqlObject, FormatOption option) {
return toSQLString(sqlObject, JdbcConstants.SQL_SERVER, option);
}
public static String toTeradataString(SQLObject sqlObject) {
return toTeradataString(sqlObject, null);
}
public static String toTeradataString(SQLObject sqlObject, FormatOption option) {
return toSQLString(sqlObject, JdbcConstants.TERADATA, option);
}
public static String formatPGSql(String sql, FormatOption option) {
return format(sql, JdbcConstants.POSTGRESQL, option);
@@ -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;
}
}
@@ -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;
}
}
@@ -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 {
}
@@ -0,0 +1,78 @@
package com.alibaba.druid.sql.dialect.teradata.ast.expr;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLExprImpl;
import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class TeradataIntervalExpr extends SQLExprImpl implements TeradataExpr {
private SQLExpr value;
private TeradataIntervalUnit unit;
public TeradataIntervalExpr() {
}
public SQLExpr getValue() {
return this.value;
}
public void setValue(SQLExpr value) {
this.value = value;
}
public TeradataIntervalUnit getUnit() {
return unit;
}
public void setUnit(TeradataIntervalUnit unit) {
this.unit = unit;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TeradataIntervalExpr other = (TeradataIntervalExpr) obj;
if (unit != other.unit) {
return false;
}
if (value == null) {
if (other.value != null) {
return false;
}
} else if (!value.equals(other.value)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((unit == null)? 0 : unit.hashCode());
result = prime * result + ((value == null)? 0 : value.hashCode());
return result;
}
@Override
protected void accept0(SQLASTVisitor visitor) {
TeradataASTVisitor tdVisitor = (TeradataASTVisitor) visitor;
if (tdVisitor.visit(this)) {
acceptChild(visitor, this.value);
}
tdVisitor.endVisit(this);
}
}
@@ -0,0 +1,14 @@
package com.alibaba.druid.sql.dialect.teradata.ast.expr;
public enum TeradataIntervalUnit {
YEAR, MONTH, DAY,
HOUR, MINUTE, SECOND;
public final String name_lcase;
private TeradataIntervalUnit() {
this.name_lcase = name().toLowerCase();
}
}
@@ -0,0 +1,56 @@
package com.alibaba.druid.sql.dialect.teradata.ast.stmt;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class TeradataInsertStatement extends SQLInsertStatement {
private List<ValuesClause> valuesList = new ArrayList<ValuesClause>();
public ValuesClause getValues() {
if (valuesList.size() == 0) {
return null;
}
return valuesList.get(0);
}
public void setValues(ValuesClause values) {
if (valuesList.size() == 0) {
valuesList.add(values);
} else {
valuesList.set(0, values);
}
}
public List<ValuesClause> getValuesList() {
return valuesList;
}
public void addValueCause(ValuesClause valueClause) {
valueClause.setParent(this);
valuesList.add(valueClause);
}
@Override
protected void accept0(SQLASTVisitor visitor) {
if (visitor instanceof TeradataASTVisitor) {
accept0((TeradataASTVisitor) visitor);
} else {
throw new IllegalArgumentException("not support visitor type : " + visitor.getClass().getName());
}
}
protected void accept0(TeradataASTVisitor visitor) {
if (visitor.visit(this)) {
this.acceptChild(visitor, getTableSource());
this.acceptChild(visitor, getColumns());
this.acceptChild(visitor, getValuesList());
this.acceptChild(visitor, getQuery());
}
visitor.endVisit(this);
}
}
@@ -0,0 +1,49 @@
package com.alibaba.druid.sql.dialect.teradata.ast.stmt;
import com.alibaba.druid.sql.SQLUtils;
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;
}
public String toString() {
return SQLUtils.toTeradataString(this);
}
}
@@ -15,8 +15,25 @@
*/
package com.alibaba.druid.sql.dialect.teradata.parser;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLKeep;
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.SQLCastExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
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.dialect.teradata.ast.expr.TeradataIntervalExpr;
import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalUnit;
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.SQLSelectParser;
import com.alibaba.druid.sql.parser.Token;
import com.alibaba.druid.util.JdbcConstants;
public class TeradataExprParser extends SQLExprParser {
@@ -35,5 +52,276 @@ 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();
}
}
public SQLSelectParser createSelectParser() {
return new TeradataSelectParser(this);
}
protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) {
if (acceptLPAREN) {
accept(Token.LPAREN);
}
if (expr instanceof SQLIdentifierExpr) {
String methodName = ((SQLIdentifierExpr) expr).getName();
SQLMethodInvokeExpr methodExpr = new SQLMethodInvokeExpr(methodName);
if ("trim".equalsIgnoreCase(methodName)) {
if (identifierEquals("LEADING")
|| identifierEquals("TRAILING")
|| identifierEquals("BOTH")) {
methodExpr.putAttribute("trim_option", lexer.stringVal());
lexer.nextToken();
if (lexer.token() == Token.LITERAL_CHARS) {
SQLExpr trim_character = this.primary();
trim_character.setParent(methodExpr);
methodExpr.putAttribute("trim_character", trim_character);
}
} else {
SQLExpr trim_character = this.primary();
trim_character.setParent(methodExpr);
methodExpr.putAttribute("trim_character", trim_character);
}
if (lexer.token() == Token.FROM) {
lexer.nextToken();
SQLExpr trim_source = this.expr();
methodExpr.addParameter(trim_source);
}
accept(Token.RPAREN);
return primaryRest(methodExpr);
}
}
return super.methodRest(expr, false);
}
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;
}
protected SQLExpr parseInterval() {
accept(Token.INTERVAL);
if (lexer.token() == Token.LITERAL_CHARS) {
SQLExpr value = expr();
if (lexer.token() != Token.IDENTIFIER) {
throw new ParserException("Syntax error");
}
String unit = lexer.stringVal();
lexer.nextToken();
TeradataIntervalExpr intervalExpr = new TeradataIntervalExpr();
intervalExpr.setValue(value);
intervalExpr.setUnit(TeradataIntervalUnit.valueOf(unit.toUpperCase()));
return intervalExpr;
} else {
throw new ParserException("TODO with other interval");
}
}
}
@@ -15,26 +15,140 @@
*/
package com.alibaba.druid.sql.dialect.teradata.parser;
import static com.alibaba.druid.sql.parser.LayoutCharacters.EOI;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.druid.sql.parser.Keywords;
import com.alibaba.druid.sql.parser.Lexer;
import com.alibaba.druid.sql.parser.NotAllowCommentException;
import com.alibaba.druid.sql.parser.Token;
public class TeradataLexer extends Lexer {
public final static Keywords DEFAULT_PG_KEYWORDS;
public final static Keywords DEFAULT_TD_KEYWORDS;
static {
Map<String, Token> map = new HashMap<String, Token>();
map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
DEFAULT_PG_KEYWORDS = new Keywords(map);
map.put("SEL", Token.SEL);
map.put("LOCKING", Token.LOCKING);
map.put("ACCESS", Token.ACCESS);
// map.put("PARTITION", Token.PARTITION);
// map.put("PARTITIONED", Token.PARTITIONED);
map.put("RANGE_N", Token.RANGE_N);
map.put("VOLATILE", Token.VOLATILE);
map.put("MULTISET", Token.MULTISET);
map.put("FORMAT", Token.FORMAT);
DEFAULT_TD_KEYWORDS = new Keywords(map);
}
public TeradataLexer(String input){
super(input);
super.keywods = DEFAULT_PG_KEYWORDS;
super.keywods = DEFAULT_TD_KEYWORDS;
}
public void scanComment() {
if (ch != '/' && ch != '-') {
throw new IllegalStateException();
}
mark = pos;
bufPos = 0;
scanChar();
// /*+ */
if (ch == '*') {
scanChar();
bufPos++;
while (ch == ' ') {
scanChar();
bufPos++;
}
boolean isHint = false;
int startHintSp = bufPos + 1;
if (ch == '+') {
isHint = true;
scanChar();
bufPos++;
}
for (;;) {
if (ch == '*' && charAt(pos + 1) == '/') {
bufPos += 2;
scanChar();
scanChar();
break;
}
scanChar();
bufPos++;
}
if (isHint) {
stringVal = subString(mark + startHintSp, (bufPos - startHintSp) - 1);
token = Token.HINT;
} else {
stringVal = subString(mark, bufPos);
token = Token.MULTI_LINE_COMMENT;
if (keepComments) {
addComment(stringVal);
}
}
if (token != Token.HINT && !isAllowComment()) {
throw new NotAllowCommentException();
}
return;
}
if (!isAllowComment()) {
throw new NotAllowCommentException();
}
if (ch == '/' || ch == '-') {
scanChar();
bufPos++;
for (;;) {
if (ch == '\r') {
if (charAt(pos + 1) == '\n') {
bufPos += 2;
scanChar();
break;
}
bufPos++;
break;
} else if (ch == EOI) {
break;
}
if (ch == '\n') {
scanChar();
bufPos++;
break;
}
scanChar();
bufPos++;
}
stringVal = subString(mark + 1, bufPos);
token = Token.LINE_COMMENT;
if (keepComments) {
addComment(stringVal);
}
endOfComment = isEOF();
return;
}
}
}
@@ -0,0 +1,62 @@
package com.alibaba.druid.sql.dialect.teradata.parser;
import com.alibaba.druid.sql.ast.SQLSetQuantifier;
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.SQLExprParser;
import com.alibaba.druid.sql.parser.SQLSelectParser;
import com.alibaba.druid.sql.parser.Token;
public class TeradataSelectParser extends SQLSelectParser{
public TeradataSelectParser(SQLExprParser exprParser) {
super(exprParser);
}
public TeradataSelectParser(String sql) {
this(new TeradataExprParser(sql));
}
public SQLSelectQuery query() {
if (lexer.token() == Token.LPAREN) {
lexer.nextToken();
SQLSelectQuery select = query();
accept(Token.RPAREN);
return queryRest(select);
}
TeradataSelectQueryBlock queryBlock = new TeradataSelectQueryBlock();
if (lexer.token() == Token.SEL
|| lexer.token() == Token.SELECT) {
lexer.nextToken();
if (lexer.token() == Token.COMMENT) {
lexer.nextToken();
}
if (lexer.token() == (Token.DISTINCT)) {
queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT);
lexer.nextToken();
} else if (lexer.token() == (Token.ALL)) {
queryBlock.setDistionOption(SQLSetQuantifier.ALL);
lexer.nextToken();
}
parseSelectList(queryBlock);
}
parseFrom(queryBlock);
parseWhere(queryBlock);
parseGroupBy(queryBlock);
queryBlock.setOrderBy(this.exprParser.parseOrderBy());
return queryRest(queryBlock);
}
}
@@ -15,8 +15,25 @@
*/
package com.alibaba.druid.sql.dialect.teradata.parser;
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.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause;
import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataInsertStatement;
import com.alibaba.druid.sql.parser.Lexer;
import com.alibaba.druid.sql.parser.ParserException;
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 +44,212 @@ 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 SQLInsertStatement parseInsert() {
TeradataInsertStatement insertStatement = new TeradataInsertStatement();
if (lexer.token() == Token.INSERT) {
lexer.nextToken();
if (lexer.token() == Token.INTO) {
lexer.nextToken();
}
SQLName tableName = this.exprParser.name();
insertStatement.setTableName(tableName);
if (lexer.token() == Token.IDENTIFIER && !identifierEquals("VALUE")) {
insertStatement.setAlias(lexer.stringVal());
lexer.nextToken();
}
}
int columnSize = 0;
if (lexer.token() == (Token.LPAREN)) {
lexer.nextToken();
if (lexer.token() == (Token.SELECT)) {
SQLSelect select = this.exprParser.createSelectParser().select();
select.setParent(insertStatement);
insertStatement.setQuery(select);
} else {
this.exprParser.exprList(insertStatement.getColumns(), insertStatement);
columnSize = insertStatement.getColumns().size();
}
accept(Token.RPAREN);
}
if (lexer.token() == Token.VALUES || identifierEquals("VALUE")) {
lexer.nextTokenLParen();
parseValueClause(insertStatement.getValuesList(), columnSize);
} else if (lexer.token() == Token.SET) {
lexer.nextToken();
SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
insertStatement.getValuesList().add(values);
for (;;) {
SQLName name = this.exprParser.name();
insertStatement.addColumn(name);
if (lexer.token() == Token.EQ) {
lexer.nextToken();
} else {
accept(Token.COLONEQ);
}
values.addValue(this.exprParser.expr());
if (lexer.token() == Token.COMMA) {
lexer.nextToken();
continue;
}
break;
}
} else if (lexer.token() == (Token.SELECT)) {
SQLSelect select = this.exprParser.createSelectParser().select();
select.setParent(insertStatement);
insertStatement.setQuery(select);
} else if (lexer.token() == (Token.LPAREN)) {
lexer.nextToken();
SQLSelect select = this.exprParser.createSelectParser().select();
select.setParent(insertStatement);
insertStatement.setQuery(select);
accept(Token.RPAREN);
}
return insertStatement;
}
private void parseValueClause(List<ValuesClause> valueClauseList, int columnSize) {
for (;;) {
if (lexer.token() != Token.LPAREN) {
throw new ParserException("syntax error, expect ')'");
}
lexer.nextTokenValue();
if (lexer.token() != Token.RPAREN) {
List<SQLExpr> valueExprList;
if (columnSize > 0) {
valueExprList = new ArrayList<SQLExpr>(columnSize);
} else {
valueExprList = new ArrayList<SQLExpr>();
}
for (;;) {
SQLExpr expr;
if (lexer.token() == Token.LITERAL_INT) {
expr = new SQLIntegerExpr(lexer.integerValue());
lexer.nextTokenComma();
} else if (lexer.token() == Token.LITERAL_CHARS) {
expr = new SQLCharExpr(lexer.stringVal());
lexer.nextTokenComma();
} else if (lexer.token() == Token.LITERAL_NCHARS) {
expr = new SQLNCharExpr(lexer.stringVal());
lexer.nextTokenComma();
} else {
expr = exprParser.expr();
}
if (lexer.token() == Token.COMMA) {
valueExprList.add(expr);
lexer.nextTokenValue();
continue;
} else if (lexer.token() == Token.RPAREN) {
valueExprList.add(expr);
break;
} else {
expr = this.exprParser.primaryRest(expr);
if (lexer.token() != Token.COMMA && lexer.token() != Token.RPAREN) {
expr = this.exprParser.exprRest(expr);
}
valueExprList.add(expr);
if (lexer.token() == Token.COMMA) {
lexer.nextToken();
continue;
} else {
break;
}
}
}
SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(valueExprList);
valueClauseList.add(values);
} else {
SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(new ArrayList<SQLExpr>(0));
valueClauseList.add(values);
}
if (lexer.token() != Token.RPAREN) {
throw new ParserException("syntax error");
}
if (!parseCompleteValues && valueClauseList.size() >= parseValuesSize) {
lexer.skipToEOF();
break;
}
lexer.nextTokenComma();
if (lexer.token() == Token.COMMA) {
lexer.nextTokenLParen();
continue;
} else {
break;
}
}
}
public void parseStatementList(List<SQLStatement> 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<SQLStatement> statementList) {
if (lexer.token() == Token.SEL) {
statementList.add(parseSelect());
return true;
}
return false;
}
}
@@ -15,8 +15,26 @@
*/
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.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.SQLASTVisitor;
public interface TeradataASTVisitor extends SQLASTVisitor {
boolean visit(TeradataAnalyticWindowing x);
void endVisit(TeradataAnalyticWindowing x);
boolean visit(TeradataAnalytic x);
boolean visit(SQLSelectQueryBlock x);
boolean visit(SQLSelect x);
boolean visit(TeradataIntervalExpr x);
void endVisit(TeradataIntervalExpr x);
}
@@ -15,8 +15,41 @@
*/
package com.alibaba.druid.sql.dialect.teradata.visitor;
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.SQLASTVisitorAdapter;
public class TeradataASTVisitorAdapter extends SQLASTVisitorAdapter implements TeradataASTVisitor {
@Override
public boolean visit(TeradataAnalyticWindowing x) {
// TODO Auto-generated method stub
return true;
}
@Override
public void endVisit(TeradataAnalyticWindowing x) {
// TODO Auto-generated method stub
}
@Override
public boolean visit(TeradataAnalytic x) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean visit(TeradataIntervalExpr x) {
// TODO Auto-generated method stub
return true;
}
@Override
public void endVisit(TeradataIntervalExpr x) {
// TODO Auto-generated method stub
}
}
@@ -0,0 +1,73 @@
package com.alibaba.druid.sql.dialect.teradata.visitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.visitor.SQLEvalVisitor;
import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils;
import com.alibaba.druid.sql.visitor.functions.Function;
public class TeradataEvalVisitor extends TeradataASTVisitorAdapter implements SQLEvalVisitor {
private Map<String, Function> functions = new HashMap<String, Function>();
private List<Object> parameters = new ArrayList<Object>();
private int variantIndex = -1;
private boolean markVariantIndex = true;
public TeradataEvalVisitor() {
this(new ArrayList<Object>(1));
}
public TeradataEvalVisitor(List<Object> parameters) {
this.parameters = parameters;
}
@Override
public Function getFunction(String funcName) {
return functions.get(funcName);
}
@Override
public void registerFunction(String funcName, Function function) {
functions.put(funcName, function);
}
@Override
public void unregisterFunction(String funcName) {
functions.remove(funcName);
}
@Override
public List<Object> getParameters() {
return parameters;
}
@Override
public void setParameters(List<Object> parameters) {
this.parameters = parameters;
}
@Override
public int incrementAndGetVariantIndex() {
return ++variantIndex;
}
@Override
public boolean isMarkVariantIndex() {
return markVariantIndex;
}
@Override
public void setMarkVariantIndex(boolean markVariantIndex) {
this.markVariantIndex = markVariantIndex;
}
public boolean visit(SQLBinaryOpExpr x) {
return SQLEvalVisitorUtils.visit(this, x);
}
}
@@ -15,6 +15,18 @@
*/
package com.alibaba.druid.sql.dialect.teradata.visitor;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLSetQuantifier;
import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
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.dialect.teradata.ast.stmt.TeradataSelectQueryBlock;
import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;
public class TeradataOutputVisitor extends SQLASTOutputVisitor implements TeradataASTVisitor {
@@ -22,5 +34,173 @@ public class TeradataOutputVisitor extends SQLASTOutputVisitor implements Terada
public TeradataOutputVisitor(Appendable appender){
super(appender);
}
public boolean visit(TeradataAnalytic x) {
print0(ucase ? "OVER (" : "over (");
boolean space = false;
if (x.getPartitionBy().size() > 0) {
print0(ucase ? "PARTITION BY " : "partition by ");
printAndAccept(x.getPartitionBy(), ", ");
space = true;
}
if (x.getOrderBy() != null) {
if (space) {
print(' ');
}
x.getOrderBy().accept(this);
space = true;
}
if (x.getWindowing() != null) {
if (space) {
print(' ');
}
x.getWindowing().accept(this);
}
print(')');
return false;
}
@Override
public boolean visit(TeradataAnalyticWindowing x) {
print0(x.getType().name().toUpperCase());
print(' ');
x.getExpr().accept(this);
return false;
}
@Override
public void endVisit(TeradataAnalyticWindowing x) {
// TODO Auto-generated method stub
}
public boolean visit(SQLSelectQueryBlock select) {
if(select instanceof TeradataSelectQueryBlock) {
return visit((TeradataSelectQueryBlock) select);
}
return super.visit(select);
}
public boolean visit(TeradataSelectQueryBlock x) {
if (x.getOrderBy() != null) {
x.getOrderBy().setParent(x);
}
print0(ucase ? "SELECT " : "select ");
if (SQLSetQuantifier.ALL == x.getDistionOption()) {
print0(ucase ? "ALL " : "all ");
} else if (SQLSetQuantifier.DISTINCT == x.getDistionOption()) {
print0(ucase ? "DISTINCT " : "distinct ");
} else if (SQLSetQuantifier.UNIQUE == x.getDistionOption()) {
print0(ucase ? "UNIQUE " : "unique ");
}
printSelectList(x.getSelectList());
if (x.getInto() != null) {
println();
print0(ucase ? "INTO " : "into ");
x.getInto().accept(this);
}
println();
print0(ucase ? "FROM " : "from ");
if (x.getFrom() == null) {
print0(ucase ? "DUAL" : "dual");
} else {
x.getFrom().setParent(x);
x.getFrom().accept(this);
}
if (x.getWhere() != null) {
println();
print0(ucase ? "WHERE " : "where ");
x.getWhere().setParent(x);
x.getWhere().accept(this);
}
if (x.getGroupBy() != null) {
println();
x.getGroupBy().accept(this);
}
if (x.getOrderBy() != null) {
println();
x.getOrderBy().accept(this);
}
return false;
}
public boolean visit(SQLSelect x) {
return super.visit(x);
}
protected void visitAggreateRest(SQLAggregateExpr aggregateExpr) {
{
SQLOrderBy value = (SQLOrderBy) aggregateExpr.getAttribute("ORDER BY");
if (value != null) {
print(' ');
((SQLObject) value).accept(this);
}
}
{
Object value = aggregateExpr.getAttribute("SEPARATOR");
if (value != null) {
print0(ucase ? " SEPARATOR " : " separator ");
((SQLObject) value).accept(this);
}
}
}
@Override
public boolean visit(TeradataIntervalExpr x) {
print0(ucase ? "INTERVAL " : "interval ");
x.getValue().accept(this);
print(' ');
print0(ucase ? x.getUnit().name() : x.getUnit().name_lcase);
return false;
}
@Override
public void endVisit(TeradataIntervalExpr x) {
}
public void endVisit(SQLMethodInvokeExpr x) {
}
public boolean visit(SQLMethodInvokeExpr x) {
if ("trim".equalsIgnoreCase(x.getMethodName())) {
SQLExpr trim_character = (SQLExpr) x.getAttribute("trim_character");
print0(x.getMethodName());
print('(');
String trim_option = (String) x.getAttribute("trim_option");
if (trim_option != null && trim_option.length() != 0) {
print0(trim_option);
print(' ');
}
if (trim_character != null) {
trim_character.accept(this);
}
if (x.getParameters().size() > 0) {
print0(ucase ? " FROM " : " from ");
x.getParameters().get(0).accept(this);
}
print(')');
return false;
}
return super.visit(x);
}
}
@@ -15,6 +15,9 @@
*/
package com.alibaba.druid.sql.dialect.teradata.visitor;
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.util.JdbcUtils;
@@ -22,7 +25,34 @@ public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements Tera
@Override
public String getDbType() {
return JdbcUtils.POSTGRESQL;
return JdbcUtils.TERADATA;
}
@Override
public boolean visit(TeradataAnalyticWindowing x) {
return true;
}
@Override
public void endVisit(TeradataAnalyticWindowing x) {
// TODO Auto-generated method stub
}
@Override
public boolean visit(TeradataAnalytic x) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean visit(TeradataIntervalExpr x) {
return true;
}
@Override
public void endVisit(TeradataIntervalExpr x) {
}
}
@@ -261,6 +261,16 @@ public enum Token {
PARTITION,
PARTITIONED,
OVERWRITE,
// Teradata
SEL("SEL"),
LOCKING("LOCKING"),
ACCESS("ACCESS"),
VOLATILE("VOLATILE"),
MULTISET("MULTISET"),
POSITION("POSITION"),
RANGE_N("RANGE_N"),
FORMAT("FORMAT"),
LPAREN("("),
RPAREN(")"),
@@ -44,6 +44,7 @@ import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlEvalVisitorImpl;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleEvalVisitor;
import com.alibaba.druid.sql.dialect.postgresql.visitor.PGEvalVisitor;
import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerEvalVisitor;
import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataEvalVisitor;
import com.alibaba.druid.sql.visitor.functions.Ascii;
import com.alibaba.druid.sql.visitor.functions.Bin;
import com.alibaba.druid.sql.visitor.functions.BitLength;
@@ -178,6 +179,10 @@ public class SQLEvalVisitorUtils {
if (JdbcUtils.DB2.equals(dbType)) {
return new DB2EvalVisitor();
}
if (JdbcUtils.TERADATA.equals(dbType)) {
return new TeradataEvalVisitor();
}
return new SQLEvalVisitorImpl();
}