blob: 34182d0f86b003b5c61fc36e05d171babaf32672 [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.single.decorator;
import org.apache.shardingsphere.infra.config.rule.decorator.RuleConfigurationDecorator;
import org.apache.shardingsphere.infra.database.DatabaseTypeEngine;
import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
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.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.state.datasource.DataSourceStateManager;
import org.apache.shardingsphere.single.api.config.SingleRuleConfiguration;
import org.apache.shardingsphere.single.api.constant.SingleTableConstants;
import org.apache.shardingsphere.single.datanode.SingleTableDataNodeLoader;
import org.apache.shardingsphere.single.exception.InvalidSingleRuleConfigurationException;
import org.apache.shardingsphere.single.exception.SingleTableNotFoundException;
import org.apache.shardingsphere.single.rule.SingleRule;
import org.apache.shardingsphere.single.util.SingleTableLoadUtils;
import javax.sql.DataSource;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
/**
* Single rule configuration decorator.
*/
public final class SingleRuleConfigurationDecorator implements RuleConfigurationDecorator<SingleRuleConfiguration> {
@Override
public SingleRuleConfiguration decorate(final String databaseName, final Map<String, DataSource> dataSources,
final Collection<ShardingSphereRule> builtRules, final SingleRuleConfiguration ruleConfig) {
return new SingleRuleConfiguration(decorateTables(databaseName, dataSources, new LinkedList<>(builtRules), ruleConfig.getTables()), ruleConfig.getDefaultDataSource().orElse(null));
}
private Collection<String> decorateTables(final String databaseName, final Map<String, DataSource> dataSources, final Collection<ShardingSphereRule> builtRules, final Collection<String> tables) {
builtRules.removeIf(SingleRule.class::isInstance);
if (tables.isEmpty() && builtRules.isEmpty()) {
return Collections.emptyList();
}
Collection<String> splitTables = SingleTableLoadUtils.splitTableLines(tables);
if (!isExpandRequired(splitTables)) {
return splitTables;
}
Map<String, DataSource> enabledDataSources = DataSourceStateManager.getInstance().getEnabledDataSources(databaseName, dataSources);
Map<String, DataSource> aggregatedDataSources = SingleTableLoadUtils.getAggregatedDataSourceMap(enabledDataSources, builtRules);
DatabaseType databaseType = enabledDataSources.isEmpty() ? DatabaseTypeEngine.getDefaultStorageType() : DatabaseTypeEngine.getStorageType(enabledDataSources.values().iterator().next());
Collection<String> excludedTables = SingleTableLoadUtils.getExcludedTables(builtRules);
Map<String, Collection<DataNode>> actualDataNodes = SingleTableDataNodeLoader.load(databaseName, aggregatedDataSources, excludedTables);
Collection<DataNode> configuredDataNodes = SingleTableLoadUtils.convertToDataNodes(databaseName, databaseType, splitTables);
checkRuleConfiguration(databaseName, aggregatedDataSources, excludedTables, configuredDataNodes);
boolean isSchemaSupportedDatabaseType = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getDefaultSchema().isPresent();
if (splitTables.contains(SingleTableConstants.ALL_TABLES) || splitTables.contains(SingleTableConstants.ALL_SCHEMA_TABLES)) {
return loadAllTables(isSchemaSupportedDatabaseType, actualDataNodes);
}
return loadSpecifiedTables(isSchemaSupportedDatabaseType, actualDataNodes, builtRules, configuredDataNodes);
}
private boolean isExpandRequired(final Collection<String> splitTables) {
return splitTables.stream().anyMatch(each -> each.contains(SingleTableConstants.ASTERISK));
}
private Collection<String> loadSpecifiedTables(final boolean isSchemaSupportedDatabaseType, final Map<String, Collection<DataNode>> actualDataNodes,
final Collection<ShardingSphereRule> builtRules, final Collection<DataNode> configuredDataNodes) {
Collection<String> expandRequiredDataSources = new LinkedHashSet<>();
Map<String, DataNode> expectedDataNodes = new LinkedHashMap<>();
for (DataNode each : configuredDataNodes) {
if (SingleTableConstants.ASTERISK.equals(each.getTableName())) {
expandRequiredDataSources.add(each.getDataSourceName());
} else {
expectedDataNodes.put(each.getTableName(), each);
}
}
if (expandRequiredDataSources.isEmpty()) {
return loadSpecifiedTablesWithoutExpand(isSchemaSupportedDatabaseType, actualDataNodes, configuredDataNodes);
}
Collection<String> featureRequiredSingleTables = SingleTableLoadUtils.getFeatureRequiredSingleTables(builtRules);
return loadSpecifiedTablesWithExpand(isSchemaSupportedDatabaseType, actualDataNodes, featureRequiredSingleTables, expandRequiredDataSources, expectedDataNodes);
}
private Collection<String> loadSpecifiedTablesWithExpand(final boolean isSchemaSupportedDatabaseType, final Map<String, Collection<DataNode>> actualDataNodes,
final Collection<String> featureRequiredSingleTables, final Collection<String> expandRequiredDataSources,
final Map<String, DataNode> expectedDataNodes) {
Collection<String> result = new LinkedHashSet<>();
for (Entry<String, Collection<DataNode>> entry : actualDataNodes.entrySet()) {
if (featureRequiredSingleTables.contains(entry.getKey())) {
continue;
}
DataNode physicalDataNode = entry.getValue().iterator().next();
if (expandRequiredDataSources.contains(physicalDataNode.getDataSourceName())) {
result.add(getTableNodeString(isSchemaSupportedDatabaseType, physicalDataNode));
continue;
}
if (expectedDataNodes.containsKey(entry.getKey())) {
DataNode dataNode = expectedDataNodes.get(entry.getKey());
String tableNodeStr = getTableNodeString(isSchemaSupportedDatabaseType, physicalDataNode);
ShardingSpherePreconditions.checkState(physicalDataNode.equals(dataNode),
() -> new InvalidSingleRuleConfigurationException(String.format("Single table `%s` is found that does not match %s", tableNodeStr,
getTableNodeString(isSchemaSupportedDatabaseType, dataNode))));
result.add(tableNodeStr);
}
}
return result;
}
private Collection<String> loadSpecifiedTablesWithoutExpand(final boolean isSchemaSupportedDatabaseType, final Map<String, Collection<DataNode>> actualDataNodes,
final Collection<DataNode> configuredDataNodes) {
Collection<String> result = new LinkedHashSet<>();
for (DataNode each : configuredDataNodes) {
ShardingSpherePreconditions.checkContainsKey(actualDataNodes, each.getTableName(), () -> new SingleTableNotFoundException(getTableNodeString(isSchemaSupportedDatabaseType, each)));
DataNode actualDataNode = actualDataNodes.get(each.getTableName()).iterator().next();
String tableNodeStr = getTableNodeString(isSchemaSupportedDatabaseType, actualDataNode);
ShardingSpherePreconditions.checkState(actualDataNode.equals(each),
() -> new InvalidSingleRuleConfigurationException(String.format("Single table '%s' is found that does not match %s", tableNodeStr,
getTableNodeString(isSchemaSupportedDatabaseType, each))));
result.add(tableNodeStr);
}
return result;
}
private Collection<String> loadAllTables(final boolean isSchemaSupportedDatabaseType, final Map<String, Collection<DataNode>> actualDataNodes) {
Collection<String> result = new LinkedList<>();
for (Entry<String, Collection<DataNode>> entry : actualDataNodes.entrySet()) {
result.add(getTableNodeString(isSchemaSupportedDatabaseType, entry.getValue().iterator().next()));
}
return result;
}
private String getTableNodeString(final boolean isSchemaSupportedDatabaseType, final DataNode dataNode) {
return isSchemaSupportedDatabaseType
? formatTableName(dataNode.getDataSourceName(), dataNode.getSchemaName(), dataNode.getTableName())
: formatTableName(dataNode.getDataSourceName(), dataNode.getTableName());
}
private void checkRuleConfiguration(final String databaseName, final Map<String, DataSource> dataSources, final Collection<String> excludedTables, final Collection<DataNode> dataNodes) {
for (DataNode each : dataNodes) {
if (!SingleTableConstants.ASTERISK.equals(each.getDataSourceName())) {
ShardingSpherePreconditions.checkContainsKey(dataSources, each.getDataSourceName(),
() -> new InvalidSingleRuleConfigurationException(String.format("Data source `%s` does not exist in database `%s`", each.getDataSourceName(), databaseName)));
}
ShardingSpherePreconditions.checkNotContains(excludedTables, each.getTableName(),
() -> new InvalidSingleRuleConfigurationException(String.format("Table `%s` existed and is not a single table in database `%s`", each.getTableName(), databaseName)));
}
}
private String formatTableName(final String dataSourceName, final String tableName) {
return String.format("%s.%s", dataSourceName, tableName);
}
private String formatTableName(final String dataSourceName, final String schemaName, final String tableName) {
return String.format("%s.%s.%s", dataSourceName, schemaName, tableName);
}
@Override
public Class<SingleRuleConfiguration> getType() {
return SingleRuleConfiguration.class;
}
}