diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLRecordDataType.java b/src/main/java/com/alibaba/druid/sql/ast/SQLRecordDataType.java new file mode 100644 index 0000000000..c451aba20f --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLRecordDataType.java @@ -0,0 +1,56 @@ +/* + * 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.sql.ast; + +import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; + +import java.util.ArrayList; +import java.util.List; + +public class SQLRecordDataType extends SQLDataTypeImpl implements SQLDataType { + private final List columns = new ArrayList(); + + public List getColumns() { + return columns; + } + + public void addColumn(SQLColumnDefinition column) { + column.setParent(this); + this.columns.add(column); + } + + public SQLRecordDataType clone() { + SQLRecordDataType x = new SQLRecordDataType(); + cloneTo(x); + + for (SQLColumnDefinition c : columns) { + SQLColumnDefinition c2 = c.clone(); + c2.setParent(x); + x.columns.add(c2); + } + + return x; + } + + protected void accept0(SQLASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, this.columns); + } + + visitor.endVisit(this); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java index c1e43ea598..9c4ecdc442 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLInsertInto.java @@ -24,12 +24,10 @@ import com.alibaba.druid.sql.ast.SQLObjectImpl; import com.alibaba.druid.sql.ast.statement.SQLInsertStatement.ValuesClause; public abstract class SQLInsertInto extends SQLObjectImpl { - - protected SQLExprTableSource tableSource; - - protected final List columns = new ArrayList(); - protected SQLSelect query; - + protected SQLExprTableSource tableSource; + protected final List columns = new ArrayList(); + protected transient String columnsString; + protected SQLSelect query; protected final List valuesList = new ArrayList(); public SQLInsertInto(){ @@ -139,4 +137,12 @@ public abstract class SQLInsertInto extends SQLObjectImpl { } valuesList.add(valueClause); } + + public String getColumnsString() { + return columnsString; + } + + public void setColumnsString(String columnsString) { + this.columnsString = columnsString; + } } 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 73c07d60a2..aaf0e85608 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 @@ -2299,8 +2299,9 @@ public class MySqlStatementParser extends SQLStatementParser { } public SQLInsertStatement parseInsert() { - MySqlInsertStatement insertStatement = new MySqlInsertStatement(); + MySqlInsertStatement stmt = new MySqlInsertStatement(); + SQLName tableName = null; if (lexer.token() == Token.INSERT) { lexer.nextToken(); @@ -2309,31 +2310,31 @@ public class MySqlStatementParser extends SQLStatementParser { long hash = lexer.hash_lower(); if (hash == FnvHash.Constants.LOW_PRIORITY) { - insertStatement.setLowPriority(true); + stmt.setLowPriority(true); lexer.nextToken(); continue; } if (hash == FnvHash.Constants.DELAYED) { - insertStatement.setDelayed(true); + stmt.setDelayed(true); lexer.nextToken(); continue; } if (hash == FnvHash.Constants.HIGH_PRIORITY) { - insertStatement.setHighPriority(true); + stmt.setHighPriority(true); lexer.nextToken(); continue; } if (hash == FnvHash.Constants.IGNORE) { - insertStatement.setIgnore(true); + stmt.setIgnore(true); lexer.nextToken(); continue; } if (hash == FnvHash.Constants.ROLLBACK_ON_FAIL) { - insertStatement.setRollbackOnFail(true); + stmt.setRollbackOnFail(true); lexer.nextToken(); continue; } @@ -2347,18 +2348,18 @@ public class MySqlStatementParser extends SQLStatementParser { lexer.nextToken(); } - SQLName tableName = this.exprParser.name(); - insertStatement.setTableName(tableName); + tableName = this.exprParser.name(); + stmt.setTableName(tableName); if (lexer.token() == Token.HINT) { String comment = "/*" + lexer.stringVal() + "*/"; lexer.nextToken(); - insertStatement.getTableSource().addAfterComment(comment); + stmt.getTableSource().addAfterComment(comment); } if (lexer.token() == Token.IDENTIFIER && !lexer.identifierEquals(FnvHash.Constants.VALUE)) { - insertStatement.setAlias(lexer.stringVal()); + stmt.setAlias(lexer.stringVal()); lexer.nextToken(); } @@ -2366,69 +2367,109 @@ public class MySqlStatementParser extends SQLStatementParser { 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 { - List columns = insertStatement.getColumns(); - //this.exprParser.exprList(insertStatement.getColumns(), insertStatement); + boolean useInsertColumnsCache = lexer.isEnabled(SQLParserFeature.UseInsertColumnsCache); + InsertColumnsCache insertColumnsCache = null; - if (lexer.token() != Token.RPAREN) { - for (; ; ) { - String identName; - long hash; + InsertColumnsCache.Entry cachedColumns = null; + if (useInsertColumnsCache) { + insertColumnsCache = this.insertColumnsCache; + if (insertColumnsCache == null) { + insertColumnsCache = InsertColumnsCache.global; + } - Token token = lexer.token(); - if (token == Token.IDENTIFIER) { - identName = lexer.stringVal(); - hash = lexer.hash_lower(); - } else if (token == Token.LITERAL_CHARS) { - identName = '\'' + lexer.stringVal() + '\''; - hash = 0; - } else { - identName = lexer.stringVal(); - hash = 0; - } - lexer.nextTokenComma(); - SQLExpr expr = new SQLIdentifierExpr(identName, hash); - while (lexer.token() == Token.DOT) { - lexer.nextToken(); - String propertyName = lexer.stringVal(); - lexer.nextToken(); - expr = new SQLPropertyExpr(expr, propertyName); - } - - expr.setParent(insertStatement); - columns.add(expr); - columnSize++; - - if (lexer.token() == Token.COMMA) { - lexer.nextTokenIdent(); - continue; - } - - break; - } - columnSize = insertStatement.getColumns().size(); + if (tableName != null) { + cachedColumns = insertColumnsCache.get(tableName.hashCode64()); } } - accept(Token.RPAREN); + + int pos = lexer.pos(); + if (cachedColumns != null + && lexer.text.startsWith(cachedColumns.columnsString, pos)) { + List columns = stmt.getColumns(); + List cachedColumns2 = cachedColumns.columns; + for (int i = 0, size = cachedColumns2.size(); i < size; i++) { + columns.add(cachedColumns2.get(i).clone()); + } + stmt.setColumnsString(cachedColumns.columnsString); + int p2 = pos + cachedColumns.columnsString.length(); + lexer.reset(p2); + lexer.nextToken(); + } else { + lexer.nextToken(); + if (lexer.token() == Token.SELECT) { + SQLSelect select = this.exprParser.createSelectParser().select(); + select.setParent(stmt); + stmt.setQuery(select); + } else { + List columns = stmt.getColumns(); + + if (lexer.token() != Token.RPAREN) { + for (; ; ) { + String identName; + long hash; + + Token token = lexer.token(); + if (token == Token.IDENTIFIER) { + identName = lexer.stringVal(); + hash = lexer.hash_lower(); + } else if (token == Token.LITERAL_CHARS) { + identName = '\'' + lexer.stringVal() + '\''; + hash = 0; + } else { + identName = lexer.stringVal(); + hash = 0; + } + lexer.nextTokenComma(); + SQLExpr expr = new SQLIdentifierExpr(identName, hash); + while (lexer.token() == Token.DOT) { + lexer.nextToken(); + String propertyName = lexer.stringVal(); + lexer.nextToken(); + expr = new SQLPropertyExpr(expr, propertyName); + } + + expr.setParent(stmt); + columns.add(expr); + columnSize++; + + if (lexer.token() == Token.COMMA) { + lexer.nextTokenIdent(); + continue; + } + + break; + } + columnSize = stmt.getColumns().size(); + + if (insertColumnsCache != null && tableName != null) { + String columnsString = lexer.subString(pos, lexer.pos() - pos); + + List clonedColumns = new ArrayList(columnSize); + for (int i = 0; i < columns.size(); i++) { + clonedColumns.add(columns.get(i).clone()); + } + + insertColumnsCache.put(tableName.hashCode64(), columnsString, clonedColumns); + stmt.setColumnsString(columnsString); + } + } + } + accept(Token.RPAREN); + } } if (lexer.token() == Token.VALUES || lexer.identifierEquals(FnvHash.Constants.VALUE)) { lexer.nextTokenLParen(); - parseValueClause(insertStatement.getValuesList(), columnSize, insertStatement); + parseValueClause(stmt.getValuesList(), columnSize, stmt); } else if (lexer.token() == Token.SET) { lexer.nextToken(); SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); - insertStatement.addValueCause(values); + stmt.addValueCause(values); for (; ; ) { SQLName name = this.exprParser.name(); - insertStatement.addColumn(name); + stmt.addColumn(name); if (lexer.token() == Token.EQ) { lexer.nextToken(); } else { @@ -2446,13 +2487,13 @@ public class MySqlStatementParser extends SQLStatementParser { } else if (lexer.token() == (Token.SELECT)) { SQLSelect select = this.exprParser.createSelectParser().select(); - select.setParent(insertStatement); - insertStatement.setQuery(select); + select.setParent(stmt); + stmt.setQuery(select); } else if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); SQLSelect select = this.exprParser.createSelectParser().select(); - select.setParent(insertStatement); - insertStatement.setQuery(select); + select.setParent(stmt); + stmt.setQuery(select); accept(Token.RPAREN); } @@ -2462,7 +2503,7 @@ public class MySqlStatementParser extends SQLStatementParser { accept(Token.KEY); accept(Token.UPDATE); - List duplicateKeyUpdate = insertStatement.getDuplicateKeyUpdate(); + List duplicateKeyUpdate = stmt.getDuplicateKeyUpdate(); for (;;) { SQLName name = this.exprParser.name(); accept(Token.EQ); @@ -2474,7 +2515,7 @@ public class MySqlStatementParser extends SQLStatementParser { } SQLBinaryOpExpr assignment = new SQLBinaryOpExpr(name, SQLBinaryOperator.Equality, value); - assignment.setParent(insertStatement); + assignment.setParent(stmt); duplicateKeyUpdate.add(assignment); if (lexer.token() == Token.COMMA) { @@ -2485,7 +2526,7 @@ public class MySqlStatementParser extends SQLStatementParser { } } - return insertStatement; + return stmt; } public MySqlSelectParser createSQLSelectParser() { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index bb233ce44e..10e6398380 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -2656,14 +2656,39 @@ public class OracleStatementParser extends SQLStatementParser { if (type) { accept(Token.IS); - acceptIdentifier("REF"); - accept(Token.CURSOR); - varItem.setDataType(new SQLDataTypeImpl("REF CURSOR")); + if (lexer.identifierEquals(FnvHash.Constants.RECORD)) { + lexer.nextToken(); + + SQLRecordDataType recordDataType = new SQLRecordDataType(); + + accept(Token.LPAREN); + for (;;) { + SQLColumnDefinition column = this.exprParser.parseColumn(); + recordDataType.addColumn(column); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + accept(Token.RPAREN); + varItem.setDataType(recordDataType); + } else { + acceptIdentifier("REF"); + accept(Token.CURSOR); + varItem.setDataType(new SQLDataTypeImpl("REF CURSOR")); + } } else { varItem.setDataType(this.exprParser.parseDataType(false)); } varItem.setParent(varDecl); + if (lexer.token() == Token.COLONEQ) { + lexer.nextToken(); + SQLExpr defaultVal = this.exprParser.expr(); + varItem.setValue(defaultVal); + } + varDecl.getItems().add(varItem); accept(Token.SEMI); diff --git a/src/main/java/com/alibaba/druid/sql/parser/InsertColumnsCache.java b/src/main/java/com/alibaba/druid/sql/parser/InsertColumnsCache.java new file mode 100644 index 0000000000..aa875809d6 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/parser/InsertColumnsCache.java @@ -0,0 +1,80 @@ +/* + * 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.sql.parser; + +import com.alibaba.druid.sql.ast.SQLExpr; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class InsertColumnsCache { + public static InsertColumnsCache global; + + static { + global = new InsertColumnsCache(8192); + } + + public ConcurrentMap cache = new ConcurrentHashMap(); + + private final Entry[] buckets; + private final int indexMask; + + public InsertColumnsCache(int tableSize){ + this.indexMask = tableSize - 1; + this.buckets = new Entry[tableSize]; + } + + public final Entry get(long hashCode64) { + final int bucket = ((int) hashCode64) & indexMask; + for (Entry entry = buckets[bucket]; entry != null; entry = entry.next) { + if (hashCode64 == entry.hashCode64) { + return entry; + } + } + + return null; + } + + public boolean put(long hashCode64, String columnsString, List columns) { + final int bucket = ((int) hashCode64) & indexMask; + + for (Entry entry = buckets[bucket]; entry != null; entry = entry.next) { + if (hashCode64 == entry.hashCode64) { + return true; + } + } + + Entry entry = new Entry(hashCode64, columnsString, columns, buckets[bucket]); + buckets[bucket] = entry; // 并发是处理时会可能导致缓存丢失,但不影响正确性 + + return false; + } + + public final static class Entry { + public final long hashCode64; + public final String columnsString; + public final List columns; + public final Entry next; + + public Entry(long hashCode64, String columnsString, List columns, Entry next) { + this.hashCode64 = hashCode64; + this.columnsString = columnsString; + this.columns = columns; + this.next = next; + } + } +} diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index 0be0d513d6..12e4c9aa32 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -43,7 +43,7 @@ public class Lexer { protected static SymbolTable symbols_l2 = new SymbolTable(512); protected int features = 0; //SQLParserFeature.of(SQLParserFeature.EnableSQLBinaryOpExprGroup); - protected final String text; + public final String text; protected int pos; protected int mark; @@ -211,6 +211,11 @@ public class Lexer { this.reset(this.savePoint); } + public void reset(int pos) { + this.pos = pos; + this.ch = charAt(pos); + } + public Lexer(String input, boolean skipComment){ this.skipComment = skipComment; 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 f59ff298c3..8bf0136228 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -1244,12 +1244,17 @@ public class SQLExprParser extends SQLParser { public SQLName name() { String identName; + long hash = 0; if (lexer.token == Token.LITERAL_ALIAS) { identName = lexer.stringVal(); lexer.nextToken(); } else if (lexer.token == Token.IDENTIFIER) { identName = lexer.stringVal(); + char c0 = identName.charAt(0); + if (c0 != '[') { + hash = lexer.hash_lower(); + } lexer.nextToken(); } else if (lexer.token == Token.LITERAL_CHARS) { identName = '\'' + lexer.stringVal() + '\''; @@ -1312,7 +1317,7 @@ public class SQLExprParser extends SQLParser { } } - SQLName name = new SQLIdentifierExpr(identName); + SQLName name = new SQLIdentifierExpr(identName, hash); name = nameRest(name); diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParserFeature.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParserFeature.java index 4a944aa0fe..fb6a15154c 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParserFeature.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParserFeature.java @@ -17,6 +17,7 @@ package com.alibaba.druid.sql.parser; public enum SQLParserFeature { KeepInsertValueClauseOriginalString, + UseInsertColumnsCache, EnableSQLBinaryOpExprGroup, OptimizedForParameterized, KeepComments, 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 eee19f2108..d8d3601cb1 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -34,6 +34,7 @@ public class SQLStatementParser extends SQLParser { protected boolean parseCompleteValues = true; protected int parseValuesSize = 3; protected SQLSelectListCache selectListCache = null; + protected InsertColumnsCache insertColumnsCache = null; public SQLStatementParser(String sql){ this(sql, null); 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 15b0bafc9c..8d3907b51e 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -643,6 +643,38 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet SQLExpr left = x.getLeft(); SQLExpr right = x.getRight(); + if (inputParameters != null + && operator != SQLBinaryOperator.Equality) { + int varIndex = -1; + if (right instanceof SQLVariantRefExpr) { + varIndex = ((SQLVariantRefExpr) right).getIndex(); + } + + Object param = null; + if (varIndex >= 0 && varIndex < inputParameters.size()) { + param = inputParameters.get(varIndex); + } + + if (param instanceof Collection) { + Collection values = (Collection) param; + + if (values.size() > 0) { + print('('); + int valIndex = 0; + for (Object value : values) { + if (valIndex++ != 0) { + print0(ucase ? " OR " : " or "); + } + printExpr(left); + printOperator(operator); + printParameter(value); + } + print(')'); + return false; + } + } + } + if (operator.isRelational() && left instanceof SQLIntegerExpr && right instanceof SQLIntegerExpr) { @@ -4604,8 +4636,15 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet @Override public boolean visit(SQLDeclareItem x) { + SQLDataType dataType = x.getDataType(); + + if (dataType instanceof SQLRecordDataType) { + print0(ucase ? "TYPE " : "type "); + } + x.getName().accept(this); + if (x.getType() == SQLDeclareItem.Type.TABLE) { print0(ucase ? " TABLE" : " table"); int size = x.getTableElementList().size(); @@ -4628,9 +4667,14 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } else if (x.getType() == SQLDeclareItem.Type.CURSOR) { print0(ucase ? " CURSOR" : " cursor"); } else { - if (x.getDataType() != null) { - print(' '); - x.getDataType().accept(this); + + if (dataType != null) { + if (dataType instanceof SQLRecordDataType) { + print0(ucase ? " IS " : " is "); + } else { + print(' '); + } + dataType.accept(this); } if (x.getValue() != null) { if (JdbcConstants.MYSQL.equals(getDbType())) { @@ -5720,6 +5764,28 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet return false; } + @Override + public boolean visit(SQLRecordDataType x) { + print0(ucase ? "RECORD (" : "record ("); + indentCount++; + println(); + List columns = x.getColumns(); + for (int i = 0; i < columns.size(); i++) { + if (i != 0) { + println(); + } + columns.get(i).accept(this); + if (i != columns.size() - 1) { + print0(", "); + } + } + indentCount--; + println(); + print(')'); + + return false; + } + @Override public boolean visit(SQLExprStatement x) { x.getExpr().accept(this); diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java index 15f27b144c..24f3c11702 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitor.java @@ -793,4 +793,7 @@ public interface SQLASTVisitor { boolean visit(SQLDropSynonymStatement x); void endVisit(SQLDropSynonymStatement x); + + boolean visit(SQLRecordDataType x); + void endVisit(SQLRecordDataType x); } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java index cd86827c11..21c85acb96 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTVisitorAdapter.java @@ -1846,6 +1846,16 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor { } + @Override + public boolean visit(SQLRecordDataType x) { + return true; + } + + @Override + public void endVisit(SQLRecordDataType x) { + + } + public final boolean isEnabled(VisitorFeature feature) { return VisitorFeature.isEnabled(this.features, feature); } diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index c5afa8f93d..0c33b804ee 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -521,6 +521,7 @@ public final class FnvHash { long TRUNC = fnv1a_64_lower("TRUNC"); long SYSTIMESTAMP = fnv1a_64_lower("SYSTIMESTAMP"); long TYPE = fnv1a_64_lower("TYPE"); + long RECORD = fnv1a_64_lower("RECORD"); long MAP = fnv1a_64_lower("MAP"); long ONLY = fnv1a_64_lower("ONLY"); long MEMBER = fnv1a_64_lower("MEMBER"); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_restore_0.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_restore_0.java new file mode 100644 index 0000000000..0cd5041b26 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/param/MySqlParameterizedOutputVisitorTest_restore_0.java @@ -0,0 +1,31 @@ +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.ArrayList; +import java.util.List; + +/** + * Created by wenshao on 16/8/23. + */ +public class MySqlParameterizedOutputVisitorTest_restore_0 extends TestCase { + public void test_for_parameterize() throws Exception { + String sqlTemplate = "SELECT id, name, x, y, city_code FROM `geo_poi_raw_data` `geo_poi_raw_data` WHERE 1 = 1 AND `id` > ? ORDER BY `id`"; + String params = "[[1200000,1250000]]"; + params = params.replaceAll("''", "'"); + sqlTemplate = SQLUtils.formatMySql(sqlTemplate); + String table = "[\"`geo_poi_raw_data`\"]"; + String formattedSql = ParseUtil.restore(sqlTemplate, table, params); + assertEquals("SELECT id, name, x, y, city_code\n" + + "FROM `geo_poi_raw_data` `geo_poi_raw_data`\n" + + "WHERE 1 = 1\n" + + "\tAND (`id`>1200000 OR `id`>1250000)\n" + + "ORDER BY `id`", formattedSql); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest3.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest3.java new file mode 100644 index 0000000000..36021bd837 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest3.java @@ -0,0 +1,147 @@ +/* + * 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.oracle.create; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; + +import java.util.List; + +public class OracleCreatePackageTest3 extends OracleTest { + + public void test_types() throws Exception { + String sql = // + "\n" + + "CREATE OR REPLACE PACKAGE Mail_fck_bpi AS\n" + + "-- --------------------------------------------------------------------------\n" + + "-- Name : http://www.oracle-base.com/dba/miscellaneous/soap_api\n" + + "-- Author : DR Timothy S Hall\n" + + "-- Description : SOAP related functions for consuming web services.\n" + + "-- Ammedments :\n" + + "-- When Who What\n" + + "-- =========== ======== =================================================\n" + + "-- 04-OCT-2003 Tim Hall Initial Creation\n" + + "-- 23-FEB-2006 Tim Hall Parameterized the \"soap\" envelope tags.\n" + + "-- --------------------------------------------------------------------------\n" + + "TYPE t_request IS RECORD (\n" + + " method VARCHAR2(256),\n" + + " namespace VARCHAR2(256),\n" + + " body VARCHAR2(32767),\n" + + " envelope_tag VARCHAR2(30)\n" + + ");\n" + + "TYPE t_response IS RECORD\n" + + "(\n" + + " doc XMLTYPE,\n" + + " envelope_tag VARCHAR2(30)\n" + + ");\n" + + "PROCEDURE set_proxy_authentication(p_username IN VARCHAR2,\n" + + "\t\t\t\t p_password IN VARCHAR2);\n" + + "FUNCTION new_request(p_method IN VARCHAR2,\n" + + "\t\t p_namespace IN VARCHAR2,\n" + + "\t\t p_envelope_tag IN VARCHAR2 DEFAULT 'SOAP-ENV')\n" + + " RETURN t_request;\n" + + "PROCEDURE add_parameter(p_request IN OUT NOCOPY t_request,\n" + + "\t\t\tp_name IN VARCHAR2,\n" + + "\t\t\tp_type IN VARCHAR2,\n" + + "\t\t\tp_value IN VARCHAR2);\n" + + "FUNCTION invoke(p_request IN OUT NOCOPY t_request,\n" + + "\t\tp_url IN VARCHAR2,\n" + + "\t\tp_action IN VARCHAR2)\n" + + " RETURN t_response;\n" + + "FUNCTION get_return_value(p_response IN OUT NOCOPY t_response,\n" + + "\t\t\t p_name IN VARCHAR2,\n" + + "\t\t\t p_namespace IN VARCHAR2)\n" + + " RETURN VARCHAR2;\n" + + "END Mail_fck_bpi;"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + assertEquals(1, statementList.size()); + + assertEquals("CREATE OR REPLACE PACKAGE Mail_fck_bpi\n" + + "\tTYPE t_request IS RECORD (\n" + + "\t\tmethod VARCHAR2(256), \n" + + "\t\tnamespace VARCHAR2(256), \n" + + "\t\tbody VARCHAR2(32767), \n" + + "\t\tenvelope_tag VARCHAR2(30)\n" + + "\t);\n" + + "\tTYPE t_response IS RECORD (\n" + + "\t\tdoc XMLTYPE, \n" + + "\t\tenvelope_tag VARCHAR2(30)\n" + + "\t);\n" + + "\tPROCEDURE set_proxy_authentication (\n" + + "\t\tp_username IN VARCHAR2, \n" + + "\t\tp_password IN VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tFUNCTION new_request (\n" + + "\t\tp_method IN VARCHAR2, \n" + + "\t\tp_namespace IN VARCHAR2, \n" + + "\t\tp_envelope_tag IN VARCHAR2 := 'SOAP-ENV'\n" + + "\t)\n" + + "\tRETURN t_request\n" + + "\t\n" + + "\tPROCEDURE add_parameter (\n" + + "\t\tp_request IN OUT NOCOPY t_request, \n" + + "\t\tp_name IN VARCHAR2, \n" + + "\t\tp_type IN VARCHAR2, \n" + + "\t\tp_value IN VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tFUNCTION invoke (\n" + + "\t\tp_request IN OUT NOCOPY t_request, \n" + + "\t\tp_url IN VARCHAR2, \n" + + "\t\tp_action IN VARCHAR2\n" + + "\t)\n" + + "\tRETURN t_response\n" + + "\t\n" + + "\tFUNCTION get_return_value (\n" + + "\t\tp_response IN OUT NOCOPY t_response, \n" + + "\t\tp_name IN VARCHAR2, \n" + + "\t\tp_namespace IN VARCHAR2\n" + + "\t)\n" + + "\tRETURN VARCHAR2\n" + + "\t\n" + + "END Mail_fck_bpi;",// + SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + +// Assert.assertEquals(2, visitor.getTables().size()); +// +// Assert.assertEquals(5, visitor.getColumns().size()); +// +// Assert.assertTrue(visitor.containsColumn("employees", "employee_id")); +// Assert.assertTrue(visitor.containsColumn("employees", "*")); +// Assert.assertTrue(visitor.containsColumn("departments", "department_id")); +// Assert.assertTrue(visitor.containsColumn("employees", "salary")); +// Assert.assertTrue(visitor.containsColumn("employees", "commission_pct")); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest4.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest4.java new file mode 100644 index 0000000000..7f971393f2 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreatePackageTest4.java @@ -0,0 +1,143 @@ +/* + * 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 XONDITIONS 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.oracle.create; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; + +import java.util.List; + +public class OracleCreatePackageTest4 extends OracleTest { + + public void test_types() throws Exception { + String sql = // + "CREATE OR REPLACE PACKAGE XON_CALC_RIGHT_PRICE_PKG IS\n" + + " RIGHT_PRICE_TYPE varchar2(1) := '0'; -- 鏉冮檺浠风被鍨嬶細1銆佸浗闄呮潈闄愪环锛�2銆佸浗鍐呮潈闄愪环\n" + + " /*鍒濆\uE750鍖栨潈闄愪环璁$畻鐩稿叧鏁版嵁*/\n" + + " PROCEDURE INIT_CALC_RIGHT_PRICE(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER,\n" + + " OUT_STATUS OUT VARCHAR2);\n" + + " /*鏇存柊璁惧\uE62C鏉冮檺浠风郴鏁�*/\n" + + " PROCEDURE UPDATE_DEV_RIGHT_PRICE_FACTOR(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER,\n" + + " OUT_STATUS OUT VARCHAR2);\n" + + " /*鍖归厤鐗规畩鍝佺殑鏉冮檺浠风郴鏁�*/\n" + + " PROCEDURE UPDATE_DEV_SPEC_FACTOR(IN_XON_HEADER_ID NUMBER, --閰嶇疆鍚堝悓ID\n" + + " IN_CALC_ID NUMBER, --璁$畻ID\n" + + " OUT_STATUS OUT VARCHAR2);\n" + + " /*鏇存柊鍏朵粬绫诲瀷鎴愭湰鍙婃潈闄愪环绯绘暟*/\n" + + " PROCEDURE UPDATE_OTH_RIGHT_PRICE_INFO(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER,\n" + + " OUT_STATUS OUT VARCHAR2);\n" + + " /*鏍规嵁鏉冮檺浠风郴鏁帮紝璁$畻鏉冮檺浠�*/\n" + + " PROCEDURE CALC_RIGHT_PRICE_BY_FACTOR(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER,\n" + + " OUT_STATUS OUT VARCHAR2);\n" + + " /*\\*姹囨�绘眰寰楀晢鍔$郴鏁�*\\\n" + + " PROCEDURE UPDATE_BUSINESS_FACTOR(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER,\n" + + " OUT_STATUS OUT VARCHAR2);*/\n" + + " /*鏍规嵁鏉冮檺浠疯\uE178绠楃殑鍩虹\uE505淇℃伅锛屾眹鎬昏\uE178绠楁潈闄愪环*/\n" + + " PROCEDURE CALC_RIGHT_PRICE(IN_XON_HEADER_ID NUMBER, IN_CALC_ID NUMBER);\n" + + " /*鏍规嵁鍥藉唴鏉冮檺浠疯\uE178绠楃殑鍩虹\uE505淇℃伅锛屾眹鎬昏\uE178绠楁潈闄愪环*/\n" + + " PROCEDURE DOM_CALC_RIGHT_PRICE(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER);\n" + + " /*鏉冮檺浠疯\uE178绠楃殑鎬荤殑璋冨害杩囩▼*/\n" + + " PROCEDURE CALC_RIGHT_PRICE_MAIN(IN_XON_HEADER_ID NUMBER,\n" + + " IN_CALC_ID NUMBER);\n" + + "END XON_CALC_RIGHT_PRICE_PKG;"; + + OracleStatementParser parser = new OracleStatementParser(sql); + List statementList = parser.parseStatementList(); + SQLStatement stmt = statementList.get(0); + print(statementList); + + assertEquals(1, statementList.size()); + + assertEquals("CREATE OR REPLACE PACKAGE XON_CALC_RIGHT_PRICE_PKG\n" + + "\tRIGHT_PRICE_TYPE varchar2(1) = '0';\n" + + "\tPROCEDURE INIT_CALC_RIGHT_PRICE (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER, \n" + + "\t\tOUT_STATUS OUT VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE UPDATE_DEV_RIGHT_PRICE_FACTOR (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER, \n" + + "\t\tOUT_STATUS OUT VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE UPDATE_DEV_SPEC_FACTOR (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER, \n" + + "\t\tOUT_STATUS OUT VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE UPDATE_OTH_RIGHT_PRICE_INFO (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER, \n" + + "\t\tOUT_STATUS OUT VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE CALC_RIGHT_PRICE_BY_FACTOR (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER, \n" + + "\t\tOUT_STATUS OUT VARCHAR2\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE CALC_RIGHT_PRICE (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE DOM_CALC_RIGHT_PRICE (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER\n" + + "\t)\n" + + "\t;\n" + + "\tPROCEDURE CALC_RIGHT_PRICE_MAIN (\n" + + "\t\tIN_XON_HEADER_ID NUMBER, \n" + + "\t\tIN_CALC_ID NUMBER\n" + + "\t)\n" + + "\t;\n" + + "END XON_CALC_RIGHT_PRICE_PKG;",// + SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + +// Assert.assertEquals(2, visitor.getTables().size()); +// +// Assert.assertEquals(5, visitor.getColumns().size()); +// +// Assert.assertTrue(visitor.containsColumn("employees", "employee_id")); +// Assert.assertTrue(visitor.containsColumn("employees", "*")); +// Assert.assertTrue(visitor.containsColumn("departments", "department_id")); +// Assert.assertTrue(visitor.containsColumn("employees", "salary")); +// Assert.assertTrue(visitor.containsColumn("employees", "commission_pct")); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_3.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_3.java index 7f1b4385c8..cc42697d0c 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_3.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/pl/Oracle_pl_3.java @@ -87,8 +87,8 @@ public class Oracle_pl_3 extends OracleTest { String output = SQLUtils.toOracleString(stmt); assertEquals("CREATE OR REPLACE TYPE type_body_elements AS OBJECT (\n" + "\tsome_string varchar2(64), \n" + - "\tFUNCTION function_one () RETURN varchar2, \n" + - "\tFUNCTION function_two () RETURN varchar2\n" + + "\tMEMBER FUNCTION function_one () RETURN varchar2, \n" + + "\tMEMBER FUNCTION function_two () RETURN varchar2\n" + ");", // output); } @@ -96,8 +96,8 @@ public class Oracle_pl_3 extends OracleTest { String output = SQLUtils.toOracleString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); assertEquals("create or replace type type_body_elements AS OBJECT (\n" + "\tsome_string varchar2(64), \n" + - "\tfunction function_one () return varchar2, \n" + - "\tfunction function_two () return varchar2\n" + + "\tmember function function_one () return varchar2, \n" + + "\tmember function function_two () return varchar2\n" + ");", // output); } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerCreateTableTest_1.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerCreateTableTest_1.java index c21a0fa0c4..ab1351fd78 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerCreateTableTest_1.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerCreateTableTest_1.java @@ -53,7 +53,7 @@ public class SQLServerCreateTableTest_1 extends TestCase { SQLServerSchemaStatVisitor visitor = new SQLServerSchemaStatVisitor(); stmt.accept(visitor); -// System.out.println("Tables : " + visitor.getTables()); + System.out.println("Tables : " + visitor.getTables()); // System.out.println("fields : " + visitor.getColumns()); // System.out.println("coditions : " + visitor.getConditions()); // System.out.println("orderBy : " + visitor.getOrderByColumns());