mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
Merge pull request #202 from wenshao/master
sharding support & bug fixed & improve sqlserver sql parser
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, TablePartition> partitions = new HashMap<String, TablePartition>();
|
||||
|
||||
public String getLogicTable() {
|
||||
return logicTable;
|
||||
}
|
||||
|
||||
public void setLogicTable(String logicTable) {
|
||||
this.logicTable = logicTable;
|
||||
}
|
||||
|
||||
public Map<String, TablePartition> getPartitions() {
|
||||
return partitions;
|
||||
}
|
||||
|
||||
public void setPartitions(Map<String, TablePartition> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<MappingRuleEntry> getEntries();
|
||||
|
||||
String getPartition(Object value);
|
||||
}
|
||||
@@ -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<MappingRuleEntry> entries = new ArrayList<MappingRuleEntry>();
|
||||
|
||||
public List<MappingRuleEntry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void setEntries(List<MappingRuleEntry> 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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<Object> values = new ArrayList<Object>();
|
||||
private String partition;
|
||||
|
||||
public MappingRuleListEntry() {
|
||||
|
||||
}
|
||||
|
||||
public MappingRuleListEntry(String partition, Object...values) {
|
||||
this.partition = partition;
|
||||
for (Object value : values) {
|
||||
this.values.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(List<Object> 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, LogicTable> logicTables = new HashMap<String, LogicTable>();
|
||||
private Map<String, MappingRule> mappingRules = new HashMap<String, MappingRule>();
|
||||
|
||||
public Map<String, LogicTable> getLogicTables() {
|
||||
return logicTables;
|
||||
}
|
||||
|
||||
public void setLogicTables(Map<String, LogicTable> logicTables) {
|
||||
this.logicTables = logicTables;
|
||||
}
|
||||
|
||||
public Map<String, MappingRule> getMappingRules() {
|
||||
return mappingRules;
|
||||
}
|
||||
|
||||
public void setMappingRules(Map<String, MappingRule> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object> parameters;
|
||||
|
||||
private List<SQLStatement> result = new ArrayList<SQLStatement>(2);
|
||||
|
||||
public MySqlShardingVisitor(RouteConfig routeConfig, List<Object> 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<Object> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.alibaba.druid.sharding.sql;
|
||||
|
||||
public interface ShardingVisitor {
|
||||
public static final String ATTR_DB = "sharding.db";
|
||||
}
|
||||
+67
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-4
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>This interface is a member of the
|
||||
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
|
||||
* Java Collections Framework</a>.
|
||||
*
|
||||
* @since 1.7
|
||||
* @author Doug Lea
|
||||
* @param <E> the type of elements held in this collection
|
||||
*/
|
||||
public interface TransferQueue<E> extends BlockingQueue<E> {
|
||||
/**
|
||||
* Transfers the element to a waiting consumer immediately, if possible.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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();
|
||||
}
|
||||
@@ -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<SQLStatement> 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<SQLStatement> 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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user