View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.sharding.rule.checker;
19  
20  import com.google.common.base.Splitter;
21  import lombok.RequiredArgsConstructor;
22  import org.apache.shardingsphere.infra.datanode.DataNode;
23  import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
24  import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
25  import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableReferenceRuleConfiguration;
26  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ComplexShardingStrategyConfiguration;
27  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
28  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
29  import org.apache.shardingsphere.sharding.exception.metadata.DuplicateShardingActualDataNodeException;
30  import org.apache.shardingsphere.sharding.exception.metadata.InvalidBindingTablesException;
31  import org.apache.shardingsphere.sharding.exception.metadata.ShardingTableRuleNotFoundException;
32  import org.apache.shardingsphere.sharding.rule.BindingTableCheckedConfiguration;
33  import org.apache.shardingsphere.sharding.rule.ShardingRule;
34  import org.apache.shardingsphere.sharding.rule.ShardingTable;
35  import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
36  
37  import java.util.Collection;
38  import java.util.Collections;
39  import java.util.HashSet;
40  import java.util.Iterator;
41  import java.util.Map;
42  import java.util.Optional;
43  import java.util.stream.Collectors;
44  
45  @RequiredArgsConstructor
46  public class ShardingRuleChecker {
47      
48      private final ShardingRule shardingRule;
49      
50      /**
51       * Check sharding rule.
52       *
53       * @param ruleConfig sharding rule configuration
54       */
55      public void check(final ShardingRuleConfiguration ruleConfig) {
56          checkUniqueActualDataNodesInTableRules();
57          checkBindingTableConfiguration(ruleConfig);
58      }
59      
60      private void checkUniqueActualDataNodesInTableRules() {
61          Collection<DataNode> uniqueActualDataNodes = new HashSet<>(shardingRule.getShardingTables().size(), 1F);
62          shardingRule.getShardingTables().forEach((key, value) -> checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.getActualDataNodes().iterator().next()));
63      }
64      
65      private void checkUniqueActualDataNodes(final Collection<DataNode> uniqueActualDataNodes, final String logicTable, final DataNode sampleActualDataNode) {
66          ShardingSpherePreconditions.checkNotContains(uniqueActualDataNodes, sampleActualDataNode,
67                  () -> new DuplicateShardingActualDataNodeException(logicTable, sampleActualDataNode.getDataSourceName(), sampleActualDataNode.getTableName()));
68          uniqueActualDataNodes.add(sampleActualDataNode);
69      }
70      
71      private void checkBindingTableConfiguration(final ShardingRuleConfiguration ruleConfig) {
72          checkBindingTablesNumericSuffix(ruleConfig.getBindingTableGroups(), shardingRule.getShardingTables());
73          BindingTableCheckedConfiguration checkedConfig = new BindingTableCheckedConfiguration(shardingRule.getDataSourceNames(), shardingRule.getShardingAlgorithms(),
74                  ruleConfig.getBindingTableGroups(), shardingRule.getDefaultDatabaseShardingStrategyConfig(), shardingRule.getDefaultTableShardingStrategyConfig(),
75                  shardingRule.getDefaultShardingColumn());
76          ShardingSpherePreconditions.checkState(isValidBindingTableConfiguration(shardingRule.getShardingTables(), checkedConfig),
77                  () -> new InvalidBindingTablesException("Invalid binding table configuration."));
78      }
79      
80      private void checkBindingTablesNumericSuffix(final Collection<ShardingTableReferenceRuleConfiguration> bindingTableGroups, final Map<String, ShardingTable> shardingTables) {
81          for (ShardingTableReferenceRuleConfiguration each : bindingTableGroups) {
82              Collection<String> bindingTables = Splitter.on(",").trimResults().splitToList(each.getReference());
83              if (bindingTables.size() <= 1) {
84                  continue;
85              }
86              for (String logicTable : bindingTables) {
87                  ShardingSpherePreconditions.checkState(hasValidNumericSuffix(getShardingTable(logicTable, shardingTables)),
88                          () -> new InvalidBindingTablesException(String.format("Alphabetical table suffixes are not supported in binding tables '%s'.", each.getReference())));
89              }
90          }
91      }
92      
93      private boolean hasValidNumericSuffix(final ShardingTable shardingTable) {
94          String logicTable = shardingTable.getLogicTable();
95          int logicTableLength = logicTable.length();
96          for (DataNode each : shardingTable.getActualDataNodes()) {
97              String tableName = each.getTableName();
98              if (tableName.equalsIgnoreCase(logicTable)) {
99                  continue;
100             }
101             if (tableName.length() > logicTableLength && tableName.regionMatches(true, 0, logicTable, 0, logicTableLength) && !Character.isDigit(tableName.charAt(tableName.length() - 1))) {
102                 return false;
103             }
104         }
105         return true;
106     }
107     
108     private boolean isValidBindingTableConfiguration(final Map<String, ShardingTable> shardingTables, final BindingTableCheckedConfiguration checkedConfig) {
109         for (ShardingTableReferenceRuleConfiguration each : checkedConfig.getBindingTableGroups()) {
110             Collection<String> bindingTables = Splitter.on(",").trimResults().splitToList(each.getReference());
111             if (bindingTables.size() <= 1) {
112                 continue;
113             }
114             Iterator<String> iterator = bindingTables.iterator();
115             ShardingTable sampleShardingTable = getShardingTable(iterator.next(), shardingTables);
116             while (iterator.hasNext()) {
117                 ShardingTable shardingTable = getShardingTable(iterator.next(), shardingTables);
118                 if (!isValidActualDataSourceName(sampleShardingTable, shardingTable) || !isValidActualTableName(sampleShardingTable, shardingTable)) {
119                     return false;
120                 }
121                 if (!isBindingShardingAlgorithm(sampleShardingTable, shardingTable, true, checkedConfig) || !isBindingShardingAlgorithm(sampleShardingTable, shardingTable, false, checkedConfig)) {
122                     return false;
123                 }
124             }
125         }
126         return true;
127     }
128     
129     private ShardingTable getShardingTable(final String logicTableName, final Map<String, ShardingTable> shardingTables) {
130         ShardingTable result = shardingTables.get(logicTableName);
131         ShardingSpherePreconditions.checkNotNull(result, () -> new ShardingTableRuleNotFoundException(Collections.singleton(logicTableName)));
132         return result;
133     }
134     
135     private boolean isValidActualDataSourceName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
136         return sampleShardingTable.getActualDataSourceNames().equals(shardingTable.getActualDataSourceNames());
137     }
138     
139     private boolean isValidActualTableName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
140         for (String each : sampleShardingTable.getActualDataSourceNames()) {
141             Collection<String> sampleActualTableNames = sampleShardingTable.getActualTableNames(each).stream()
142                     .map(actualTableName -> actualTableName.replace(sampleShardingTable.getTableDataNode().getPrefix(), "")).collect(Collectors.toSet());
143             Collection<String> actualTableNames =
144                     shardingTable.getActualTableNames(each).stream().map(optional -> optional.replace(shardingTable.getTableDataNode().getPrefix(), "")).collect(Collectors.toSet());
145             if (!sampleActualTableNames.equals(actualTableNames)) {
146                 return false;
147             }
148         }
149         return true;
150     }
151     
152     private boolean isBindingShardingAlgorithm(final ShardingTable sampleShardingTable, final ShardingTable shardingTable, final boolean databaseAlgorithm,
153                                                final BindingTableCheckedConfiguration checkedConfig) {
154         return getAlgorithmExpression(sampleShardingTable, databaseAlgorithm, checkedConfig).equals(getAlgorithmExpression(shardingTable, databaseAlgorithm, checkedConfig));
155     }
156     
157     private Optional<String> getAlgorithmExpression(final ShardingTable shardingTable, final boolean databaseAlgorithm, final BindingTableCheckedConfiguration checkedConfig) {
158         ShardingStrategyConfiguration shardingStrategyConfig = databaseAlgorithm
159                 ? shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable)
160                 : shardingRule.getTableShardingStrategyConfiguration(shardingTable);
161         ShardingAlgorithm shardingAlgorithm = checkedConfig.getShardingAlgorithms().get(shardingStrategyConfig.getShardingAlgorithmName());
162         String dataNodePrefix = databaseAlgorithm ? shardingTable.getDataSourceDataNode().getPrefix() : shardingTable.getTableDataNode().getPrefix();
163         String shardingColumn = getShardingColumn(shardingStrategyConfig, shardingRule.getDefaultShardingColumn());
164         return null == shardingAlgorithm ? Optional.empty() : shardingAlgorithm.getAlgorithmStructure(dataNodePrefix, shardingColumn);
165     }
166     
167     private String getShardingColumn(final ShardingStrategyConfiguration shardingStrategyConfig, final String defaultShardingColumn) {
168         String shardingColumn = defaultShardingColumn;
169         if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
170             shardingColumn = ((ComplexShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumns();
171         }
172         if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
173             shardingColumn = ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn();
174         }
175         return null == shardingColumn ? "" : shardingColumn;
176     }
177     
178     /**
179      * Check to be added data nodes.
180      *
181      * @param toBeAddedDataNodes to be added data nodes
182      * @param isAlteration is alteration
183      */
184     public void checkToBeAddedDataNodes(final Map<String, Collection<DataNode>> toBeAddedDataNodes, final boolean isAlteration) {
185         Collection<DataNode> uniqueActualDataNodes = new HashSet<>(shardingRule.getShardingTables().size() + toBeAddedDataNodes.size(), 1F);
186         shardingRule.getShardingTables().forEach((key, value) -> {
187             if (isAlteration && toBeAddedDataNodes.containsKey(key)) {
188                 return;
189             }
190             checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.getActualDataNodes().iterator().next());
191         });
192         toBeAddedDataNodes.forEach((key, value) -> checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.iterator().next()));
193     }
194 }