From 53cff80649296475ab526f7cf7eae22ba31178d4 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Mon, 28 Mar 2016 19:25:27 +0800 Subject: [PATCH 01/10] Add Lexer and Tokens for Teradata --- .../teradata/parser/TeradataLexer.java | 120 +++++++++++++++++- .../com/alibaba/druid/sql/parser/Token.java | 10 ++ 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java index ebd7d2e530..42bbd60dc1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataLexer.java @@ -15,26 +15,140 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import static com.alibaba.druid.sql.parser.LayoutCharacters.EOI; + import java.util.HashMap; import java.util.Map; import com.alibaba.druid.sql.parser.Keywords; import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.NotAllowCommentException; import com.alibaba.druid.sql.parser.Token; public class TeradataLexer extends Lexer { - public final static Keywords DEFAULT_PG_KEYWORDS; + public final static Keywords DEFAULT_TD_KEYWORDS; static { Map map = new HashMap(); map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords()); - DEFAULT_PG_KEYWORDS = new Keywords(map); + map.put("SEL", Token.SEL); + map.put("LOCKING", Token.LOCKING); + map.put("ACCESS", Token.ACCESS); + +// map.put("PARTITION", Token.PARTITION); +// map.put("PARTITIONED", Token.PARTITIONED); + map.put("RANGE_N", Token.RANGE_N); + + map.put("VOLATILE", Token.VOLATILE); + map.put("MULTISET", Token.MULTISET); + + map.put("FORMAT", Token.FORMAT); + + DEFAULT_TD_KEYWORDS = new Keywords(map); } public TeradataLexer(String input){ super(input); - super.keywods = DEFAULT_PG_KEYWORDS; + super.keywods = DEFAULT_TD_KEYWORDS; + } + + public void scanComment() { + if (ch != '/' && ch != '-') { + throw new IllegalStateException(); + } + + mark = pos; + bufPos = 0; + scanChar(); + + // /*+ */ + if (ch == '*') { + scanChar(); + bufPos++; + + while (ch == ' ') { + scanChar(); + bufPos++; + } + + boolean isHint = false; + int startHintSp = bufPos + 1; + if (ch == '+') { + isHint = true; + scanChar(); + bufPos++; + } + + for (;;) { + if (ch == '*' && charAt(pos + 1) == '/') { + bufPos += 2; + scanChar(); + scanChar(); + break; + } + + scanChar(); + bufPos++; + } + + if (isHint) { + stringVal = subString(mark + startHintSp, (bufPos - startHintSp) - 1); + token = Token.HINT; + } else { + stringVal = subString(mark, bufPos); + token = Token.MULTI_LINE_COMMENT; + if (keepComments) { + addComment(stringVal); + } + } + + if (token != Token.HINT && !isAllowComment()) { + throw new NotAllowCommentException(); + } + + return; + } + + if (!isAllowComment()) { + throw new NotAllowCommentException(); + } + + if (ch == '/' || ch == '-') { + scanChar(); + bufPos++; + + for (;;) { + if (ch == '\r') { + if (charAt(pos + 1) == '\n') { + bufPos += 2; + scanChar(); + break; + } + bufPos++; + break; + } else if (ch == EOI) { + break; + } + + if (ch == '\n') { + scanChar(); + bufPos++; + break; + } + + scanChar(); + bufPos++; + } + + stringVal = subString(mark + 1, bufPos); + token = Token.LINE_COMMENT; + if (keepComments) { + addComment(stringVal); + } + endOfComment = isEOF(); + return; + } } } 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 a623d8a7ca..7f5dd6f188 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Token.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Token.java @@ -261,6 +261,16 @@ public enum Token { PARTITION, PARTITIONED, OVERWRITE, + + // Teradata + SEL("SEL"), + LOCKING("LOCKING"), + ACCESS("ACCESS"), + VOLATILE("VOLATILE"), + MULTISET("MULTISET"), + POSITION("POSITION"), + RANGE_N("RANGE_N"), + FORMAT("FORMAT"), LPAREN("("), RPAREN(")"), From 7fdfd4505a6ae583fa635f317577764f94ebab62 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Mon, 28 Mar 2016 19:26:40 +0800 Subject: [PATCH 02/10] add testcase for TD Lexer --- .../teradata/parser/TeradataLexerTest.java | 70 +++++++++++++++++++ .../teradata/parser/TeradataLexerTest1.java | 50 +++++++++++++ .../teradata/parser/TeradataLexerTest2.java | 54 ++++++++++++++ .../teradata/parser/TeradataLexerTest3.java | 45 ++++++++++++ 4 files changed, 219 insertions(+) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest1.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest2.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest3.java diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest.java new file mode 100644 index 0000000000..aa27d537bf --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest.java @@ -0,0 +1,70 @@ +package com.alibaba.druid.bvt.sql.teradata.parser; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataLexer; +import com.alibaba.druid.sql.parser.Token; + +public class TeradataLexerTest extends TestCase{ + + public void test_0() throws Exception { + String sql = "SELECT a.user_id , " + + "SUM(CASE WHEN comment_score=2 THEN 1 ELSE 0 END) AS posfb ," + + "SUM(CASE WHEN comment_score=-1 THEN 1 ELSE 0 END) AS negfb ," + + "SUM(CASE WHEN comment_score=0 THEN 1 ELSE 0 END) AS neutfb " + + "FROM dw_feedback_detail a " + + "WHERE a.creation_dt >= '2012-11-13' " + + "AND a.ID_TYPE = 'S' " + + "AND a.TRX_TYPE <> 1 " + + "AND a.user_id IN ( SELECT DISTINCT aa.user_id " + + "FROM DW_TNS_RCNT_SLR_ACTVTY_SL aa " + + "INNER JOIN DW_CATEGORY_GROUPINGS b " + + "ON aa.site_id=b.site_id AND aa.LEAF_CATEG_ID=b.LEAF_CATEG_ID " + + "WHERE aa.leaf_categ_id NOT IN (100843,100844,100845,100846,100847,100848,100849,100850) " + + "AND b.META_CATEG_ID NOT IN (2038,6000,9800,10542,60089) " + + "GROUP BY aa.user_id " + + "HAVING SUM(LIVE_GMV_USD) + SUM(END_GMV_USD) > 10000 " + + "AND SUM(END_GMV_USD) > 50 " + + "AND SUM(live_lstg_count) + SUM(SUCC_LSTG_COUNT) > 5 " + + "AND SUM(live_lstg_count) + SUM(SUCC_LSTG_COUNT) <> 99999 " + + "AND " + + "CASE WHEN (SUM(LIVE_GMV_USD)/(SUM(LIVE_LSTG_QTY) + 0.0001)>=SUM(END_GMV_USD)/(SUM(END_QTY_SOLD)+0.0001)) " + + "THEN (SUM(LIVE_GMV_USD)/(SUM(LIVE_LSTG_QTY) + 0.0001)) " + + "ELSE (SUM(END_GMV_USD)/(SUM(END_QTY_SOLD)+0.0001)) END > 50 " + + "AND " + + "CASE WHEN (SUM(LIVE_GMV_USD)/(SUM(LIVE_LSTG_QTY) + 0.0001)>=SUM(END_GMV_USD)/(SUM(END_QTY_SOLD)+0.0001)) " + + "THEN (SUM(LIVE_GMV_USD)/(SUM(LIVE_LSTG_QTY) + 0.0001)) " + + "ELSE (SUM(END_GMV_USD)/(SUM(END_QTY_SOLD)+0.0001)) END < 100000 ) " + + "GROUP BY a.user_id ORDER BY a.user_id;"; + TeradataLexer lexer = new TeradataLexer(sql); + + for(;;) { + lexer.nextToken(); + Token tok = lexer.token(); + switch (tok) { + case IDENTIFIER: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case HINT: + System.out.println(tok.name() + "\t\t\t\t" + lexer.stringVal()); + break; + case LITERAL_INT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + case LITERAL_CHARS: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case LITERAL_FLOAT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + default: + System.out.println(tok.name() + "\t\t\t" + tok.name); + break; + } + + if (tok == Token.EOF) { + break; + } + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest1.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest1.java new file mode 100644 index 0000000000..48eb79580e --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest1.java @@ -0,0 +1,50 @@ +package com.alibaba.druid.bvt.sql.teradata.parser; + +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataLexer; +import com.alibaba.druid.sql.parser.Token; + +import junit.framework.TestCase; + +public class TeradataLexerTest1 extends TestCase{ + + public void test_0() throws Exception { + String sql = "LOCKING TABLE TEST1 FOR ACCESS;" + + "CREATE MULTISET TABLE TEST(" + + "ITEM_ID DECIMAL(18,0) NOT NULL," + + "AUCT_START_DT DATE NOT NULL," + + "AUCT_END_DT DATE NOT NULL," + + "PRIMARY INDEX(ITEM_ID)" + + "PARTITION BY RANGE_N(AUCT_END_DT BETWEEN " + + "DATE '2010-01-01' AND DATE '2012-12-31'));"; + TeradataLexer lexer = new TeradataLexer(sql); + + for(;;) { + lexer.nextToken(); + Token tok = lexer.token(); + switch (tok) { + case IDENTIFIER: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case HINT: + System.out.println(tok.name() + "\t\t\t\t" + lexer.stringVal()); + break; + case LITERAL_INT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + case LITERAL_CHARS: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case LITERAL_FLOAT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + default: + System.out.println(tok.name() + "\t\t\t" + tok.name); + break; + } + + if (tok == Token.EOF) { + break; + } + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest2.java new file mode 100644 index 0000000000..f7232268be --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest2.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.bvt.sql.teradata.parser; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataLexer; +import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.util.Utils; + +public class TeradataLexerTest2 extends TestCase{ + + public void test_0() throws Exception { + InputStream is = null; + // mainly test SEL keyword + is = Thread.currentThread().getContextClassLoader().getResourceAsStream("bvt/parser/teradata-1.txt"); + Reader reader = new InputStreamReader(is, "UTF-8"); + String input = Utils.read(reader); + String sql = input.trim(); + + TeradataLexer lexer = new TeradataLexer(sql); + + for(;;) { + lexer.nextToken(); + Token tok = lexer.token(); + switch (tok) { + case IDENTIFIER: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case HINT: + System.out.println(tok.name() + "\t\t\t\t" + lexer.stringVal()); + break; + case LITERAL_INT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + case LITERAL_CHARS: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case LITERAL_FLOAT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + default: + System.out.println(tok.name() + "\t\t\t" + tok.name); + break; + } + + if (tok == Token.EOF) { + break; + } + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest3.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest3.java new file mode 100644 index 0000000000..4b964c0857 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/parser/TeradataLexerTest3.java @@ -0,0 +1,45 @@ +package com.alibaba.druid.bvt.sql.teradata.parser; + +import junit.framework.TestCase; + +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataLexer; +import com.alibaba.druid.sql.parser.Token; + +public class TeradataLexerTest3 extends TestCase{ + // test COMMENT + public void test_0() throws Exception { + String sql = "/*SUM(CASE WHEN comment_score=2 THEN 1 ELSE 0 END) AS posfb ," + + "SUM(CASE WHEN comment_score=-1 THEN 1 ELSE 0 END) AS negfb ,*/"; + TeradataLexer lexer = new TeradataLexer(sql); + + lexer.scanComment(); + for(;;) { + lexer.nextToken(); + Token tok = lexer.token(); + switch (tok) { + case IDENTIFIER: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case HINT: + System.out.println(tok.name() + "\t\t\t\t" + lexer.stringVal()); + break; + case LITERAL_INT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + case LITERAL_CHARS: + System.out.println(tok.name() + "\t\t" + lexer.stringVal()); + break; + case LITERAL_FLOAT: + System.out.println(tok.name() + "\t\t" + lexer.numberString()); + break; + default: + System.out.println(tok.name() + "\t\t\t" + tok.name); + break; + } + + if (tok == Token.EOF) { + break; + } + } + } +} From 81ebc6c49c3978e89d5417103ee0c4770d2a2bd4 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Mon, 28 Mar 2016 19:30:25 +0800 Subject: [PATCH 03/10] add complex query for TD test --- src/test/resources/bvt/parser/teradata-1.txt | 36 ++++++++++++++++++++ src/test/resources/bvt/parser/teradata-2.txt | 10 ++++++ 2 files changed, 46 insertions(+) create mode 100644 src/test/resources/bvt/parser/teradata-1.txt create mode 100644 src/test/resources/bvt/parser/teradata-2.txt diff --git a/src/test/resources/bvt/parser/teradata-1.txt b/src/test/resources/bvt/parser/teradata-1.txt new file mode 100644 index 0000000000..60f5eefa87 --- /dev/null +++ b/src/test/resources/bvt/parser/teradata-1.txt @@ -0,0 +1,36 @@ +sel + w.cal_dt, + w.nt_login, + case when ldap.uid is not null then 2 + else 1 end as user_batch_ind, + case when user_batch_ind=1 then 8 + else coalesce(p.user_persona_id,1) end as user_persona_id, + asset.fe_asset_id, + asset.fe_asset_name, + asset.fe_asset_alias_name, + 1 as fe_asset_type_cd, + asset.prdctn_ind, + 6 as pltfrm_id, --tableau + w.prjct_id, + asset.leaf_team_id, + asset.cert_ind, + asset.cert_dt, + asset.prdct_id, + asset.src_cre_nt_login, + w.access_cnt +from ddm_data_assets_w.ds_tableau_rpt_usage_dtl_w w +join (select a.*,row_number() over (partition by lctn_txt order by src_cre_dt desc) as rn from ddm_data_assets_t.ds_fe_asset a) asset +on w.lctn_txt=asset.lctn_txt +and asset.pltfrm_id=6 --tableau +and asset.rn=1 +left join + ( select uid from corp_hr_v.ldap_emply + group by 1) ldap +on w.nt_login=ldap.uid +left join ddm_ram_t.ds_user_persona_info_hist p +on w.nt_login=p.nt_login +and w.cal_dt>=p.start_dt +and w.cal_dt=cast('20151023' as date format 'yyyymmdd')-5 +and w.cal_dt Date: Mon, 28 Mar 2016 19:34:01 +0800 Subject: [PATCH 04/10] Add basic implementation for Teradata Select --- .../teradata/ast/expr/TeradataAnalytic.java | 36 +++ .../ast/expr/TeradataAnalyticWindowing.java | 49 ++++ .../teradata/ast/expr/TeradataExpr.java | 8 + .../ast/stmt/TeradataSelectQueryBlock.java | 44 ++++ .../teradata/parser/TeradataExprParser.java | 222 ++++++++++++++++++ .../parser/TeradataStatementParser.java | 60 +++++ 6 files changed, 419 insertions(+) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java new file mode 100644 index 0000000000..44433ec16d --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalytic.java @@ -0,0 +1,36 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLOver; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataAnalytic extends SQLOver implements TeradataExpr { + + private TeradataAnalyticWindowing windowing; + + public TeradataAnalytic(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((TeradataASTVisitor) visitor); + } + + public void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.partitionBy); + acceptChild(visitor, this.orderBy); + acceptChild(visitor, this.windowing); + } + visitor.endVisit(this); + } + + public TeradataAnalyticWindowing getWindowing() { + return this.windowing; + } + + public void setWindowing(TeradataAnalyticWindowing windowing) { + this.windowing = windowing; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java new file mode 100644 index 0000000000..76c6658bef --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataAnalyticWindowing.java @@ -0,0 +1,49 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLObjectImpl; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataAnalyticWindowing extends SQLObjectImpl implements TeradataExpr { + + private Type type; + private SQLExpr expr; + + public TeradataAnalyticWindowing(){ + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((TeradataASTVisitor) visitor); + } + + public void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.expr); + } + visitor.endVisit(this); + } + + public SQLExpr getExpr() { + return this.expr; + } + + public void setExpr(SQLExpr expr) { + this.expr = expr; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public static enum Type { + ROWS, RANGE; + } +} + diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java new file mode 100644 index 0000000000..60fe89ea34 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataExpr.java @@ -0,0 +1,8 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.teradata.ast.TeradataObject; + +public interface TeradataExpr extends SQLExpr, TeradataObject { + +} \ No newline at end of file diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java new file mode 100644 index 0000000000..706db3dc67 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java @@ -0,0 +1,44 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.stmt; + +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { + private SQLOrderBy orderBy; + + public TeradataSelectQueryBlock() { + + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof TeradataASTVisitor) { + accept0((TeradataASTVisitor) visitor); + return; + } + + super.accept0(visitor); + } + + protected void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.selectList); + acceptChild(visitor, this.into); + acceptChild(visitor, this.from); + acceptChild(visitor, this.where); + acceptChild(visitor, this.groupBy); + acceptChild(visitor, this.orderBy); + } + visitor.endVisit(this); + } + + public SQLOrderBy getOrderBy() { + return orderBy; + } + + public void setOrderBy(SQLOrderBy orderBy) { + this.orderBy = orderBy; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java index 94eaeeb6fa..4e938a1ac6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java @@ -15,8 +15,24 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLKeep; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.SQLKeep.DenseRank; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; +import com.alibaba.druid.sql.ast.expr.SQLCastExpr; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.Token; import com.alibaba.druid.util.JdbcConstants; public class TeradataExprParser extends SQLExprParser { @@ -35,5 +51,211 @@ public class TeradataExprParser extends SQLExprParser { this.dbType = JdbcConstants.TERADATA; } + public SQLExpr primary() { + SQLExpr sqlExpr = null; + + final Token tok = lexer.token(); + + switch (tok){ + case SEL: + SQLQueryExpr queryExpr = new SQLQueryExpr(createSelectParser().select()); + sqlExpr = queryExpr; + return primaryRest(sqlExpr); + case CAST: + System.out.println("cast test!"); + lexer.nextToken(); + accept(Token.LPAREN); + SQLCastExpr cast = new SQLCastExpr(); + cast.setExpr(expr()); + accept(Token.AS); + cast.setDataType(parseDataType()); + // TODO: deal with FORMAT + // currently ignore FORMAT keywords + if(lexer.token() == Token.FORMAT) { + lexer.nextToken(); + lexer.nextToken(); + } + accept(Token.RPAREN); + return primaryRest(cast); + default: + return super.primary(); + } + } + + protected SQLAggregateExpr parseAggregateExpr(String methodName) { + methodName = methodName.toUpperCase(); + + SQLAggregateExpr aggregateExpr; + if (lexer.token() == Token.UNIQUE) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.UNIQUE); + lexer.nextToken(); + } else if (lexer.token() == (Token.ALL)) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.ALL); + lexer.nextToken(); + } else if (lexer.token() == (Token.DISTINCT)) { + aggregateExpr = new SQLAggregateExpr(methodName, SQLAggregateOption.DISTINCT); + lexer.nextToken(); + } else { + aggregateExpr = new SQLAggregateExpr(methodName); + } + exprList(aggregateExpr.getArguments(), aggregateExpr); + accept(Token.RPAREN); + + if (lexer.token() == Token.KEEP) { + lexer.nextToken(); + + SQLKeep keep = new SQLKeep(); + accept(Token.LPAREN); + acceptIdentifier("DENSE_RANK"); + if (identifierEquals("FIRST")) { + lexer.nextToken(); + keep.setDenseRank(DenseRank.FIRST); + } else { + acceptIdentifier("LAST"); + keep.setDenseRank(DenseRank.LAST); + } + + SQLOrderBy orderBy = this.parseOrderBy(); + keep.setOrderBy(orderBy); + + aggregateExpr.setKeep(keep); + + accept(Token.RPAREN); + } + + if (lexer.token() == Token.OVER) { + TeradataAnalytic over = new TeradataAnalytic(); + + lexer.nextToken(); + accept(Token.LPAREN); + + if (identifierEquals("PARTITION")) { + lexer.nextToken(); + accept(Token.BY); + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + exprList(over.getPartitionBy(), over); + accept(Token.RPAREN); + } else { + exprList(over.getPartitionBy(), over); + } + } + + over.setOrderBy(parseOrderBy()); + if (over.getOrderBy() != null) { + TeradataAnalyticWindowing windowing = null; + if (lexer.stringVal().equalsIgnoreCase("ROWS")) { + lexer.nextToken(); + windowing = new TeradataAnalyticWindowing(); + windowing.setType(TeradataAnalyticWindowing.Type.ROWS); + } else if (lexer.stringVal().equalsIgnoreCase("RANGE")) { + lexer.nextToken(); + windowing = new TeradataAnalyticWindowing(); + windowing.setType(TeradataAnalyticWindowing.Type.RANGE); + } + + if (windowing != null) { + if (lexer.stringVal().equalsIgnoreCase("BETWEEN")) { + String between = null; + String and = null; + lexer.nextToken(); + if(lexer.token() == Token.LITERAL_INT) { + int val_1 = lexer.integerValue().intValue(); + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + between = "BETWEEN " + String.valueOf(val_1) + " PRECEDING"; + } else if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + between = "BETWEEN " + String.valueOf(val_1) + " FOLLOWING"; + } else { + throw new ParserException("syntax error"); + } + } else if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + between = "BETWEEN CURRENT ROW"; + } + } else if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + between = "BETWEEN UNBOUNDED PRECEDING"; + } + } else { + throw new ParserException("syntax error"); + } + if (lexer.stringVal().equalsIgnoreCase("AND")) { + lexer.nextToken(); + if(lexer.token() == Token.LITERAL_INT) { + System.out.println("yes, inside and"); + int val_2 = lexer.integerValue().intValue(); + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + and = " AND " + String.valueOf(val_2) + " PRECEDING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } else if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + and = " AND " + String.valueOf(val_2) + " FOLLOWING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } else { + throw new ParserException("syntax error"); + } + } else if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + and = " AND CURRENT ROW"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } + } else if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("FOLLOWING")) { + lexer.nextToken(); + and = " AND UNBOUNDED FOLLOWING"; + windowing.setExpr(new SQLIdentifierExpr(between + and)); + } + } else { + throw new ParserException("syntax error"); + } + } else { + throw new ParserException("syntax error"); + } + } else { + if (lexer.stringVal().equalsIgnoreCase("CURRENT")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("ROW")) { + lexer.nextToken(); + windowing.setExpr(new SQLIdentifierExpr("CURRENT ROW")); + over.setWindowing(windowing); + } else { + throw new ParserException("syntax error"); + } + } + if (lexer.stringVal().equalsIgnoreCase("UNBOUNDED")) { + lexer.nextToken(); + if (lexer.stringVal().equalsIgnoreCase("PRECEDING")) { + lexer.nextToken(); + System.out.println("yes, inside!!"); + windowing.setExpr(new SQLIdentifierExpr("UNBOUNDED PRECEDING")); + } else { + throw new ParserException("syntax error"); + } + } + } + over.setWindowing(windowing); + } + } + + accept(Token.RPAREN); + + aggregateExpr.setOver(over); + } + return aggregateExpr; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java index 2fe2c5917e..1506862e0f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java @@ -15,8 +15,15 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.util.JdbcConstants; public class TeradataStatementParser extends SQLStatementParser { @@ -27,5 +34,58 @@ public class TeradataStatementParser extends SQLStatementParser { public TeradataStatementParser(Lexer lexer){ super(new TeradataExprParser(lexer)); } + + public TeradataExprParser getExprParser() { + return (TeradataExprParser) exprParser; + } + + public TeradataSelectParser createSQLSelectParser() { + return new TeradataSelectParser(this.exprParser); + } + + public SQLSelectStatement parseSelect() { + TeradataSelectParser selectParser = new TeradataSelectParser(this.exprParser); + return new SQLSelectStatement(selectParser.select(), JdbcConstants.TERADATA); + } + + public void parseStatementList(List statementList, int max) { + for (;;) { + if (max != -1) { + if (statementList.size() >= max) { + return; + } + } + + if (lexer.token() == Token.EOF) { + return; + } + if (lexer.token() == Token.END) { + return; + } + if (lexer.token() == Token.ELSE) { + return; + } + + if (lexer.token() == (Token.SEMI)) { + lexer.nextToken(); + continue; + } + + if (lexer.token() == (Token.SELECT)) { + statementList.add(parseSelect()); + continue; + } else { + super.parseStatementList(statementList, max); + } + } + } + + public boolean parseStatementListDialect(List statementList) { + if (lexer.token() == Token.SEL) { + statementList.add(parseSelect()); + return true; + } + return false; + } } From 611f4f8163b27893265e93acf9c501a3c407ccf2 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Mon, 28 Mar 2016 19:36:14 +0800 Subject: [PATCH 05/10] Add TD select parser --- .../teradata/parser/TeradataSelectParser.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java new file mode 100644 index 0000000000..bdd0fbd204 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataSelectParser.java @@ -0,0 +1,62 @@ +package com.alibaba.druid.sql.dialect.teradata.parser; + +import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; +import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.SQLSelectParser; +import com.alibaba.druid.sql.parser.Token; + +public class TeradataSelectParser extends SQLSelectParser{ + + public TeradataSelectParser(SQLExprParser exprParser) { + super(exprParser); + } + + public TeradataSelectParser(String sql) { + this(new TeradataExprParser(sql)); + } + + public SQLSelectQuery query() { + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + SQLSelectQuery select = query(); + accept(Token.RPAREN); + + return queryRest(select); + } + + TeradataSelectQueryBlock queryBlock = new TeradataSelectQueryBlock(); + + if (lexer.token() == Token.SEL + || lexer.token() == Token.SELECT) { + + lexer.nextToken(); + + if (lexer.token() == Token.COMMENT) { + lexer.nextToken(); + } + + if (lexer.token() == (Token.DISTINCT)) { + queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); + lexer.nextToken(); + } else if (lexer.token() == (Token.ALL)) { + queryBlock.setDistionOption(SQLSetQuantifier.ALL); + lexer.nextToken(); + } + parseSelectList(queryBlock); + } + parseFrom(queryBlock); + + parseWhere(queryBlock); + + parseGroupBy(queryBlock); + + queryBlock.setOrderBy(this.exprParser.parseOrderBy()); + + return queryRest(queryBlock); + } + +} From 30de1616306aea860d096123bb9c86a7ae14f757 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Mon, 28 Mar 2016 19:37:06 +0800 Subject: [PATCH 06/10] Add basic implementation for TD Visitor --- .../teradata/visitor/TeradataASTVisitor.java | 13 ++ .../visitor/TeradataASTVisitorAdapter.java | 20 +++ .../visitor/TeradataOutputVisitor.java | 135 ++++++++++++++++++ .../visitor/TeradataSchemaStatVisitor.java | 22 ++- 4 files changed, 189 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java index c4a3c8e48c..46e9b2fbdb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java @@ -15,8 +15,21 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface TeradataASTVisitor extends SQLASTVisitor { + boolean visit(TeradataAnalyticWindowing x); + + void endVisit(TeradataAnalyticWindowing x); + + boolean visit(TeradataAnalytic x); + + boolean visit(SQLSelectQueryBlock x); + + boolean visit(SQLSelect x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java index 3dcdb0e84b..d7380d2bea 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java @@ -15,8 +15,28 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class TeradataASTVisitorAdapter extends SQLASTVisitorAdapter implements TeradataASTVisitor { + @Override + public boolean visit(TeradataAnalyticWindowing x) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void endVisit(TeradataAnalyticWindowing x) { + // TODO Auto-generated method stub + + } + + @Override + public boolean visit(TeradataAnalytic x) { + // TODO Auto-generated method stub + return false; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java index bbedbacee2..6960a77d60 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java @@ -15,6 +15,15 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.SQLSetQuantifier; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; public class TeradataOutputVisitor extends SQLASTOutputVisitor implements TeradataASTVisitor { @@ -22,5 +31,131 @@ public class TeradataOutputVisitor extends SQLASTOutputVisitor implements Terada public TeradataOutputVisitor(Appendable appender){ super(appender); } + + public boolean visit(TeradataAnalytic x) { + print0(ucase ? "OVER (" : "over ("); + + boolean space = false; + if (x.getPartitionBy().size() > 0) { + print0(ucase ? "PARTITION BY " : "partition by "); + printAndAccept(x.getPartitionBy(), ", "); + space = true; + } + + if (x.getOrderBy() != null) { + if (space) { + print(' '); + } + x.getOrderBy().accept(this); + space = true; + } + + if (x.getWindowing() != null) { + if (space) { + print(' '); + } + x.getWindowing().accept(this); + } + + print(')'); + + return false; + } + + @Override + public boolean visit(TeradataAnalyticWindowing x) { + print0(x.getType().name().toUpperCase()); + print(' '); + x.getExpr().accept(this); + return false; + } + + @Override + public void endVisit(TeradataAnalyticWindowing x) { + // TODO Auto-generated method stub + + } + + public boolean visit(SQLSelectQueryBlock select) { + if(select instanceof TeradataSelectQueryBlock) { + return visit((TeradataSelectQueryBlock) select); + } + + return super.visit(select); + } + + public boolean visit(TeradataSelectQueryBlock x) { + if (x.getOrderBy() != null) { + x.getOrderBy().setParent(x); + } + + print0(ucase ? "SELECT " : "select "); + + if (SQLSetQuantifier.ALL == x.getDistionOption()) { + print0(ucase ? "ALL " : "all "); + } else if (SQLSetQuantifier.DISTINCT == x.getDistionOption()) { + print0(ucase ? "DISTINCT " : "distinct "); + } else if (SQLSetQuantifier.UNIQUE == x.getDistionOption()) { + print0(ucase ? "UNIQUE " : "unique "); + } + + printSelectList(x.getSelectList()); + + if (x.getInto() != null) { + println(); + print0(ucase ? "INTO " : "into "); + x.getInto().accept(this); + } + + println(); + print0(ucase ? "FROM " : "from "); + if (x.getFrom() == null) { + print0(ucase ? "DUAL" : "dual"); + } else { + x.getFrom().setParent(x); + x.getFrom().accept(this); + } + + if (x.getWhere() != null) { + println(); + print0(ucase ? "WHERE " : "where "); + x.getWhere().setParent(x); + x.getWhere().accept(this); + } + + if (x.getGroupBy() != null) { + println(); + x.getGroupBy().accept(this); + } + + if (x.getOrderBy() != null) { + println(); + x.getOrderBy().accept(this); + } + + return false; + + } + + public boolean visit(SQLSelect x) { + return super.visit(x); + } + + protected void visitAggreateRest(SQLAggregateExpr aggregateExpr) { + { + SQLOrderBy value = (SQLOrderBy) aggregateExpr.getAttribute("ORDER BY"); + if (value != null) { + print(' '); + ((SQLObject) value).accept(this); + } + } + { + Object value = aggregateExpr.getAttribute("SEPARATOR"); + if (value != null) { + print0(ucase ? " SEPARATOR " : " separator "); + ((SQLObject) value).accept(this); + } + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java index 43472f728c..141732f44f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java @@ -15,6 +15,8 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; import com.alibaba.druid.util.JdbcUtils; @@ -22,7 +24,25 @@ public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements Tera @Override public String getDbType() { - return JdbcUtils.POSTGRESQL; + return JdbcUtils.TERADATA; } + @Override + public boolean visit(TeradataAnalyticWindowing x) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void endVisit(TeradataAnalyticWindowing x) { + // TODO Auto-generated method stub + + } + + @Override + public boolean visit(TeradataAnalytic x) { + // TODO Auto-generated method stub + return false; + } + } From ce91facc6751eb1719b8199f89e81f5bad47c92e Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Tue, 29 Mar 2016 18:57:37 +0800 Subject: [PATCH 07/10] Add Teradata test case for Select --- .../sql/teradata/TeradataSelectTest_1.java | 294 ++++++++++++++++++ .../sql/teradata/TeradataSelectTest_2.java | 109 +++++++ .../com/alibaba/druid/sql/TeradataTest.java | 30 ++ 3 files changed, 433 insertions(+) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_1.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_2.java create mode 100644 src/test/java/com/alibaba/druid/sql/TeradataTest.java diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_1.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_1.java new file mode 100644 index 0000000000..7b252f227c --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_1.java @@ -0,0 +1,294 @@ +package com.alibaba.druid.bvt.sql.teradata; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.TeradataTest; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; +import com.alibaba.druid.sql.ast.expr.SQLNotExpr; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataStatementParser; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.stat.TableStat.Column; + +public class TeradataSelectTest_1 extends TeradataTest{ + // case #1: general SELECT...FROM...WHERE + public void test_select() throws Exception { + String sql = "SELECT t1.name, t2.salary FROM employee t1, info t2 WHERE t1.name = t2.name;"; + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(3, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employee"))); + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("info"))); + + Assert.assertTrue(visitor.getColumns().contains(new Column("employee", "name"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("info", "name"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("info", "salary"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + // case #2: test ORDER BY...ASC/DESC + public void test_orderBy() throws Exception { + String sql = "select * from tb order by id asc,name desc"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + + Assert.assertNotNull(queryBlock.getOrderBy()); + System.out.println("select order by: " + queryBlock.getOrderBy().getItems().get(0).getExpr()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(3, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + Assert.assertEquals(2, visitor.getOrderByColumns().size()); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + // case #3: test SELECT CONCAT... + public void test_concat() throws Exception { + String sql = "SELECT CONCAT(last_name,', ',first_name) AS full_name FROM mytable ORDER BY full_name;"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + + print(statementList); + Assert.assertEquals(1, statementList.size()); + + SQLSelectStatement selectStmt = (SQLSelectStatement) statemen; + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + + Assert.assertNotNull(queryBlock.getOrderBy()); + System.out.println("select order by: " + queryBlock.getOrderBy().getItems().get(0).getExpr()); + + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(2, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("mytable"))); + + Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "last_name"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("mytable", "first_name"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + // case #4: test constant + public void test_constant() throws Exception { + String sql = "select 1"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(0, visitor.getTables().size()); + Assert.assertEquals(0, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + // case #5: test sub-query + public void test_subQuery() throws Exception { + String sql = "SELECT COUNT(*) a FROM (select nickname,mobile,comment,createdate from ub_userdiscuss order by discuss_id desc) b "; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + // test for sub-query + { + Assert.assertTrue(queryBlock.getFrom() instanceof SQLSubqueryTableSource); + TeradataSelectQueryBlock subBlock = (TeradataSelectQueryBlock)((SQLSubqueryTableSource)queryBlock.getFrom()).getSelect().getQuery(); + Assert.assertNotNull(subBlock.getOrderBy()); + } + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(5, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + Assert.assertEquals(1, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("ub_userdiscuss"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + public void test_comment() throws Exception { + String sql = "select * from test /*!40101fff*/"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(1, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("test"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + public void test_not() throws Exception { + String sql = "select a from t where not a<1 and not b>1"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + { + SQLExpr where = queryBlock.getWhere(); + Assert.assertTrue(where instanceof SQLBinaryOpExpr); + SQLBinaryOpExpr binaryWhere = (SQLBinaryOpExpr) where; + Assert.assertEquals(binaryWhere.getOperator(), SQLBinaryOperator.BooleanAnd); + + Assert.assertTrue(binaryWhere.getLeft() instanceof SQLNotExpr); + Assert.assertTrue(binaryWhere.getRight() instanceof SQLNotExpr); + System.out.println("left expr: " + ((SQLNotExpr)binaryWhere.getLeft()).getExpr()); + } + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(2, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("t"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + public void test_interval() throws Exception { + String sql = "select bsvariety, max(bsh) as bsh, min(bsl) as bsl " + + " from exchange_market_info " + + " where bsdate>(current_date - interval '1' DAY)" + + " group by bsvariety;"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertTrue(visitor.getColumns().contains(new Column("exchange_market_info", "bsvariety"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("exchange_market_info", "bsh"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("exchange_market_info", "bsl"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("exchange_market_info", "bsdate"))); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(5, visitor.getColumns().size()); + Assert.assertEquals(1, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + + } + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_2.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_2.java new file mode 100644 index 0000000000..d7050e7c4d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_2.java @@ -0,0 +1,109 @@ +package com.alibaba.druid.bvt.sql.teradata; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.TeradataTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLJoinTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataStatementParser; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat.Column; + +public class TeradataSelectTest_2 extends TeradataTest{ + public void test_crossJoin() throws Exception { + String sql = "SELECT * FROM t1 CROSS JOIN t2 ON t1.id = t2.id;"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statement = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + { + SQLSelectStatement selectStmt = (SQLSelectStatement) statement; + SQLSelect selectSQL = selectStmt.getSelect(); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) selectSQL.getQuery(); + + Assert.assertTrue(queryBlock.getFrom() instanceof SQLJoinTableSource); + + SQLJoinTableSource joinSource = (SQLJoinTableSource) queryBlock.getFrom(); + + Assert.assertEquals(joinSource.getJoinType().toString(), "CROSS_JOIN"); + Assert.assertEquals(joinSource.getLeft().toString(), "t1"); + Assert.assertEquals(joinSource.getRight().toString(), "t2"); + Assert.assertNotNull(joinSource.getCondition()); + } + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + statement.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(2, visitor.getTables().size()); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + public void test_leftJoin() throws Exception { + String sql = "SELECT host.id as id" // + + ", host.item_id as itemId" // + + ", host.node_id as nodeId" // + + ", host.node_type as nodeType" // + + ", host.begin_time as beginTime" // + + ", host.end_time as endTime" // + + ", host.gmt_create as gmtCreate" // + + ", host.gmt_modify as gmtModify" // + + ", host.reason as reason" // + + ", host.creator_id as creatorId" // + + ", host.modifier_id as modifierId" // + + ", user.name as creator" // + + ", user.name as modifier" // + + ", user.nick_name as nickName " // + + " FROM notice_close_node host left join sys_user user on user.id = host.modifier_id"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement statemen = statementList.get(0); + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + { + SQLSelectStatement selectStmt = (SQLSelectStatement) statemen; + SQLSelect selectSQL = selectStmt.getSelect(); + TeradataSelectQueryBlock queryBlock = (TeradataSelectQueryBlock) selectSQL.getQuery(); + + Assert.assertTrue(queryBlock.getFrom() instanceof SQLJoinTableSource); + + SQLJoinTableSource joinSource = (SQLJoinTableSource) queryBlock.getFrom(); + + Assert.assertEquals(joinSource.getJoinType().toString(), "LEFT_OUTER_JOIN"); + Assert.assertEquals(joinSource.getLeft().toString(), "notice_close_node"); + Assert.assertEquals(joinSource.getRight().toString(), "sys_user"); + Assert.assertNotNull(joinSource.getCondition()); + } + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + statemen.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(2, visitor.getTables().size()); + Assert.assertEquals(15, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getColumns().contains(new Column("sys_user", "id"))); + Assert.assertTrue(visitor.getColumns().contains(new Column("notice_close_node", "modifier_id"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } +} diff --git a/src/test/java/com/alibaba/druid/sql/TeradataTest.java b/src/test/java/com/alibaba/druid/sql/TeradataTest.java new file mode 100644 index 0000000000..38d869e66f --- /dev/null +++ b/src/test/java/com/alibaba/druid/sql/TeradataTest.java @@ -0,0 +1,30 @@ +package com.alibaba.druid.sql; + +import java.util.List; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataOutputVisitor; + +import junit.framework.TestCase; + +public class TeradataTest extends TestCase{ + protected String output(List stmtList) { + StringBuilder out = new StringBuilder(); + TeradataOutputVisitor visitor = new TeradataOutputVisitor(out); + + for (SQLStatement stmt : stmtList) { + stmt.accept(visitor); + } + + return out.toString(); + } + + protected void print(List stmtList) { + String text = output(stmtList); + String outputProperty = System.getProperty("druid.output"); + if ("false".equals(outputProperty)) { + return; + } + System.out.println(text); + } +} From c21bcfc9e42f3ac78c4e571bb7c4178d4fe5413a Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Tue, 29 Mar 2016 19:01:52 +0800 Subject: [PATCH 08/10] Add support for Teradata interval --- .../ast/expr/TeradataIntervalExpr.java | 78 +++++++++++++++++++ .../ast/expr/TeradataIntervalUnit.java | 14 ++++ .../teradata/parser/TeradataExprParser.java | 30 ++++++- .../teradata/visitor/TeradataASTVisitor.java | 5 ++ .../visitor/TeradataASTVisitorAdapter.java | 13 ++++ .../teradata/visitor/TeradataEvalVisitor.java | 73 +++++++++++++++++ .../visitor/TeradataOutputVisitor.java | 15 ++++ .../visitor/TeradataSchemaStatVisitor.java | 12 ++- .../sql/visitor/SQLEvalVisitorUtils.java | 5 ++ 9 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalExpr.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalUnit.java create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataEvalVisitor.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalExpr.java new file mode 100644 index 0000000000..e1128cd704 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalExpr.java @@ -0,0 +1,78 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLExprImpl; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataIntervalExpr extends SQLExprImpl implements TeradataExpr { + + private SQLExpr value; + private TeradataIntervalUnit unit; + + public TeradataIntervalExpr() { + + } + + public SQLExpr getValue() { + return this.value; + } + + public void setValue(SQLExpr value) { + this.value = value; + } + + public TeradataIntervalUnit getUnit() { + return unit; + } + + public void setUnit(TeradataIntervalUnit unit) { + this.unit = unit; + } + + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TeradataIntervalExpr other = (TeradataIntervalExpr) obj; + if (unit != other.unit) { + return false; + } + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((unit == null)? 0 : unit.hashCode()); + result = prime * result + ((value == null)? 0 : value.hashCode()); + return result; + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + TeradataASTVisitor tdVisitor = (TeradataASTVisitor) visitor; + if (tdVisitor.visit(this)) { + acceptChild(visitor, this.value); + } + tdVisitor.endVisit(this); + } + + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalUnit.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalUnit.java new file mode 100644 index 0000000000..cec3f559a5 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/expr/TeradataIntervalUnit.java @@ -0,0 +1,14 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.expr; + +public enum TeradataIntervalUnit { + + YEAR, MONTH, DAY, + + HOUR, MINUTE, SECOND; + + public final String name_lcase; + + private TeradataIntervalUnit() { + this.name_lcase = name().toLowerCase(); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java index 4e938a1ac6..e111143145 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java @@ -15,13 +15,10 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; -import java.util.List; - import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLKeep; -import com.alibaba.druid.sql.ast.SQLOrderBy; -import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.SQLKeep.DenseRank; +import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; @@ -29,6 +26,8 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalExpr; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalUnit; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; @@ -258,4 +257,27 @@ public class TeradataExprParser extends SQLExprParser { return aggregateExpr; } + protected SQLExpr parseInterval() { + accept(Token.INTERVAL); + + if (lexer.token() == Token.LITERAL_CHARS) { + SQLExpr value = expr(); + + if (lexer.token() != Token.IDENTIFIER) { + throw new ParserException("Syntax error"); + } + + String unit = lexer.stringVal(); + lexer.nextToken(); + + TeradataIntervalExpr intervalExpr = new TeradataIntervalExpr(); + intervalExpr.setValue(value); + intervalExpr.setUnit(TeradataIntervalUnit.valueOf(unit.toUpperCase())); + + return intervalExpr; + } else { + throw new ParserException("TODO with other interval"); + } + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java index 46e9b2fbdb..34d49ffbed 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitor.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public interface TeradataASTVisitor extends SQLASTVisitor { @@ -32,4 +33,8 @@ public interface TeradataASTVisitor extends SQLASTVisitor { boolean visit(SQLSelectQueryBlock x); boolean visit(SQLSelect x); + + boolean visit(TeradataIntervalExpr x); + + void endVisit(TeradataIntervalExpr x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java index d7380d2bea..d286c6ddc4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataASTVisitorAdapter.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.teradata.visitor; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalExpr; import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter; public class TeradataASTVisitorAdapter extends SQLASTVisitorAdapter implements TeradataASTVisitor { @@ -39,4 +40,16 @@ public class TeradataASTVisitorAdapter extends SQLASTVisitorAdapter implements T return false; } + @Override + public boolean visit(TeradataIntervalExpr x) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void endVisit(TeradataIntervalExpr x) { + // TODO Auto-generated method stub + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataEvalVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataEvalVisitor.java new file mode 100644 index 0000000000..14e482d1d8 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataEvalVisitor.java @@ -0,0 +1,73 @@ +package com.alibaba.druid.sql.dialect.teradata.visitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; +import com.alibaba.druid.sql.visitor.SQLEvalVisitor; +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; +import com.alibaba.druid.sql.visitor.functions.Function; + +public class TeradataEvalVisitor extends TeradataASTVisitorAdapter implements SQLEvalVisitor { + private Map functions = new HashMap(); + private List parameters = new ArrayList(); + + private int variantIndex = -1; + + private boolean markVariantIndex = true; + + public TeradataEvalVisitor() { + this(new ArrayList(1)); + } + + public TeradataEvalVisitor(List parameters) { + this.parameters = parameters; + } + + @Override + public Function getFunction(String funcName) { + return functions.get(funcName); + } + + @Override + public void registerFunction(String funcName, Function function) { + functions.put(funcName, function); + } + + @Override + public void unregisterFunction(String funcName) { + functions.remove(funcName); + } + + @Override + public List getParameters() { + return parameters; + } + + @Override + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + public int incrementAndGetVariantIndex() { + return ++variantIndex; + } + + @Override + public boolean isMarkVariantIndex() { + return markVariantIndex; + } + + @Override + public void setMarkVariantIndex(boolean markVariantIndex) { + this.markVariantIndex = markVariantIndex; + } + + public boolean visit(SQLBinaryOpExpr x) { + return SQLEvalVisitorUtils.visit(this, x); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java index 6960a77d60..f0bc9ab3dc 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalExpr; import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; @@ -158,4 +159,18 @@ public class TeradataOutputVisitor extends SQLASTOutputVisitor implements Terada } } } + + @Override + public boolean visit(TeradataIntervalExpr x) { + print0(ucase ? "INTERVAL " : "interval "); + x.getValue().accept(this); + print(' '); + print0(ucase ? x.getUnit().name() : x.getUnit().name_lcase); + return false; + } + + @Override + public void endVisit(TeradataIntervalExpr x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java index 141732f44f..fffec46566 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataSchemaStatVisitor.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.dialect.teradata.visitor; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; +import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalExpr; import com.alibaba.druid.sql.visitor.SchemaStatVisitor; import com.alibaba.druid.util.JdbcUtils; @@ -29,7 +30,6 @@ public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements Tera @Override public boolean visit(TeradataAnalyticWindowing x) { - // TODO Auto-generated method stub return true; } @@ -45,4 +45,14 @@ public class TeradataSchemaStatVisitor extends SchemaStatVisitor implements Tera return false; } + @Override + public boolean visit(TeradataIntervalExpr x) { + return true; + } + + @Override + public void endVisit(TeradataIntervalExpr x) { + + } + } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index 835cf24593..03247f8ec0 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -44,6 +44,7 @@ import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlEvalVisitorImpl; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleEvalVisitor; import com.alibaba.druid.sql.dialect.postgresql.visitor.PGEvalVisitor; import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerEvalVisitor; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataEvalVisitor; import com.alibaba.druid.sql.visitor.functions.Ascii; import com.alibaba.druid.sql.visitor.functions.Bin; import com.alibaba.druid.sql.visitor.functions.BitLength; @@ -178,6 +179,10 @@ public class SQLEvalVisitorUtils { if (JdbcUtils.DB2.equals(dbType)) { return new DB2EvalVisitor(); } + + if (JdbcUtils.TERADATA.equals(dbType)) { + return new TeradataEvalVisitor(); + } return new SQLEvalVisitorImpl(); } From ddcc4f6187a10c4fb1527529cadc9c4bd6313989 Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Wed, 30 Mar 2016 14:55:11 +0800 Subject: [PATCH 09/10] Add support for Teradata trim() --- .../java/com/alibaba/druid/sql/SQLUtils.java | 8 ++++ .../ast/stmt/TeradataSelectQueryBlock.java | 5 +++ .../teradata/parser/TeradataExprParser.java | 39 ++++++++++++++++ .../visitor/TeradataOutputVisitor.java | 30 +++++++++++++ .../sql/teradata/TeradataSelectTest_3.java | 45 +++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_3.java diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index 54316c274c..53bad0bf76 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -172,6 +172,14 @@ public class SQLUtils { public static String toSQLServerString(SQLObject sqlObject, FormatOption option) { return toSQLString(sqlObject, JdbcConstants.SQL_SERVER, option); } + + public static String toTeradataString(SQLObject sqlObject) { + return toTeradataString(sqlObject, null); + } + + public static String toTeradataString(SQLObject sqlObject, FormatOption option) { + return toSQLString(sqlObject, JdbcConstants.TERADATA, option); + } public static String formatPGSql(String sql, FormatOption option) { return format(sql, JdbcConstants.POSTGRESQL, option); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java index 706db3dc67..cefebc2ea8 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataSelectQueryBlock.java @@ -1,5 +1,6 @@ package com.alibaba.druid.sql.dialect.teradata.ast.stmt; +import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; @@ -41,4 +42,8 @@ public class TeradataSelectQueryBlock extends SQLSelectQueryBlock { public void setOrderBy(SQLOrderBy orderBy) { this.orderBy = orderBy; } + + public String toString() { + return SQLUtils.toTeradataString(this); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java index e111143145..3976fba0ec 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLAggregateOption; import com.alibaba.druid.sql.ast.expr.SQLCastExpr; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalyticWindowing; @@ -81,6 +82,44 @@ public class TeradataExprParser extends SQLExprParser { } } + protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) { + if (acceptLPAREN) { + accept(Token.LPAREN); + } + + if (expr instanceof SQLIdentifierExpr) { + String methodName = ((SQLIdentifierExpr) expr).getName(); + SQLMethodInvokeExpr methodExpr = new SQLMethodInvokeExpr(methodName); + if ("trim".equalsIgnoreCase(methodName)) { + if (identifierEquals("LEADING") + || identifierEquals("TRAILING") + || identifierEquals("BOTH")) { + methodExpr.putAttribute("trim_option", lexer.stringVal()); + lexer.nextToken(); + if (lexer.token() == Token.LITERAL_CHARS) { + SQLExpr trim_character = this.primary(); + trim_character.setParent(methodExpr); + methodExpr.putAttribute("trim_character", trim_character); + } + } else { + SQLExpr trim_character = this.primary(); + trim_character.setParent(methodExpr); + methodExpr.putAttribute("trim_character", trim_character); + } + + if (lexer.token() == Token.FROM) { + lexer.nextToken(); + SQLExpr trim_source = this.expr(); + methodExpr.addParameter(trim_source); + } + + accept(Token.RPAREN); + return primaryRest(methodExpr); + } + } + return super.methodRest(expr, false); + } + protected SQLAggregateExpr parseAggregateExpr(String methodName) { methodName = methodName.toUpperCase(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java index f0bc9ab3dc..e09580fde5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/visitor/TeradataOutputVisitor.java @@ -15,10 +15,12 @@ */ package com.alibaba.druid.sql.dialect.teradata.visitor; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLObject; import com.alibaba.druid.sql.ast.SQLOrderBy; import com.alibaba.druid.sql.ast.SQLSetQuantifier; import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataAnalytic; @@ -173,4 +175,32 @@ public class TeradataOutputVisitor extends SQLASTOutputVisitor implements Terada public void endVisit(TeradataIntervalExpr x) { } + + public void endVisit(SQLMethodInvokeExpr x) { + + } + + public boolean visit(SQLMethodInvokeExpr x) { + if ("trim".equalsIgnoreCase(x.getMethodName())) { + SQLExpr trim_character = (SQLExpr) x.getAttribute("trim_character"); + + print0(x.getMethodName()); + print('('); + String trim_option = (String) x.getAttribute("trim_option"); + if (trim_option != null && trim_option.length() != 0) { + print0(trim_option); + print(' '); + } + if (trim_character != null) { + trim_character.accept(this); + } + if (x.getParameters().size() > 0) { + print0(ucase ? " FROM " : " from "); + x.getParameters().get(0).accept(this); + } + print(')'); + return false; + } + return super.visit(x); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_3.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_3.java new file mode 100644 index 0000000000..a962062a17 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataSelectTest_3.java @@ -0,0 +1,45 @@ +package com.alibaba.druid.bvt.sql.teradata; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.TeradataTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataStatementParser; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; +import com.alibaba.druid.stat.TableStat.Column; + +public class TeradataSelectTest_3 extends TeradataTest { + public void test_trim() throws Exception { + String sql = "select trim(trailing from columnname) from dbc.columnsv;"; + + TeradataStatementParser parser = new TeradataStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertTrue(stmt instanceof SQLSelectStatement); + print(statementList); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(1, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("dbc.columnsv"))); + + Assert.assertTrue(visitor.getColumns().contains(new Column("dbc.columnsv", "columnname"))); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + + } +} From 32139bd98a009385d464abc8dd6e2fdb9e4e21fb Mon Sep 17 00:00:00 2001 From: Louis Xu Date: Thu, 31 Mar 2016 17:38:55 +0800 Subject: [PATCH 10/10] Add implementation and testcase for Teradata Insert --- .../ast/stmt/TeradataInsertStatement.java | 56 ++++++ .../teradata/parser/TeradataExprParser.java | 5 + .../parser/TeradataStatementParser.java | 166 +++++++++++++++++- .../sql/teradata/TeradataInsertTest_1.java | 95 ++++++++++ 4 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataInsertStatement.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataInsertTest_1.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataInsertStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataInsertStatement.java new file mode 100644 index 0000000000..1b51738075 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/ast/stmt/TeradataInsertStatement.java @@ -0,0 +1,56 @@ +package com.alibaba.druid.sql.dialect.teradata.ast.stmt; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +public class TeradataInsertStatement extends SQLInsertStatement { + private List valuesList = new ArrayList(); + + public ValuesClause getValues() { + if (valuesList.size() == 0) { + return null; + } + return valuesList.get(0); + } + + public void setValues(ValuesClause values) { + if (valuesList.size() == 0) { + valuesList.add(values); + } else { + valuesList.set(0, values); + } + } + + public List getValuesList() { + return valuesList; + } + + public void addValueCause(ValuesClause valueClause) { + valueClause.setParent(this); + valuesList.add(valueClause); + } + + @Override + protected void accept0(SQLASTVisitor visitor) { + if (visitor instanceof TeradataASTVisitor) { + accept0((TeradataASTVisitor) visitor); + } else { + throw new IllegalArgumentException("not support visitor type : " + visitor.getClass().getName()); + } + } + + protected void accept0(TeradataASTVisitor visitor) { + if (visitor.visit(this)) { + this.acceptChild(visitor, getTableSource()); + this.acceptChild(visitor, getColumns()); + this.acceptChild(visitor, getValuesList()); + this.acceptChild(visitor, getQuery()); + } + + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java index 3976fba0ec..93ca424523 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataExprParser.java @@ -32,6 +32,7 @@ import com.alibaba.druid.sql.dialect.teradata.ast.expr.TeradataIntervalUnit; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLExprParser; +import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.Token; import com.alibaba.druid.util.JdbcConstants; @@ -82,6 +83,10 @@ public class TeradataExprParser extends SQLExprParser { } } + public SQLSelectParser createSelectParser() { + return new TeradataSelectParser(this); + } + protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) { if (acceptLPAREN) { accept(Token.LPAREN); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java index 1506862e0f..5ca482bcfd 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/teradata/parser/TeradataStatementParser.java @@ -15,12 +15,22 @@ */ package com.alibaba.druid.sql.dialect.teradata.parser; +import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLCharExpr; +import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; +import com.alibaba.druid.sql.ast.expr.SQLNCharExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; -import com.alibaba.druid.sql.dialect.oracle.parser.OracleSelectParser; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataInsertStatement; import com.alibaba.druid.sql.parser.Lexer; +import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLStatementParser; import com.alibaba.druid.sql.parser.Token; import com.alibaba.druid.util.JdbcConstants; @@ -48,6 +58,160 @@ public class TeradataStatementParser extends SQLStatementParser { return new SQLSelectStatement(selectParser.select(), JdbcConstants.TERADATA); } + public SQLInsertStatement parseInsert() { + TeradataInsertStatement insertStatement = new TeradataInsertStatement(); + + if (lexer.token() == Token.INSERT) { + lexer.nextToken(); + + if (lexer.token() == Token.INTO) { + lexer.nextToken(); + } + + SQLName tableName = this.exprParser.name(); + insertStatement.setTableName(tableName); + + if (lexer.token() == Token.IDENTIFIER && !identifierEquals("VALUE")) { + insertStatement.setAlias(lexer.stringVal()); + lexer.nextToken(); + } + } + + int columnSize = 0; + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + if (lexer.token() == (Token.SELECT)) { + SQLSelect select = this.exprParser.createSelectParser().select(); + select.setParent(insertStatement); + insertStatement.setQuery(select); + } else { + this.exprParser.exprList(insertStatement.getColumns(), insertStatement); + columnSize = insertStatement.getColumns().size(); + } + accept(Token.RPAREN); + } + + if (lexer.token() == Token.VALUES || identifierEquals("VALUE")) { + lexer.nextTokenLParen(); + parseValueClause(insertStatement.getValuesList(), columnSize); + } else if (lexer.token() == Token.SET) { + lexer.nextToken(); + + SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); + insertStatement.getValuesList().add(values); + + for (;;) { + SQLName name = this.exprParser.name(); + insertStatement.addColumn(name); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + } else { + accept(Token.COLONEQ); + } + values.addValue(this.exprParser.expr()); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + + break; + } + + } else if (lexer.token() == (Token.SELECT)) { + SQLSelect select = this.exprParser.createSelectParser().select(); + select.setParent(insertStatement); + insertStatement.setQuery(select); + } else if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + SQLSelect select = this.exprParser.createSelectParser().select(); + select.setParent(insertStatement); + insertStatement.setQuery(select); + accept(Token.RPAREN); + } + + return insertStatement; + } + + private void parseValueClause(List valueClauseList, int columnSize) { + for (;;) { + if (lexer.token() != Token.LPAREN) { + throw new ParserException("syntax error, expect ')'"); + } + lexer.nextTokenValue(); + + if (lexer.token() != Token.RPAREN) { + List valueExprList; + if (columnSize > 0) { + valueExprList = new ArrayList(columnSize); + } else { + valueExprList = new ArrayList(); + } + + for (;;) { + SQLExpr expr; + if (lexer.token() == Token.LITERAL_INT) { + expr = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextTokenComma(); + } else if (lexer.token() == Token.LITERAL_CHARS) { + expr = new SQLCharExpr(lexer.stringVal()); + lexer.nextTokenComma(); + } else if (lexer.token() == Token.LITERAL_NCHARS) { + expr = new SQLNCharExpr(lexer.stringVal()); + lexer.nextTokenComma(); + } else { + expr = exprParser.expr(); + } + + if (lexer.token() == Token.COMMA) { + valueExprList.add(expr); + lexer.nextTokenValue(); + continue; + } else if (lexer.token() == Token.RPAREN) { + valueExprList.add(expr); + break; + } else { + expr = this.exprParser.primaryRest(expr); + if (lexer.token() != Token.COMMA && lexer.token() != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } + + valueExprList.add(expr); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } else { + break; + } + } + } + + SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(valueExprList); + valueClauseList.add(values); + } else { + SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(new ArrayList(0)); + valueClauseList.add(values); + } + + if (lexer.token() != Token.RPAREN) { + throw new ParserException("syntax error"); + } + + if (!parseCompleteValues && valueClauseList.size() >= parseValuesSize) { + lexer.skipToEOF(); + break; + } + + lexer.nextTokenComma(); + if (lexer.token() == Token.COMMA) { + lexer.nextTokenLParen(); + continue; + } else { + break; + } + } + } + public void parseStatementList(List statementList, int max) { for (;;) { if (max != -1) { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataInsertTest_1.java b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataInsertTest_1.java new file mode 100644 index 0000000000..5d36ef00be --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/teradata/TeradataInsertTest_1.java @@ -0,0 +1,95 @@ +package com.alibaba.druid.bvt.sql.teradata; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.TeradataTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataInsertStatement; +import com.alibaba.druid.sql.dialect.teradata.ast.stmt.TeradataSelectQueryBlock; +import com.alibaba.druid.sql.dialect.teradata.parser.TeradataStatementParser; +import com.alibaba.druid.sql.dialect.teradata.visitor.TeradataSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat.Column; + + +public class TeradataInsertTest_1 extends TeradataTest { + public void test_values() throws Exception { + String ins_sql = "INSERT INTO t1(c1, c2) VALUES " + + "('a', 'b')," + + "('c', 'd')," + + "('e', 'f')"; + + TeradataStatementParser parser = new TeradataStatementParser(ins_sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertTrue(stmt instanceof SQLInsertStatement); + + TeradataInsertStatement insertStmt = (TeradataInsertStatement) stmt; + Assert.assertEquals("t1", insertStmt.getTableSource().toString()); + Assert.assertEquals("c1", insertStmt.getColumns().get(0).toString()); + Assert.assertEquals("c2", insertStmt.getColumns().get(1).toString()); + Assert.assertEquals(3, insertStmt.getValuesList().size()); + Assert.assertEquals(2, insertStmt.getValuesList().get(0).getValues().size()); + + SQLSelect insertQuery = insertStmt.getQuery(); + Assert.assertNull(insertQuery); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + insertStmt.accept(visitor); + System.out.println(visitor.getColumns()); + System.out.println(visitor.getTables()); + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } + + public void test_select() throws Exception { + String ins_sql = "INSERT INTO d1.t1(col1, col2) " + + "SELECT c1, c2 " + + "FROM d2.t2 " + + "WHERE c1=0;"; + TeradataStatementParser parser = new TeradataStatementParser(ins_sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + Assert.assertTrue(stmt instanceof SQLInsertStatement); + + TeradataInsertStatement insertStmt = (TeradataInsertStatement) stmt; + SQLSelect insertQuery = insertStmt.getQuery(); + Assert.assertNotNull(insertQuery); + + // retrieve SELECT sub-clause from INSERT query + TeradataSelectQueryBlock insertBlock = (TeradataSelectQueryBlock) insertQuery.getQuery(); + Assert.assertNotNull(insertBlock); + + // target table + Assert.assertEquals("d1.t1", insertStmt.getTableSource().toString()); + Assert.assertEquals(2, insertStmt.getColumns().size()); + Assert.assertEquals("col1", insertStmt.getColumns().get(0).toString()); + Assert.assertEquals("col2", insertStmt.getColumns().get(1).toString()); + // source table + Assert.assertEquals("d2.t2", insertBlock.getFrom().toString()); + Assert.assertEquals(2, insertBlock.getSelectList().size()); + Assert.assertEquals("c1", insertBlock.getSelectList().get(0).toString()); + Assert.assertEquals("c2", insertBlock.getSelectList().get(1).toString()); + + System.out.println("***********below is useful info **********"); + System.out.println("target table: " + insertStmt.getTableSource()); + System.out.println("target columns: " + insertStmt.getColumns()); + System.out.println("source from: " + insertBlock.getFrom()); + System.out.println("source columns : " + insertBlock.getSelectList()); + System.out.println("source condition: " + insertBlock.getWhere()); + System.out.println("********"); + + TeradataSchemaStatVisitor visitor = new TeradataSchemaStatVisitor(); + stmt.accept(visitor); + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + System.out.println("------ end of " + new Object(){}.getClass().getEnclosingMethod().getName() + " ------"); + } +}