From 306f9562733bee6e7146e4d0c398f565a7ee679f Mon Sep 17 00:00:00 2001 From: wenshao Date: Mon, 8 May 2017 03:27:32 +0800 Subject: [PATCH] improved PageUtils & support natural join. issue #1711 --- .../com/alibaba/druid/sql/PagerUtils.java | 9 ++- .../sql/ast/statement/SQLJoinTableSource.java | 11 +++ .../druid/sql/parser/SQLSelectParser.java | 14 ++++ .../sql/visitor/SQLASTOutputVisitor.java | 5 ++ ...gerUtilsTest_hasUnorderedLimit_oracle.java | 5 ++ ...ySqlParameterizedOutputVisitorTest_36.java | 40 +++++++++++ .../sql/mysql/select/MySqlSelectTest_35.java | 69 +++++++++++++++++++ .../sql/mysql/select/MySqlSelectTest_36.java | 69 +++++++++++++++++++ 8 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_36.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_35.java create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_36.java diff --git a/src/main/java/com/alibaba/druid/sql/PagerUtils.java b/src/main/java/com/alibaba/druid/sql/PagerUtils.java index 7514c27987..0fced4029d 100644 --- a/src/main/java/com/alibaba/druid/sql/PagerUtils.java +++ b/src/main/java/com/alibaba/druid/sql/PagerUtils.java @@ -572,8 +572,14 @@ public class PagerUtils { if (parent instanceof SQLSelectQuery) { if (parent instanceof OracleSelectQueryBlock) { OracleSelectQueryBlock queryBlock = (OracleSelectQueryBlock) parent; - if (queryBlock.getFrom() instanceof SQLExprTableSource) { + SQLTableSource from = queryBlock.getFrom(); + if (from instanceof SQLExprTableSource) { selectQuery = queryBlock; + } else if (from instanceof SQLSubqueryTableSource) { + SQLSelect subSelect = ((SQLSubqueryTableSource) from).getSelect(); + if (subSelect.getQuery() instanceof OracleSelectQueryBlock) { + selectQuery = (OracleSelectQueryBlock) subSelect.getQuery(); + } } } break; @@ -581,6 +587,7 @@ public class PagerUtils { } } + if (selectQuery != null) { SQLObject parent = selectQuery.getParent(); if (parent instanceof SQLSelect) { diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java index 6a227578c7..4894e16864 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLJoinTableSource.java @@ -29,6 +29,9 @@ public class SQLJoinTableSource extends SQLTableSourceImpl { protected SQLExpr condition; protected final List using = new ArrayList(); + + protected boolean natural = false; + public SQLJoinTableSource(String alias){ super(alias); } @@ -93,6 +96,14 @@ public class SQLJoinTableSource extends SQLTableSourceImpl { return this.using; } + public boolean isNatural() { + return natural; + } + + public void setNatural(boolean natural) { + this.natural = natural; + } + public void output(StringBuffer buf) { this.left.output(buf); buf.append(' '); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 2876891355..985cde3148 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -458,6 +458,12 @@ public class SQLSelectParser extends SQLParser { SQLJoinTableSource.JoinType joinType = null; + boolean natural = false; + if (JdbcConstants.MYSQL.equals(dbType) && identifierEquals("NATURAL")) { + lexer.nextToken(); + natural = true; + } + if (lexer.token() == Token.LEFT) { lexer.nextToken(); if (lexer.token() == Token.OUTER) { @@ -516,6 +522,14 @@ public class SQLSelectParser extends SQLParser { join.setJoinType(joinType); join.setRight(parseTableSource()); + if (!natural) { + if (JdbcConstants.MYSQL.equals(dbType) && "NATURAL".equalsIgnoreCase(tableSource.getAlias())) { + tableSource.setAlias(null); + natural = true; + } + } + join.setNatural(natural); + if (lexer.token() == Token.ON) { lexer.nextToken(); join.setCondition(expr()); 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 21e0792c78..5c4f671f83 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -1856,6 +1856,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet print(','); } else { println(); + + if (x.isNatural()) { + print0(ucase ? "NATURAL " : "natural "); + } + printJoinType(x.getJoinType()); } print(' '); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_hasUnorderedLimit_oracle.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_hasUnorderedLimit_oracle.java index d7bac89e63..752643bc1b 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_hasUnorderedLimit_oracle.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_hasUnorderedLimit_oracle.java @@ -30,4 +30,9 @@ public class PagerUtilsTest_hasUnorderedLimit_oracle extends TestCase { "\nWHERE ROWNUM <= 10"; assertTrue(PagerUtils.hasUnorderedLimit(sql, JdbcConstants.ORACLE)); } + + public void test_true_1() throws Exception { + String sql = "select * from (select * from t where id > 1 ) where rownum < 1000"; + assertTrue(PagerUtils.hasUnorderedLimit(sql, JdbcConstants.ORACLE)); + } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_36.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_36.java new file mode 100644 index 0000000000..7a153f200f --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_36.java @@ -0,0 +1,40 @@ +package com.alibaba.druid.bvt.sql.mysql.param; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; +import com.alibaba.druid.util.JdbcConstants; +import junit.framework.TestCase; + +import java.util.List; + +/** + * Created by wenshao on 16/8/23. + */ +public class MySqlParameterizedOutputVisitorTest_36 extends TestCase { + public void test_for_parameterize() throws Exception { + final String dbType = JdbcConstants.MYSQL; + + + String sql = "INSERT INTO v_j (jb) VALUES (0x7801848fbd6ec23014465f057986c4ffc69150972e0c1dd9bc5cdbd7c56d8028762221c4bbd72addbb1e9def48df832c399281ecb9a4865345c996ac39e2ed743abe376e434cda0a8b7b26920908c6701e64a45a29c3c137bdde272403fbdbb5cdc391922b1ea323c3866d37ee55fc88aa01f76fd29167ab0618470fe1fb348f2d79ae751afafe8cf7dcc11273dd4108584a1731c132d60ec6ecc1c32e5f43b7e6a9e0bce2dcafac87a5de7e0fcd589af856f3054b85cb7460d20a6194e4ca2d9472fd75f3391e446c3c498d7b11a5e7009c098d946a4941614a2fb7e4cfeb01020fe0ad899602a34c09ce2c9388497903d26bf2fc090000ffff01196b1f)"; + + + + SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); + List stmtList = parser.parseStatementList(); + SQLStatement statement = stmtList.get(0); + + StringBuilder out = new StringBuilder(); + // List parameters = new ArrayList(); + SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(out, JdbcConstants.MYSQL); + visitor.setParameterized(true); + visitor.setParameterizedMergeInList(true); + // visitor.setParameters(parameters); + visitor.setExportTables(true); + visitor.setPrettyFormat(false); + statement.accept(visitor); + System.out.println(out); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_35.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_35.java new file mode 100644 index 0000000000..70610d7801 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_35.java @@ -0,0 +1,69 @@ +/* + * 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_35 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT * FROM t1 natural LEFT JOIN t2;"; + + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL); + 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("SELECT *\n" + + "FROM t1\n" + + "\tNATURAL LEFT JOIN t2", // + output); + } + { + String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + Assert.assertEquals("select *\n" + + "from t1\n" + + "\tnatural left join t2", // + output); + } + } + + + +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_36.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_36.java new file mode 100644 index 0000000000..682bd0f88d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_36.java @@ -0,0 +1,69 @@ +/* + * 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_36 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "SELECT * FROM mytable t1 natural LEFT JOIN t2;"; + + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL); + 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("SELECT *\n" + + "FROM mytable t1\n" + + "\tNATURAL LEFT JOIN t2", // + output); + } + { + String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); + Assert.assertEquals("select *\n" + + "from mytable t1\n" + + "\tnatural left join t2", // + output); + } + } + + + +}