diff --git a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java index b5fa0f6e7f..7d5c243ec7 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java +++ b/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -90,7 +90,22 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { } public void incrementExecuteCount() { - this.getPoolableConnection().getConnectionHolder().getDataSource().incrementExecuteCount(); + DruidPooledConnection conn = this.getPoolableConnection(); + + if (conn == null) { + return; + } + + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder == null) { + return; + } + + if (holder.getDataSource() == null) { + return; + } + + holder.getDataSource().incrementExecuteCount(); } protected void transactionRecord(String sql) throws SQLException { @@ -259,7 +274,10 @@ public class DruidPooledStatement extends PoolableWrapper implements Statement { clearResultSet(); stmt.close(); this.closed = true; - conn.getConnectionHolder().removeTrace(this); + + if (conn.getConnectionHolder() != null) { + conn.getConnectionHolder().removeTrace(this); + } } } diff --git a/src/main/java/com/alibaba/druid/sharding/ShardingDataSource.java b/src/main/java/com/alibaba/druid/sharding/ShardingDataSource.java new file mode 100644 index 0000000000..0efeec00d9 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/ShardingDataSource.java @@ -0,0 +1,20 @@ +/* + * 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.sharding; + +public class ShardingDataSource { + +} diff --git a/src/main/java/com/alibaba/druid/sharding/ShardingRuntimeException.java b/src/main/java/com/alibaba/druid/sharding/ShardingRuntimeException.java new file mode 100644 index 0000000000..eeb3998549 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/ShardingRuntimeException.java @@ -0,0 +1,40 @@ +/* + * 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.sharding; + +import com.alibaba.druid.DruidRuntimeException; + + +public class ShardingRuntimeException extends DruidRuntimeException { + + private static final long serialVersionUID = 1L; + + public ShardingRuntimeException(){ + super(); + } + + public ShardingRuntimeException(String message, Throwable cause){ + super(message, cause); + } + + public ShardingRuntimeException(String message){ + super(message); + } + + public ShardingRuntimeException(Throwable cause){ + super(cause); + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/LogicTable.java b/src/main/java/com/alibaba/druid/sharding/config/LogicTable.java new file mode 100644 index 0000000000..3b56d26ce7 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/LogicTable.java @@ -0,0 +1,49 @@ +/* + * 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.sharding.config; + +import java.util.HashMap; +import java.util.Map; + +public class LogicTable { + + private String logicTable; + private Map partitions = new HashMap(); + + public String getLogicTable() { + return logicTable; + } + + public void setLogicTable(String logicTable) { + this.logicTable = logicTable; + } + + public Map getPartitions() { + return partitions; + } + + public void setPartitions(Map partitions) { + this.partitions = partitions; + } + + public void addPartition(String partitionName, String physicalTableName) { + this.partitions.put(partitionName, new TablePartition(partitionName, physicalTableName)); + } + + public void addPartition(String partitionName, String database, String physicalTableName) { + this.partitions.put(partitionName, new TablePartition(partitionName, database, physicalTableName)); + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/MappingRule.java b/src/main/java/com/alibaba/druid/sharding/config/MappingRule.java new file mode 100644 index 0000000000..4feeac4030 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/MappingRule.java @@ -0,0 +1,29 @@ +/* + * 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.sharding.config; + +import java.util.List; + +public interface MappingRule { + + String getTable(); + + String getColumn(); + + List getEntries(); + + String getPartition(Object value); +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/MappingRuleAdapter.java b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleAdapter.java new file mode 100644 index 0000000000..9e17ce24f3 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleAdapter.java @@ -0,0 +1,71 @@ +/* + * 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.sharding.config; + +import java.util.ArrayList; +import java.util.List; + +public class MappingRuleAdapter implements MappingRule { + + private String defaultPartition; + + private String table; + private String column; + + private List entries = new ArrayList(); + + public List getEntries() { + return entries; + } + + public void setEntries(List entries) { + this.entries = entries; + } + + public String getPartition(Object value) { + for (MappingRuleEntry entry : this.entries) { + if (entry.match(value)) { + return entry.getPartition(); + } + } + + return this.getDefaultPartition(); + } + + public String getTable() { + return table; + } + + public void setTable(String table) { + this.table = table; + } + + public String getColumn() { + return column; + } + + public void setColumn(String column) { + this.column = column; + } + + public String getDefaultPartition() { + return defaultPartition; + } + + public void setDefaultPartition(String defaultPartition) { + this.defaultPartition = defaultPartition; + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/MappingRuleEntry.java b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleEntry.java new file mode 100644 index 0000000000..e2d530bc50 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleEntry.java @@ -0,0 +1,23 @@ +/* + * 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.sharding.config; + +public interface MappingRuleEntry { + + boolean match(Object value); + + String getPartition(); +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/MappingRuleListEntry.java b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleListEntry.java new file mode 100644 index 0000000000..780be48d3a --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleListEntry.java @@ -0,0 +1,64 @@ +/* + * 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.sharding.config; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; + + +public class MappingRuleListEntry implements MappingRuleEntry { + private List values = new ArrayList(); + private String partition; + + public MappingRuleListEntry() { + + } + + public MappingRuleListEntry(String partition, Object...values) { + this.partition = partition; + for (Object value : values) { + this.values.add(value); + } + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } + + public String getPartition() { + return partition; + } + + public void setPartition(String partition) { + this.partition = partition; + } + + @Override + public boolean match(Object value) { + for (Object item : this.values) { + if (SQLEvalVisitorUtils.eq(item, value)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/MappingRuleLtEntry.java b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleLtEntry.java new file mode 100644 index 0000000000..4f9dd4f8ca --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/MappingRuleLtEntry.java @@ -0,0 +1,45 @@ +/* + * 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.sharding.config; + +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; + +public class MappingRuleLtEntry implements MappingRuleEntry { + + private Object value; + private String partition; + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getPartition() { + return partition; + } + + public void setPartition(String partition) { + this.partition = partition; + } + + @Override + public boolean match(Object value) { + return SQLEvalVisitorUtils.eq(this.value, value); + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/RouteConfig.java b/src/main/java/com/alibaba/druid/sharding/config/RouteConfig.java new file mode 100644 index 0000000000..e8f34d3851 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/RouteConfig.java @@ -0,0 +1,76 @@ +/* + * 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.sharding.config; + +import java.util.HashMap; +import java.util.Map; + +public class RouteConfig { + + private Map logicTables = new HashMap(); + private Map mappingRules = new HashMap(); + + public Map getLogicTables() { + return logicTables; + } + + public void setLogicTables(Map logicTables) { + this.logicTables = logicTables; + } + + public Map getMappingRules() { + return mappingRules; + } + + public void setMappingRules(Map mappingRules) { + this.mappingRules = mappingRules; + } + + public MappingRule getMappingRule(String table) { + if (table == null) { + return null; + } + + String lowerTable = table.toLowerCase(); + + return mappingRules.get(lowerTable); + } + + public TablePartition getPartition(String table, String partitionName) { + if (table == null) { + throw new IllegalArgumentException("table is null"); + } + if (partitionName == null) { + throw new IllegalArgumentException("partitionName is null"); + } + + String lowerTable = table.toLowerCase(); + LogicTable config = logicTables.get(lowerTable); + if (config == null) { + throw new IllegalArgumentException("table's partition config not exists, tableName : " + table); + } + + String lowerPartitionName = partitionName.toLowerCase(); + TablePartition tablePartition = config.getPartitions().get(lowerPartitionName); + + if (tablePartition == null) { + throw new IllegalArgumentException("table's partition config not exists, tableName : " + table + + ", partition : " + lowerPartitionName); + } + + return tablePartition; + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/config/TablePartition.java b/src/main/java/com/alibaba/druid/sharding/config/TablePartition.java new file mode 100644 index 0000000000..2a64f0110d --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/config/TablePartition.java @@ -0,0 +1,62 @@ +/* + * 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.sharding.config; + +public class TablePartition { + + private String name; + private String database; + private String table; + + public TablePartition(){ + } + + public TablePartition(String name, String table){ + this.name = name; + this.table = table; + } + + public TablePartition(String name, String database, String table){ + this.name = name; + this.database = database; + this.table = table; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTable() { + return table; + } + + public void setTable(String table) { + this.table = table; + } + +} diff --git a/src/main/java/com/alibaba/druid/sharding/sql/MySqlShardingVisitor.java b/src/main/java/com/alibaba/druid/sharding/sql/MySqlShardingVisitor.java new file mode 100644 index 0000000000..d336c1e876 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/sql/MySqlShardingVisitor.java @@ -0,0 +1,111 @@ +/* + * 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.sharding.sql; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.alibaba.druid.sharding.ShardingRuntimeException; +import com.alibaba.druid.sharding.config.MappingRule; +import com.alibaba.druid.sharding.config.RouteConfig; +import com.alibaba.druid.sharding.config.TablePartition; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter; +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; + +public class MySqlShardingVisitor extends MySqlASTVisitorAdapter implements ShardingVisitor { + + private final RouteConfig routeConfig; + + private final List parameters; + + private List result = new ArrayList(2); + + public MySqlShardingVisitor(RouteConfig routeConfig, List parameters){ + this.routeConfig = routeConfig; + this.parameters = parameters; + } + + public MySqlShardingVisitor(RouteConfig routeConfig, Object... parameters){ + this(routeConfig, Arrays.asList(parameters)); + } + + public RouteConfig getRouteConfig() { + return routeConfig; + } + + public List getParameters() { + return parameters; + } + + public boolean visit(MySqlInsertStatement x) { + String table = x.getTableName().getSimleName(); + + MappingRule mappingRule = routeConfig.getMappingRule(table); + + if (mappingRule == null) { + result.add(x); + return false; + } + + if (x.getValues() == null) { + throw new ShardingRuntimeException("sharding rule violation, insert's values clause is null"); + } + + String column = mappingRule.getColumn(); + + int columnIndex = -1; + for (int i = 0; i < x.getColumns().size(); ++i) { + SQLExpr columnExpr = x.getColumns().get(i); + if (columnExpr instanceof SQLIdentifierExpr) { + String columnName = ((SQLIdentifierExpr) columnExpr).getName(); + if (column.equalsIgnoreCase(columnName)) { + columnIndex = i; + break; + } + } + } + + if (columnIndex == -1) { + throw new ShardingRuntimeException("sharding rule violation, columns not set : " + column); + } + + SQLExpr valueExpr = x.getValues().getValues().get(columnIndex); + Object value = SQLEvalVisitorUtils.eval(null, valueExpr, parameters); + + String partition = mappingRule.getPartition(value); + + if (partition == null) { + throw new ShardingRuntimeException("sharding rule violation, partition not match, value : " + value); + } + + TablePartition tablePartition = routeConfig.getPartition(table, partition); + + x.setTableName(new SQLIdentifierExpr(tablePartition.getTable())); + + if (tablePartition.getDatabase() != null) { + x.putAttribute(ATTR_DB, tablePartition.getDatabase()); + } + + result.add(x); + + return false; + } +} diff --git a/src/main/java/com/alibaba/druid/sharding/sql/ShardingVisitor.java b/src/main/java/com/alibaba/druid/sharding/sql/ShardingVisitor.java new file mode 100644 index 0000000000..a72fdabb09 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sharding/sql/ShardingVisitor.java @@ -0,0 +1,5 @@ +package com.alibaba.druid.sharding.sql; + +public interface ShardingVisitor { + public static final String ATTR_DB = "sharding.db"; +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java index afc498c4e1..b29aa7d642 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/parser/SQLServerStatementParser.java @@ -15,9 +15,16 @@ */ package com.alibaba.druid.sql.dialect.sqlserver.parser; +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertInto; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.SQLSelectParser; import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.parser.Token; public class SQLServerStatementParser extends SQLStatementParser { @@ -32,5 +39,65 @@ public class SQLServerStatementParser extends SQLStatementParser { public SQLServerStatementParser(Lexer lexer){ super(new SQLServerExprParser(lexer)); } + + public SQLStatement parseInsert() { + SQLServerInsertStatement insertStatement = new SQLServerInsertStatement(); + if (lexer.token() == Token.INSERT) { + accept(Token.INSERT); + } + + parseInsert0(insertStatement); + return insertStatement; + } + + protected void parseInsert0(SQLInsertInto insert, boolean acceptSubQuery) { + SQLServerInsertStatement insertStatement = (SQLServerInsertStatement) insert; + + if (lexer.token() == Token.INTO) { + lexer.nextToken(); + + SQLName tableName = this.exprParser.name(); + insertStatement.setTableName(tableName); + + if (lexer.token() == Token.LITERAL_ALIAS) { + insertStatement.setAlias(as()); + } + + parseInsert0_hinits(insertStatement); + + if (lexer.token() == Token.IDENTIFIER) { + insertStatement.setAlias(lexer.stringVal()); + lexer.nextToken(); + } + } + + if (lexer.token() == (Token.LPAREN)) { + lexer.nextToken(); + this.exprParser.exprList(insertStatement.getColumns()); + accept(Token.RPAREN); + } + + if (lexer.token() == Token.VALUES) { + lexer.nextToken(); + + for (;;) { + accept(Token.LPAREN); + SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause(); + this.exprParser.exprList(values.getValues()); + insertStatement.getValuesList().add(values); + accept(Token.RPAREN); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } else { + break; + } + } + } else if (acceptSubQuery && (lexer.token() == Token.SELECT || lexer.token() == Token.LPAREN)) { + SQLQueryExpr queryExpr = (SQLQueryExpr) this.exprParser.expr(); + insertStatement.setQuery(queryExpr.getSubQuery()); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java index 460b137729..5f6bf013ad 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/visitor/SQLServerOutputVisitor.java @@ -103,7 +103,8 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe if (x.getColumns().size() > 0) { incrementIndent(); - print(" ("); + println(); + print("("); for (int i = 0, size = x.getColumns().size(); i < size; ++i) { if (i != 0) { if (i % 5 == 0) { @@ -120,14 +121,15 @@ public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLSe if (x.getValuesList().size() != 0) { println(); - print("VALUES "); + print("VALUES"); + println(); for (int i = 0, size = x.getValuesList().size(); i < size; ++i) { if (i != 0) { - print(", "); + print(","); + println(); } x.getValuesList().get(i).accept(this); } - } if (x.getQuery() != null) { diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java index 4b08cbf15a..6e50fb87e0 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLEvalVisitorUtils.java @@ -1305,6 +1305,10 @@ public class SQLEvalVisitorUtils { return d1.equals(d2); } + + if (a instanceof String || b instanceof String) { + return _string(a).equals(_string(b)); + } throw new IllegalArgumentException(); } diff --git a/src/main/java/com/alibaba/druid/util/TransferQueue.java b/src/main/java/com/alibaba/druid/util/TransferQueue.java deleted file mode 100644 index f6a7df121a..0000000000 --- a/src/main/java/com/alibaba/druid/util/TransferQueue.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.util; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.TimeUnit; - - -/** - * A {@link BlockingQueue} in which producers may wait for consumers - * to receive elements. A {@code TransferQueue} may be useful for - * example in message passing applications in which producers - * sometimes (using method {@link #transfer}) await receipt of - * elements by consumers invoking {@code take} or {@code poll}, while - * at other times enqueue elements (via method {@code put}) without - * waiting for receipt. - * {@linkplain #tryTransfer(Object) Non-blocking} and - * {@linkplain #tryTransfer(Object,long,TimeUnit) time-out} versions of - * {@code tryTransfer} are also available. - * A {@code TransferQueue} may also be queried, via {@link - * #hasWaitingConsumer}, whether there are any threads waiting for - * items, which is a converse analogy to a {@code peek} operation. - * - *

Like other blocking queues, a {@code TransferQueue} may be - * capacity bounded. If so, an attempted transfer operation may - * initially block waiting for available space, and/or subsequently - * block waiting for reception by a consumer. Note that in a queue - * with zero capacity, such as {@link SynchronousQueue}, {@code put} - * and {@code transfer} are effectively synonymous. - * - *

This interface is a member of the - * - * Java Collections Framework. - * - * @since 1.7 - * @author Doug Lea - * @param the type of elements held in this collection - */ -public interface TransferQueue extends BlockingQueue { - /** - * Transfers the element to a waiting consumer immediately, if possible. - * - *

More precisely, transfers the specified element immediately - * if there exists a consumer already waiting to receive it (in - * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), - * otherwise returning {@code false} without enqueuing the element. - * - * @param e the element to transfer - * @return {@code true} if the element was transferred, else - * {@code false} - * @throws ClassCastException if the class of the specified element - * prevents it from being added to this queue - * @throws NullPointerException if the specified element is null - * @throws IllegalArgumentException if some property of the specified - * element prevents it from being added to this queue - */ - boolean tryTransfer(E e); - - /** - * Transfers the element to a consumer, waiting if necessary to do so. - * - *

More precisely, transfers the specified element immediately - * if there exists a consumer already waiting to receive it (in - * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), - * else waits until the element is received by a consumer. - * - * @param e the element to transfer - * @throws InterruptedException if interrupted while waiting, - * in which case the element is not left enqueued - * @throws ClassCastException if the class of the specified element - * prevents it from being added to this queue - * @throws NullPointerException if the specified element is null - * @throws IllegalArgumentException if some property of the specified - * element prevents it from being added to this queue - */ - void transfer(E e) throws InterruptedException; - - /** - * Transfers the element to a consumer if it is possible to do so - * before the timeout elapses. - * - *

More precisely, transfers the specified element immediately - * if there exists a consumer already waiting to receive it (in - * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), - * else waits until the element is received by a consumer, - * returning {@code false} if the specified wait time elapses - * before the element can be transferred. - * - * @param e the element to transfer - * @param timeout how long to wait before giving up, in units of - * {@code unit} - * @param unit a {@code TimeUnit} determining how to interpret the - * {@code timeout} parameter - * @return {@code true} if successful, or {@code false} if - * the specified waiting time elapses before completion, - * in which case the element is not left enqueued - * @throws InterruptedException if interrupted while waiting, - * in which case the element is not left enqueued - * @throws ClassCastException if the class of the specified element - * prevents it from being added to this queue - * @throws NullPointerException if the specified element is null - * @throws IllegalArgumentException if some property of the specified - * element prevents it from being added to this queue - */ - boolean tryTransfer(E e, long timeout, TimeUnit unit) - throws InterruptedException; - - /** - * Returns {@code true} if there is at least one consumer waiting - * to receive an element via {@link #take} or - * timed {@link #poll(long,TimeUnit) poll}. - * The return value represents a momentary state of affairs. - * - * @return {@code true} if there is at least one waiting consumer - */ - boolean hasWaitingConsumer(); - - /** - * Returns an estimate of the number of consumers waiting to - * receive elements via {@link #take} or timed - * {@link #poll(long,TimeUnit) poll}. The return value is an - * approximation of a momentary state of affairs, that may be - * inaccurate if consumers have completed or given up waiting. - * The value may be useful for monitoring and heuristics, but - * not for synchronization control. Implementations of this - * method are likely to be noticeably slower than those for - * {@link #hasWaitingConsumer}. - * - * @return the number of consumers waiting to receive elements - */ - int getWaitingConsumerCount(); -} diff --git a/src/test/java/com/alibaba/druid/bvt/sharding/InsertTest.java b/src/test/java/com/alibaba/druid/bvt/sharding/InsertTest.java new file mode 100644 index 0000000000..9c7728de3d --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sharding/InsertTest.java @@ -0,0 +1,76 @@ +package com.alibaba.druid.bvt.sharding; + +import java.util.List; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sharding.config.LogicTable; +import com.alibaba.druid.sharding.config.MappingRule; +import com.alibaba.druid.sharding.config.MappingRuleAdapter; +import com.alibaba.druid.sharding.config.MappingRuleListEntry; +import com.alibaba.druid.sharding.config.RouteConfig; +import com.alibaba.druid.sharding.sql.MySqlShardingVisitor; +import com.alibaba.druid.sharding.sql.ShardingVisitor; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.alibaba.druid.sql.parser.SQLStatementParser; + +public class InsertTest extends TestCase { + + private RouteConfig routeConfig = new RouteConfig(); + + protected void setUp() throws Exception { + MappingRuleAdapter rule = new MappingRuleAdapter(); + + rule.setColumn("gender"); + rule.getEntries().add(new MappingRuleListEntry("person_m", "M")); + rule.getEntries().add(new MappingRuleListEntry("person_f", "F")); + + routeConfig.getMappingRules().put("person", rule); + + LogicTable logicTable = new LogicTable(); + logicTable.setLogicTable("person"); + + logicTable.addPartition("person_m", "db0", "person_001"); + logicTable.addPartition("person_f", "db1", "person_002"); + + routeConfig.getLogicTables().put("person", logicTable); + } + + public void test_insert_m() throws Exception { + String sql = "insert into person (name, gender) values ('a', 'M')"; + + SQLStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + Assert.assertEquals(1, stmtList.size()); + + SQLStatement stmt = stmtList.get(0); + MySqlShardingVisitor visitor = new MySqlShardingVisitor(routeConfig); + stmt.accept(visitor); + + String result = SQLUtils.toMySqlString(stmt); + + Assert.assertEquals("INSERT INTO person_001 (name, gender)" + // + "\nVALUES ('a', 'M')", result); + Assert.assertEquals("db0", stmt.getAttribute(ShardingVisitor.ATTR_DB)); + } + + public void test_insert_f() throws Exception { + String sql = "insert into person (name, gender) values ('a', 'F')"; + + SQLStatementParser parser = new MySqlStatementParser(sql); + List stmtList = parser.parseStatementList(); + Assert.assertEquals(1, stmtList.size()); + + SQLStatement stmt = stmtList.get(0); + MySqlShardingVisitor visitor = new MySqlShardingVisitor(routeConfig); + stmt.accept(visitor); + + String result = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("INSERT INTO person_002 (name, gender)" + // + "\nVALUES ('a', 'F')", result); + Assert.assertEquals("db1", stmt.getAttribute(ShardingVisitor.ATTR_DB)); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest2.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest2.java index 1b21ec901c..620f413bfe 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest2.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest2.java @@ -24,13 +24,16 @@ import com.alibaba.druid.sql.test.TestUtils; public class SQLServerInsertTest2 extends TestCase { - public void test_isEmpty() throws Exception { - String sql = "INSERT INTO Production.UnitMeasure " + // - "VALUES (N'F2', N'Square Feet', GETDATE());;"; + public void test_multi_values() throws Exception { + String sql = "INSERT INTO test (name)" + // + "VALUES (N'a'), (N'b'), (N'c')"; - String expect = "INSERT INTO Production.UnitMeasure" + // + String expect = "INSERT INTO test" + // + "\n\t(name)" + // "\nVALUES" + // - "\n(N'F2', N'Square Feet', GETDATE())"; + "\n(N'a')," + // + "\n(N'b')," + // + "\n(N'c')"; SQLServerStatementParser parser = new SQLServerStatementParser(sql); SQLStatement stmt = parser.parseStatementList().get(0); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest3.java b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest3.java new file mode 100644 index 0000000000..06b22bbbab --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/sqlserver/SQLServerInsertTest3.java @@ -0,0 +1,44 @@ +/* + * 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.sqlserver; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.sqlserver.parser.SQLServerStatementParser; +import com.alibaba.druid.sql.test.TestUtils; + +public class SQLServerInsertTest3 extends TestCase { + + public void test_isEmpty() throws Exception { + String sql = "INSERT INTO Production.UnitMeasure " + // + "VALUES (N'F2', N'Square Feet', GETDATE());;"; + + String expect = "INSERT INTO Production.UnitMeasure" + // + "\nVALUES" + // + "\n(N'F2', N'Square Feet', GETDATE())"; + + SQLServerStatementParser parser = new SQLServerStatementParser(sql); + SQLStatement stmt = parser.parseStatementList().get(0); + + String text = TestUtils.outputSqlServer(stmt); + + Assert.assertEquals(expect, text); + + System.out.println(text); + } +}