blob: ee2401b244c1be1bd5ddf36e2f1c7e612ad966ef [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.shardingsphere.sharding.rule;
import com.cedarsoftware.util.CaseInsensitiveMap;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import lombok.Getter;
import org.apache.shardingsphere.infra.algorithm.core.context.AlgorithmSQLContext;
import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
import org.apache.shardingsphere.infra.algorithm.keygen.core.KeyGenerateAlgorithm;
import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
import org.apache.shardingsphere.infra.datanode.DataNode;
import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
import org.apache.shardingsphere.infra.expr.core.InlineExpressionParserFactory;
import org.apache.shardingsphere.infra.instance.InstanceContext;
import org.apache.shardingsphere.infra.instance.InstanceContextAware;
import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
import org.apache.shardingsphere.infra.rule.scope.DatabaseRule;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingAutoTableRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableReferenceRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.audit.ShardingAuditStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ComplexShardingStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.NoneShardingStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.sharding.api.sharding.ShardingAutoTableAlgorithm;
import org.apache.shardingsphere.sharding.cache.ShardingCache;
import org.apache.shardingsphere.sharding.exception.metadata.ShardingTableRuleNotFoundException;
import org.apache.shardingsphere.sharding.rule.attribute.ShardingDataNodeRuleAttribute;
import org.apache.shardingsphere.sharding.rule.attribute.ShardingTableNamesRuleAttribute;
import org.apache.shardingsphere.sharding.rule.checker.ShardingRuleChecker;
import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
import org.apache.shardingsphere.sharding.spi.ShardingAuditAlgorithm;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BinaryOperationExpression;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.AndPredicate;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.WhereSegment;
import org.apache.shardingsphere.sql.parser.sql.common.util.ExpressionExtractUtils;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Sharding rule.
*/
@Getter
public final class ShardingRule implements DatabaseRule {
private static final String ALGORITHM_EXPRESSION_KEY = "algorithm-expression";
private final ShardingRuleConfiguration configuration;
private final Collection<String> dataSourceNames;
private final Map<String, ShardingAlgorithm> shardingAlgorithms = new CaseInsensitiveMap<>();
private final Map<String, KeyGenerateAlgorithm> keyGenerators = new CaseInsensitiveMap<>();
private final Map<String, ShardingAuditAlgorithm> auditors = new CaseInsensitiveMap<>();
private final Map<String, ShardingTable> shardingTables = new CaseInsensitiveMap<>();
private final Map<String, BindingTableRule> bindingTableRules = new CaseInsensitiveMap<>();
private final ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig;
private final ShardingStrategyConfiguration defaultTableShardingStrategyConfig;
private final ShardingAuditStrategyConfiguration defaultAuditStrategy;
private final KeyGenerateAlgorithm defaultKeyGenerateAlgorithm;
private final String defaultShardingColumn;
private final ShardingCache shardingCache;
private final RuleAttributes attributes;
private final ShardingRuleChecker shardingRuleChecker = new ShardingRuleChecker(this);
public ShardingRule(final ShardingRuleConfiguration ruleConfig, final Map<String, DataSource> dataSources, final InstanceContext instanceContext) {
configuration = ruleConfig;
this.dataSourceNames = getDataSourceNames(ruleConfig.getTables(), ruleConfig.getAutoTables(), dataSources.keySet());
ruleConfig.getShardingAlgorithms().forEach((key, value) -> shardingAlgorithms.put(key, TypedSPILoader.getService(ShardingAlgorithm.class, value.getType(), value.getProps())));
ruleConfig.getKeyGenerators().forEach((key, value) -> keyGenerators.put(key, TypedSPILoader.getService(KeyGenerateAlgorithm.class, value.getType(), value.getProps())));
ruleConfig.getAuditors().forEach((key, value) -> auditors.put(key, TypedSPILoader.getService(ShardingAuditAlgorithm.class, value.getType(), value.getProps())));
shardingTables.putAll(createShardingTables(ruleConfig.getTables(), ruleConfig.getDefaultKeyGenerateStrategy()));
shardingTables.putAll(createShardingAutoTables(ruleConfig.getAutoTables(), ruleConfig.getDefaultKeyGenerateStrategy()));
bindingTableRules.putAll(createBindingTableRules(ruleConfig.getBindingTableGroups()));
defaultDatabaseShardingStrategyConfig = createDefaultDatabaseShardingStrategyConfiguration(ruleConfig);
defaultTableShardingStrategyConfig = createDefaultTableShardingStrategyConfiguration(ruleConfig);
defaultAuditStrategy = null == ruleConfig.getDefaultAuditStrategy() ? new ShardingAuditStrategyConfiguration(Collections.emptyList(), true) : ruleConfig.getDefaultAuditStrategy();
defaultKeyGenerateAlgorithm = null == ruleConfig.getDefaultKeyGenerateStrategy()
? TypedSPILoader.getService(KeyGenerateAlgorithm.class, null)
: keyGenerators.get(ruleConfig.getDefaultKeyGenerateStrategy().getKeyGeneratorName());
defaultShardingColumn = ruleConfig.getDefaultShardingColumn();
keyGenerators.values().stream().filter(InstanceContextAware.class::isInstance).forEach(each -> ((InstanceContextAware) each).setInstanceContext(instanceContext));
if (defaultKeyGenerateAlgorithm instanceof InstanceContextAware && -1 == instanceContext.getWorkerId()) {
((InstanceContextAware) defaultKeyGenerateAlgorithm).setInstanceContext(instanceContext);
}
shardingCache = null == ruleConfig.getShardingCache() ? null : new ShardingCache(ruleConfig.getShardingCache(), this);
attributes = new RuleAttributes(new ShardingDataNodeRuleAttribute(shardingTables), new ShardingTableNamesRuleAttribute(shardingTables.values()));
shardingRuleChecker.check(ruleConfig);
}
private ShardingStrategyConfiguration createDefaultDatabaseShardingStrategyConfiguration(final ShardingRuleConfiguration ruleConfig) {
Optional.ofNullable(ruleConfig.getDefaultDatabaseShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), "default"));
return null == ruleConfig.getDefaultDatabaseShardingStrategy() ? new NoneShardingStrategyConfiguration() : ruleConfig.getDefaultDatabaseShardingStrategy();
}
private ShardingStrategyConfiguration createDefaultTableShardingStrategyConfiguration(final ShardingRuleConfiguration ruleConfig) {
Optional.ofNullable(ruleConfig.getDefaultTableShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), "default"));
return null == ruleConfig.getDefaultTableShardingStrategy() ? new NoneShardingStrategyConfiguration() : ruleConfig.getDefaultTableShardingStrategy();
}
private Collection<String> getDataSourceNames(final Collection<ShardingTableRuleConfiguration> tableRuleConfigs,
final Collection<ShardingAutoTableRuleConfiguration> autoTableRuleConfigs, final Collection<String> dataSourceNames) {
if (tableRuleConfigs.isEmpty() && autoTableRuleConfigs.isEmpty()) {
return dataSourceNames;
}
if (tableRuleConfigs.stream().map(ShardingTableRuleConfiguration::getActualDataNodes).anyMatch(each -> null == each || each.isEmpty())) {
return dataSourceNames;
}
Collection<String> result = new LinkedHashSet<>();
tableRuleConfigs.forEach(each -> result.addAll(getDataSourceNames(each)));
autoTableRuleConfigs.forEach(each -> result.addAll(getDataSourceNames(each)));
return result;
}
private Collection<String> getDataSourceNames(final ShardingAutoTableRuleConfiguration shardingAutoTableRuleConfig) {
List<String> actualDataSources = InlineExpressionParserFactory.newInstance(shardingAutoTableRuleConfig.getActualDataSources()).splitAndEvaluate();
return new HashSet<>(actualDataSources);
}
private Collection<String> getDataSourceNames(final ShardingTableRuleConfiguration shardingTableRuleConfig) {
List<String> actualDataNodes = InlineExpressionParserFactory.newInstance(shardingTableRuleConfig.getActualDataNodes()).splitAndEvaluate();
return actualDataNodes.stream().map(each -> new DataNode(each).getDataSourceName()).collect(Collectors.toList());
}
private Map<String, ShardingTable> createShardingTables(final Collection<ShardingTableRuleConfiguration> tableRuleConfigs,
final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
return tableRuleConfigs.stream().map(each -> createShardingTable(each, defaultKeyGenerateStrategyConfig))
.collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, CaseInsensitiveMap::new));
}
private ShardingTable createShardingTable(final ShardingTableRuleConfiguration tableRuleConfig, final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
Optional.ofNullable(tableRuleConfig.getDatabaseShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), tableRuleConfig.getLogicTable()));
Optional.ofNullable(tableRuleConfig.getTableShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), tableRuleConfig.getLogicTable()));
return new ShardingTable(tableRuleConfig, dataSourceNames, getDefaultGenerateKeyColumn(defaultKeyGenerateStrategyConfig));
}
private void checkManualShardingAlgorithm(final String shardingAlgorithmName, final String logicTable) {
ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(shardingAlgorithmName);
ShardingSpherePreconditions.checkState(!(shardingAlgorithm instanceof ShardingAutoTableAlgorithm),
() -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` tables sharding configuration can not use auto sharding algorithm.", logicTable));
}
private Map<String, ShardingTable> createShardingAutoTables(final Collection<ShardingAutoTableRuleConfiguration> autoTableRuleConfigs,
final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
return autoTableRuleConfigs.stream().map(each -> createShardingAutoTable(defaultKeyGenerateStrategyConfig, each))
.collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, CaseInsensitiveMap::new));
}
private ShardingTable createShardingAutoTable(final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig, final ShardingAutoTableRuleConfiguration autoTableRuleConfig) {
checkAutoShardingAlgorithm(autoTableRuleConfig.getShardingStrategy().getShardingAlgorithmName(), autoTableRuleConfig.getLogicTable());
ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(autoTableRuleConfig.getShardingStrategy().getShardingAlgorithmName());
return new ShardingTable(autoTableRuleConfig, dataSourceNames, (ShardingAutoTableAlgorithm) shardingAlgorithm, getDefaultGenerateKeyColumn(defaultKeyGenerateStrategyConfig));
}
private void checkAutoShardingAlgorithm(final String shardingAlgorithmName, final String logicTable) {
ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(shardingAlgorithmName);
ShardingSpherePreconditions.checkState(shardingAlgorithm instanceof ShardingAutoTableAlgorithm,
() -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` autoTables sharding configuration must use auto sharding algorithm.", logicTable));
}
private String getDefaultGenerateKeyColumn(final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
return Optional.ofNullable(defaultKeyGenerateStrategyConfig).map(KeyGenerateStrategyConfiguration::getColumn).orElse(null);
}
private Map<String, BindingTableRule> createBindingTableRules(final Collection<ShardingTableReferenceRuleConfiguration> bindingTableGroups) {
Map<String, BindingTableRule> result = new LinkedHashMap<>();
for (ShardingTableReferenceRuleConfiguration each : bindingTableGroups) {
BindingTableRule bindingTableRule = createBindingTableRule(each.getReference());
for (String logicTable : bindingTableRule.getAllLogicTables()) {
result.put(logicTable, bindingTableRule);
}
}
return result;
}
private BindingTableRule createBindingTableRule(final String bindingTableGroup) {
Map<String, ShardingTable> shardingTables = Splitter.on(",").trimResults().splitToList(bindingTableGroup).stream()
.map(this::getShardingTable).collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, LinkedHashMap::new));
BindingTableRule result = new BindingTableRule();
result.getShardingTables().putAll(shardingTables);
return result;
}
/**
* Get database sharding strategy configuration.
*
* @param shardingTable sharding table
* @return database sharding strategy configuration
*/
public ShardingStrategyConfiguration getDatabaseShardingStrategyConfiguration(final ShardingTable shardingTable) {
return getDatabaseShardingStrategyConfiguration(shardingTable, defaultDatabaseShardingStrategyConfig);
}
private ShardingStrategyConfiguration getDatabaseShardingStrategyConfiguration(final ShardingTable shardingTable, final ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig) {
return null == shardingTable.getDatabaseShardingStrategyConfig() ? defaultDatabaseShardingStrategyConfig : shardingTable.getDatabaseShardingStrategyConfig();
}
/**
* Get table sharding strategy configuration.
*
* @param shardingTable sharding table
* @return table sharding strategy configuration
*/
public ShardingStrategyConfiguration getTableShardingStrategyConfiguration(final ShardingTable shardingTable) {
return getTableShardingStrategyConfiguration(shardingTable, defaultTableShardingStrategyConfig);
}
private ShardingStrategyConfiguration getTableShardingStrategyConfiguration(final ShardingTable shardingTable, final ShardingStrategyConfiguration defaultTableShardingStrategyConfig) {
return null == shardingTable.getTableShardingStrategyConfig() ? defaultTableShardingStrategyConfig : shardingTable.getTableShardingStrategyConfig();
}
/**
* Get audit strategy configuration.
*
* @param shardingTable sharding table
* @return audit strategy configuration
*/
public ShardingAuditStrategyConfiguration getAuditStrategyConfiguration(final ShardingTable shardingTable) {
return null == shardingTable.getAuditStrategyConfig() ? defaultAuditStrategy : shardingTable.getAuditStrategyConfig();
}
/**
* Find sharding table.
*
* @param logicTableName logic table name
* @return sharding table
*/
public Optional<ShardingTable> findShardingTable(final String logicTableName) {
if (Strings.isNullOrEmpty(logicTableName) || !shardingTables.containsKey(logicTableName)) {
return Optional.empty();
}
return Optional.of(shardingTables.get(logicTableName));
}
/**
* Find sharding table via actual table name.
*
* @param actualTableName actual table name
* @return sharding table
*/
public Optional<ShardingTable> findShardingTableByActualTable(final String actualTableName) {
for (ShardingTable each : shardingTables.values()) {
if (each.isExisted(actualTableName)) {
return Optional.of(each);
}
}
return Optional.empty();
}
/**
* Get sharding table.
*
* @param logicTableName logic table name
* @return sharding table
* @throws ShardingTableRuleNotFoundException sharding table rule not found exception
*/
public ShardingTable getShardingTable(final String logicTableName) {
Optional<ShardingTable> shardingTable = findShardingTable(logicTableName);
if (shardingTable.isPresent()) {
return shardingTable.get();
}
throw new ShardingTableRuleNotFoundException(Collections.singleton(logicTableName));
}
/**
* Judge whether logic table is all binding tables or not.
*
* @param logicTableNames logic table names
* @return whether logic table is all binding tables or not
*/
public boolean isAllBindingTables(final Collection<String> logicTableNames) {
if (logicTableNames.isEmpty()) {
return false;
}
Optional<BindingTableRule> bindingTableRule = findBindingTableRule(logicTableNames);
if (!bindingTableRule.isPresent()) {
return false;
}
Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
result.addAll(bindingTableRule.get().getAllLogicTables());
return !result.isEmpty() && result.containsAll(logicTableNames);
}
/**
* Judge whether logic table is all binding tables.
*
* @param database database
* @param sqlStatementContext sqlStatementContext
* @param logicTableNames logic table names
* @return whether logic table is all binding tables
*/
public boolean isAllBindingTables(final ShardingSphereDatabase database, final SQLStatementContext sqlStatementContext, final Collection<String> logicTableNames) {
if (!(sqlStatementContext instanceof SelectStatementContext && ((SelectStatementContext) sqlStatementContext).isContainsJoinQuery())) {
return isAllBindingTables(logicTableNames);
}
if (!isAllBindingTables(logicTableNames)) {
return false;
}
String defaultSchemaName = new DatabaseTypeRegistry(sqlStatementContext.getDatabaseType()).getDefaultSchemaName(database.getName());
ShardingSphereSchema schema = sqlStatementContext.getTablesContext().getSchemaName().map(database::getSchema).orElseGet(() -> database.getSchema(defaultSchemaName));
SelectStatementContext select = (SelectStatementContext) sqlStatementContext;
return isJoinConditionContainsShardingColumns(schema, select, logicTableNames, select.getWhereSegments());
}
private Optional<BindingTableRule> findBindingTableRule(final Collection<String> logicTableNames) {
for (String each : logicTableNames) {
Optional<BindingTableRule> result = findBindingTableRule(each);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}
/**
* Find binding table rule via logic table name.
*
* @param logicTableName logic table name
* @return binding table rule
*/
public Optional<BindingTableRule> findBindingTableRule(final String logicTableName) {
return Optional.ofNullable(bindingTableRules.get(logicTableName));
}
/**
* Judge whether logic table is all sharding table or not.
*
* @param logicTableNames logic table names
* @return whether logic table is all sharding table or not
*/
public boolean isAllShardingTables(final Collection<String> logicTableNames) {
if (logicTableNames.isEmpty()) {
return false;
}
for (String each : logicTableNames) {
if (!isShardingTable(each)) {
return false;
}
}
return true;
}
/**
* Judge whether logic table is sharding table or not.
*
* @param logicTableName logic table name
* @return whether logic table is sharding table or not
*/
public boolean isShardingTable(final String logicTableName) {
return shardingTables.containsKey(logicTableName);
}
/**
* Judge whether all tables are in same data source or not.
*
* @param logicTableNames logic table names
* @return whether all tables are in same data source or not
*/
public boolean isAllTablesInSameDataSource(final Collection<String> logicTableNames) {
Collection<String> dataSourceNames = logicTableNames.stream().map(shardingTables::get)
.filter(Objects::nonNull).flatMap(each -> each.getActualDataSourceNames().stream()).collect(Collectors.toSet());
return 1 == dataSourceNames.size();
}
/**
* Judge whether contains sharding table or not.
*
* @param logicTableNames logic table names
* @return whether contains sharding table or not
*/
public boolean containsShardingTable(final Collection<String> logicTableNames) {
for (String each : logicTableNames) {
if (isShardingTable(each)) {
return true;
}
}
return false;
}
/**
* Find sharding column.
*
* @param columnName column name
* @param tableName table name
* @return sharding column
*/
public Optional<String> findShardingColumn(final String columnName, final String tableName) {
return Optional.ofNullable(shardingTables.get(tableName)).flatMap(optional -> findShardingColumn(optional, columnName));
}
private Optional<String> findShardingColumn(final ShardingTable shardingTable, final String columnName) {
Optional<String> databaseShardingColumn = findShardingColumn(getDatabaseShardingStrategyConfiguration(shardingTable), columnName);
if (databaseShardingColumn.isPresent()) {
return databaseShardingColumn;
}
return findShardingColumn(getTableShardingStrategyConfiguration(shardingTable), columnName);
}
private Optional<String> findShardingColumn(final ShardingStrategyConfiguration shardingStrategyConfig, final String columnName) {
if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
String shardingColumn = null == ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn()
? defaultShardingColumn
: ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn();
return shardingColumn.equalsIgnoreCase(columnName) ? Optional.of(shardingColumn) : Optional.empty();
}
if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
List<String> shardingColumns = Splitter.on(",").trimResults().splitToList(((ComplexShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumns());
for (String each : shardingColumns) {
if (each.equalsIgnoreCase(columnName)) {
return Optional.of(each);
}
}
}
return Optional.empty();
}
/**
* Judge whether given logic table column is generate key column or not.
*
* @param columnName column name
* @param tableName table name
* @return whether given logic table column is generate key column or not
*/
public boolean isGenerateKeyColumn(final String columnName, final String tableName) {
return Optional.ofNullable(shardingTables.get(tableName)).filter(each -> isGenerateKeyColumn(each, columnName)).isPresent();
}
private boolean isGenerateKeyColumn(final ShardingTable shardingTable, final String columnName) {
Optional<String> generateKeyColumn = shardingTable.getGenerateKeyColumn();
return generateKeyColumn.isPresent() && generateKeyColumn.get().equalsIgnoreCase(columnName);
}
/**
* Find column name of generated key.
*
* @param logicTableName logic table name
* @return column name of generated key
*/
public Optional<String> findGenerateKeyColumnName(final String logicTableName) {
return Optional.ofNullable(shardingTables.get(logicTableName)).filter(each -> each.getGenerateKeyColumn().isPresent()).flatMap(ShardingTable::getGenerateKeyColumn);
}
/**
* Find the generated keys of logic table.
*
* @param algorithmSQLContext key generate context
* @param keyGenerateCount key generate count
* @return generated keys
*/
public Collection<? extends Comparable<?>> generateKeys(final AlgorithmSQLContext algorithmSQLContext, final int keyGenerateCount) {
return getKeyGenerateAlgorithm(algorithmSQLContext.getTableName()).generateKeys(algorithmSQLContext, keyGenerateCount);
}
/**
* Judge whether support auto increment or not.
*
* @param logicTableName logic table name
* @return whether support auto increment or not
*/
public boolean isSupportAutoIncrement(final String logicTableName) {
return getKeyGenerateAlgorithm(logicTableName).isSupportAutoIncrement();
}
private KeyGenerateAlgorithm getKeyGenerateAlgorithm(final String logicTableName) {
ShardingTable shardingTable = getShardingTable(logicTableName);
return null == shardingTable.getKeyGeneratorName() ? defaultKeyGenerateAlgorithm : keyGenerators.get(shardingTable.getKeyGeneratorName());
}
/**
* Find data node by logic table name.
*
* @param logicTableName logic table name
* @return data node
*/
public DataNode getDataNode(final String logicTableName) {
ShardingTable shardingTable = getShardingTable(logicTableName);
return shardingTable.getActualDataNodes().get(0);
}
/**
* Get sharding logic table names.
*
* @param logicTableNames logic table names
* @return sharding logic table names
*/
public Collection<String> getShardingLogicTableNames(final Collection<String> logicTableNames) {
Collection<String> result = new LinkedList<>();
for (String each : logicTableNames) {
if (isShardingTable(each)) {
result.add(each);
}
}
return result;
}
/**
* Get sharding rule table names.
*
* @param logicTableNames logic table names
* @return sharding rule table names
*/
public Collection<String> getShardingRuleTableNames(final Collection<String> logicTableNames) {
return logicTableNames.stream().filter(this::isShardingTable).collect(Collectors.toList());
}
/**
* Get logic and actual binding tables.
*
* @param dataSourceName data source name
* @param logicTable logic table name
* @param actualTable actual table name
* @param availableLogicBindingTables available logic binding table names
* @return logic and actual binding tables
*/
public Map<String, String> getLogicAndActualTablesFromBindingTable(final String dataSourceName,
final String logicTable, final String actualTable, final Collection<String> availableLogicBindingTables) {
return findBindingTableRule(logicTable).map(optional -> optional.getLogicAndActualTables(dataSourceName, logicTable, actualTable, availableLogicBindingTables))
.orElseGet(Collections::emptyMap);
}
/**
* Is sharding cache enabled.
*
* @return is sharding cache enabled
*/
public boolean isShardingCacheEnabled() {
return null != shardingCache;
}
private boolean isJoinConditionContainsShardingColumns(final ShardingSphereSchema schema, final SelectStatementContext select,
final Collection<String> tableNames, final Collection<WhereSegment> whereSegments) {
Collection<String> databaseJoinConditionTables = new HashSet<>(tableNames.size(), 1F);
Collection<String> tableJoinConditionTables = new HashSet<>(tableNames.size(), 1F);
for (WhereSegment each : whereSegments) {
Collection<AndPredicate> andPredicates = ExpressionExtractUtils.getAndPredicates(each.getExpr());
if (andPredicates.size() > 1) {
return false;
}
for (AndPredicate andPredicate : andPredicates) {
databaseJoinConditionTables.addAll(getJoinConditionTables(schema, select, andPredicate.getPredicates(), true));
tableJoinConditionTables.addAll(getJoinConditionTables(schema, select, andPredicate.getPredicates(), false));
}
}
ShardingTable shardingTable = getShardingTable(tableNames.iterator().next());
boolean containsDatabaseShardingColumns = !(getDatabaseShardingStrategyConfiguration(shardingTable) instanceof StandardShardingStrategyConfiguration)
|| databaseJoinConditionTables.containsAll(tableNames);
boolean containsTableShardingColumns =
!(getTableShardingStrategyConfiguration(shardingTable) instanceof StandardShardingStrategyConfiguration) || tableJoinConditionTables.containsAll(tableNames);
return containsDatabaseShardingColumns && containsTableShardingColumns;
}
private Collection<String> getJoinConditionTables(final ShardingSphereSchema schema, final SelectStatementContext select,
final Collection<ExpressionSegment> predicates, final boolean isDatabaseJoinCondition) {
Collection<String> result = new LinkedList<>();
for (ExpressionSegment each : predicates) {
if (!isJoinConditionExpression(each)) {
continue;
}
ColumnSegment leftColumn = (ColumnSegment) ((BinaryOperationExpression) each).getLeft();
ColumnSegment rightColumn = (ColumnSegment) ((BinaryOperationExpression) each).getRight();
Map<String, String> columnExpressionTableNames = select.getTablesContext().findTableNamesByColumnSegment(Arrays.asList(leftColumn, rightColumn), schema);
Optional<ShardingTable> leftShardingTable = findShardingTable(columnExpressionTableNames.get(leftColumn.getExpression()));
Optional<ShardingTable> rightShardingTable = findShardingTable(columnExpressionTableNames.get(rightColumn.getExpression()));
if (!leftShardingTable.isPresent() || !rightShardingTable.isPresent()) {
continue;
}
ShardingStrategyConfiguration leftConfig = isDatabaseJoinCondition
? getDatabaseShardingStrategyConfiguration(leftShardingTable.get())
: getTableShardingStrategyConfiguration(leftShardingTable.get());
ShardingStrategyConfiguration rightConfig = isDatabaseJoinCondition
? getDatabaseShardingStrategyConfiguration(rightShardingTable.get())
: getTableShardingStrategyConfiguration(rightShardingTable.get());
if (findShardingColumn(leftConfig, leftColumn.getIdentifier().getValue()).isPresent()
&& findShardingColumn(rightConfig, rightColumn.getIdentifier().getValue()).isPresent()) {
result.add(columnExpressionTableNames.get(leftColumn.getExpression()));
result.add(columnExpressionTableNames.get(rightColumn.getExpression()));
}
}
return result;
}
private boolean isJoinConditionExpression(final ExpressionSegment expression) {
if (!(expression instanceof BinaryOperationExpression)) {
return false;
}
BinaryOperationExpression binaryExpression = (BinaryOperationExpression) expression;
return binaryExpression.getLeft() instanceof ColumnSegment && binaryExpression.getRight() instanceof ColumnSegment && "=".equals(binaryExpression.getOperator());
}
}