From 5706f65fcf4417438964d692a9ecd6eec5fc500c Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 14 May 2017 10:14:34 +0800 Subject: [PATCH] mysql sql format support keepComments. --- .../java/com/alibaba/druid/sql/SQLUtils.java | 11 ++- .../dialect/mysql/parser/MySqlExprParser.java | 5 ++ .../sql/dialect/mysql/parser/MySqlLexer.java | 7 ++ .../mysql/parser/MySqlSelectParser.java | 4 ++ .../mysql/parser/MySqlStatementParser.java | 4 ++ .../mysql/visitor/MySqlOutputVisitor.java | 4 +- .../druid/sql/parser/SQLParserUtils.java | 15 +++- .../sql/mysql/select/MySqlSelectTest_40.java | 71 +++++++++++++++++++ .../sql/mysql/select/MySqlSelectTest_41.java | 71 +++++++++++++++++++ 9 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_40.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_41.java diff --git a/src/main/java/com/alibaba/druid/sql/SQLUtils.java b/src/main/java/com/alibaba/druid/sql/SQLUtils.java index a3a1e15fa4..3943a5d820 100644 --- a/src/main/java/com/alibaba/druid/sql/SQLUtils.java +++ b/src/main/java/com/alibaba/druid/sql/SQLUtils.java @@ -242,7 +242,7 @@ public class SQLUtils { public static String format(String sql, String dbType, List parameters, FormatOption option) { try { - SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, true); parser.setKeepComments(true); List statementList = parser.parseStatementList(); @@ -438,6 +438,15 @@ public class SQLUtils { return stmtList; } + public static List parseStatements(String sql, String dbType, boolean keepComments) { + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, keepComments); + List stmtList = parser.parseStatementList(); + if (parser.getLexer().token() != Token.EOF) { + throw new DruidRuntimeException("syntax error : " + sql); + } + return stmtList; + } + /** * @author owenludong.lud * @param columnName 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 e5d5a29113..93e548f6da 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 @@ -73,6 +73,11 @@ public class MySqlExprParser extends SQLExprParser { this.lexer.nextToken(); } + public MySqlExprParser(String sql, boolean keepComments){ + this(new MySqlLexer(sql, keepComments, keepComments)); + this.lexer.nextToken(); + } + public SQLExpr relationalRest(SQLExpr expr) { if (identifierEquals("REGEXP")) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java index be53650bd1..3689ba158d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlLexer.java @@ -75,6 +75,13 @@ public class MySqlLexer extends Lexer { super.keywods = DEFAULT_MYSQL_KEYWORDS; } + public MySqlLexer(String input, boolean skipComment, boolean keepComments){ + super(input, skipComment); + this.skipComment = skipComment; + this.keepComments = keepComments; + super.keywods = DEFAULT_MYSQL_KEYWORDS; + } + public void scanSharp() { if (ch != '#') { throw new ParserException("illegal stat"); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java index 38f82a6fa1..3516e9eed3 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java @@ -87,6 +87,10 @@ public class MySqlSelectParser extends SQLSelectParser { MySqlSelectQueryBlock queryBlock = new MySqlSelectQueryBlock(); + if (lexer.hasComment() && lexer.isKeepComments()) { + queryBlock.addBeforeComment(lexer.readAndResetComments()); + } + if (lexer.token() == Token.SELECT) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java index c2affb46d6..110d5d02f4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlStatementParser.java @@ -231,6 +231,10 @@ public class MySqlStatementParser extends SQLStatementParser { super(new MySqlExprParser(sql)); } + public MySqlStatementParser(String sql, boolean keepComments) { + super(new MySqlExprParser(sql, keepComments)); + } + public MySqlStatementParser(Lexer lexer) { super(new MySqlExprParser(lexer)); } 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 16147caca7..660b26973e 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 @@ -206,8 +206,8 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV } public boolean visit(MySqlSelectQueryBlock x) { - if (x.getOrderBy() != null) { - x.getOrderBy().setParent(x); + if ((!isParameterized()) && isPrettyFormat() && x.hasBeforeComment()) { + printComment(x.getBeforeCommentsDirect(), "\n"); } print0(ucase ? "SELECT " : "select "); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java index 8ad9ad5750..bd95e35f8b 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserUtils.java @@ -36,21 +36,32 @@ import com.alibaba.druid.sql.dialect.postgresql.parser.PGSQLStatementParser; import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerExprParser; import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerLexer; import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.util.JdbcConstants; import com.alibaba.druid.util.JdbcUtils; public class SQLParserUtils { public static SQLStatementParser createSQLStatementParser(String sql, String dbType) { + boolean keepComments; + if (JdbcConstants.ODPS.equals(dbType)) { + keepComments = true; + } else { + keepComments = false; + } + return createSQLStatementParser(sql, dbType, keepComments); + } + + public static SQLStatementParser createSQLStatementParser(String sql, String dbType, boolean keepComments) { if (JdbcUtils.ORACLE.equals(dbType) || JdbcUtils.ALI_ORACLE.equals(dbType)) { return new OracleStatementParser(sql); } if (JdbcUtils.MYSQL.equals(dbType)) { - return new MySqlStatementParser(sql); + return new MySqlStatementParser(sql, keepComments); } if (JdbcUtils.MARIADB.equals(dbType)) { - return new MySqlStatementParser(sql); + return new MySqlStatementParser(sql, keepComments); } if (JdbcUtils.POSTGRESQL.equals(dbType) diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_40.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_40.java new file mode 100644 index 0000000000..2fe9aba49a --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_40.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2017 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.select; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class MySqlSelectTest_40 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "/*0abcde*/select max(id) from base_request_log"; + + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL, true); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.MYSQL); + 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.assertEquals(0, visitor.getOrderByColumns().size()); + + { + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("/*0abcde*/SELECT MAX(id)\n" + + "FROM base_request_log", // + output); + } + { + String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + Assert.assertEquals("/*0abcde*/select max(id)\n" + + "from base_request_log", // + output); + } + + { + String output = SQLUtils.toMySqlString(stmt, new SQLUtils.FormatOption(true, true, true)); + Assert.assertEquals("SELECT MAX(id)\n" + + "FROM base_request_log", // + output); + } + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_41.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_41.java new file mode 100644 index 0000000000..97873f838e --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_41.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2017 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.select; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class MySqlSelectTest_41 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "/*0abcde*/select max(id) from base_request_log"; + + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL, true); + SQLStatement stmt = statementList.get(0); + + Assert.assertEquals(1, statementList.size()); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.MYSQL); + 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.assertEquals(0, visitor.getOrderByColumns().size()); + + { + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("/*0abcde*/SELECT MAX(id)\n" + + "FROM base_request_log", // + output); + } + { + String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + Assert.assertEquals("/*0abcde*/select max(id)\n" + + "from base_request_log", // + output); + } + + { + String output = SQLUtils.toMySqlString(stmt, new SQLUtils.FormatOption(true, true, true)); + Assert.assertEquals("SELECT MAX(id)\n" + + "FROM base_request_log", // + output); + } + } +}