From 646ef3153b5e29482a2139f5949b40a1116d9334 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 29 Jun 2013 00:30:35 +0800 Subject: [PATCH] support mysql group_concat --- .../dialect/mysql/parser/MySqlExprParser.java | 20 +++++ .../mysql/visitor/MySqlOutputVisitor.java | 22 +++++- .../oracle/parser/OracleExprParser.java | 29 +------- .../postgresql/parser/PGExprParser.java | 23 ++---- .../sqlserver/parser/SQLServerExprParser.java | 56 ++++++-------- .../druid/sql/parser/SQLExprParser.java | 18 +++-- .../sql/visitor/SQLASTOutputVisitor.java | 7 ++ .../bvt/sql/mysql/MySqlSelectTest_12.java | 70 ++++++++++++++++++ .../mysql/MySqlSelectTest_group_concat.java | 74 +++++++++++++++++++ .../mysql/MySqlSelectTest_group_concat_2.java | 72 ++++++++++++++++++ 10 files changed, 306 insertions(+), 85 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_12.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat_2.java diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index a3a5777e93..56b8baf69f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -18,6 +18,8 @@ package com.alibaba.druid.sql.dialect.mysql.parser; import com.alibaba.druid.sql.ast.SQLDataType; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr; import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator; import com.alibaba.druid.sql.ast.expr.SQLCharExpr; @@ -55,8 +57,11 @@ import com.alibaba.druid.sql.parser.Token; public class MySqlExprParser extends SQLExprParser { + public static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "GROUP_CONCAT", "MAX", "MIN", "STDDEV", "SUM" }; + public MySqlExprParser(Lexer lexer){ super(lexer); + this.aggregateFunctions = AGGREGATE_FUNCTIONS; } public MySqlExprParser(String sql){ @@ -769,4 +774,19 @@ public class MySqlExprParser extends SQLExprParser { accept(Token.RPAREN); return fk; } + + protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) { + if (lexer.token() == Token.ORDER) { + SQLOrderBy orderBy = this.parseOrderBy(); + aggregateExpr.putAttribute("ORDER BY", orderBy); + } + if (identifierEquals("SEPARATOR")) { + lexer.nextToken(); + + SQLExpr seperator = this.primary(); + + aggregateExpr.putAttribute("SEPARATOR", seperator); + } + return aggregateExpr; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index e1efa7860e..3bb2c34bad 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -22,7 +22,9 @@ import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLDataType; 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.SQLCharExpr; import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLNullExpr; @@ -594,8 +596,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV && (!varName.equals("?")) // && (!varName.startsWith("#")) // && (!varName.startsWith("$")) // - && (!varName.startsWith(":")) - ) { + && (!varName.startsWith(":"))) { print("@@"); } } @@ -3061,4 +3062,21 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } + 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) { + print(" SEPARATOR "); + ((SQLObject) value).accept(this); + } + } + } + } // diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index dbd4d15df7..c84003327f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -72,22 +72,6 @@ import com.alibaba.druid.sql.parser.Token; public class OracleExprParser extends SQLExprParser { - - - - - - - - - - - - - - - - public boolean allowStringAdditive = false; /** @@ -137,10 +121,11 @@ public class OracleExprParser extends SQLExprParser { public OracleExprParser(Lexer lexer){ super(lexer); + this.aggregateFunctions = AGGREGATE_FUNCTIONS; } public OracleExprParser(String text){ - super(new OracleLexer(text)); + this(new OracleLexer(text)); this.lexer.nextToken(); } @@ -267,16 +252,6 @@ public class OracleExprParser extends SQLExprParser { return parseDataTypeRest(dataType); } - public boolean isAggreateFunction(String word) { - for (int i = 0; i < AGGREGATE_FUNCTIONS.length; ++i) { - if (AGGREGATE_FUNCTIONS[i].compareToIgnoreCase(word) == 0) { - return true; - } - } - - return false; - } - public SQLExpr primary() { final Token tok = lexer.token(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java index 0cb3c1266c..394364c024 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/postgresql/parser/PGExprParser.java @@ -25,25 +25,16 @@ import com.alibaba.druid.sql.parser.Token; public class PGExprParser extends SQLExprParser { + public final static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM", "ROW_NUMBER" }; + public PGExprParser(String sql){ - super(new PGLexer(sql)); + this(new PGLexer(sql)); this.lexer.nextToken(); } public PGExprParser(Lexer lexer){ super(lexer); - } - - public boolean isAggreateFunction(String word) { - String[] aggregateFunctions = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM", "ROW_NUMBER" }; - - for (int i = 0; i < aggregateFunctions.length; ++i) { - if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { - return true; - } - } - - return false; + this.aggregateFunctions = AGGREGATE_FUNCTIONS; } @Override @@ -71,16 +62,16 @@ public class PGExprParser extends SQLExprParser { return null; } - + public SQLExpr primaryRest(SQLExpr expr) { if (lexer.token() == Token.COLONCOLON) { lexer.nextToken(); SQLExpr typeExpr = this.expr(); SQLBinaryOpExpr castExpr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.PostgresStyleTypeCast, typeExpr); - + return primaryRest(castExpr); } - + return super.primaryRest(expr); } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java index e9d8cc3838..f98b5b0a15 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerExprParser.java @@ -26,28 +26,30 @@ import com.alibaba.druid.sql.parser.Token; public class SQLServerExprParser extends SQLExprParser { + public final static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "ROW_NUMBER", "STDDEV", "SUM" }; + public SQLServerExprParser(Lexer lexer){ super(lexer); + this.aggregateFunctions = AGGREGATE_FUNCTIONS; } - public SQLServerExprParser(String sql) { - super(new SQLServerLexer(sql)); + public SQLServerExprParser(String sql){ + this(new SQLServerLexer(sql)); this.lexer.nextToken(); } - + public SQLExpr primary() { - - + if (lexer.token() == Token.LBRACKET) { lexer.nextToken(); SQLExpr name = this.name(); accept(Token.RBRACKET); return primaryRest(name); } - + return super.primary(); } - + public SQLServerSelectParser createSelectParser() { return new SQLServerSelectParser(this); } @@ -56,31 +58,31 @@ public class SQLServerExprParser extends SQLExprParser { if (lexer.token() == Token.DOTDOT) { expr = nameRest((SQLName) expr); } - + return super.primaryRest(expr); } - + protected SQLExpr dotRest(SQLExpr expr) { boolean backet = false; - + if (lexer.token() == Token.LBRACKET) { lexer.nextToken(); backet = true; } - + expr = super.dotRest(expr); if (backet) { accept(Token.RBRACKET); } - + return expr; } - + public SQLName nameRest(SQLName expr) { if (lexer.token() == Token.DOTDOT) { lexer.nextToken(); - + boolean backet = false; if (lexer.token() == Token.LBRACKET) { lexer.nextToken(); @@ -88,7 +90,7 @@ public class SQLServerExprParser extends SQLExprParser { } String text = lexer.stringVal(); lexer.nextToken(); - + if (backet) { accept(Token.RBRACKET); } @@ -99,44 +101,32 @@ public class SQLServerExprParser extends SQLExprParser { return super.nameRest(expr); } - - public boolean isAggreateFunction(String word) { - String[] aggregateFunctions = { "AVG", "COUNT", "MAX", "MIN", "ROW_NUMBER", "STDDEV", "SUM" }; - for (int i = 0; i < aggregateFunctions.length; ++i) { - if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { - return true; - } - } - - return false; - } - public SQLServerTop parseTop() { if (lexer.token() == Token.TOP) { SQLServerTop top = new SQLServerTop(); lexer.nextToken(); - + boolean paren = false; if (lexer.token() == Token.LPAREN) { paren = true; lexer.nextToken(); } - + top.setExpr(primary()); - + if (paren) { accept(Token.RPAREN); } - + if (lexer.token() == Token.PERCENT) { lexer.nextToken(); top.setPercent(true); } - + return top; } - + return null; } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index 484e3101f4..925786c581 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -71,6 +71,10 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; public class SQLExprParser extends SQLParser { + public final static String[] AGGREGATE_FUNCTIONS = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM" }; + + protected String[] aggregateFunctions = AGGREGATE_FUNCTIONS; + public SQLExprParser(String sql){ super(sql); } @@ -703,8 +707,6 @@ public class SQLExprParser extends SQLParser { } public boolean isAggreateFunction(String word) { - String[] aggregateFunctions = { "AVG", "COUNT", "MAX", "MIN", "STDDEV", "SUM" }; - for (int i = 0; i < aggregateFunctions.length; ++i) { if (aggregateFunctions[i].compareToIgnoreCase(word) == 0) { return true; @@ -728,7 +730,9 @@ public class SQLExprParser extends SQLParser { aggregateExpr = new SQLAggregateExpr(methodName); } - exprList(aggregateExpr.getArguments()); + exprList(aggregateExpr.getArguments(), aggregateExpr); + + parseAggregateExprRest(aggregateExpr); accept(Token.RPAREN); @@ -752,10 +756,6 @@ public class SQLExprParser extends SQLParser { over.setOrderBy(parseOrderBy()); - // if (over.getOrderBy() != null) { - // //TODO window - // } - accept(Token.RPAREN); aggregateExpr.setOver(over); @@ -764,6 +764,10 @@ public class SQLExprParser extends SQLParser { return aggregateExpr; } + protected SQLAggregateExpr parseAggregateExprRest(SQLAggregateExpr aggregateExpr) { + return aggregateExpr; + } + public SQLOrderBy parseOrderBy() { if (lexer.token() == Token.ORDER) { SQLOrderBy orderBy = new SQLOrderBy(); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 0608dce45a..240be1f789 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -511,6 +511,9 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { } printAndAccept(x.getArguments(), ", "); + + visitAggreateRest(x); + print(")"); if (x.getOver() != null) { @@ -519,6 +522,10 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter { } return false; } + + protected void visitAggreateRest(SQLAggregateExpr aggregateExpr) { + + } public boolean visit(SQLAllColumnExpr x) { print("*"); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_12.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_12.java new file mode 100644 index 0000000000..b86e02316e --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_12.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; + +public class MySqlSelectTest_12 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT COUNT(*) a FROM (select nickname,mobile,comment,createdate from ub_userdiscuss order by discuss_id desc) b "; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + MySqlSelectQueryBlock queryBlock = (MySqlSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + 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"))); + + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT COUNT(*) AS a" + // + "\nFROM (SELECT nickname, mobile, comment, createdate" + // + "\n\tFROM ub_userdiscuss" + // + "\n\tORDER BY discuss_id DESC" + // + "\n\t) b", // + output); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat.java new file mode 100644 index 0000000000..fc63490b08 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat.java @@ -0,0 +1,74 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; + +public class MySqlSelectTest_group_concat extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT GROUP_CONCAT(ext_customer_id Separator '.') " // + + "from ad_texts " // + + "where customer_id=13001 " // + + "and description1 like '%爱丽%' " // + + "order by id asc;"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + MySqlSelectQueryBlock queryBlock = (MySqlSelectQueryBlock) select.getQuery(); + Assert.assertNotNull(queryBlock.getOrderBy()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(4, visitor.getColumns().size()); + Assert.assertEquals(2, visitor.getConditions().size()); + Assert.assertEquals(1, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("ad_texts"))); + + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT GROUP_CONCAT(ext_customer_id SEPARATOR '.')" // + + "\nFROM ad_texts" // + + "\nWHERE customer_id = 13001" // + + "\n\tAND description1 LIKE '%爱丽%'" // + + "\nORDER BY id ASC", // + output); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat_2.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat_2.java new file mode 100644 index 0000000000..bcde1d7c82 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/MySqlSelectTest_group_concat_2.java @@ -0,0 +1,72 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.sql.mysql; + +import java.util.List; + +import org.junit.Assert; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor; +import com.alibaba.druid.stat.TableStat; + +public class MySqlSelectTest_group_concat_2 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT student_name, " // + + "GROUP_CONCAT(DISTINCT test_score " // + + " ORDER BY test_score DESC SEPARATOR ' ') " // + + "FROM student " // + + "GROUP BY student_name"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + + SQLSelectStatement selectStmt = (SQLSelectStatement) stmt; + + SQLSelect select = selectStmt.getSelect(); + Assert.assertNotNull(select.getQuery()); + MySqlSelectQueryBlock queryBlock = (MySqlSelectQueryBlock) select.getQuery(); + Assert.assertNull(queryBlock.getOrderBy()); + + print(statementList); + + Assert.assertEquals(1, statementList.size()); + + MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); + stmt.accept(visitor); + + Assert.assertEquals(1, visitor.getTables().size()); + Assert.assertEquals(2, visitor.getColumns().size()); + Assert.assertEquals(0, visitor.getConditions().size()); + Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("student"))); + + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT student_name, GROUP_CONCAT(DISTINCT test_score ORDER BY test_score DESC SEPARATOR ' ')" // + + "\nFROM student" // + + "\nGROUP BY student_name", // + output); + } +}