From 6f5bb55384c157615b81afb369d023475e530845 Mon Sep 17 00:00:00 2001 From: wenshao Date: Mon, 23 Dec 2013 23:59:03 +0800 Subject: [PATCH] add odps sql parser support --- .../java/com/alibaba/druid/sql/SQLUtils.java | 15 +- .../odps/ast/OdpsCreateTableStatement.java | 69 + .../sql/dialect/odps/ast/OdpsInsert.java | 79 + .../dialect/odps/ast/OdpsInsertStatement.java | 42 + .../odps/parser/OdpsCreateTableParser.java | 95 + .../dialect/odps/parser/OdpsExprParser.java | 29 + .../sql/dialect/odps/parser/OdpsLexer.java | 126 + .../odps/parser/OdpsStatementParser.java | 122 + .../dialect/odps/visitor/OdpsASTVisitor.java | 21 + .../odps/visitor/OdpsOutputVisitor.java | 263 ++ .../odps/visitor/OdpsSchemaStatVisitor.java | 43 + .../com/alibaba/druid/sql/parser/Lexer.java | 33 +- .../druid/sql/parser/SQLExprParser.java | 3278 ++++++++--------- .../druid/sql/parser/SQLParserUtils.java | 5 + .../com/alibaba/druid/sql/parser/Token.java | 5 + .../sql/visitor/SQLASTOutputVisitor.java | 61 +- .../com/alibaba/druid/util/JdbcConstants.java | 5 + .../druid/bvt/sql/odps/OdpsInsertTest.java | 91 + .../bvt/sql/odps/OdpsMultiInsertTest.java | 91 + .../druid/bvt/sql/odps/OdpsResourceTest.java | 91 + .../druid/bvt/sql/odps/OdpsSelectTest.java | 91 + src/test/resources/bvt/parser/odps-0.sql | 24 + src/test/resources/bvt/parser/odps-1.sql | 77 + src/test/resources/bvt/parser/odps-2.sql | 110 + src/test/resources/bvt/parser/odps-3.sql | 61 + src/test/resources/bvt/parser/z321.sql | 106 + 26 files changed, 3356 insertions(+), 1677 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsCreateTableStatement.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsert.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsertStatement.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsCreateTableParser.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsExprParser.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsInsertTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsMultiInsertTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsResourceTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsSelectTest.java create mode 100644 src/test/resources/bvt/parser/odps-0.sql create mode 100644 src/test/resources/bvt/parser/odps-1.sql create mode 100644 src/test/resources/bvt/parser/odps-2.sql create mode 100644 src/test/resources/bvt/parser/odps-3.sql create mode 100644 src/test/resources/bvt/parser/z321.sql diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index d82ea56868..d11bbaf3af 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.dialect.db2.visitor.DB2OutputVisitor; import com.alibaba.druid.sql.dialect.db2.visitor.DB2SchemaStatVisitor; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGOutputVisitor; @@ -68,6 +69,10 @@ public class SQLUtils { if (JdbcUtils.DB2.equals(dbType)) { return toDB2String(sqlObject); } + + if (JdbcUtils.ODPS.equals(dbType)) { + return toDB2String(sqlObject); + } return toSQLServerString(sqlObject); } @@ -80,10 +85,18 @@ public class SQLUtils { return sql; } + public static String toOdpsString(SQLObject sqlObject) { + StringBuilder out = new StringBuilder(); + sqlObject.accept(new OdpsOutputVisitor(out)); + + String sql = out.toString(); + return sql; + } + public static String toMySqlString(SQLObject sqlObject) { StringBuilder out = new StringBuilder(); sqlObject.accept(new MySqlOutputVisitor(out)); - + String sql = out.toString(); return sql; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsCreateTableStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsCreateTableStatement.java new file mode 100644 index 0000000000..deb632d6ef --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsCreateTableStatement.java @@ -0,0 +1,69 @@ +package com.alibaba.druid.sql.dialect.odps.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OdpsCreateTableStatement extends SQLCreateTableStatement { + + private boolean ifNotExiists = false; + + private SQLExprTableSource like; + + protected SQLExpr comment; + + protected List partitionColumns = new ArrayList(2); + + public boolean isIfNotExiists() { + return ifNotExiists; + } + + public void setIfNotExiists(boolean ifNotExiists) { + this.ifNotExiists = ifNotExiists; + } + + public SQLExprTableSource getLike() { + return like; + } + + public void setLike(SQLName like) { + this.setLike(new SQLExprTableSource(like)); + } + + public void setLike(SQLExprTableSource like) { + this.like = like; + } + + public SQLExpr getComment() { + return comment; + } + + public void setComment(SQLExpr comment) { + this.comment = comment; + } + + public List getPartitionColumns() { + return partitionColumns; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + accept0((OdpsASTVisitor) visitor); + } + + protected void accept0(OdpsASTVisitor visitor) { + if (visitor.visit(this)) { + this.acceptChild(visitor, tableSource); + this.acceptChild(visitor, tableElementList); + this.acceptChild(visitor, partitionColumns); + } + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsert.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsert.java new file mode 100644 index 0000000000..904e0196d1 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsert.java @@ -0,0 +1,79 @@ +package com.alibaba.druid.sql.dialect.odps.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObjectImpl; +import com.alibaba.druid.sql.ast.statement.SQLAssignItem; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + + +public class OdpsInsert extends SQLObjectImpl { + private boolean overwrite = false; + private List partitions = new ArrayList(); + protected SQLExprTableSource tableSource; + + private SQLSelect query; + + public boolean isOverwrite() { + return overwrite; + } + + public void setOverwrite(boolean overwrite) { + this.overwrite = overwrite; + } + + public List getPartitions() { + return partitions; + } + + public void setPartitions(List partitions) { + this.partitions = partitions; + } + + + public SQLExprTableSource getTableSource() { + return tableSource; + } + + public void setTableSource(SQLExprTableSource tableSource) { + if (tableSource != null) { + tableSource.setParent(this); + } + this.tableSource = tableSource; + } + + public void setTableSource(SQLName tableName) { + this.setTableSource(new SQLExprTableSource(tableName)); + } + + public SQLSelect getQuery() { + return query; + } + + public void setQuery(SQLSelect query) { + if (query != null) { + query.setParent(this); + } + this.query = query; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + accept0((OdpsASTVisitor) visitor); + } + + protected void accept0(OdpsASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, tableSource); + acceptChild(visitor, partitions); + acceptChild(visitor, query); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsertStatement.java new file mode 100644 index 0000000000..57670918cc --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/ast/OdpsInsertStatement.java @@ -0,0 +1,42 @@ +package com.alibaba.druid.sql.dialect.odps.ast; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.SQLStatementImpl; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class OdpsInsertStatement extends SQLStatementImpl implements SQLStatement { + + private SQLSubqueryTableSource from; + + private List items = new ArrayList(); + + public void setFrom(SQLSubqueryTableSource from) { + this.from = from; + } + + public SQLSubqueryTableSource getFrom() { + return from; + } + + public List getItems() { + return items; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + accept0((OdpsASTVisitor) visitor); + } + + protected void accept0(OdpsASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, from); + acceptChild(visitor, items); + } + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsCreateTableParser.java new file mode 100644 index 0000000000..f7e4ff4ec9 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsCreateTableParser.java @@ -0,0 +1,95 @@ +package com.alibaba.druid.sql.dialect.odps.parser; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; +import com.alibaba.druid.sql.parser.ParserException; +import com.alibaba.druid.sql.parser.SQLCreateTableParser; +import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.Token; + +public class OdpsCreateTableParser extends SQLCreateTableParser { + + public OdpsCreateTableParser(String sql){ + super(new OdpsExprParser(sql)); + } + + public OdpsCreateTableParser(SQLExprParser exprParser){ + super(exprParser); + } + + public SQLCreateTableStatement parseCrateTable(boolean acceptCreate) { + OdpsCreateTableStatement stmt = new OdpsCreateTableStatement(); + + if (acceptCreate) { + accept(Token.CREATE); + } + + accept(Token.TABLE); + + if (lexer.token() == Token.IF || identifierEquals("IF")) { + lexer.nextToken(); + accept(Token.NOT); + accept(Token.EXISTS); + + stmt.setIfNotExiists(true); + } + + stmt.setName(this.exprParser.name()); + + if (lexer.token() == Token.LIKE) { + lexer.nextToken(); + SQLName name = this.exprParser.name(); + stmt.setLike(name); + } else { + accept(Token.LPAREN); + + for (;;) { + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException("expect identifier"); + } + + SQLColumnDefinition column = this.exprParser.parseColumn(); + stmt.getTableElementList().add(column); + + if (!(lexer.token() == (Token.COMMA))) { + break; + } else { + lexer.nextToken(); + } + } + accept(Token.RPAREN); + } + + if (lexer.token() == Token.COMMENT) { + lexer.nextToken(); + stmt.setComment(this.exprParser.primary()); + } + + if (lexer.token() == Token.PARTITIONED) { + lexer.nextToken(); + accept(Token.BY); + accept(Token.LPAREN); + + for (;;) { + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException("expect identifier"); + } + + SQLColumnDefinition column = this.exprParser.parseColumn(); + stmt.getPartitionColumns().add(column); + + if (!(lexer.token() == (Token.COMMA))) { + break; + } else { + lexer.nextToken(); + } + } + + accept(Token.RPAREN); + } + + return stmt; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsExprParser.java new file mode 100644 index 0000000000..e9e5d5f03d --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsExprParser.java @@ -0,0 +1,29 @@ +package com.alibaba.druid.sql.dialect.odps.parser; + +import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.SQLExprParser; + +public class OdpsExprParser extends SQLExprParser { + + public final static String[] AGGREGATE_FUNCTIONS = { "AVG", // + "COUNT", // + "LAG", + "LEAD", + "MAX", // + "MIN", // + "STDDEV", // + "SUM", // + "ROW_NUMBER"// + }; + + public OdpsExprParser(Lexer lexer){ + super(lexer); + + this.aggregateFunctions = AGGREGATE_FUNCTIONS; + } + + public OdpsExprParser(String sql){ + this(new OdpsLexer(sql)); + this.lexer.nextToken(); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java new file mode 100644 index 0000000000..4f89be00e4 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsLexer.java @@ -0,0 +1,126 @@ +package com.alibaba.druid.sql.dialect.odps.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 OdpsLexer extends Lexer { + + public final static Keywords DEFAULT_ODPS_KEYWORDS; + + static { + Map map = new HashMap(); + + map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords()); + + map.put("PARTITION", Token.PARTITION); + map.put("PARTITIONED", Token.PARTITIONED); + map.put("OVERWRITE", Token.OVERWRITE); + map.put("OVER", Token.OVER); + + DEFAULT_ODPS_KEYWORDS = new Keywords(map); + } + + public OdpsLexer(String input){ + super(input); + super.keywods = DEFAULT_ODPS_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 (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; + return; + } + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java new file mode 100644 index 0000000000..c8c7ab7b2a --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/parser/OdpsStatementParser.java @@ -0,0 +1,122 @@ +package com.alibaba.druid.sql.dialect.odps.parser; + +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLAssignItem; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.parser.SQLCreateTableParser; +import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.SQLSelectParser; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.parser.Token; + +public class OdpsStatementParser extends SQLStatementParser { + + public OdpsStatementParser(String sql){ + super(new OdpsExprParser(sql)); + } + + public OdpsStatementParser(SQLExprParser exprParser){ + super(exprParser); + } + + public SQLCreateTableStatement parseCreateTable() { + SQLCreateTableParser parser = new OdpsCreateTableParser(this.exprParser); + return parser.parseCrateTable(); + } + + public SQLCreateTableParser getSQLCreateTableParser() { + return new OdpsCreateTableParser(this.exprParser); + } + + public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.FROM) { + SQLStatement stmt = this.parseInsert(); + statementList.add(stmt); + return true; + } + return false; + } + + public SQLStatement parseInsert() { + OdpsInsertStatement stmt = new OdpsInsertStatement(); + + if (lexer.token() == Token.FROM) { + lexer.nextToken(); + accept(Token.LPAREN); + + SQLSelectParser selectParser = createSQLSelectParser(); + SQLSelect select = selectParser.select(); + + accept(Token.RPAREN); + + String alias = lexer.stringVal(); + accept(Token.IDENTIFIER); + + SQLSubqueryTableSource from = new SQLSubqueryTableSource(select, alias); + + stmt.setFrom(from); + } + + for (;;) { + OdpsInsert insert = parseOdpsInsert(); + stmt.getItems().add(insert); + + if (lexer.token() != Token.INSERT) { + break; + } + } + + return stmt; + } + + public OdpsInsert parseOdpsInsert() { + OdpsInsert insert = new OdpsInsert(); + SQLSelectParser selectParser = createSQLSelectParser(); + + accept(Token.INSERT); + + if (lexer.token() == Token.INTO) { + lexer.nextToken(); + } else { + accept(Token.OVERWRITE); + insert.setOverwrite(true); + } + + accept(Token.TABLE); + insert.setTableSource(this.exprParser.name()); + + if (lexer.token() == Token.PARTITION) { + lexer.nextToken(); + accept(Token.LPAREN); + for (;;) { + SQLAssignItem ptExpr = new SQLAssignItem(); + ptExpr.setTarget(this.exprParser.name()); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + SQLExpr ptValue = this.exprParser.expr(); + ptExpr.setValue(ptValue); + } + insert.getPartitions().add(ptExpr); + if (!(lexer.token() == (Token.COMMA))) { + break; + } else { + lexer.nextToken(); + } + } + accept(Token.RPAREN); + } + + SQLSelect query = selectParser.select(); + insert.setQuery(query); + + return insert; + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java new file mode 100644 index 0000000000..aa52398f85 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsASTVisitor.java @@ -0,0 +1,21 @@ +package com.alibaba.druid.sql.dialect.odps.visitor; + +import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public interface OdpsASTVisitor extends SQLASTVisitor { + + void endVisit(OdpsCreateTableStatement x); + + boolean visit(OdpsCreateTableStatement x); + + void endVisit(OdpsInsertStatement x); + + boolean visit(OdpsInsertStatement x); + + void endVisit(OdpsInsert x); + + boolean visit(OdpsInsert x); +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java new file mode 100644 index 0000000000..6cce9b5c80 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsOutputVisitor.java @@ -0,0 +1,263 @@ +package com.alibaba.druid.sql.dialect.odps.visitor; + +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLCaseExpr; +import com.alibaba.druid.sql.ast.statement.SQLAssignItem; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource.JoinType; +import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; + +public class OdpsOutputVisitor extends SQLASTOutputVisitor implements OdpsASTVisitor { + + public OdpsOutputVisitor(Appendable appender){ + super(appender); + } + + public boolean visit(OdpsCreateTableStatement x) { + if (x.isIfNotExiists()) { + print("CREATE TABLE IF NOT EXISTS "); + } else { + print("CREATE TABLE "); + } + + x.getName().accept(this); + + x.getName().accept(this); + + if (x.getLike() != null) { + print(" LIKE "); + x.getLike().accept(this); + } + + int size = x.getTableElementList().size(); + if (size > 0) { + print(" ("); + incrementIndent(); + println(); + for (int i = 0; i < size; ++i) { + if (i != 0) { + print(", "); + println(); + } + x.getTableElementList().get(i).accept(this); + } + decrementIndent(); + println(); + print(")"); + } + + if (x.getComment() != null) { + println(); + print("COMMENT "); + x.getComment().accept(this); + } + + int partitionSize = x.getPartitionColumns().size(); + if (partitionSize > 0) { + println(); + print("PARTITIONED ("); + for (int i = 0; i < partitionSize; ++i) { + if (i != 0) { + print(", "); + } + x.getPartitionColumns().get(i).accept(this); + } + print(")"); + } + + return false; + } + + @Override + public void endVisit(OdpsCreateTableStatement x) { + super.endVisit((SQLCreateTableStatement) x); + } + + public SQLStatement parseInsert() { + OdpsInsertStatement stmt = new OdpsInsertStatement(); + + return stmt; + } + + @Override + public void endVisit(OdpsInsertStatement x) { + + } + + @Override + public boolean visit(OdpsInsertStatement x) { + if (x.getFrom() != null) { + print("FROM ("); + incrementIndent(); + println(); + x.getFrom().getSelect().accept(this); + decrementIndent(); + println(); + print(") "); + print(x.getFrom().getAlias()); + } + + for (OdpsInsert insert : x.getItems()) { + println(); + println(); + insert.accept(this); + } + return false; + } + + @Override + public void endVisit(OdpsInsert x) { + + } + + @Override + public boolean visit(OdpsInsert x) { + if (x.isOverwrite()) { + print("INSERT OVERWRITE TABLE "); + } else { + print("INSERT INTO OVERWRITE "); + } + x.getTableSource().accept(this); + + int partitions = x.getPartitions().size(); + if (partitions > 0) { + print(" PARTITION ("); + for (int i = 0; i < partitions; ++i) { + if (i != 0) { + print(", "); + } + + SQLAssignItem assign = x.getPartitions().get(i); + assign.getTarget().accept(this); + + if (assign.getValue() != null) { + print("="); + assign.getValue().accept(this); + } + } + print(")"); + } + println(); + x.getQuery().accept(this); + + return false; + } + + public boolean visit(SQLCaseExpr x) { + incrementIndent(); + print("CASE "); + if (x.getValueExpr() != null) { + x.getValueExpr().accept(this); + println(); + } + + printAndAccept(x.getItems(), " "); + + if (x.getElseExpr() != null) { + println(); + print("ELSE "); + x.getElseExpr().accept(this); + } + + println(); + print("END"); + decrementIndent(); + return false; + } + + public boolean visit(SQLSelectGroupByClause x) { + int itemSize = x.getItems().size(); + if (itemSize > 0) { + print("GROUP BY "); + incrementIndent(); + for (int i = 0; i < itemSize; ++i) { + if (i != 0) { + println(", "); + } + x.getItems().get(i).accept(this); + } + decrementIndent(); + } + + if (x.getHaving() != null) { + println(); + print("HAVING "); + x.getHaving().accept(this); + } + return false; + } + + protected void printSelectList(List selectList) { + incrementIndent(); + for (int i = 0, size = selectList.size(); i < size; ++i) { + if (i != 0) { + print(", "); + println(); + } + + selectList.get(i).accept(this); + } + decrementIndent(); + } + + @Override + public boolean visit(SQLSubqueryTableSource x) { + print("("); + incrementIndent(); + println(); + x.getSelect().accept(this); + decrementIndent(); + println(); + print(")"); + + if (x.getAlias() != null) { + print(' '); + print(x.getAlias()); + } + + return false; + } + + @Override + public boolean visit(SQLJoinTableSource x) { + x.getLeft().accept(this); + + if (x.getJoinType() == JoinType.COMMA) { + print(","); + } else { + println(); + print(JoinType.toString(x.getJoinType())); + } + print(" "); + x.getRight().accept(this); + + if (x.getCondition() != null) { + incrementIndent(); + print(" ON "); + x.getCondition().accept(this); + decrementIndent(); + } + + if (x.getUsing().size() > 0) { + print(" USING ("); + printAndAccept(x.getUsing(), ", "); + print(")"); + } + + if (x.getAlias() != null) { + print(" AS "); + print(x.getAlias()); + } + + return false; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java new file mode 100644 index 0000000000..0d2a18ec82 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/odps/visitor/OdpsSchemaStatVisitor.java @@ -0,0 +1,43 @@ +package com.alibaba.druid.sql.dialect.odps.visitor; + +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsCreateTableStatement; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsert; +import com.alibaba.druid.sql.dialect.odps.ast.OdpsInsertStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; + + +public class OdpsSchemaStatVisitor extends SchemaStatVisitor implements OdpsASTVisitor { + + @Override + public void endVisit(OdpsCreateTableStatement x) { + super.endVisit((SQLCreateTableStatement) x); + } + + @Override + public boolean visit(OdpsCreateTableStatement x) { + return super.visit((SQLCreateTableStatement) x); + } + + @Override + public void endVisit(OdpsInsertStatement x) { + + } + + @Override + public boolean visit(OdpsInsertStatement x) { + return false; + } + + @Override + public void endVisit(OdpsInsert x) { + + } + + @Override + public boolean visit(OdpsInsert x) { + return false; + } + + +} diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index 16f05be6c9..bfbe3f1d23 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -667,7 +667,6 @@ public class Lexer { bufPos = 1; char ch; - boolean mybatisFlag = false; if (charAt(pos + 1) == '@') { ch = charAt(++pos); @@ -675,7 +674,29 @@ public class Lexer { } else if (charAt(pos + 1) == '{') { pos++; bufPos++; - mybatisFlag = true; + + for (;;) { + ch = charAt(++pos); + + if (ch == '}') { + break; + } + + bufPos++; + continue; + } + + if (ch != '}') { + throw new ParserException("syntax error"); + } + ++pos; + bufPos++; + + this.ch = charAt(pos); + + stringVal = addSymbol(); + token = Token.VARIANT; + return; } for (;;) { @@ -689,14 +710,6 @@ public class Lexer { continue; } - if (mybatisFlag) { - if (ch != '}') { - throw new ParserException("syntax error"); - } - ++pos; - bufPos++; - } - this.ch = charAt(pos); stringVal = addSymbol(); 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 7b1c10df9f..0306cec847 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -1,1639 +1,1639 @@ -/* - * Copyright 1999-2011 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.alibaba.druid.sql.parser; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import com.alibaba.druid.sql.ast.SQLCommentHint; -import com.alibaba.druid.sql.ast.SQLDataType; -import com.alibaba.druid.sql.ast.SQLDataTypeImpl; -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLName; -import com.alibaba.druid.sql.ast.SQLObject; -import com.alibaba.druid.sql.ast.SQLOrderBy; -import com.alibaba.druid.sql.ast.SQLOrderingSpecification; -import com.alibaba.druid.sql.ast.SQLOver; -import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; -import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; -import com.alibaba.druid.sql.ast.expr.SQLAllExpr; -import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; -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.SQLCharExpr; -import com.alibaba.druid.sql.ast.expr.SQLCurrentOfCursorExpr; -import com.alibaba.druid.sql.ast.expr.SQLDefaultExpr; -import com.alibaba.druid.sql.ast.expr.SQLExistsExpr; -import com.alibaba.druid.sql.ast.expr.SQLHexExpr; -import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; -import com.alibaba.druid.sql.ast.expr.SQLInListExpr; -import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; -import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; -import com.alibaba.druid.sql.ast.expr.SQLListExpr; -import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; -import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; -import com.alibaba.druid.sql.ast.expr.SQLNotExpr; -import com.alibaba.druid.sql.ast.expr.SQLNullExpr; -import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; -import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; -import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; -import com.alibaba.druid.sql.ast.expr.SQLSomeExpr; -import com.alibaba.druid.sql.ast.expr.SQLUnaryExpr; -import com.alibaba.druid.sql.ast.expr.SQLUnaryOperator; -import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; -import com.alibaba.druid.sql.ast.statement.NotNullConstraint; -import com.alibaba.druid.sql.ast.statement.SQLAssignItem; -import com.alibaba.druid.sql.ast.statement.SQLCharactorDataType; -import com.alibaba.druid.sql.ast.statement.SQLCheck; -import com.alibaba.druid.sql.ast.statement.SQLColumnCheck; -import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; -import com.alibaba.druid.sql.ast.statement.SQLColumnPrimaryKey; -import com.alibaba.druid.sql.ast.statement.SQLColumnReference; -import com.alibaba.druid.sql.ast.statement.SQLColumnUniqueKey; -import com.alibaba.druid.sql.ast.statement.SQLConstaint; -import com.alibaba.druid.sql.ast.statement.SQLForeignKeyConstraint; -import com.alibaba.druid.sql.ast.statement.SQLForeignKeyImpl; -import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey; -import com.alibaba.druid.sql.ast.statement.SQLPrimaryKeyImpl; -import com.alibaba.druid.sql.ast.statement.SQLSelect; -import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; -import com.alibaba.druid.sql.ast.statement.SQLUnique; - -public class SQLExprParser extends SQLParser { - - public final static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM" }; - - protected String[] aggregateFunctions = AGGREGATE_FUNCTIONS; - - public SQLExprParser(String sql){ - super(sql); - } - - public SQLExprParser(Lexer lexer){ - super(lexer); - } - - public SQLExpr expr() { - if (lexer.token() == Token.STAR) { - lexer.nextToken(); - - SQLExpr expr = new SQLAllColumnExpr(); - - if (lexer.token() == Token.DOT) { - lexer.nextToken(); - accept(Token.STAR); - return new SQLPropertyExpr(expr, "*"); - } - - return expr; - } - - SQLExpr expr = primary(); - - if (lexer.token() == Token.COMMA) { - return expr; - } - - return exprRest(expr); - } - - public SQLExpr exprRest(SQLExpr expr) { - expr = bitXorRest(expr); - expr = multiplicativeRest(expr); - expr = additiveRest(expr); - expr = shiftRest(expr); - expr = bitAndRest(expr); - expr = bitOrRest(expr); - expr = inRest(expr); - expr = relationalRest(expr); - expr = equalityRest(expr); - expr = andRest(expr); - expr = orRest(expr); - - return expr; - } - - public final SQLExpr bitXor() { - SQLExpr expr = primary(); - return bitXorRest(expr); - } - - public SQLExpr bitXorRest(SQLExpr expr) { - if (lexer.token() == Token.CARET) { - lexer.nextToken(); - SQLExpr rightExp = primary(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseXor, rightExp); - expr = bitXorRest(expr); - } - - return expr; - } - - public final SQLExpr multiplicative() { - SQLExpr expr = bitXor(); - return multiplicativeRest(expr); - } - - public SQLExpr multiplicativeRest(SQLExpr expr) { - if (lexer.token() == Token.STAR) { - lexer.nextToken(); - SQLExpr rightExp = bitXor(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Multiply, rightExp); - expr = multiplicativeRest(expr); - } else if (lexer.token() == Token.SLASH) { - lexer.nextToken(); - SQLExpr rightExp = bitXor(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Divide, rightExp); - expr = multiplicativeRest(expr); - } else if (lexer.token() == Token.PERCENT) { - lexer.nextToken(); - SQLExpr rightExp = bitXor(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Modulus, rightExp); - expr = multiplicativeRest(expr); - } - return expr; - } - - public SQLExpr primary() { - SQLExpr sqlExpr = null; - - final Token tok = lexer.token(); - - switch (tok) { - case LPAREN: - lexer.nextToken(); - sqlExpr = expr(); - if (lexer.token() == Token.COMMA) { - SQLListExpr listExpr = new SQLListExpr(); - listExpr.getItems().add(sqlExpr); - do { - lexer.nextToken(); - listExpr.getItems().add(expr()); - } while (lexer.token() == Token.COMMA); - - sqlExpr = listExpr; - } - accept(Token.RPAREN); - break; - case INSERT: - lexer.nextToken(); - if (lexer.token() != Token.LPAREN) { - throw new ParserException("syntax error"); - } - sqlExpr = new SQLIdentifierExpr("INSERT"); - break; - case IDENTIFIER: - sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); - lexer.nextToken(); - break; - case NEW: - throw new ParserException("TODO"); - case LITERAL_INT: - sqlExpr = new SQLIntegerExpr(lexer.integerValue()); - lexer.nextToken(); - break; - case LITERAL_FLOAT: - sqlExpr = new SQLNumberExpr(lexer.decimalValue()); - lexer.nextToken(); - break; - case LITERAL_CHARS: - sqlExpr = new SQLCharExpr(lexer.stringVal()); - lexer.nextToken(); - break; - case LITERAL_NCHARS: - sqlExpr = new SQLNCharExpr(lexer.stringVal()); - lexer.nextToken(); - break; - case VARIANT: - SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(lexer.stringVal()); - lexer.nextToken(); - if (varRefExpr.getName().equals("@") && lexer.token() == Token.LITERAL_CHARS) { - varRefExpr.setName("@'" + lexer.stringVal() + "'"); - lexer.nextToken(); - } else if (varRefExpr.getName().equals("@@") && lexer.token() == Token.LITERAL_CHARS) { - varRefExpr.setName("@@'" + lexer.stringVal() + "'"); - lexer.nextToken(); - } - sqlExpr = varRefExpr; - break; - case DEFAULT: - sqlExpr = new SQLDefaultExpr(); - lexer.nextToken(); - break; - case DUAL: - case KEY: - case DISTINCT: - case LIMIT: - case SCHEMA: - case COLUMN: - case IF: - case END: - case COMMENT: - case COMPUTE: - case ENABLE: - case DISABLE: - case INITIALLY: - case SEQUENCE: - case USER: - case EXPLAIN: - case WITH: - case GRANT: - case REPLACE: - case INDEX: - case MODEL: - case PCTFREE: - case INITRANS: - case MAXTRANS: - case SEGMENT: - case CREATION: - case IMMEDIATE: - case DEFERRED: - case STORAGE: - case NEXT: - case MINEXTENTS: - case MAXEXTENTS: - case MAXSIZE: - case PCTINCREASE: - case FLASH_CACHE: - case CELL_FLASH_CACHE: - case KEEP: - case NONE: - case LOB: - case STORE: - case ROW: - case CHUNK: - case CACHE: - case NOCACHE: - case LOGGING: - case NOCOMPRESS: - case KEEP_DUPLICATES: - case EXCEPTIONS: - case PURGE: - case FULL: - case TO: - case IDENTIFIED: - case PASSWORD: - case BINARY: - case WINDOW: - case OFFSET: - case SHARE: - case START: - case CONNECT: - case MATCHED: - case ERRORS: - case REJECT: - case UNLIMITED: - case BEGIN: - case EXCLUSIVE: - case MODE: - case ADVISE: - sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); - lexer.nextToken(); - break; - case CASE: - SQLCaseExpr caseExpr = new SQLCaseExpr(); - lexer.nextToken(); - if (lexer.token() != Token.WHEN) { - caseExpr.setValueExpr(expr()); - } - - accept(Token.WHEN); - SQLExpr testExpr = expr(); - accept(Token.THEN); - SQLExpr valueExpr = expr(); - SQLCaseExpr.Item caseItem = new SQLCaseExpr.Item(testExpr, valueExpr); - caseExpr.addItem(caseItem); - - while (lexer.token() == Token.WHEN) { - lexer.nextToken(); - testExpr = expr(); - accept(Token.THEN); - valueExpr = expr(); - caseItem = new SQLCaseExpr.Item(testExpr, valueExpr); - caseExpr.getItems().add(caseItem); - } - - if (lexer.token() == Token.ELSE) { - lexer.nextToken(); - caseExpr.setElseExpr(expr()); - } - - accept(Token.END); - - sqlExpr = caseExpr; - break; - case EXISTS: - lexer.nextToken(); - accept(Token.LPAREN); - sqlExpr = new SQLExistsExpr(createSelectParser().select()); - accept(Token.RPAREN); - break; - case NOT: - lexer.nextToken(); - if (lexer.token() == Token.EXISTS) { - lexer.nextToken(); - accept(Token.LPAREN); - sqlExpr = new SQLExistsExpr(createSelectParser().select(), true); - accept(Token.RPAREN); - } else if (lexer.token() == Token.LPAREN) { - lexer.nextToken(); - - SQLExpr notTarget = expr(); - - accept(Token.RPAREN); - notTarget = exprRest(notTarget); - - sqlExpr = new SQLNotExpr(notTarget); - - return primaryRest(sqlExpr); - } else { - SQLExpr restExpr = relational(); - sqlExpr = new SQLNotExpr(restExpr); - } - break; - case SELECT: - SQLQueryExpr queryExpr = new SQLQueryExpr(createSelectParser().select()); - sqlExpr = queryExpr; - break; - case CAST: - lexer.nextToken(); - accept(Token.LPAREN); - SQLCastExpr cast = new SQLCastExpr(); - cast.setExpr(expr()); - accept(Token.AS); - cast.setDataType(parseDataType()); - accept(Token.RPAREN); - - sqlExpr = cast; - break; - case SUB: - lexer.nextToken(); - switch (lexer.token()) { - case LITERAL_INT: - Number integerValue = lexer.integerValue(); - if (integerValue instanceof Integer) { - int intVal = ((Integer) integerValue).intValue(); - if (intVal == Integer.MIN_VALUE) { - integerValue = Long.valueOf(((long) intVal) * -1); - } else { - integerValue = Integer.valueOf(intVal * -1); - } - } else if (integerValue instanceof Long) { - long longVal = ((Long) integerValue).longValue(); - if (longVal == 2147483648L) { - integerValue = Integer.valueOf((int) (((long) longVal) * -1)); - } else { - integerValue = Long.valueOf(longVal * -1); - } - } else { - integerValue = ((BigInteger) integerValue).negate(); - } - sqlExpr = new SQLIntegerExpr(integerValue); - lexer.nextToken(); - break; - case LITERAL_FLOAT: - sqlExpr = new SQLNumberExpr(lexer.decimalValue().negate()); - lexer.nextToken(); - break; - case IDENTIFIER: // 当负号后面为字段的情况 - sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); - sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr); - lexer.nextToken(); - break; - case QUES: - sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, new SQLVariantRefExpr("?")); - lexer.nextToken(); - break; - case LPAREN: - lexer.nextToken(); - sqlExpr = expr(); - accept(Token.RPAREN); - sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr); - break; - default: - throw new ParserException("TODO : " + lexer.token()); - } - break; - case PLUS: - lexer.nextToken(); - switch (lexer.token()) { - case LITERAL_INT: - sqlExpr = new SQLIntegerExpr(lexer.integerValue()); - lexer.nextToken(); - break; - case LITERAL_FLOAT: - sqlExpr = new SQLNumberExpr(lexer.decimalValue()); - lexer.nextToken(); - break; - case LPAREN: - lexer.nextToken(); - sqlExpr = expr(); - accept(Token.RPAREN); - sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Plus, sqlExpr); - break; - default: - throw new ParserException("TODO"); - } - break; - case TILDE: - lexer.nextToken(); - SQLExpr unaryValueExpr = expr(); - SQLUnaryExpr unary = new SQLUnaryExpr(SQLUnaryOperator.Compl, unaryValueExpr); - sqlExpr = unary; - break; - case QUES: - lexer.nextToken(); - SQLVariantRefExpr quesVarRefExpr = new SQLVariantRefExpr("?"); - quesVarRefExpr.setIndex(lexer.nextVarIndex()); - sqlExpr = quesVarRefExpr; - break; - case LEFT: - sqlExpr = new SQLIdentifierExpr("LEFT"); - lexer.nextToken(); - break; - case RIGHT: - sqlExpr = new SQLIdentifierExpr("RIGHT"); - lexer.nextToken(); - break; - case DATABASE: - sqlExpr = new SQLIdentifierExpr("DATABASE"); - lexer.nextToken(); - break; - case LOCK: - sqlExpr = new SQLIdentifierExpr("LOCK"); - lexer.nextToken(); - break; - case NULL: - sqlExpr = new SQLNullExpr(); - lexer.nextToken(); - break; - case BANG: - lexer.nextToken(); - SQLExpr bangExpr = expr(); - sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Not, bangExpr); - break; - case LITERAL_HEX: - String hex = lexer.hexString(); - sqlExpr = new SQLHexExpr(hex); - lexer.nextToken(); - break; - case INTERVAL: - sqlExpr = parseInterval(); - break; - case COLON: - lexer.nextToken(); - if (lexer.token == Token.LITERAL_ALIAS) { - sqlExpr = new SQLVariantRefExpr(":\"" + lexer.stringVal() + "\""); - lexer.nextToken(); - } - break; - case ANY: - lexer.nextToken(); - if (lexer.token() == Token.LPAREN) { - SQLAnyExpr anyExpr = new SQLAnyExpr(); - - accept(Token.LPAREN); - SQLSelect anySubQuery = createSelectParser().select(); - anyExpr.setSubQuery(anySubQuery); - accept(Token.RPAREN); - - anySubQuery.setParent(anyExpr); - - sqlExpr = anyExpr; - } else { - sqlExpr = new SQLIdentifierExpr("ANY"); - } - break; - case SOME: - lexer.nextToken(); - SQLSomeExpr someExpr = new SQLSomeExpr(); - - accept(Token.LPAREN); - SQLSelect someSubQuery = createSelectParser().select(); - someExpr.setSubQuery(someSubQuery); - accept(Token.RPAREN); - - someSubQuery.setParent(someExpr); - - sqlExpr = someExpr; - break; - case ALL: - lexer.nextToken(); - SQLAllExpr allExpr = new SQLAllExpr(); - - accept(Token.LPAREN); - SQLSelect allSubQuery = createSelectParser().select(); - allExpr.setSubQuery(allSubQuery); - accept(Token.RPAREN); - - allSubQuery.setParent(allExpr); - - sqlExpr = allExpr; - break; - case LITERAL_ALIAS: - sqlExpr = new SQLIdentifierExpr('"' + lexer.stringVal() + '"'); - lexer.nextToken(); - break; - case EOF: - throw new EOFParserException(); - default: - throw new ParserException("ERROR. token : " + tok + ", pos : " + lexer.pos()); - } - - return primaryRest(sqlExpr); - } - - protected SQLExpr parseInterval() { - throw new ParserException("TODO"); - } - - public SQLSelectParser createSelectParser() { - return new SQLSelectParser(this); - } - - public SQLExpr primaryRest(SQLExpr expr) { - if (expr == null) { - throw new IllegalArgumentException("expr"); - } - - if (lexer.token() == Token.OF) { - if (expr instanceof SQLIdentifierExpr) { - String name = ((SQLIdentifierExpr) expr).getName(); - if ("CURRENT".equalsIgnoreCase(name)) { - lexer.nextToken(); - SQLName cursorName = this.name(); - return new SQLCurrentOfCursorExpr(cursorName); - } - } - } - - if (lexer.token() == Token.DOT) { - lexer.nextToken(); - - if (expr instanceof SQLCharExpr) { - String text = ((SQLCharExpr) expr).getText(); - expr = new SQLIdentifierExpr(text); - } - - expr = dotRest(expr); - return primaryRest(expr); - } else { - if (lexer.token() == Token.LPAREN) { - return methodRest(expr, true); - } - } - - return expr; - } - - protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) { - if (acceptLPAREN) { - accept(Token.LPAREN); - } - - if (expr instanceof SQLName || expr instanceof SQLDefaultExpr) { - String methodName; - - SQLMethodInvokeExpr methodInvokeExpr; - if (expr instanceof SQLPropertyExpr) { - methodName = ((SQLPropertyExpr) expr).getName(); - methodInvokeExpr = new SQLMethodInvokeExpr(methodName); - methodInvokeExpr.setOwner(((SQLPropertyExpr) expr).getOwner()); - } else { - methodName = expr.toString(); - methodInvokeExpr = new SQLMethodInvokeExpr(methodName); - } - - if (isAggreateFunction(methodName)) { - SQLAggregateExpr aggregateExpr = parseAggregateExpr(methodName); - - return aggregateExpr; - } - - if (lexer.token() != Token.RPAREN) { - exprList(methodInvokeExpr.getParameters(), methodInvokeExpr); - } - - accept(Token.RPAREN); - - return primaryRest(methodInvokeExpr); - } - - throw new ParserException("not support token:" + lexer.token()); - } - - protected SQLExpr dotRest(SQLExpr expr) { - if (lexer.token() == Token.STAR) { - lexer.nextToken(); - expr = new SQLPropertyExpr(expr, "*"); - } else { - String name; - - if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.LITERAL_CHARS - || lexer.token() == Token.LITERAL_ALIAS) { - name = lexer.stringVal(); - lexer.nextToken(); - } else if (lexer.getKeywods().containsValue(lexer.token())) { - name = lexer.stringVal(); - lexer.nextToken(); - } else { - throw new ParserException("error : " + lexer.stringVal()); - } - - if (lexer.token() == Token.LPAREN) { - lexer.nextToken(); - - SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(name); - methodInvokeExpr.setOwner(expr); - if (lexer.token() == Token.RPAREN) { - lexer.nextToken(); - } else { - if (lexer.token() == Token.PLUS) { - methodInvokeExpr.addParameter(new SQLIdentifierExpr("+")); - lexer.nextToken(); - } else { - exprList(methodInvokeExpr.getParameters(), methodInvokeExpr); - } - accept(Token.RPAREN); - } - expr = methodInvokeExpr; - } else { - expr = new SQLPropertyExpr(expr, name); - } - } - - expr = primaryRest(expr); - return expr; - } - - public final SQLExpr groupComparisionRest(SQLExpr expr) { - return expr; - } - - public final void names(Collection exprCol) { - names(exprCol, null); - } - - public final void names(Collection exprCol, SQLObject parent) { - if (lexer.token() == Token.RBRACE) { - return; - } - - if (lexer.token() == Token.EOF) { - return; - } - - SQLName name = name(); - name.setParent(parent); - exprCol.add(name); - - while (lexer.token() == Token.COMMA) { - lexer.nextToken(); - - name = name(); - name.setParent(parent); - exprCol.add(name); - } - } - - public final void exprList(Collection exprCol) { - exprList(exprCol, null); - } - - public final void exprList(Collection exprCol, SQLObject parent) { - if (lexer.token() == Token.RPAREN || lexer.token() == Token.RBRACKET) { - return; - } - - if (lexer.token() == Token.EOF) { - return; - } - - SQLExpr expr = expr(); - expr.setParent(parent); - exprCol.add(expr); - - while (lexer.token() == Token.COMMA) { - lexer.nextToken(); - expr = expr(); - expr.setParent(parent); - exprCol.add(expr); - } - } - - public SQLName name() { - String identName; - if (lexer.token() == Token.LITERAL_ALIAS) { - identName = '"' + lexer.stringVal() + '"'; - lexer.nextToken(); - } else if (lexer.token() == Token.IDENTIFIER) { - identName = lexer.stringVal(); - - lexer.nextToken(); - } else if (lexer.token() == Token.LITERAL_CHARS) { - identName = '\'' + lexer.stringVal() + '\''; - lexer.nextToken(); - } else if (lexer.token() == Token.VARIANT) { - identName = lexer.stringVal(); - lexer.nextToken(); - } else { - switch (lexer.token()) { - case MODEL: - case PCTFREE: - case INITRANS: - case MAXTRANS: - case SEGMENT: - case CREATION: - case IMMEDIATE: - case DEFERRED: - case STORAGE: - case NEXT: - case MINEXTENTS: - case MAXEXTENTS: - case MAXSIZE: - case PCTINCREASE: - case FLASH_CACHE: - case CELL_FLASH_CACHE: - case KEEP: - case NONE: - case LOB: - case STORE: - case ROW: - case CHUNK: - case CACHE: - case NOCACHE: - case LOGGING: - case NOCOMPRESS: - case KEEP_DUPLICATES: - case EXCEPTIONS: - case PURGE: - case INITIALLY: - case END: - case COMMENT: - case ENABLE: - case DISABLE: - case SEQUENCE: - case USER: - case ANALYZE: - case OPTIMIZE: - case GRANT: - identName = lexer.stringVal(); - lexer.nextToken(); - break; - default: - throw new ParserException("error " + lexer.token()); - } - } - - SQLName name = new SQLIdentifierExpr(identName); - - name = nameRest(name); - - return name; - } - - public SQLName nameRest(SQLName name) { - if (lexer.token() == Token.DOT) { - lexer.nextToken(); - - if (lexer.token() == Token.KEY) { - name = new SQLPropertyExpr(name, "KEY"); - lexer.nextToken(); - return name; - } - - if (lexer.token() != Token.LITERAL_ALIAS && lexer.token() != Token.IDENTIFIER - && (!lexer.getKeywods().containsValue(lexer.token()))) { - throw new ParserException("error, " + lexer.token()); - } - - if (lexer.token() == Token.LITERAL_ALIAS) { - name = new SQLPropertyExpr(name, '"' + lexer.stringVal() + '"'); - } else { - name = new SQLPropertyExpr(name, lexer.stringVal()); - } - lexer.nextToken(); - name = nameRest(name); - } - - return name; - } - - public boolean isAggreateFunction(String word) { - for (int i = 0; i < aggregateFunctions.length; ++i) { - if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { - return true; - } - } - - return false; - } - - protected SQLAggregateExpr parseAggregateExpr(String methodName) { - methodName = methodName.toUpperCase(); - - SQLAggregateExpr aggregateExpr; - if (lexer.token() == Token.ALL) { - aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.ALL); - lexer.nextToken(); - } else if (lexer.token() == Token.DISTINCT) { - aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.DISTINCT); - lexer.nextToken(); - } else { - aggregateExpr = new SQLAggregateExpr(methodName); - } - - exprList(aggregateExpr.getArguments(), aggregateExpr); - - parseAggregateExprRest(aggregateExpr); - - accept(Token.RPAREN); - - if (lexer.token() == Token.OVER) { - lexer.nextToken(); - SQLOver over = new SQLOver(); - accept(Token.LPAREN); - - if (identifierEquals("PARTITION")) { - lexer.nextToken(); - accept(Token.BY); - - if (lexer.token() == (Token.LPAREN)) { - lexer.nextToken(); - exprList(over.getPartitionBy()); - accept(Token.RPAREN); - } else { - exprList(over.getPartitionBy()); - } - } - - over.setOrderBy(parseOrderBy()); - - accept(Token.RPAREN); - aggregateExpr.setOver(over); - - } - - return aggregateExpr; - } - - protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) { - return aggregateExpr; - } - - public SQLOrderBy parseOrderBy() { - if (lexer.token() == Token.ORDER) { - SQLOrderBy orderBy = new SQLOrderBy(); - - lexer.nextToken(); - - accept(Token.BY); - - orderBy.getItems().add(parseSelectOrderByItem()); - - while (lexer.token() == Token.COMMA) { - lexer.nextToken(); - orderBy.getItems().add(parseSelectOrderByItem()); - } - - return orderBy; - } - - return null; - } - - public SQLSelectOrderByItem parseSelectOrderByItem() { - SQLSelectOrderByItem item = new SQLSelectOrderByItem(); - - item.setExpr(expr()); - - if (lexer.token() == Token.ASC) { - lexer.nextToken(); - item.setType(SQLOrderingSpecification.ASC); - } else if (lexer.token() == Token.DESC) { - lexer.nextToken(); - item.setType(SQLOrderingSpecification.DESC); - } - - return item; - } - - public final SQLExpr bitAnd() { - SQLExpr expr = shift(); - return bitAndRest(expr); - } - - public final SQLExpr bitAndRest(SQLExpr expr) { - while (lexer.token() == Token.AMP) { - lexer.nextToken(); - SQLExpr rightExp = shift(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseAnd, rightExp); - } - return expr; - } - - public final SQLExpr bitOr() { - SQLExpr expr = bitAnd(); - return bitOrRest(expr); - } - - public final SQLExpr bitOrRest(SQLExpr expr) { - if (lexer.token() == Token.BAR) { - lexer.nextToken(); - SQLExpr rightExp = bitAnd(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseOr, rightExp); - expr = bitAndRest(expr); - } - return expr; - } - - public final SQLExpr equality() { - SQLExpr expr = bitOr(); - return equalityRest(expr); - } - - public SQLExpr equalityRest(SQLExpr expr) { - SQLExpr rightExp; - if (lexer.token() == Token.EQ) { - lexer.nextToken(); - try { - rightExp = bitOr(); - } catch (EOFParserException e) { - throw new ParserException("EOF, " + expr + "=", e); - } - rightExp = equalityRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Equality, rightExp); - } else if (lexer.token() == Token.BANGEQ) { - lexer.nextToken(); - rightExp = bitOr(); - - rightExp = equalityRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotEqual, rightExp); - } else if (lexer.token() == Token.COLONEQ) { - lexer.nextToken(); - rightExp = expr(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Assignment, rightExp); - } - - return expr; - } - - public final SQLExpr inRest(SQLExpr expr) { - if (lexer.token() == Token.IN) { - lexer.nextToken(); - - SQLInListExpr inListExpr = new SQLInListExpr(expr); - if (lexer.token() == Token.LPAREN) { - lexer.nextToken(); - exprList(inListExpr.getTargetList()); - accept(Token.RPAREN); - expr = inListExpr; - } else { - SQLExpr itemExpr = primary(); - inListExpr.getTargetList().add(itemExpr); - } - - expr = inListExpr; - - if (inListExpr.getTargetList().size() == 1) { - SQLExpr targetExpr = inListExpr.getTargetList().get(0); - if (targetExpr instanceof SQLQueryExpr) { - SQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(); - inSubQueryExpr.setExpr(inListExpr.getExpr()); - inSubQueryExpr.setSubQuery(((SQLQueryExpr) targetExpr).getSubQuery()); - expr = inSubQueryExpr; - } - } - } - - return expr; - } - - public final SQLExpr additive() { - SQLExpr expr = multiplicative(); - return additiveRest(expr); - } - - public SQLExpr additiveRest(SQLExpr expr) { - if (lexer.token() == Token.PLUS) { - lexer.nextToken(); - SQLExpr rightExp = multiplicative(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Add, rightExp); - expr = additiveRest(expr); - } else if (lexer.token() == Token.BARBAR) { - lexer.nextToken(); - SQLExpr rightExp = multiplicative(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Concat, rightExp); - expr = additiveRest(expr); - } else if (lexer.token() == Token.SUB) { - lexer.nextToken(); - SQLExpr rightExp = multiplicative(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Subtract, rightExp); - expr = additiveRest(expr); - } - - return expr; - } - - public final SQLExpr shift() { - SQLExpr expr = additive(); - return shiftRest(expr); - } - - public SQLExpr shiftRest(SQLExpr expr) { - if (lexer.token() == Token.LTLT) { - lexer.nextToken(); - SQLExpr rightExp = additive(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LeftShift, rightExp); - expr = shiftRest(expr); - } else if (lexer.token() == Token.GTGT) { - lexer.nextToken(); - SQLExpr rightExp = additive(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.RightShift, rightExp); - expr = shiftRest(expr); - } - - return expr; - } - - public SQLExpr and() { - SQLExpr expr = relational(); - return andRest(expr); - } - - public SQLExpr andRest(SQLExpr expr) { - for (;;) { - if (lexer.token() == Token.AND || lexer.token() == Token.AMPAMP) { - lexer.nextToken(); - SQLExpr rightExp = relational(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanAnd, rightExp); - } else { - break; - } - } - - return expr; - } - - public SQLExpr or() { - SQLExpr expr = and(); - return orRest(expr); - } - - public SQLExpr orRest(SQLExpr expr) { - - for (;;) { - if (lexer.token() == Token.OR) { - lexer.nextToken(); - SQLExpr rightExp = and(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanOr, rightExp); - } else if (lexer.token() == Token.XOR) { - lexer.nextToken(); - SQLExpr rightExp = and(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanXor, rightExp); - } else { - break; - } - } - - return expr; - } - - public SQLExpr relational() { - SQLExpr expr = equality(); - - return relationalRest(expr); - } - - public SQLExpr relationalRest(SQLExpr expr) { - SQLExpr rightExp; - - if (lexer.token() == Token.LT) { - SQLBinaryOperator op = SQLBinaryOperator.LessThan; - - lexer.nextToken(); - if (lexer.token() == Token.EQ) { - lexer.nextToken(); - op = SQLBinaryOperator.LessThanOrEqual; - } - - rightExp = bitOr(); - expr = new SQLBinaryOpExpr(expr, op, rightExp); - // expr = relationalRest(expr); - } else if (lexer.token() == Token.LTEQ) { - lexer.nextToken(); - rightExp = bitOr(); - - // rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrEqual, rightExp); - } else if (lexer.token() == Token.LTEQGT) { - lexer.nextToken(); - rightExp = bitOr(); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrEqualOrGreaterThan, rightExp); - } else if (lexer.token() == Token.GT) { - SQLBinaryOperator op = SQLBinaryOperator.GreaterThan; - - lexer.nextToken(); - - if (lexer.token() == Token.EQ) { - lexer.nextToken(); - op = SQLBinaryOperator.GreaterThanOrEqual; - } - - rightExp = bitOr(); - - expr = new SQLBinaryOpExpr(expr, op, rightExp); - } else if (lexer.token() == Token.GTEQ) { - lexer.nextToken(); - rightExp = bitOr(); - - // rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.GreaterThanOrEqual, rightExp); - } else if (lexer.token() == Token.BANGLT) { - lexer.nextToken(); - rightExp = bitOr(); - - // rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotLessThan, rightExp); - } else if (lexer.token() == Token.BANGGT) { - lexer.nextToken(); - rightExp = bitOr(); - - rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotGreaterThan, rightExp); - } else if (lexer.token() == Token.LTGT) { - lexer.nextToken(); - rightExp = bitOr(); - - // rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrGreater, rightExp); - } else if (lexer.token() == Token.LIKE) { - lexer.nextToken(); - rightExp = bitOr(); - - // rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Like, rightExp); - - if (lexer.token() == Token.ESCAPE) { - lexer.nextToken(); - rightExp = primary(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Escape, rightExp); - } - } else if (lexer.token() == (Token.NOT)) { - lexer.nextToken(); - expr = notRationalRest(expr); - } else if (lexer.token() == (Token.BETWEEN)) { - lexer.nextToken(); - SQLExpr beginExpr = bitOr(); - accept(Token.AND); - SQLExpr endExpr = bitOr(); - expr = new SQLBetweenExpr(expr, beginExpr, endExpr); - } else if (lexer.token() == (Token.IS)) { - lexer.nextToken(); - - if (lexer.token() == (Token.NOT)) { - lexer.nextToken(); - SQLExpr rightExpr = primary(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.IsNot, rightExpr); - } else { - SQLExpr rightExpr = primary(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Is, rightExpr); - } - } else if (lexer.token() == Token.IN) { - expr = inRest(expr); - } - - return expr; - } - - public SQLExpr notRationalRest(SQLExpr expr) { - if (lexer.token() == (Token.LIKE)) { - lexer.nextToken(); - SQLExpr rightExp = equality(); - - rightExp = relationalRest(rightExp); - - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotLike, rightExp); - - if (lexer.token() == Token.ESCAPE) { - lexer.nextToken(); - rightExp = expr(); - expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Escape, rightExp); - } - } else if (lexer.token() == Token.IN) { - lexer.nextToken(); - accept(Token.LPAREN); - - SQLInListExpr inListExpr = new SQLInListExpr(expr, true); - exprList(inListExpr.getTargetList()); - expr = inListExpr; - - accept(Token.RPAREN); - - if (inListExpr.getTargetList().size() == 1) { - SQLExpr targetExpr = inListExpr.getTargetList().get(0); - if (targetExpr instanceof SQLQueryExpr) { - SQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(); - inSubQueryExpr.setNot(true); - inSubQueryExpr.setExpr(inListExpr.getExpr()); - inSubQueryExpr.setSubQuery(((SQLQueryExpr) targetExpr).getSubQuery()); - expr = inSubQueryExpr; - } - } - - expr = relationalRest(expr); - return expr; - } else if (lexer.token() == (Token.BETWEEN)) { - lexer.nextToken(); - SQLExpr beginExpr = bitOr(); - accept(Token.AND); - SQLExpr endExpr = bitOr(); - - expr = new SQLBetweenExpr(expr, true, beginExpr, endExpr); - - return expr; - } else { - throw new ParserException("TODO " + lexer.token()); - } - return expr; - } - - public SQLDataType parseDataType() { - - if (lexer.token() == Token.DEFAULT || lexer.token() == Token.NOT || lexer.token() == Token.NULL) { - return null; - } - - SQLName typeExpr = name(); - String typeName = typeExpr.toString(); - - if (isCharType(typeName)) { - SQLCharactorDataType charType = new SQLCharactorDataType(typeName); - - if (lexer.token() == Token.LPAREN) { - lexer.nextToken(); - SQLExpr arg = this.expr(); - arg.setParent(charType); - charType.getArguments().add(arg); - accept(Token.RPAREN); - } - - return parseCharTypeRest(charType); - } - - if ("character".equalsIgnoreCase(typeName) && "varying".equalsIgnoreCase(lexer.stringVal())) { - typeName += ' ' + lexer.stringVal(); - lexer.nextToken(); - } - - SQLDataType dataType = new SQLDataTypeImpl(typeName); - return parseDataTypeRest(dataType); - } - - protected SQLDataType parseDataTypeRest(SQLDataType dataType) { - if (lexer.token() == Token.LPAREN) { - lexer.nextToken(); - exprList(dataType.getArguments(), dataType); - accept(Token.RPAREN); - } - - return dataType; - } - - protected boolean isCharType(String dataTypeName) { - return "char".equalsIgnoreCase(dataTypeName) // - || "varchar".equalsIgnoreCase(dataTypeName) - || "nchar".equalsIgnoreCase(dataTypeName) - || "nvarchar".equalsIgnoreCase(dataTypeName) - // - ; - } - - protected SQLDataType parseCharTypeRest(SQLCharactorDataType charType) { - if (identifierEquals("CHARACTER")) { - lexer.nextToken(); - - accept(Token.SET); - - if (lexer.token() != Token.IDENTIFIER && lexer.token() != Token.LITERAL_CHARS) { - throw new ParserException(); - } - charType.setCharSetName(lexer.stringVal()); - lexer.nextToken(); - - if (lexer.token() == Token.IDENTIFIER) { - if (lexer.stringVal().equalsIgnoreCase("COLLATE")) { - lexer.nextToken(); - - if (lexer.token() != Token.IDENTIFIER) { - throw new ParserException(); - } - charType.setCollate(lexer.stringVal()); - lexer.nextToken(); - } - } - } - return charType; - } - - public void accept(Token token) { - if (lexer.token() == token) { - lexer.nextToken(); - } else { - throw new ParserException("syntax error, expect " + token + ", actual " + lexer.token() + " " - + lexer.stringVal()); - } - } - - public SQLColumnDefinition parseColumn() { - SQLColumnDefinition column = createColumnDefinition(); - column.setName(name()); - column.setDataType(parseDataType()); - - return parseColumnRest(column); - } - - protected SQLColumnDefinition createColumnDefinition() { - SQLColumnDefinition column = new SQLColumnDefinition(); - return column; - } - - public SQLColumnDefinition parseColumnRest(SQLColumnDefinition column) { - if (lexer.token() == Token.DEFAULT) { - lexer.nextToken(); - column.setDefaultExpr(bitOr()); - return parseColumnRest(column); - } - - if (lexer.token() == Token.NOT) { - lexer.nextToken(); - accept(Token.NULL); - column.getConstaints().add(new NotNullConstraint()); - return parseColumnRest(column); - } - - if (lexer.token() == Token.NULL) { - lexer.nextToken(); - column.setDefaultExpr(new SQLNullExpr()); - return parseColumnRest(column); - } - - if (lexer.token == Token.PRIMARY) { - lexer.nextToken(); - accept(Token.KEY); - column.getConstaints().add(new SQLColumnPrimaryKey()); - return parseColumnRest(column); - } - - if (lexer.token == Token.UNIQUE) { - lexer.nextToken(); - if (lexer.token() == Token.KEY) { - lexer.nextToken(); - } - column.getConstaints().add(new SQLColumnPrimaryKey()); - return parseColumnRest(column); - } - - if (lexer.token == Token.CONSTRAINT) { - lexer.nextToken(); - - SQLName name = this.name(); - - if (lexer.token() == Token.PRIMARY) { - lexer.nextToken(); - accept(Token.KEY); - SQLColumnPrimaryKey pk = new SQLColumnPrimaryKey(); - pk.setName(name); - column.getConstaints().add(pk); - return parseColumnRest(column); - } - - if (lexer.token() == Token.UNIQUE) { - lexer.nextToken(); - SQLColumnUniqueKey uk = new SQLColumnUniqueKey(); - uk.setName(name); - column.getConstaints().add(uk); - return parseColumnRest(column); - } - - if (lexer.token() == Token.REFERENCES) { - lexer.nextToken(); - SQLColumnReference ref = new SQLColumnReference(); - ref.setName(name); - ref.setTable(this.name()); - accept(Token.LPAREN); - this.names(ref.getColumns(), ref); - accept(Token.RPAREN); - column.getConstaints().add(ref); - return parseColumnRest(column); - } - - if (lexer.token() == Token.NOT) { - lexer.nextToken(); - accept(Token.NULL); - NotNullConstraint notNull = new NotNullConstraint(); - notNull.setName(name); - column.getConstaints().add(notNull); - return parseColumnRest(column); - } - - if (lexer.token == Token.CHECK) { - SQLColumnCheck check = parseColumnCheck(); - check.setName(name); - check.setParent(column); - column.getConstaints().add(check); - return parseColumnRest(column); - } - - if (lexer.token == Token.DEFAULT) { - lexer.nextToken(); - SQLExpr expr = this.expr(); - column.setDefaultExpr(expr); - return parseColumnRest(column); - } - - throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); - } - - if (lexer.token == Token.CHECK) { - SQLColumnCheck check = parseColumnCheck(); - column.getConstaints().add(check); - return parseColumnRest(column); - } - - if (lexer.token() == Token.COMMENT) { - lexer.nextToken(); - column.setComment(primary()); - } - - return column; - } - - protected SQLColumnCheck parseColumnCheck() { - lexer.nextToken(); - SQLExpr expr = this.expr(); - SQLColumnCheck check = new SQLColumnCheck(expr); - - if (lexer.token() == Token.DISABLE) { - lexer.nextToken(); - check.setEnable(false); - } else if (lexer.token() == Token.ENABLE) { - lexer.nextToken(); - check.setEnable(true); - } - return check; - } - - public SQLPrimaryKey parsePrimaryKey() { - accept(Token.PRIMARY); - accept(Token.KEY); - - SQLPrimaryKeyImpl pk = new SQLPrimaryKeyImpl(); - accept(Token.LPAREN); - exprList(pk.getColumns()); - accept(Token.RPAREN); - - return pk; - } - - public SQLUnique parseUnique() { - accept(Token.UNIQUE); - - SQLUnique unique = new SQLUnique(); - accept(Token.LPAREN); - exprList(unique.getColumns()); - accept(Token.RPAREN); - - return unique; - } - - public SQLAssignItem parseAssignItem() { - SQLAssignItem item = new SQLAssignItem(); - - SQLExpr var = primary(); - - if (var instanceof SQLIdentifierExpr) { - var = new SQLVariantRefExpr(((SQLIdentifierExpr) var).getName()); - } - item.setTarget(var); - if (lexer.token() == Token.COLONEQ) { - lexer.nextToken(); - } else { - accept(Token.EQ); - } - item.setValue(expr()); - - return item; - } - - public List parseHints() { - List hints = new ArrayList(); - parseHints(hints); - return hints; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void parseHints(List hints) { - if (lexer.token() == Token.HINT) { - hints.add(new SQLCommentHint(lexer.stringVal())); - lexer.nextToken(); - } - } - - public SQLConstaint parseConstaint() { - SQLName name = null; - - if (lexer.token() == Token.CONSTRAINT) { - lexer.nextToken(); - name = this.name(); - } - - SQLConstaint constraint; - if (lexer.token() == Token.PRIMARY) { - constraint = parsePrimaryKey(); - } else if (lexer.token() == Token.UNIQUE) { - constraint = parseUnique(); - } else if (lexer.token() == Token.FOREIGN) { - constraint = parseForeignKey(); - } else if (lexer.token() == Token.CHECK) { - constraint = parseCheck(); - } else { - throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); - } - - constraint.setName(name); - - return constraint; - } - - public SQLCheck parseCheck() { - accept(Token.CHECK); - SQLCheck check = createCheck(); - accept(Token.LPAREN); - check.setExpr(this.expr()); - accept(Token.RPAREN); - return check; - } - - protected SQLCheck createCheck() { - return new SQLCheck(); - } - - public SQLForeignKeyConstraint parseForeignKey() { - accept(Token.FOREIGN); - accept(Token.KEY); - - SQLForeignKeyConstraint fk = createForeignKey(); - - accept(Token.LPAREN); - this.names(fk.getReferencingColumns()); - accept(Token.RPAREN); - - accept(Token.REFERENCES); - - fk.setReferencedTableName(this.name()); - - accept(Token.LPAREN); - this.names(fk.getReferencedColumns()); - accept(Token.RPAREN); - return fk; - } - - protected SQLForeignKeyConstraint createForeignKey() { - return new SQLForeignKeyImpl(); - } -} +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.sql.parser; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLCommentHint; +import com.alibaba.druid.sql.ast.SQLDataType; +import com.alibaba.druid.sql.ast.SQLDataTypeImpl; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLOrderingSpecification; +import com.alibaba.druid.sql.ast.SQLOver; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr; +import com.alibaba.druid.sql.ast.expr.SQLAllExpr; +import com.alibaba.druid.sql.ast.expr.SQLAnyExpr; +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.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLCurrentOfCursorExpr; +import com.alibaba.druid.sql.ast.expr.SQLDefaultExpr; +import com.alibaba.druid.sql.ast.expr.SQLExistsExpr; +import com.alibaba.druid.sql.ast.expr.SQLHexExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLInListExpr; +import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; +import com.alibaba.druid.sql.ast.expr.SQLListExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; +import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLNotExpr; +import com.alibaba.druid.sql.ast.expr.SQLNullExpr; +import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; +import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.expr.SQLSomeExpr; +import com.alibaba.druid.sql.ast.expr.SQLUnaryExpr; +import com.alibaba.druid.sql.ast.expr.SQLUnaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.NotNullConstraint; +import com.alibaba.druid.sql.ast.statement.SQLAssignItem; +import com.alibaba.druid.sql.ast.statement.SQLCharactorDataType; +import com.alibaba.druid.sql.ast.statement.SQLCheck; +import com.alibaba.druid.sql.ast.statement.SQLColumnCheck; +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.ast.statement.SQLColumnPrimaryKey; +import com.alibaba.druid.sql.ast.statement.SQLColumnReference; +import com.alibaba.druid.sql.ast.statement.SQLColumnUniqueKey; +import com.alibaba.druid.sql.ast.statement.SQLConstaint; +import com.alibaba.druid.sql.ast.statement.SQLForeignKeyConstraint; +import com.alibaba.druid.sql.ast.statement.SQLForeignKeyImpl; +import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey; +import com.alibaba.druid.sql.ast.statement.SQLPrimaryKeyImpl; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; +import com.alibaba.druid.sql.ast.statement.SQLUnique; + +public class SQLExprParser extends SQLParser { + + public final static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM" }; + + protected String[] aggregateFunctions = AGGREGATE_FUNCTIONS; + + public SQLExprParser(String sql){ + super(sql); + } + + public SQLExprParser(Lexer lexer){ + super(lexer); + } + + public SQLExpr expr() { + if (lexer.token() == Token.STAR) { + lexer.nextToken(); + + SQLExpr expr = new SQLAllColumnExpr(); + + if (lexer.token() == Token.DOT) { + lexer.nextToken(); + accept(Token.STAR); + return new SQLPropertyExpr(expr, "*"); + } + + return expr; + } + + SQLExpr expr = primary(); + + if (lexer.token() == Token.COMMA) { + return expr; + } + + return exprRest(expr); + } + + public SQLExpr exprRest(SQLExpr expr) { + expr = bitXorRest(expr); + expr = multiplicativeRest(expr); + expr = additiveRest(expr); + expr = shiftRest(expr); + expr = bitAndRest(expr); + expr = bitOrRest(expr); + expr = inRest(expr); + expr = relationalRest(expr); + expr = equalityRest(expr); + expr = andRest(expr); + expr = orRest(expr); + + return expr; + } + + public final SQLExpr bitXor() { + SQLExpr expr = primary(); + return bitXorRest(expr); + } + + public SQLExpr bitXorRest(SQLExpr expr) { + if (lexer.token() == Token.CARET) { + lexer.nextToken(); + SQLExpr rightExp = primary(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseXor, rightExp); + expr = bitXorRest(expr); + } + + return expr; + } + + public final SQLExpr multiplicative() { + SQLExpr expr = bitXor(); + return multiplicativeRest(expr); + } + + public SQLExpr multiplicativeRest(SQLExpr expr) { + if (lexer.token() == Token.STAR) { + lexer.nextToken(); + SQLExpr rightExp = bitXor(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Multiply, rightExp); + expr = multiplicativeRest(expr); + } else if (lexer.token() == Token.SLASH) { + lexer.nextToken(); + SQLExpr rightExp = bitXor(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Divide, rightExp); + expr = multiplicativeRest(expr); + } else if (lexer.token() == Token.PERCENT) { + lexer.nextToken(); + SQLExpr rightExp = bitXor(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Modulus, rightExp); + expr = multiplicativeRest(expr); + } + return expr; + } + + public SQLExpr primary() { + SQLExpr sqlExpr = null; + + final Token tok = lexer.token(); + + switch (tok) { + case LPAREN: + lexer.nextToken(); + sqlExpr = expr(); + if (lexer.token() == Token.COMMA) { + SQLListExpr listExpr = new SQLListExpr(); + listExpr.getItems().add(sqlExpr); + do { + lexer.nextToken(); + listExpr.getItems().add(expr()); + } while (lexer.token() == Token.COMMA); + + sqlExpr = listExpr; + } + accept(Token.RPAREN); + break; + case INSERT: + lexer.nextToken(); + if (lexer.token() != Token.LPAREN) { + throw new ParserException("syntax error"); + } + sqlExpr = new SQLIdentifierExpr("INSERT"); + break; + case IDENTIFIER: + sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); + lexer.nextToken(); + break; + case NEW: + throw new ParserException("TODO"); + case LITERAL_INT: + sqlExpr = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextToken(); + break; + case LITERAL_FLOAT: + sqlExpr = new SQLNumberExpr(lexer.decimalValue()); + lexer.nextToken(); + break; + case LITERAL_CHARS: + sqlExpr = new SQLCharExpr(lexer.stringVal()); + lexer.nextToken(); + break; + case LITERAL_NCHARS: + sqlExpr = new SQLNCharExpr(lexer.stringVal()); + lexer.nextToken(); + break; + case VARIANT: + SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(lexer.stringVal()); + lexer.nextToken(); + if (varRefExpr.getName().equals("@") && lexer.token() == Token.LITERAL_CHARS) { + varRefExpr.setName("@'" + lexer.stringVal() + "'"); + lexer.nextToken(); + } else if (varRefExpr.getName().equals("@@") && lexer.token() == Token.LITERAL_CHARS) { + varRefExpr.setName("@@'" + lexer.stringVal() + "'"); + lexer.nextToken(); + } + sqlExpr = varRefExpr; + break; + case DEFAULT: + sqlExpr = new SQLDefaultExpr(); + lexer.nextToken(); + break; + case DUAL: + case KEY: + case DISTINCT: + case LIMIT: + case SCHEMA: + case COLUMN: + case IF: + case END: + case COMMENT: + case COMPUTE: + case ENABLE: + case DISABLE: + case INITIALLY: + case SEQUENCE: + case USER: + case EXPLAIN: + case WITH: + case GRANT: + case REPLACE: + case INDEX: + case MODEL: + case PCTFREE: + case INITRANS: + case MAXTRANS: + case SEGMENT: + case CREATION: + case IMMEDIATE: + case DEFERRED: + case STORAGE: + case NEXT: + case MINEXTENTS: + case MAXEXTENTS: + case MAXSIZE: + case PCTINCREASE: + case FLASH_CACHE: + case CELL_FLASH_CACHE: + case KEEP: + case NONE: + case LOB: + case STORE: + case ROW: + case CHUNK: + case CACHE: + case NOCACHE: + case LOGGING: + case NOCOMPRESS: + case KEEP_DUPLICATES: + case EXCEPTIONS: + case PURGE: + case FULL: + case TO: + case IDENTIFIED: + case PASSWORD: + case BINARY: + case WINDOW: + case OFFSET: + case SHARE: + case START: + case CONNECT: + case MATCHED: + case ERRORS: + case REJECT: + case UNLIMITED: + case BEGIN: + case EXCLUSIVE: + case MODE: + case ADVISE: + sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); + lexer.nextToken(); + break; + case CASE: + SQLCaseExpr caseExpr = new SQLCaseExpr(); + lexer.nextToken(); + if (lexer.token() != Token.WHEN) { + caseExpr.setValueExpr(expr()); + } + + accept(Token.WHEN); + SQLExpr testExpr = expr(); + accept(Token.THEN); + SQLExpr valueExpr = expr(); + SQLCaseExpr.Item caseItem = new SQLCaseExpr.Item(testExpr, valueExpr); + caseExpr.addItem(caseItem); + + while (lexer.token() == Token.WHEN) { + lexer.nextToken(); + testExpr = expr(); + accept(Token.THEN); + valueExpr = expr(); + caseItem = new SQLCaseExpr.Item(testExpr, valueExpr); + caseExpr.getItems().add(caseItem); + } + + if (lexer.token() == Token.ELSE) { + lexer.nextToken(); + caseExpr.setElseExpr(expr()); + } + + accept(Token.END); + + sqlExpr = caseExpr; + break; + case EXISTS: + lexer.nextToken(); + accept(Token.LPAREN); + sqlExpr = new SQLExistsExpr(createSelectParser().select()); + accept(Token.RPAREN); + break; + case NOT: + lexer.nextToken(); + if (lexer.token() == Token.EXISTS) { + lexer.nextToken(); + accept(Token.LPAREN); + sqlExpr = new SQLExistsExpr(createSelectParser().select(), true); + accept(Token.RPAREN); + } else if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + SQLExpr notTarget = expr(); + + accept(Token.RPAREN); + notTarget = exprRest(notTarget); + + sqlExpr = new SQLNotExpr(notTarget); + + return primaryRest(sqlExpr); + } else { + SQLExpr restExpr = relational(); + sqlExpr = new SQLNotExpr(restExpr); + } + break; + case SELECT: + SQLQueryExpr queryExpr = new SQLQueryExpr(createSelectParser().select()); + sqlExpr = queryExpr; + break; + case CAST: + lexer.nextToken(); + accept(Token.LPAREN); + SQLCastExpr cast = new SQLCastExpr(); + cast.setExpr(expr()); + accept(Token.AS); + cast.setDataType(parseDataType()); + accept(Token.RPAREN); + + sqlExpr = cast; + break; + case SUB: + lexer.nextToken(); + switch (lexer.token()) { + case LITERAL_INT: + Number integerValue = lexer.integerValue(); + if (integerValue instanceof Integer) { + int intVal = ((Integer) integerValue).intValue(); + if (intVal == Integer.MIN_VALUE) { + integerValue = Long.valueOf(((long) intVal) * -1); + } else { + integerValue = Integer.valueOf(intVal * -1); + } + } else if (integerValue instanceof Long) { + long longVal = ((Long) integerValue).longValue(); + if (longVal == 2147483648L) { + integerValue = Integer.valueOf((int) (((long) longVal) * -1)); + } else { + integerValue = Long.valueOf(longVal * -1); + } + } else { + integerValue = ((BigInteger) integerValue).negate(); + } + sqlExpr = new SQLIntegerExpr(integerValue); + lexer.nextToken(); + break; + case LITERAL_FLOAT: + sqlExpr = new SQLNumberExpr(lexer.decimalValue().negate()); + lexer.nextToken(); + break; + case IDENTIFIER: // 当负号后面为字段的情况 + sqlExpr = new SQLIdentifierExpr(lexer.stringVal()); + sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr); + lexer.nextToken(); + break; + case QUES: + sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, new SQLVariantRefExpr("?")); + lexer.nextToken(); + break; + case LPAREN: + lexer.nextToken(); + sqlExpr = expr(); + accept(Token.RPAREN); + sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Negative, sqlExpr); + break; + default: + throw new ParserException("TODO : " + lexer.token()); + } + break; + case PLUS: + lexer.nextToken(); + switch (lexer.token()) { + case LITERAL_INT: + sqlExpr = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextToken(); + break; + case LITERAL_FLOAT: + sqlExpr = new SQLNumberExpr(lexer.decimalValue()); + lexer.nextToken(); + break; + case LPAREN: + lexer.nextToken(); + sqlExpr = expr(); + accept(Token.RPAREN); + sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Plus, sqlExpr); + break; + default: + throw new ParserException("TODO"); + } + break; + case TILDE: + lexer.nextToken(); + SQLExpr unaryValueExpr = expr(); + SQLUnaryExpr unary = new SQLUnaryExpr(SQLUnaryOperator.Compl, unaryValueExpr); + sqlExpr = unary; + break; + case QUES: + lexer.nextToken(); + SQLVariantRefExpr quesVarRefExpr = new SQLVariantRefExpr("?"); + quesVarRefExpr.setIndex(lexer.nextVarIndex()); + sqlExpr = quesVarRefExpr; + break; + case LEFT: + sqlExpr = new SQLIdentifierExpr("LEFT"); + lexer.nextToken(); + break; + case RIGHT: + sqlExpr = new SQLIdentifierExpr("RIGHT"); + lexer.nextToken(); + break; + case DATABASE: + sqlExpr = new SQLIdentifierExpr("DATABASE"); + lexer.nextToken(); + break; + case LOCK: + sqlExpr = new SQLIdentifierExpr("LOCK"); + lexer.nextToken(); + break; + case NULL: + sqlExpr = new SQLNullExpr(); + lexer.nextToken(); + break; + case BANG: + lexer.nextToken(); + SQLExpr bangExpr = expr(); + sqlExpr = new SQLUnaryExpr(SQLUnaryOperator.Not, bangExpr); + break; + case LITERAL_HEX: + String hex = lexer.hexString(); + sqlExpr = new SQLHexExpr(hex); + lexer.nextToken(); + break; + case INTERVAL: + sqlExpr = parseInterval(); + break; + case COLON: + lexer.nextToken(); + if (lexer.token == Token.LITERAL_ALIAS) { + sqlExpr = new SQLVariantRefExpr(":\"" + lexer.stringVal() + "\""); + lexer.nextToken(); + } + break; + case ANY: + lexer.nextToken(); + if (lexer.token() == Token.LPAREN) { + SQLAnyExpr anyExpr = new SQLAnyExpr(); + + accept(Token.LPAREN); + SQLSelect anySubQuery = createSelectParser().select(); + anyExpr.setSubQuery(anySubQuery); + accept(Token.RPAREN); + + anySubQuery.setParent(anyExpr); + + sqlExpr = anyExpr; + } else { + sqlExpr = new SQLIdentifierExpr("ANY"); + } + break; + case SOME: + lexer.nextToken(); + SQLSomeExpr someExpr = new SQLSomeExpr(); + + accept(Token.LPAREN); + SQLSelect someSubQuery = createSelectParser().select(); + someExpr.setSubQuery(someSubQuery); + accept(Token.RPAREN); + + someSubQuery.setParent(someExpr); + + sqlExpr = someExpr; + break; + case ALL: + lexer.nextToken(); + SQLAllExpr allExpr = new SQLAllExpr(); + + accept(Token.LPAREN); + SQLSelect allSubQuery = createSelectParser().select(); + allExpr.setSubQuery(allSubQuery); + accept(Token.RPAREN); + + allSubQuery.setParent(allExpr); + + sqlExpr = allExpr; + break; + case LITERAL_ALIAS: + sqlExpr = new SQLIdentifierExpr('"' + lexer.stringVal() + '"'); + lexer.nextToken(); + break; + case EOF: + throw new EOFParserException(); + default: + throw new ParserException("ERROR. token : " + tok + ", pos : " + lexer.pos()); + } + + return primaryRest(sqlExpr); + } + + protected SQLExpr parseInterval() { + throw new ParserException("TODO"); + } + + public SQLSelectParser createSelectParser() { + return new SQLSelectParser(this); + } + + public SQLExpr primaryRest(SQLExpr expr) { + if (expr == null) { + throw new IllegalArgumentException("expr"); + } + + if (lexer.token() == Token.OF) { + if (expr instanceof SQLIdentifierExpr) { + String name = ((SQLIdentifierExpr) expr).getName(); + if ("CURRENT".equalsIgnoreCase(name)) { + lexer.nextToken(); + SQLName cursorName = this.name(); + return new SQLCurrentOfCursorExpr(cursorName); + } + } + } + + if (lexer.token() == Token.DOT) { + lexer.nextToken(); + + if (expr instanceof SQLCharExpr) { + String text = ((SQLCharExpr) expr).getText(); + expr = new SQLIdentifierExpr(text); + } + + expr = dotRest(expr); + return primaryRest(expr); + } else { + if (lexer.token() == Token.LPAREN) { + return methodRest(expr, true); + } + } + + return expr; + } + + protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) { + if (acceptLPAREN) { + accept(Token.LPAREN); + } + + if (expr instanceof SQLName || expr instanceof SQLDefaultExpr) { + String methodName; + + SQLMethodInvokeExpr methodInvokeExpr; + if (expr instanceof SQLPropertyExpr) { + methodName = ((SQLPropertyExpr) expr).getName(); + methodInvokeExpr = new SQLMethodInvokeExpr(methodName); + methodInvokeExpr.setOwner(((SQLPropertyExpr) expr).getOwner()); + } else { + methodName = expr.toString(); + methodInvokeExpr = new SQLMethodInvokeExpr(methodName); + } + + if (isAggreateFunction(methodName)) { + SQLAggregateExpr aggregateExpr = parseAggregateExpr(methodName); + + return aggregateExpr; + } + + if (lexer.token() != Token.RPAREN) { + exprList(methodInvokeExpr.getParameters(), methodInvokeExpr); + } + + accept(Token.RPAREN); + + return primaryRest(methodInvokeExpr); + } + + throw new ParserException("not support token:" + lexer.token()); + } + + protected SQLExpr dotRest(SQLExpr expr) { + if (lexer.token() == Token.STAR) { + lexer.nextToken(); + expr = new SQLPropertyExpr(expr, "*"); + } else { + String name; + + if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.LITERAL_CHARS + || lexer.token() == Token.LITERAL_ALIAS) { + name = lexer.stringVal(); + lexer.nextToken(); + } else if (lexer.getKeywods().containsValue(lexer.token())) { + name = lexer.stringVal(); + lexer.nextToken(); + } else { + throw new ParserException("error : " + lexer.stringVal()); + } + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(name); + methodInvokeExpr.setOwner(expr); + if (lexer.token() == Token.RPAREN) { + lexer.nextToken(); + } else { + if (lexer.token() == Token.PLUS) { + methodInvokeExpr.addParameter(new SQLIdentifierExpr("+")); + lexer.nextToken(); + } else { + exprList(methodInvokeExpr.getParameters(), methodInvokeExpr); + } + accept(Token.RPAREN); + } + expr = methodInvokeExpr; + } else { + expr = new SQLPropertyExpr(expr, name); + } + } + + expr = primaryRest(expr); + return expr; + } + + public final SQLExpr groupComparisionRest(SQLExpr expr) { + return expr; + } + + public final void names(Collection exprCol) { + names(exprCol, null); + } + + public final void names(Collection exprCol, SQLObject parent) { + if (lexer.token() == Token.RBRACE) { + return; + } + + if (lexer.token() == Token.EOF) { + return; + } + + SQLName name = name(); + name.setParent(parent); + exprCol.add(name); + + while (lexer.token() == Token.COMMA) { + lexer.nextToken(); + + name = name(); + name.setParent(parent); + exprCol.add(name); + } + } + + public final void exprList(Collection exprCol) { + exprList(exprCol, null); + } + + public final void exprList(Collection exprCol, SQLObject parent) { + if (lexer.token() == Token.RPAREN || lexer.token() == Token.RBRACKET) { + return; + } + + if (lexer.token() == Token.EOF) { + return; + } + + SQLExpr expr = expr(); + expr.setParent(parent); + exprCol.add(expr); + + while (lexer.token() == Token.COMMA) { + lexer.nextToken(); + expr = expr(); + expr.setParent(parent); + exprCol.add(expr); + } + } + + public SQLName name() { + String identName; + if (lexer.token() == Token.LITERAL_ALIAS) { + identName = '"' + lexer.stringVal() + '"'; + lexer.nextToken(); + } else if (lexer.token() == Token.IDENTIFIER) { + identName = lexer.stringVal(); + + lexer.nextToken(); + } else if (lexer.token() == Token.LITERAL_CHARS) { + identName = '\'' + lexer.stringVal() + '\''; + lexer.nextToken(); + } else if (lexer.token() == Token.VARIANT) { + identName = lexer.stringVal(); + lexer.nextToken(); + } else { + switch (lexer.token()) { + case MODEL: + case PCTFREE: + case INITRANS: + case MAXTRANS: + case SEGMENT: + case CREATION: + case IMMEDIATE: + case DEFERRED: + case STORAGE: + case NEXT: + case MINEXTENTS: + case MAXEXTENTS: + case MAXSIZE: + case PCTINCREASE: + case FLASH_CACHE: + case CELL_FLASH_CACHE: + case KEEP: + case NONE: + case LOB: + case STORE: + case ROW: + case CHUNK: + case CACHE: + case NOCACHE: + case LOGGING: + case NOCOMPRESS: + case KEEP_DUPLICATES: + case EXCEPTIONS: + case PURGE: + case INITIALLY: + case END: + case COMMENT: + case ENABLE: + case DISABLE: + case SEQUENCE: + case USER: + case ANALYZE: + case OPTIMIZE: + case GRANT: + identName = lexer.stringVal(); + lexer.nextToken(); + break; + default: + throw new ParserException("error " + lexer.token()); + } + } + + SQLName name = new SQLIdentifierExpr(identName); + + name = nameRest(name); + + return name; + } + + public SQLName nameRest(SQLName name) { + if (lexer.token() == Token.DOT) { + lexer.nextToken(); + + if (lexer.token() == Token.KEY) { + name = new SQLPropertyExpr(name, "KEY"); + lexer.nextToken(); + return name; + } + + if (lexer.token() != Token.LITERAL_ALIAS && lexer.token() != Token.IDENTIFIER + && (!lexer.getKeywods().containsValue(lexer.token()))) { + throw new ParserException("error, " + lexer.token()); + } + + if (lexer.token() == Token.LITERAL_ALIAS) { + name = new SQLPropertyExpr(name, '"' + lexer.stringVal() + '"'); + } else { + name = new SQLPropertyExpr(name, lexer.stringVal()); + } + lexer.nextToken(); + name = nameRest(name); + } + + return name; + } + + public boolean isAggreateFunction(String word) { + for (int i = 0; i < aggregateFunctions.length; ++i) { + if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { + return true; + } + } + + return false; + } + + protected SQLAggregateExpr parseAggregateExpr(String methodName) { + methodName = methodName.toUpperCase(); + + SQLAggregateExpr aggregateExpr; + if (lexer.token() == Token.ALL) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.ALL); + lexer.nextToken(); + } else if (lexer.token() == Token.DISTINCT) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateExpr.Option.DISTINCT); + lexer.nextToken(); + } else { + aggregateExpr = new SQLAggregateExpr(methodName); + } + + exprList(aggregateExpr.getArguments(), aggregateExpr); + + parseAggregateExprRest(aggregateExpr); + + accept(Token.RPAREN); + + if (lexer.token() == Token.OVER) { + lexer.nextToken(); + SQLOver over = new SQLOver(); + accept(Token.LPAREN); + + if (lexer.token() == Token.PARTITION || identifierEquals("PARTITION")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + exprList(over.getPartitionBy()); + accept(Token.RPAREN); + } else { + exprList(over.getPartitionBy()); + } + } + + over.setOrderBy(parseOrderBy()); + + accept(Token.RPAREN); + aggregateExpr.setOver(over); + + } + + return aggregateExpr; + } + + protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) { + return aggregateExpr; + } + + public SQLOrderBy parseOrderBy() { + if (lexer.token() == Token.ORDER) { + SQLOrderBy orderBy = new SQLOrderBy(); + + lexer.nextToken(); + + accept(Token.BY); + + orderBy.getItems().add(parseSelectOrderByItem()); + + while (lexer.token() == Token.COMMA) { + lexer.nextToken(); + orderBy.getItems().add(parseSelectOrderByItem()); + } + + return orderBy; + } + + return null; + } + + public SQLSelectOrderByItem parseSelectOrderByItem() { + SQLSelectOrderByItem item = new SQLSelectOrderByItem(); + + item.setExpr(expr()); + + if (lexer.token() == Token.ASC) { + lexer.nextToken(); + item.setType(SQLOrderingSpecification.ASC); + } else if (lexer.token() == Token.DESC) { + lexer.nextToken(); + item.setType(SQLOrderingSpecification.DESC); + } + + return item; + } + + public final SQLExpr bitAnd() { + SQLExpr expr = shift(); + return bitAndRest(expr); + } + + public final SQLExpr bitAndRest(SQLExpr expr) { + while (lexer.token() == Token.AMP) { + lexer.nextToken(); + SQLExpr rightExp = shift(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseAnd, rightExp); + } + return expr; + } + + public final SQLExpr bitOr() { + SQLExpr expr = bitAnd(); + return bitOrRest(expr); + } + + public final SQLExpr bitOrRest(SQLExpr expr) { + if (lexer.token() == Token.BAR) { + lexer.nextToken(); + SQLExpr rightExp = bitAnd(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BitwiseOr, rightExp); + expr = bitAndRest(expr); + } + return expr; + } + + public final SQLExpr equality() { + SQLExpr expr = bitOr(); + return equalityRest(expr); + } + + public SQLExpr equalityRest(SQLExpr expr) { + SQLExpr rightExp; + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + try { + rightExp = bitOr(); + } catch (EOFParserException e) { + throw new ParserException("EOF, " + expr + "=", e); + } + rightExp = equalityRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Equality, rightExp); + } else if (lexer.token() == Token.BANGEQ) { + lexer.nextToken(); + rightExp = bitOr(); + + rightExp = equalityRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotEqual, rightExp); + } else if (lexer.token() == Token.COLONEQ) { + lexer.nextToken(); + rightExp = expr(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Assignment, rightExp); + } + + return expr; + } + + public final SQLExpr inRest(SQLExpr expr) { + if (lexer.token() == Token.IN) { + lexer.nextToken(); + + SQLInListExpr inListExpr = new SQLInListExpr(expr); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + exprList(inListExpr.getTargetList()); + accept(Token.RPAREN); + expr = inListExpr; + } else { + SQLExpr itemExpr = primary(); + inListExpr.getTargetList().add(itemExpr); + } + + expr = inListExpr; + + if (inListExpr.getTargetList().size() == 1) { + SQLExpr targetExpr = inListExpr.getTargetList().get(0); + if (targetExpr instanceof SQLQueryExpr) { + SQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(); + inSubQueryExpr.setExpr(inListExpr.getExpr()); + inSubQueryExpr.setSubQuery(((SQLQueryExpr) targetExpr).getSubQuery()); + expr = inSubQueryExpr; + } + } + } + + return expr; + } + + public final SQLExpr additive() { + SQLExpr expr = multiplicative(); + return additiveRest(expr); + } + + public SQLExpr additiveRest(SQLExpr expr) { + if (lexer.token() == Token.PLUS) { + lexer.nextToken(); + SQLExpr rightExp = multiplicative(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Add, rightExp); + expr = additiveRest(expr); + } else if (lexer.token() == Token.BARBAR) { + lexer.nextToken(); + SQLExpr rightExp = multiplicative(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Concat, rightExp); + expr = additiveRest(expr); + } else if (lexer.token() == Token.SUB) { + lexer.nextToken(); + SQLExpr rightExp = multiplicative(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Subtract, rightExp); + expr = additiveRest(expr); + } + + return expr; + } + + public final SQLExpr shift() { + SQLExpr expr = additive(); + return shiftRest(expr); + } + + public SQLExpr shiftRest(SQLExpr expr) { + if (lexer.token() == Token.LTLT) { + lexer.nextToken(); + SQLExpr rightExp = additive(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LeftShift, rightExp); + expr = shiftRest(expr); + } else if (lexer.token() == Token.GTGT) { + lexer.nextToken(); + SQLExpr rightExp = additive(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.RightShift, rightExp); + expr = shiftRest(expr); + } + + return expr; + } + + public SQLExpr and() { + SQLExpr expr = relational(); + return andRest(expr); + } + + public SQLExpr andRest(SQLExpr expr) { + for (;;) { + if (lexer.token() == Token.AND || lexer.token() == Token.AMPAMP) { + lexer.nextToken(); + SQLExpr rightExp = relational(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanAnd, rightExp); + } else { + break; + } + } + + return expr; + } + + public SQLExpr or() { + SQLExpr expr = and(); + return orRest(expr); + } + + public SQLExpr orRest(SQLExpr expr) { + + for (;;) { + if (lexer.token() == Token.OR) { + lexer.nextToken(); + SQLExpr rightExp = and(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanOr, rightExp); + } else if (lexer.token() == Token.XOR) { + lexer.nextToken(); + SQLExpr rightExp = and(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.BooleanXor, rightExp); + } else { + break; + } + } + + return expr; + } + + public SQLExpr relational() { + SQLExpr expr = equality(); + + return relationalRest(expr); + } + + public SQLExpr relationalRest(SQLExpr expr) { + SQLExpr rightExp; + + if (lexer.token() == Token.LT) { + SQLBinaryOperator op = SQLBinaryOperator.LessThan; + + lexer.nextToken(); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + op = SQLBinaryOperator.LessThanOrEqual; + } + + rightExp = bitOr(); + expr = new SQLBinaryOpExpr(expr, op, rightExp); + // expr = relationalRest(expr); + } else if (lexer.token() == Token.LTEQ) { + lexer.nextToken(); + rightExp = bitOr(); + + // rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrEqual, rightExp); + } else if (lexer.token() == Token.LTEQGT) { + lexer.nextToken(); + rightExp = bitOr(); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrEqualOrGreaterThan, rightExp); + } else if (lexer.token() == Token.GT) { + SQLBinaryOperator op = SQLBinaryOperator.GreaterThan; + + lexer.nextToken(); + + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + op = SQLBinaryOperator.GreaterThanOrEqual; + } + + rightExp = bitOr(); + + expr = new SQLBinaryOpExpr(expr, op, rightExp); + } else if (lexer.token() == Token.GTEQ) { + lexer.nextToken(); + rightExp = bitOr(); + + // rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.GreaterThanOrEqual, rightExp); + } else if (lexer.token() == Token.BANGLT) { + lexer.nextToken(); + rightExp = bitOr(); + + // rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotLessThan, rightExp); + } else if (lexer.token() == Token.BANGGT) { + lexer.nextToken(); + rightExp = bitOr(); + + rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotGreaterThan, rightExp); + } else if (lexer.token() == Token.LTGT) { + lexer.nextToken(); + rightExp = bitOr(); + + // rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.LessThanOrGreater, rightExp); + } else if (lexer.token() == Token.LIKE) { + lexer.nextToken(); + rightExp = bitOr(); + + // rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Like, rightExp); + + if (lexer.token() == Token.ESCAPE) { + lexer.nextToken(); + rightExp = primary(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Escape, rightExp); + } + } else if (lexer.token() == (Token.NOT)) { + lexer.nextToken(); + expr = notRationalRest(expr); + } else if (lexer.token() == (Token.BETWEEN)) { + lexer.nextToken(); + SQLExpr beginExpr = bitOr(); + accept(Token.AND); + SQLExpr endExpr = bitOr(); + expr = new SQLBetweenExpr(expr, beginExpr, endExpr); + } else if (lexer.token() == (Token.IS)) { + lexer.nextToken(); + + if (lexer.token() == (Token.NOT)) { + lexer.nextToken(); + SQLExpr rightExpr = primary(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.IsNot, rightExpr); + } else { + SQLExpr rightExpr = primary(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Is, rightExpr); + } + } else if (lexer.token() == Token.IN) { + expr = inRest(expr); + } + + return expr; + } + + public SQLExpr notRationalRest(SQLExpr expr) { + if (lexer.token() == (Token.LIKE)) { + lexer.nextToken(); + SQLExpr rightExp = equality(); + + rightExp = relationalRest(rightExp); + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotLike, rightExp); + + if (lexer.token() == Token.ESCAPE) { + lexer.nextToken(); + rightExp = expr(); + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Escape, rightExp); + } + } else if (lexer.token() == Token.IN) { + lexer.nextToken(); + accept(Token.LPAREN); + + SQLInListExpr inListExpr = new SQLInListExpr(expr, true); + exprList(inListExpr.getTargetList()); + expr = inListExpr; + + accept(Token.RPAREN); + + if (inListExpr.getTargetList().size() == 1) { + SQLExpr targetExpr = inListExpr.getTargetList().get(0); + if (targetExpr instanceof SQLQueryExpr) { + SQLInSubQueryExpr inSubQueryExpr = new SQLInSubQueryExpr(); + inSubQueryExpr.setNot(true); + inSubQueryExpr.setExpr(inListExpr.getExpr()); + inSubQueryExpr.setSubQuery(((SQLQueryExpr) targetExpr).getSubQuery()); + expr = inSubQueryExpr; + } + } + + expr = relationalRest(expr); + return expr; + } else if (lexer.token() == (Token.BETWEEN)) { + lexer.nextToken(); + SQLExpr beginExpr = bitOr(); + accept(Token.AND); + SQLExpr endExpr = bitOr(); + + expr = new SQLBetweenExpr(expr, true, beginExpr, endExpr); + + return expr; + } else { + throw new ParserException("TODO " + lexer.token()); + } + return expr; + } + + public SQLDataType parseDataType() { + + if (lexer.token() == Token.DEFAULT || lexer.token() == Token.NOT || lexer.token() == Token.NULL) { + return null; + } + + SQLName typeExpr = name(); + String typeName = typeExpr.toString(); + + if (isCharType(typeName)) { + SQLCharactorDataType charType = new SQLCharactorDataType(typeName); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + SQLExpr arg = this.expr(); + arg.setParent(charType); + charType.getArguments().add(arg); + accept(Token.RPAREN); + } + + return parseCharTypeRest(charType); + } + + if ("character".equalsIgnoreCase(typeName) && "varying".equalsIgnoreCase(lexer.stringVal())) { + typeName += ' ' + lexer.stringVal(); + lexer.nextToken(); + } + + SQLDataType dataType = new SQLDataTypeImpl(typeName); + return parseDataTypeRest(dataType); + } + + protected SQLDataType parseDataTypeRest(SQLDataType dataType) { + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + exprList(dataType.getArguments(), dataType); + accept(Token.RPAREN); + } + + return dataType; + } + + protected boolean isCharType(String dataTypeName) { + return "char".equalsIgnoreCase(dataTypeName) // + || "varchar".equalsIgnoreCase(dataTypeName) + || "nchar".equalsIgnoreCase(dataTypeName) + || "nvarchar".equalsIgnoreCase(dataTypeName) + // + ; + } + + protected SQLDataType parseCharTypeRest(SQLCharactorDataType charType) { + if (identifierEquals("CHARACTER")) { + lexer.nextToken(); + + accept(Token.SET); + + if (lexer.token() != Token.IDENTIFIER && lexer.token() != Token.LITERAL_CHARS) { + throw new ParserException(); + } + charType.setCharSetName(lexer.stringVal()); + lexer.nextToken(); + + if (lexer.token() == Token.IDENTIFIER) { + if (lexer.stringVal().equalsIgnoreCase("COLLATE")) { + lexer.nextToken(); + + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException(); + } + charType.setCollate(lexer.stringVal()); + lexer.nextToken(); + } + } + } + return charType; + } + + public void accept(Token token) { + if (lexer.token() == token) { + lexer.nextToken(); + } else { + throw new ParserException("syntax error, expect " + token + ", actual " + lexer.token() + " " + + lexer.stringVal()); + } + } + + public SQLColumnDefinition parseColumn() { + SQLColumnDefinition column = createColumnDefinition(); + column.setName(name()); + column.setDataType(parseDataType()); + + return parseColumnRest(column); + } + + protected SQLColumnDefinition createColumnDefinition() { + SQLColumnDefinition column = new SQLColumnDefinition(); + return column; + } + + public SQLColumnDefinition parseColumnRest(SQLColumnDefinition column) { + if (lexer.token() == Token.DEFAULT) { + lexer.nextToken(); + column.setDefaultExpr(bitOr()); + return parseColumnRest(column); + } + + if (lexer.token() == Token.NOT) { + lexer.nextToken(); + accept(Token.NULL); + column.getConstaints().add(new NotNullConstraint()); + return parseColumnRest(column); + } + + if (lexer.token() == Token.NULL) { + lexer.nextToken(); + column.setDefaultExpr(new SQLNullExpr()); + return parseColumnRest(column); + } + + if (lexer.token == Token.PRIMARY) { + lexer.nextToken(); + accept(Token.KEY); + column.getConstaints().add(new SQLColumnPrimaryKey()); + return parseColumnRest(column); + } + + if (lexer.token == Token.UNIQUE) { + lexer.nextToken(); + if (lexer.token() == Token.KEY) { + lexer.nextToken(); + } + column.getConstaints().add(new SQLColumnPrimaryKey()); + return parseColumnRest(column); + } + + if (lexer.token == Token.CONSTRAINT) { + lexer.nextToken(); + + SQLName name = this.name(); + + if (lexer.token() == Token.PRIMARY) { + lexer.nextToken(); + accept(Token.KEY); + SQLColumnPrimaryKey pk = new SQLColumnPrimaryKey(); + pk.setName(name); + column.getConstaints().add(pk); + return parseColumnRest(column); + } + + if (lexer.token() == Token.UNIQUE) { + lexer.nextToken(); + SQLColumnUniqueKey uk = new SQLColumnUniqueKey(); + uk.setName(name); + column.getConstaints().add(uk); + return parseColumnRest(column); + } + + if (lexer.token() == Token.REFERENCES) { + lexer.nextToken(); + SQLColumnReference ref = new SQLColumnReference(); + ref.setName(name); + ref.setTable(this.name()); + accept(Token.LPAREN); + this.names(ref.getColumns(), ref); + accept(Token.RPAREN); + column.getConstaints().add(ref); + return parseColumnRest(column); + } + + if (lexer.token() == Token.NOT) { + lexer.nextToken(); + accept(Token.NULL); + NotNullConstraint notNull = new NotNullConstraint(); + notNull.setName(name); + column.getConstaints().add(notNull); + return parseColumnRest(column); + } + + if (lexer.token == Token.CHECK) { + SQLColumnCheck check = parseColumnCheck(); + check.setName(name); + check.setParent(column); + column.getConstaints().add(check); + return parseColumnRest(column); + } + + if (lexer.token == Token.DEFAULT) { + lexer.nextToken(); + SQLExpr expr = this.expr(); + column.setDefaultExpr(expr); + return parseColumnRest(column); + } + + throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + } + + if (lexer.token == Token.CHECK) { + SQLColumnCheck check = parseColumnCheck(); + column.getConstaints().add(check); + return parseColumnRest(column); + } + + if (lexer.token() == Token.COMMENT) { + lexer.nextToken(); + column.setComment(primary()); + } + + return column; + } + + protected SQLColumnCheck parseColumnCheck() { + lexer.nextToken(); + SQLExpr expr = this.expr(); + SQLColumnCheck check = new SQLColumnCheck(expr); + + if (lexer.token() == Token.DISABLE) { + lexer.nextToken(); + check.setEnable(false); + } else if (lexer.token() == Token.ENABLE) { + lexer.nextToken(); + check.setEnable(true); + } + return check; + } + + public SQLPrimaryKey parsePrimaryKey() { + accept(Token.PRIMARY); + accept(Token.KEY); + + SQLPrimaryKeyImpl pk = new SQLPrimaryKeyImpl(); + accept(Token.LPAREN); + exprList(pk.getColumns()); + accept(Token.RPAREN); + + return pk; + } + + public SQLUnique parseUnique() { + accept(Token.UNIQUE); + + SQLUnique unique = new SQLUnique(); + accept(Token.LPAREN); + exprList(unique.getColumns()); + accept(Token.RPAREN); + + return unique; + } + + public SQLAssignItem parseAssignItem() { + SQLAssignItem item = new SQLAssignItem(); + + SQLExpr var = primary(); + + if (var instanceof SQLIdentifierExpr) { + var = new SQLVariantRefExpr(((SQLIdentifierExpr) var).getName()); + } + item.setTarget(var); + if (lexer.token() == Token.COLONEQ) { + lexer.nextToken(); + } else { + accept(Token.EQ); + } + item.setValue(expr()); + + return item; + } + + public List parseHints() { + List hints = new ArrayList(); + parseHints(hints); + return hints; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void parseHints(List hints) { + if (lexer.token() == Token.HINT) { + hints.add(new SQLCommentHint(lexer.stringVal())); + lexer.nextToken(); + } + } + + public SQLConstaint parseConstaint() { + SQLName name = null; + + if (lexer.token() == Token.CONSTRAINT) { + lexer.nextToken(); + name = this.name(); + } + + SQLConstaint constraint; + if (lexer.token() == Token.PRIMARY) { + constraint = parsePrimaryKey(); + } else if (lexer.token() == Token.UNIQUE) { + constraint = parseUnique(); + } else if (lexer.token() == Token.FOREIGN) { + constraint = parseForeignKey(); + } else if (lexer.token() == Token.CHECK) { + constraint = parseCheck(); + } else { + throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + } + + constraint.setName(name); + + return constraint; + } + + public SQLCheck parseCheck() { + accept(Token.CHECK); + SQLCheck check = createCheck(); + accept(Token.LPAREN); + check.setExpr(this.expr()); + accept(Token.RPAREN); + return check; + } + + protected SQLCheck createCheck() { + return new SQLCheck(); + } + + public SQLForeignKeyConstraint parseForeignKey() { + accept(Token.FOREIGN); + accept(Token.KEY); + + SQLForeignKeyConstraint fk = createForeignKey(); + + accept(Token.LPAREN); + this.names(fk.getReferencingColumns()); + accept(Token.RPAREN); + + accept(Token.REFERENCES); + + fk.setReferencedTableName(this.name()); + + accept(Token.LPAREN); + this.names(fk.getReferencedColumns()); + accept(Token.RPAREN); + return fk; + } + + protected SQLForeignKeyConstraint createForeignKey() { + return new SQLForeignKeyImpl(); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java index b130b9056e..3b33e89656 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.dialect.db2.parser.DB2ExprParser; import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlExprParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsExprParser; import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser; import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; import com.alibaba.druid.sql.dialect.postgresql.parser.PGExprParser; @@ -83,6 +84,10 @@ public class SQLParserUtils { if (JdbcUtils.DB2.equals(dbType)) { return new DB2ExprParser(sql); } + + if (JdbcUtils.ODPS.equals(dbType)) { + return new OdpsExprParser(sql); + } return new SQLExprParser(sql); } 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 f0f71b2794..5dc23da696 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -240,6 +240,11 @@ public enum Token { // Oracle BINARY_FLOAT, BINARY_DOUBLE, + + // odps,hive + PARTITION, + PARTITIONED, + OVERWRITE, LPAREN("("), RPAREN(")"), diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index b4349ef25d..9dcd84ebdf 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -144,9 +144,10 @@ import com.alibaba.druid.sql.ast.statement.SQLWithSubqueryClause; public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements PrintableVisitor { protected final Appendable appender; - private String indent = "\t"; - private int indentCount = 0; - private boolean prettyFormat = true; + private String indent = "\t"; + private int indentCount = 0; + private boolean prettyFormat = true; + protected int selectListNumberOfLine = 5; private List parameters; @@ -252,7 +253,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab incrementIndent(); for (int i = 0, size = selectList.size(); i < size; ++i) { if (i != 0) { - if (i % 5 == 0) { + if (i % selectListNumberOfLine == 0) { println(); } @@ -642,7 +643,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab println(); x.getOrderBy().accept(this); } - + if (x.getHintsSize() > 0) { printAndAccept(x.getHints(), ""); } @@ -894,6 +895,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab } } + if (x.getComment() != null) { + print(" COMMENT "); + x.getComment().accept(this); + } + return false; } @@ -1148,11 +1154,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab x.getOutParameter().accept(this); print(" = "); } - + print("CALL "); x.getProcedureName().accept(this); print('('); - + printAndAccept(x.getParameters(), ", "); print(')'); if (x.isBrace()) { @@ -1417,6 +1423,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab public boolean visit(SQLOver x) { print("OVER ("); if (x.getPartitionBy().size() > 0) { + print("PARTITION BY "); printAndAccept(x.getPartitionBy(), ", "); print(' '); } @@ -1790,7 +1797,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab } print(" ADMIN OPTION"); } - + if (x.getIdentifiedBy() != null) { print(" IDENTIFIED BY "); x.getIdentifiedBy().accept(this); @@ -1811,43 +1818,43 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab return false; } - + @Override public boolean visit(SQLDropFunctionStatement x) { print("DROP FUNCTION "); - + if (x.isIfExists()) { print("IF EXISTS "); } - + x.getName().accept(this); - + return false; } - + @Override public boolean visit(SQLDropTableSpaceStatement x) { print("DROP TABLESPACE "); - + if (x.isIfExists()) { print("IF EXISTS "); } - + x.getName().accept(this); - + return false; } - + @Override public boolean visit(SQLDropProcedureStatement x) { print("DROP PROCEDURE "); - + if (x.isIfExists()) { print("IF EXISTS "); } - + x.getName().accept(this); - + return false; } @@ -1891,18 +1898,18 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab x.getConstraint().accept(this); return false; } - + public boolean visit(SQLCreateTriggerStatement x) { print("CREATE "); - + if (x.isOrReplace()) { print("OR REPLEACE "); } - + print("TRIGGER "); - + x.getName().accept(this); - + incrementIndent(); println(); if (TriggerType.INSTEAD_OF.equals(x.getTriggerType())) { @@ -1910,7 +1917,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab } else { print(x.getTriggerType().name()); } - + for (TriggerEvent event : x.getTriggerEvents()) { print(' '); print(event.name()); @@ -1918,7 +1925,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Printab println(); print("ON "); x.getOn().accept(this); - + if (x.isForEachRow()) { println(); print("FOR EACH ROW"); diff --git a/src/main/java/com/alibaba/druid/util/JdbcConstants.java b/src/main/java/com/alibaba/druid/util/JdbcConstants.java index 8ae1f4625f..f203e65ce2 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcConstants.java +++ b/src/main/java/com/alibaba/druid/util/JdbcConstants.java @@ -54,4 +54,9 @@ public interface JdbcConstants { public static final String H2 = "h2"; public static final String H2_DRIVER = "org.h2.Driver"; + + /** + * 阿里云odps + */ + public static final String ODPS = "odps"; } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsInsertTest.java b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsInsertTest.java new file mode 100644 index 0000000000..b5c2d3bd3d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsInsertTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.odps; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsSchemaStatVisitor; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.Utils; + +public class OdpsInsertTest extends TestCase { + + public void test_0() throws Exception { + exec_test("bvt/parser/odps-2.sql"); + } + + public void exec_test(String resource) throws Exception { + System.out.println(resource); + InputStream is = null; + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + Reader reader = new InputStreamReader(is, "UTF-8"); + String input = Utils.read(reader); + JdbcUtils.close(reader); + String[] items = input.split("---------------------------"); + String sql = items[0].trim(); + String expect = items[1].trim(); + + OdpsStatementParser parser = new OdpsStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = new OdpsSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println(sql); + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + + System.out.println(); + System.out.println("---------------------------"); + System.out.println(SQLUtils.toOdpsString(stmt)); + } + + void mergValidate(String sql, String expect) { + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + StringBuilder out = new StringBuilder(); + MySqlParameterizedOutputVisitor visitor = new MySqlParameterizedOutputVisitor(out); + statemen.accept(visitor); + + System.out.println(out.toString()); + + Assert.assertEquals(expect, out.toString()); + } + + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsMultiInsertTest.java b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsMultiInsertTest.java new file mode 100644 index 0000000000..7e6fef0e8b --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsMultiInsertTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.odps; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsSchemaStatVisitor; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.Utils; + +public class OdpsMultiInsertTest extends TestCase { + + public void test_0() throws Exception { + exec_test("bvt/parser/odps-1.sql"); + } + + public void exec_test(String resource) throws Exception { + System.out.println(resource); + InputStream is = null; + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + Reader reader = new InputStreamReader(is, "UTF-8"); + String input = Utils.read(reader); + JdbcUtils.close(reader); + String[] items = input.split("---------------------------"); + String sql = items[0].trim(); + String expect = items[1].trim(); + + OdpsStatementParser parser = new OdpsStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = new OdpsSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println(sql); + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + + System.out.println(); + System.out.println("---------------------------"); + System.out.println(SQLUtils.toOdpsString(stmt)); + } + + void mergValidate(String sql, String expect) { + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + StringBuilder out = new StringBuilder(); + MySqlParameterizedOutputVisitor visitor = new MySqlParameterizedOutputVisitor(out); + statemen.accept(visitor); + + System.out.println(out.toString()); + + Assert.assertEquals(expect, out.toString()); + } + + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsResourceTest.java b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsResourceTest.java new file mode 100644 index 0000000000..a318c39e9a --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsResourceTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.odps; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsSchemaStatVisitor; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.Utils; + +public class OdpsResourceTest extends TestCase { + + public void test_0() throws Exception { + exec_test("bvt/parser/odps-0.sql"); + } + + public void exec_test(String resource) throws Exception { + System.out.println(resource); + InputStream is = null; + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + Reader reader = new InputStreamReader(is, "UTF-8"); + String input = Utils.read(reader); + JdbcUtils.close(reader); + String[] items = input.split("---------------------------"); + String sql = items[0].trim(); + String expect = items[1].trim(); + + OdpsStatementParser parser = new OdpsStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = new OdpsSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println(sql); + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + + System.out.println(); + System.out.println("---------------------------"); + System.out.println(SQLUtils.toOdpsString(stmt)); + } + + void mergValidate(String sql, String expect) { + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + StringBuilder out = new StringBuilder(); + MySqlParameterizedOutputVisitor visitor = new MySqlParameterizedOutputVisitor(out); + statemen.accept(visitor); + + System.out.println(out.toString()); + + Assert.assertEquals(expect, out.toString()); + } + + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsSelectTest.java b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsSelectTest.java new file mode 100644 index 0000000000..df3875f665 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/odps/OdpsSelectTest.java @@ -0,0 +1,91 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.odps; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlParameterizedOutputVisitor; +import com.alibaba.druid.sql.dialect.odps.parser.OdpsStatementParser; +import com.alibaba.druid.sql.dialect.odps.visitor.OdpsSchemaStatVisitor; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.Utils; + +public class OdpsSelectTest extends TestCase { + + public void test_0() throws Exception { + exec_test("bvt/parser/odps-3.sql"); + } + + public void exec_test(String resource) throws Exception { + System.out.println(resource); + InputStream is = null; + + is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); + Reader reader = new InputStreamReader(is, "UTF-8"); + String input = Utils.read(reader); + JdbcUtils.close(reader); + String[] items = input.split("---------------------------"); + String sql = items[0].trim(); + String expect = items[1].trim(); + + OdpsStatementParser parser = new OdpsStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = new OdpsSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println(sql); + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + + System.out.println(); + System.out.println("---------------------------"); + System.out.println(SQLUtils.toOdpsString(stmt)); + } + + void mergValidate(String sql, String expect) { + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + StringBuilder out = new StringBuilder(); + MySqlParameterizedOutputVisitor visitor = new MySqlParameterizedOutputVisitor(out); + statemen.accept(visitor); + + System.out.println(out.toString()); + + Assert.assertEquals(expect, out.toString()); + } + + +} diff --git a/src/test/resources/bvt/parser/odps-0.sql b/src/test/resources/bvt/parser/odps-0.sql new file mode 100644 index 0000000000..2d55cfaaf7 --- /dev/null +++ b/src/test/resources/bvt/parser/odps-0.sql @@ -0,0 +1,24 @@ +create table tt_xx ( + f1 string, +f2 string comment 'xxx', + f3 string comment 'xxx', + gmt_first datetime comment '首次时间', +gmt_last datetime comment '末次时间', + pv_cnt bigint comment 'PV数量', + pv_day_cnt bigint comment 'PV天数' +) +comment 'xx comment xxx' partitioned by (pt string comment '按天分区字段', seg string comment '时间跨度') +; +--------------------------- +create table tt_xx ( + f1 string, + f2 string comment 'xxx', + f3 string comment 'xxx', + gmt_first datetime comment '首次时间', + gmt_last datetime comment '末次时间', + pv_cnt bigint comment 'PV数量', + pv_day_cnt bigint comment 'PV天数' +) +comment 'xx comment xxx' +partitioned by (pt string comment '按天分区字段', seg string comment '时间跨度') +; \ No newline at end of file diff --git a/src/test/resources/bvt/parser/odps-1.sql b/src/test/resources/bvt/parser/odps-1.sql new file mode 100644 index 0000000000..982a2a69ef --- /dev/null +++ b/src/test/resources/bvt/parser/odps-1.sql @@ -0,0 +1,77 @@ +FROM ( + select * from uxoe.xhaayi + where pt = '${bizdate}' +) x + +INSERT overwrite TABLE uxoe.ialeulxx PARTITION(pt=${bizdate-1}) + select case when length(iweet_d) > 2 then iweet_d else NULL end iweet_d + , case when length(user_nick) > 2 then user_nick else NULL end user_nick + , olx + , uoex + , brand + , cpu + , umsis + , iakxh + , resolution + , os + , os_version + , client_ip + , count(*) as count + , max(from_unixtime(cast(server_time as bigint)/1000)) last_time + , min(from_unixtime(cast(server_time as bigint)/1000)) first_time + group by iweet_d, user_nick, olx, uoex, brand, cpu, umsis, iakxh, resolution, os, os_version, client_ip + +INSERT overwrite TABLE uxoe.adl_wdm_ip_device_sdt PARTITION (pt='${bizdate}', seg='d1') + select client_ip + , olx + , uoex + , brand + , cpu + , umsis + , iakxh + , reserve2 utdid + , min(from_unixtime(cast(server_time as bigint)/1000)) first_time + , max(from_unixtime(cast(server_time as bigint)/1000)) last_time + , count(*) + , count(distinct pt) + group by client_ip, olx, uoex, brand, cpu, umsis, iakxh, reserve2 + +INSERT overwrite TABLE uxoe.hxeh PARTITION(pt='${bizdate}') + select unique_id() as uuid + , regexp_extract(args, 'ongitude=(.*?)(,)') longitude + , regexp_extract(args, 'latitude=(.*?)(,)') latitude + , client_ip + , protocol_version + , olx + , uoex + , brand + , cpu + , umsis + , iakxh + , resolution + , carrier + , access + , access_subtype + , channel + , app_key + , app_version + , user_nick + , phone_number + , country + , language,os,os_version,sdk_type,sdk_version,reserve1,reserve2,reserve3,reserve4,reserve5 + , reserves,local_time + , from_unixtime(cast(server_time as bigint)/1000) server_time + , page + , event_id + , arg1 + , arg2 + , arg3 + , args + , day + , hour_id + , iweet_d + , oqlx + , cast(regexp_extract(args, 'wifi_num=(.*?)(,)') as bigint) wifi_num + WHERE length(args) > 5 + AND instr(args, 'gsm_') > 0 +--------------------------- diff --git a/src/test/resources/bvt/parser/odps-2.sql b/src/test/resources/bvt/parser/odps-2.sql new file mode 100644 index 0000000000..c970c05bd9 --- /dev/null +++ b/src/test/resources/bvt/parser/odps-2.sql @@ -0,0 +1,110 @@ +--@exclude_input=bqxef; +--@exclude_output=avwaefrwagqbw; + +INSERT overwrite TABLE avwaefrwagqbw partition (pt) +SELECT a.zxbw + , a.xbax + , a.brand + , a.cpu + , a.xx_x3 + , a.nwxus + , a.xbax xbax_first + , a.xbax xbax_first + , b.xbax xbax_last + , b.xbax xbax_last + , first_time + , last_time + , (rn_lead - rn_lag) + 1 count + , province(b.xbax, b.xbax) province + , province_code(b.xbax, b.xbax) province_code + , city(b.xbax, b.xbax) city + , city_code(b.xbax, b.xbax) city_code + , a.pt +FROM ( + SELECT row_number() over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by rn_lead) rn + , zxbw + , xbax + , brand + , cpu + , xx_x3 + , nwxus + , xbax + , xbax + , server_time first_time + , pt + , rn_lead + FROM ( + SELECT row_number() over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) rn_lead + , zxbw + , xbax + , brand + , cpu + , xx_x3 + , nwxus + , xbax + , LEAD(xbax, 1, 0) over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) xbax_lead + , xbax + , LEAD(xbax, 1, 0) over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) xbax_lead + , server_time + , pt + FROM bqxef + where length(zxbw) >= 15 + AND xbax IS NOT NULL + AND xbax IS NOT NULL + AND abs(xbax) <= 180 + AND abs(xbax) <= 90 + AND xbax <> 0 + AND xbax <> 0 + AND pt >= ${bizdate} + ) x1 + WHERE round(xbax, 2) <> round(xbax_lead, 2) + OR round(xbax, 2) <> round(xbax_lead, 2) +) a inner join ( + SELECT row_number() over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by rn_lag) rn + , zxbw + , xbax + , brand + , cpu + , xx_x3 + , nwxus + , xbax + , xbax + , server_time last_time + , pt + , rn_lag + FROM ( + SELECT row_number() over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) rn_lag + , zxbw + , xbax + , brand + , cpu + , xx_x3 + , nwxus + , xbax + , LAG(xbax, 1, 0) over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) xbax_lag + , xbax + , LAG(xbax, 1, 0) over(partition by pt, zxbw, xbax, brand, cpu, xx_x3, nwxus order by server_time, xbax, xbax) xbax_lag + , server_time + , pt + FROM bqxef + where length(zxbw) >= 15 + AND xbax IS NOT NULL + AND xbax IS NOT NULL + AND abs(xbax) <= 180 + AND abs(xbax) <= 90 + AND xbax <> 0 + AND xbax <> 0 + AND pt >= ${bizdate} + ) x2 + WHERE round(xbax, 2) <> round(xbax_lag, 2) + OR round(xbax, 2) <> round(xbax_lag, 2) +) b on a.pt = b.pt + AND a.zxbw = b.zxbw + AND a.xbax = b.xbax + AND a.brand = b.brand + AND a.cpu = b.cpu + AND a.xx_x3 = b.xx_x3 + AND a.nwxus = b.nwxus + AND a.rn = b.rn +; +--------------------------- diff --git a/src/test/resources/bvt/parser/odps-3.sql b/src/test/resources/bvt/parser/odps-3.sql new file mode 100644 index 0000000000..ebfcf7c029 --- /dev/null +++ b/src/test/resources/bvt/parser/odps-3.sql @@ -0,0 +1,61 @@ +select a.user_id + , a.user_nick + , a.xrwq + , a.exesag + , a.brand + , a.cpu + , a.xagawef + , a.qwgqwer + , b.os + , b.os_version + , b.resolution + , a.count + , a.day_count + , a.last_time + , a.first_time + , ip +from ( + select user_id + , max(user_nick) user_nick + , xrwq + , exesag + , brand + , cpu + , xagawef + , qwgqwer + , sum(count) count + , count(distinct pt) day_count + , max(coalesce(last_time, to_date(pt, 'yyyymmdd'))) last_time + , min(coalesce(first_time, to_date(pt, 'yyyymmdd'))) first_time + from x_dsls_yw3l + where xrwq <> 'xxx-xxxx--xxx' + group by user_id, xrwq, exesag, brand, cpu, xagawef, qwgqwer +) a inner join ( + select * + from ( + select user_id + , user_nick + , xrwq + , exesag + , brand + , cpu + , xagawef + , qwgqwer + , os + , os_version + , resolution + , ip + , row_number() over(partition by user_id, user_nick, xrwq, exesag, brand, cpu, xagawef, qwgqwer order by last_time desc) rn + from x_dsls_yw3l + where xrwq <> 'c1976429369bfe063ed8b3409db7c7e7d87196d9' + ) r1 + where rn = 1 +) b on a.user_id = b.user_id + AND a.xrwq = b.xrwq + AND a.exesag = b.exesag + AND a.brand = b.brand + AND a.cpu = b.cpu + AND a.xagawef = b.xagawef + AND a.qwgqwer = b.qwgqwer +; +--------------------------- diff --git a/src/test/resources/bvt/parser/z321.sql b/src/test/resources/bvt/parser/z321.sql new file mode 100644 index 0000000000..30ba9b52d9 --- /dev/null +++ b/src/test/resources/bvt/parser/z321.sql @@ -0,0 +1,106 @@ +INSERT OVERWRITE TABLE wdm_device_gps_range_day PARTITION (pt) +SELECT a.imei, + a.imsi, + a.brand, + a.cpu, + a.device_id, + a.device_model, + a.longitude AS longitude_first, + a.latitude AS latitude_first, + b.longitude AS longitude_last, + b.latitude AS latitude_last, + first_time, + last_time, + rn_lead - rn_lag + 1 AS count, + province(b.longitude, b.latitude) AS province, + province_code(b.longitude, b.latitude) AS province_code, + city(b.longitude, b.latitude) AS city, + city_code(b.longitude, b.latitude) AS city_code, + a.pt +FROM ( + SELECT ROW_NUMBER() OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY rn_lead) AS rn, + imei, + imsi, + brand, + cpu, + device_id, + device_model, + longitude, + latitude, + server_time AS first_time, + pt, + rn_lead + FROM ( + SELECT ROW_NUMBER() OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS rn_lead, + imei, + imsi, + brand, + cpu, + device_id, + device_model, + longitude, + LEAD(longitude, 1, 0) OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS longitude_lead, + latitude, + LEAD(latitude, 1, 0) OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS latitude_lead, + server_time, + pt + FROM wdm_args + WHERE length(imei) >= 15 + AND longitude IS NOT NULL + AND latitude IS NOT NULL + AND abs(longitude) <= 180 + AND abs(latitude) <= 90 + AND longitude <> 0 + AND latitude <> 0 + AND pt >= ${bizdate} + ) x1 + WHERE round(longitude, 2) <> round(longitude_lead, 2) + OR round(latitude, 2) <> round(latitude_lead, 2) +) a +INNER JOIN ( + SELECT ROW_NUMBER() OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY rn_lag) AS rn, + imei, + imsi, + brand, + cpu, + device_id, + device_model, + longitude, + latitude, + server_time AS last_time, + pt, + rn_lag + FROM ( + SELECT ROW_NUMBER() OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS rn_lag, + imei, + imsi, + brand, + cpu, + device_id, + device_model, + longitude, + LAG(longitude, 1, 0) OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS longitude_lag, + latitude, + LAG(latitude, 1, 0) OVER (PARTITION BY pt, imei, imsi, brand, cpu, device_id, device_model ORDER BY server_time, longitude, latitude) AS latitude_lag, + server_time, + pt + FROM wdm_args + WHERE length(imei) >= 15 + AND longitude IS NOT NULL + AND latitude IS NOT NULL + AND abs(longitude) <= 180 + AND abs(latitude) <= 90 + AND longitude <> 0 + AND latitude <> 0 + AND pt >= ${bizdate} + ) x2 + WHERE round(longitude, 2) <> round(longitude_lag, 2) + OR round(latitude, 2) <> round(latitude_lag, 2) +) b ON a.pt = b.pt + AND a.imei = b.imei + AND a.imsi = b.imsi + AND a.brand = b.brand + AND a.cpu = b.cpu + AND a.device_id = b.device_id + AND a.device_model = b.device_model + AND a.rn = b.rn