diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2CreateTableStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2CreateTableStatement.java index f5f9fca966..dc2560046c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2CreateTableStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/ast/stmt/DB2CreateTableStatement.java @@ -21,12 +21,16 @@ import com.alibaba.druid.sql.dialect.db2.ast.DB2Statement; import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; +import java.util.ArrayList; +import java.util.List; + public class DB2CreateTableStatement extends SQLCreateTableStatement implements DB2Statement { private boolean dataCaptureNone; private boolean dataCaptureChanges; protected SQLName database; protected SQLName validproc; + protected SQLName indexIn; public boolean isDataCaptureNone() { return dataCaptureNone; @@ -59,11 +63,22 @@ public class DB2CreateTableStatement extends SQLCreateTableStatement implements return validproc; } - public void setValidproc(SQLName validproc) { + public void setValidproc(SQLName x) { if (validproc != null) { - validproc.setParent(this); + x.setParent(this); } - this.validproc = validproc; + this.validproc = x; + } + + public SQLName getIndexIn() { + return indexIn; + } + + public void setIndexIn(SQLName x) { + if (validproc != null) { + x.setParent(this); + } + this.indexIn = x; } @Override @@ -85,6 +100,7 @@ public class DB2CreateTableStatement extends SQLCreateTableStatement implements this.acceptChild(visitor, select); this.acceptChild(visitor, database); this.acceptChild(visitor, validproc); + this.acceptChild(visitor, indexIn); } visitor.endVisit(this); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2CreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2CreateTableParser.java index 126a45fd28..c288d91943 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2CreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/parser/DB2CreateTableParser.java @@ -179,6 +179,12 @@ public class DB2CreateTableParser extends SQLCreateTableParser { lexer.nextToken(); } continue; + } else if (lexer.token() == Token.INDEX) { + lexer.nextToken(); + accept(Token.IN); + SQLName indexIn = this.exprParser.name(); + createTable.setIndexIn(indexIn); + continue; } break; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java index e591c4d62c..ced3bfd843 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/db2/visitor/DB2OutputVisitor.java @@ -101,6 +101,13 @@ public class DB2OutputVisitor extends SQLASTOutputVisitor implements DB2ASTVisit tablespace.accept(this); } + SQLName indexIn = x.getIndexIn(); + if (indexIn != null) { + println(); + print("INDEX IN "); + indexIn.accept(this); + } + SQLName database = x.getDatabase(); if (database != null) { println(); 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 b63f12c968..24001261d0 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -89,7 +89,7 @@ public class SQLExprParser extends SQLParser { if (token == Token.COMMA) { return expr; } else if (token == Token.EQ) { - expr = equalityRest(expr); + expr = equalityRest2(expr); expr = andRest(expr); expr = orRest(expr); return expr; @@ -1668,6 +1668,53 @@ public class SQLExprParser extends SQLParser { return equalityRest(expr); } + private SQLExpr equalityRest2(SQLExpr expr) { + SQLExpr rightExp; + lexer.nextToken(); + try { + if (lexer.token == Token.LITERAL_INT) { + rightExp = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextToken(); + + if (lexer.token == Token.AND + || lexer.token == Token.RPAREN + || lexer.token == Token.EOF + || lexer.token == Token.OR) { + return new SQLBinaryOpExpr(expr, SQLBinaryOperator.Equality, rightExp, dbType); + } + + rightExp = primaryRest(rightExp); + rightExp = bitXorRest(rightExp); + rightExp = multiplicativeRest(rightExp); + rightExp = additiveRest(rightExp); + rightExp = shiftRest(rightExp); + rightExp = bitAndRest(rightExp); + rightExp = bitOrRest(rightExp); + rightExp = inRest(rightExp); + rightExp = relationalRest(rightExp); + } else { + rightExp = bitOr(); + } + } catch (EOFParserException e) { + throw new ParserException("EOF, " + expr + "=", e); + } + + if (lexer.token == Token.COLONEQ) { + lexer.nextToken(); + SQLExpr colonExpr = expr(); + rightExp = new SQLBinaryOpExpr(rightExp, SQLBinaryOperator.Assignment, colonExpr, dbType); + } + + expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Equality, rightExp, dbType); + + if (lexer.token == Token.IS || lexer.token == Token.BETWEEN) { + expr = relationalRest(expr); + } + + return expr; + } + + public SQLExpr equalityRest(SQLExpr expr) { SQLExpr rightExp; @@ -1728,7 +1775,18 @@ public class SQLExprParser extends SQLParser { } for (;;) { - SQLExpr item = this.expr(); + SQLExpr item; + if (lexer.token == Token.LITERAL_INT) { + item = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextToken(); + if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) { + item = this.primaryRest(item); + item = this.exprRest(item); + } + } else { + item = this.expr(); + } + item.setParent(inListExpr); targetList.add(item); if (lexer.token == Token.COMMA) { @@ -3049,15 +3107,39 @@ public class SQLExprParser extends SQLParser { public SQLLimit parseLimit() { if (lexer.token == Token.LIMIT) { - lexer.nextToken(); + lexer.nextTokenValue(); SQLLimit limit = new SQLLimit(); - SQLExpr temp = this.expr(); + SQLExpr temp; + if (lexer.token == Token.LITERAL_INT) { + temp = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextTokenComma(); + if (lexer.token != Token.COMMA && lexer.token != Token.EOF && lexer.token != Token.IDENTIFIER) { + temp = this.primaryRest(temp); + temp = this.exprRest(temp); + } + } else { + temp = this.expr(); + } + if (lexer.token == (Token.COMMA)) { limit.setOffset(temp); - lexer.nextToken(); - limit.setRowCount(this.expr()); + lexer.nextTokenValue(); + + SQLExpr rowCount; + if (lexer.token == Token.LITERAL_INT) { + rowCount = new SQLIntegerExpr(lexer.integerValue()); + lexer.nextToken(); + if (lexer.token != Token.EOF && lexer.token != Token.IDENTIFIER) { + rowCount = this.primaryRest(rowCount); + rowCount = this.exprRest(rowCount); + } + } else { + rowCount = this.expr(); + } + + limit.setRowCount(rowCount); } else if (lexer.identifierEquals(FnvHash.Constants.OFFSET)) { limit.setRowCount(temp); lexer.nextToken(); 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 533284d61a..26dfd8d898 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -585,7 +585,7 @@ public class SQLSelectParser extends SQLParser { } protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { - if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) { + if (tableSource.getAlias() == null || tableSource.getAlias().length() == 0) { Token token = lexer.token; long hash; if (token != Token.LEFT @@ -599,6 +599,11 @@ public class SQLSelectParser extends SQLParser { String alias = tableAlias(); if (alias != null) { tableSource.setAlias(alias); + + if (lexer.token == Token.WHERE) { + return tableSource; + } + return parseTableSourceRest(tableSource); } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 17513c3e15..8e27762d7e 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -3164,6 +3164,10 @@ public class SQLStatementParser extends SQLParser { expr = new SQLIntegerExpr(lexer.integerValue()); } lexer.nextTokenComma(); + + if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } } else if (lexer.token() == Token.LITERAL_CHARS) { if (optimizedForParameterized) { expr = new SQLVariantRefExpr("?"); @@ -3172,6 +3176,9 @@ public class SQLStatementParser extends SQLParser { expr = new SQLCharExpr(lexer.stringVal()); } lexer.nextTokenComma(); + if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } } else if (lexer.token() == Token.LITERAL_NCHARS) { if (optimizedForParameterized) { expr = new SQLVariantRefExpr("?"); @@ -3180,6 +3187,20 @@ public class SQLStatementParser extends SQLParser { expr = new SQLNCharExpr(lexer.stringVal()); } lexer.nextTokenComma(); + if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } + } else if (lexer.token == Token.NULL) { + if (optimizedForParameterized) { + expr = new SQLVariantRefExpr("?"); + values.incrementReplaceCount(); + } else { + expr = new SQLNullExpr(); + } + lexer.nextTokenComma(); + if (lexer.token != Token.COMMA && lexer.token != Token.RPAREN) { + expr = this.exprParser.exprRest(expr); + } } else { expr = exprParser.expr(); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SymbolTable.java b/src/main/java/com/alibaba/druid/sql/parser/SymbolTable.java index e36d83ae8f..43e5de2a3d 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SymbolTable.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SymbolTable.java @@ -15,6 +15,8 @@ */ package com.alibaba.druid.sql.parser; +import com.alibaba.druid.util.Utils; + import java.nio.charset.Charset; /** @@ -22,8 +24,19 @@ import java.nio.charset.Charset; */ public class SymbolTable { private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final boolean JVM_16; - public static SymbolTable global = new SymbolTable(8192); + static { + String version = null; + try { + version = System.getProperty("java.specification.version"); + } catch (Throwable error) { + // skip + } + JVM_16 = "1.6".equals(version); + } + + public static SymbolTable global = new SymbolTable(32768); private final Entry[] entries; private final int indexMask; @@ -42,12 +55,16 @@ public class SymbolTable { return entry.value; } - String str = subString(buffer, offset, len); + String str = JVM_16 + ? subString(buffer, offset, len) + : buffer.substring(offset, offset + len); return str; } - String str = subString(buffer, offset, len); + String str = JVM_16 + ? subString(buffer, offset, len) + : buffer.substring(offset, offset + len); entry = new Entry(hash, len, str); entries[bucket] = entry; return str; 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 b1ca7d8c5d..c321ac626f 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -66,7 +66,8 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet protected List parameters; protected List inputParameters; - protected Set tables; + protected Set tables; + protected String table; // for improved protected boolean exportTables = false; @@ -149,6 +150,9 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } public Set getTables() { + if (this.table != null && this.tables == null) { + return Collections.singleton(this.table); + } return this.tables; } @@ -1586,7 +1590,9 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } else { if (owner instanceof SQLIdentifierExpr) { SQLIdentifierExpr ownerIdent = (SQLIdentifierExpr) owner; - printName(ownerIdent, ownerIdent.getName()); + printName(ownerIdent + , ownerIdent.getName() + , this.shardingSupport && this.parameterized); } else { printExpr(owner); } @@ -1944,7 +1950,13 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet protected void addTable(String table) { if (tables == null) { - tables = new LinkedHashSet(); + if (this.table == null) { + this.table = table; + return; + } else { + tables = new LinkedHashSet(); + tables.add(this.table); + } } this.tables.add(table); } @@ -2842,6 +2854,25 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet print0(ucase ? joinType.name : joinType.name_lcase); } + static String[] variantValuesCache = new String[64]; + static { + for (int len = 0; len < variantValuesCache.length; ++len) { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < len; ++i) { + if (i != 0) { + if (i % 5 == 0) { + buf.append("\n\t\t"); + } + buf.append(", "); + } + buf.append('?'); + } + buf.append(')'); + variantValuesCache[len] = buf.toString(); + } + } + @Override public boolean visit(ValuesClause x) { if ((!this.parameterized) @@ -2851,12 +2882,21 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet return false; } + int xReplaceCount = x.getReplaceCount(); + final List values = x.getValues(); + + this.replaceCount += xReplaceCount; + + if (xReplaceCount == values.size() && xReplaceCount < variantValuesCache.length) { + String variantValues = variantValuesCache[xReplaceCount]; + print0(variantValues); + return false; + } + print('('); this.indentCount++; - this.replaceCount += x.getReplaceCount(); - final List values = x.getValues(); for (int i = 0, size = values.size(); i < size; ++i) { if (i != 0) { if (i % 5 == 0) { diff --git a/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2CreateTableTest_05.java b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2CreateTableTest_05.java new file mode 100644 index 0000000000..ce04b3a20d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/db2/DB2CreateTableTest_05.java @@ -0,0 +1,98 @@ +/* + * 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.db2; + +import com.alibaba.druid.sql.DB2Test; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.db2.parser.DB2StatementParser; +import com.alibaba.druid.sql.dialect.db2.visitor.DB2SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class DB2CreateTableTest_05 extends DB2Test { + + public void test_0() throws Exception { + String sql = "CREATE TABLE MK.M_DM_BASE_CHARGE_TMP1\n" + + "(PROD_ID BIGINT,\n" + + "ITEM_ID SMALLINT,\n" + + "CHARGE BIGINT,\n" + + "PAGES BIGINT,\n" + + "DURATION BIGINT,\n" + + "BILLING_DURATION BIGINT\n" + + ") DATA CAPTURE NONE\n" + + "IN MK_APP_TEMP\n" + + "INDEX IN WH_INDEX1\n" + + "PARTITIONING KEY\n" + + "(PROD_ID\n" + + ") USING HASHING"; + + DB2StatementParser parser = new DB2StatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + System.out.println(SQLUtils.toDB2String(stmt)); + + assertEquals(1, statementList.size()); + + DB2SchemaStatVisitor visitor = new DB2SchemaStatVisitor(); + 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()); + + assertEquals(1, visitor.getTables().size()); + assertEquals(6, visitor.getColumns().size()); + assertEquals(0, visitor.getConditions().size()); + + assertTrue(visitor.containsTable("MK.M_DM_BASE_CHARGE_TMP1")); + +// assertTrue(visitor.getColumns().contains(new Column("DSN8B10.EMP", "WORKDEPT"))); + // assertTrue(visitor.getColumns().contains(new Column("mytable", "first_name"))); + // assertTrue(visitor.getColumns().contains(new Column("mytable", "full_name"))); + + assertEquals("CREATE TABLE MK.M_DM_BASE_CHARGE_TMP1 (\n" + + "\tPROD_ID BIGINT,\n" + + "\tITEM_ID SMALLINT,\n" + + "\tCHARGE BIGINT,\n" + + "\tPAGES BIGINT,\n" + + "\tDURATION BIGINT,\n" + + "\tBILLING_DURATION BIGINT\n" + + ")\n" + + "DATA CAPTURE NONE\n" + + "IN MK_APP_TEMP\n" + + "INDEX IN WH_INDEX1\n" + + "PARTITION BY HASH (PROD_ID)", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2)); + + assertEquals("create table MK.M_DM_BASE_CHARGE_TMP1 (\n" + + "\tPROD_ID BIGINT,\n" + + "\tITEM_ID SMALLINT,\n" + + "\tCHARGE BIGINT,\n" + + "\tPAGES BIGINT,\n" + + "\tDURATION BIGINT,\n" + + "\tBILLING_DURATION BIGINT\n" + + ")\n" + + "DATA CAPTURE NONE\n" + + "IN MK_APP_TEMP\n" + + "INDEX IN WH_INDEX1\n" + + "partition by hash (PROD_ID)", // + SQLUtils.toSQLString(stmt, JdbcConstants.DB2, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_17.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_17.java index fee907305e..e782eaf102 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_17.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_17.java @@ -24,7 +24,7 @@ public class MySqlParameterizedOutputVisitorTest_17 extends TestCase { String psql = ParameterizedOutputVisitorUtils.parameterize(sql, dbType); assertEquals("REPLACE INTO mytable (`user_id`, `c_level`, `l_level`, `t_level`, `v_level`, `tag`)\n" + "VALUES (?, ?, ?, ?, ?\n" + - "\t, ?)", psql); + "\t\t, ?)", psql); SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType); List stmtList = parser.parseStatementList(); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_exportTables.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_exportTables.java index 0bc2fb2026..d6dd5e94b2 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_exportTables.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_exportTables.java @@ -61,7 +61,33 @@ public class MySqlSelectTest_exportTables extends MysqlTest { assertEquals(1, visitor.getTables().size()); assertTrue(visitor.getTables().contains("my_table")); } - - + + public void test_1() throws Exception { + String sql = "SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;"; + + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); +// print(statementList); + + assertEquals(1, statementList.size()); + + StringBuffer out = new StringBuffer(); + SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(out, JdbcConstants.MYSQL); + visitor.setExportTables(true); + + stmt.accept(visitor); + + + assertEquals("SELECT *\n" + + "FROM table1\n" + + "\tINNER JOIN table2 ON table1.id = table2.id;", // + out.toString()); + + assertNotNull(visitor.getTables()); + assertEquals(2, visitor.getTables().size()); + assertEquals("[table1, table2]", visitor.getTables().toString()); + } }