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.route.engine.type.standard;
19  
20  import com.cedarsoftware.util.CaseInsensitiveSet;
21  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
22  import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
23  import org.apache.shardingsphere.infra.datanode.DataNode;
24  import org.apache.shardingsphere.infra.hint.HintManager;
25  import org.apache.shardingsphere.infra.hint.HintValueContext;
26  import org.apache.shardingsphere.infra.route.context.RouteContext;
27  import org.apache.shardingsphere.infra.route.context.RouteMapper;
28  import org.apache.shardingsphere.infra.route.context.RouteUnit;
29  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
30  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.HintShardingStrategyConfiguration;
31  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
32  import org.apache.shardingsphere.sharding.exception.algorithm.MismatchedShardingDataSourceRouteInfoException;
33  import org.apache.shardingsphere.sharding.exception.algorithm.NoShardingDatabaseRouteInfoException;
34  import org.apache.shardingsphere.sharding.route.engine.condition.ShardingCondition;
35  import org.apache.shardingsphere.sharding.route.engine.condition.ShardingConditions;
36  import org.apache.shardingsphere.sharding.route.engine.condition.value.ListShardingConditionValue;
37  import org.apache.shardingsphere.sharding.route.engine.condition.value.ShardingConditionValue;
38  import org.apache.shardingsphere.sharding.route.engine.type.ShardingRouteEngine;
39  import org.apache.shardingsphere.sharding.route.strategy.ShardingStrategy;
40  import org.apache.shardingsphere.sharding.route.strategy.ShardingStrategyFactory;
41  import org.apache.shardingsphere.sharding.route.strategy.type.hint.HintShardingStrategy;
42  import org.apache.shardingsphere.sharding.route.strategy.type.none.NoneShardingStrategy;
43  import org.apache.shardingsphere.sharding.rule.BindingTableRule;
44  import org.apache.shardingsphere.sharding.rule.ShardingRule;
45  import org.apache.shardingsphere.sharding.rule.ShardingTable;
46  import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
47  
48  import java.util.ArrayList;
49  import java.util.Collection;
50  import java.util.Collections;
51  import java.util.LinkedList;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.Optional;
55  
56  /**
57   * Sharding standard routing engine.
58   */
59  public final class ShardingStandardRoutingEngine implements ShardingRouteEngine {
60      
61      private final String logicTableName;
62      
63      private final ShardingConditions shardingConditions;
64      
65      private final SQLStatementContext sqlStatementContext;
66      
67      private final ConfigurationProperties props;
68      
69      private final Collection<Collection<DataNode>> originalDataNodes = new LinkedList<>();
70      
71      private final HintValueContext hintValueContext;
72      
73      public ShardingStandardRoutingEngine(final String logicTableName, final ShardingConditions shardingConditions, final SQLStatementContext sqlStatementContext,
74                                           final HintValueContext hintValueContext, final ConfigurationProperties props) {
75          this.logicTableName = logicTableName;
76          this.shardingConditions = shardingConditions;
77          this.sqlStatementContext = sqlStatementContext;
78          this.props = props;
79          this.hintValueContext = hintValueContext;
80      }
81      
82      @Override
83      public RouteContext route(final ShardingRule shardingRule) {
84          RouteContext result = new RouteContext();
85          Collection<DataNode> dataNodes = getDataNodes(shardingRule, shardingRule.getShardingTable(logicTableName));
86          result.getOriginalDataNodes().addAll(originalDataNodes);
87          for (DataNode each : dataNodes) {
88              result.getRouteUnits().add(
89                      new RouteUnit(new RouteMapper(each.getDataSourceName(), each.getDataSourceName()), Collections.singleton(new RouteMapper(logicTableName, each.getTableName()))));
90          }
91          return result;
92      }
93      
94      private Collection<DataNode> getDataNodes(final ShardingRule shardingRule, final ShardingTable shardingTable) {
95          ShardingStrategy databaseShardingStrategy = createShardingStrategy(shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable),
96                  shardingRule.getShardingAlgorithms(), shardingRule.getDefaultShardingColumn());
97          ShardingStrategy tableShardingStrategy = createShardingStrategy(shardingRule.getTableShardingStrategyConfiguration(shardingTable),
98                  shardingRule.getShardingAlgorithms(), shardingRule.getDefaultShardingColumn());
99          if (isRoutingByHint(shardingRule, shardingTable)) {
100             return routeByHint(shardingTable, databaseShardingStrategy, tableShardingStrategy);
101         }
102         if (isRoutingByShardingConditions(shardingRule, shardingTable)) {
103             return routeByShardingConditions(shardingRule, shardingTable, databaseShardingStrategy, tableShardingStrategy);
104         }
105         return routeByMixedConditions(shardingRule, shardingTable, databaseShardingStrategy, tableShardingStrategy);
106     }
107     
108     private boolean isRoutingByHint(final ShardingRule shardingRule, final ShardingTable shardingTable) {
109         return shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable) instanceof HintShardingStrategyConfiguration
110                 && shardingRule.getTableShardingStrategyConfiguration(shardingTable) instanceof HintShardingStrategyConfiguration;
111     }
112     
113     private boolean isRoutingBySQLHint() {
114         Collection<String> tableNames = sqlStatementContext.getTablesContext().getTableNames();
115         for (String each : tableNames) {
116             if (hintValueContext.containsHintShardingValue(each)) {
117                 return true;
118             }
119         }
120         return false;
121     }
122     
123     private Collection<DataNode> routeByHint(final ShardingTable shardingTable, final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
124         return route0(shardingTable, databaseShardingStrategy, getDatabaseShardingValuesFromHint(), tableShardingStrategy, getTableShardingValuesFromHint());
125     }
126     
127     private boolean isRoutingByShardingConditions(final ShardingRule shardingRule, final ShardingTable shardingTable) {
128         return !(shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable) instanceof HintShardingStrategyConfiguration
129                 || shardingRule.getTableShardingStrategyConfiguration(shardingTable) instanceof HintShardingStrategyConfiguration);
130     }
131     
132     private Collection<DataNode> routeByShardingConditions(final ShardingRule shardingRule, final ShardingTable shardingTable,
133                                                            final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
134         return shardingConditions.getConditions().isEmpty()
135                 ? route0(shardingTable, databaseShardingStrategy, Collections.emptyList(), tableShardingStrategy, Collections.emptyList())
136                 : routeByShardingConditionsWithCondition(shardingRule, shardingTable, databaseShardingStrategy, tableShardingStrategy);
137     }
138     
139     private Collection<DataNode> routeByShardingConditionsWithCondition(final ShardingRule shardingRule, final ShardingTable shardingTable,
140                                                                         final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
141         Collection<DataNode> result = new LinkedList<>();
142         for (ShardingCondition each : shardingConditions.getConditions()) {
143             Collection<DataNode> dataNodes = route0(shardingTable,
144                     databaseShardingStrategy, getShardingValuesFromShardingConditions(shardingRule, databaseShardingStrategy.getShardingColumns(), each),
145                     tableShardingStrategy, getShardingValuesFromShardingConditions(shardingRule, tableShardingStrategy.getShardingColumns(), each));
146             result.addAll(dataNodes);
147             originalDataNodes.add(dataNodes);
148         }
149         return result;
150     }
151     
152     private Collection<DataNode> routeByMixedConditions(final ShardingRule shardingRule, final ShardingTable shardingTable,
153                                                         final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
154         return shardingConditions.getConditions().isEmpty()
155                 ? routeByMixedConditionsWithHint(shardingRule, shardingTable, databaseShardingStrategy, tableShardingStrategy)
156                 : routeByMixedConditionsWithCondition(shardingRule, shardingTable, databaseShardingStrategy, tableShardingStrategy);
157     }
158     
159     private Collection<DataNode> routeByMixedConditionsWithCondition(final ShardingRule shardingRule, final ShardingTable shardingTable,
160                                                                      final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
161         Collection<DataNode> result = new LinkedList<>();
162         for (ShardingCondition each : shardingConditions.getConditions()) {
163             Collection<DataNode> dataNodes = route0(shardingTable, databaseShardingStrategy,
164                     getDatabaseShardingValues(shardingRule, databaseShardingStrategy, each), tableShardingStrategy, getTableShardingValues(shardingRule, tableShardingStrategy, each));
165             result.addAll(dataNodes);
166             originalDataNodes.add(dataNodes);
167         }
168         return result;
169     }
170     
171     private Collection<DataNode> routeByMixedConditionsWithHint(final ShardingRule shardingRule, final ShardingTable shardingTable,
172                                                                 final ShardingStrategy databaseShardingStrategy, final ShardingStrategy tableShardingStrategy) {
173         if (shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable) instanceof HintShardingStrategyConfiguration) {
174             return route0(shardingTable, databaseShardingStrategy, getDatabaseShardingValuesFromHint(), tableShardingStrategy, Collections.emptyList());
175         }
176         return route0(shardingTable, databaseShardingStrategy, Collections.emptyList(), tableShardingStrategy, getTableShardingValuesFromHint());
177     }
178     
179     private List<ShardingConditionValue> getDatabaseShardingValues(final ShardingRule shardingRule, final ShardingStrategy databaseShardingStrategy, final ShardingCondition shardingCondition) {
180         return isGettingShardingValuesFromHint(databaseShardingStrategy)
181                 ? getDatabaseShardingValuesFromHint()
182                 : getShardingValuesFromShardingConditions(shardingRule, databaseShardingStrategy.getShardingColumns(), shardingCondition);
183     }
184     
185     private List<ShardingConditionValue> getTableShardingValues(final ShardingRule shardingRule, final ShardingStrategy tableShardingStrategy, final ShardingCondition shardingCondition) {
186         return isGettingShardingValuesFromHint(tableShardingStrategy)
187                 ? getTableShardingValuesFromHint()
188                 : getShardingValuesFromShardingConditions(shardingRule, tableShardingStrategy.getShardingColumns(), shardingCondition);
189     }
190     
191     private boolean isGettingShardingValuesFromHint(final ShardingStrategy shardingStrategy) {
192         return shardingStrategy instanceof HintShardingStrategy;
193     }
194     
195     private List<ShardingConditionValue> getDatabaseShardingValuesFromHint() {
196         if (isRoutingBySQLHint()) {
197             return getDatabaseShardingValuesFromSQLHint();
198         }
199         return getShardingConditions(HintManager.isDatabaseShardingOnly() ? HintManager.getDatabaseShardingValues() : HintManager.getDatabaseShardingValues(logicTableName));
200     }
201     
202     private List<ShardingConditionValue> getDatabaseShardingValuesFromSQLHint() {
203         Collection<Comparable<?>> shardingValues = new LinkedList<>();
204         Collection<String> tableNames = sqlStatementContext.getTablesContext().getTableNames();
205         for (String each : tableNames) {
206             if (each.equals(logicTableName) && hintValueContext.containsHintShardingDatabaseValue(each)) {
207                 shardingValues.addAll(hintValueContext.getHintShardingDatabaseValue(each));
208             }
209         }
210         return getShardingConditions(shardingValues);
211     }
212     
213     private List<ShardingConditionValue> getTableShardingValuesFromHint() {
214         if (isRoutingBySQLHint()) {
215             return getTableShardingValuesFromSQLHint();
216         }
217         return getShardingConditions(HintManager.getTableShardingValues(logicTableName));
218     }
219     
220     private List<ShardingConditionValue> getTableShardingValuesFromSQLHint() {
221         Collection<Comparable<?>> shardingValues = new LinkedList<>();
222         Collection<String> tableNames = sqlStatementContext.getTablesContext().getTableNames();
223         for (String each : tableNames) {
224             if (each.equals(logicTableName) && hintValueContext.containsHintShardingTableValue(each)) {
225                 shardingValues.addAll(hintValueContext.getHintShardingTableValue(each));
226             }
227         }
228         return getShardingConditions(shardingValues);
229     }
230     
231     private List<ShardingConditionValue> getShardingConditions(final Collection<Comparable<?>> shardingValue) {
232         return shardingValue.isEmpty() ? Collections.emptyList() : Collections.singletonList(new ListShardingConditionValue<>("", logicTableName, shardingValue));
233     }
234     
235     private List<ShardingConditionValue> getShardingValuesFromShardingConditions(final ShardingRule shardingRule, final Collection<String> shardingColumns, final ShardingCondition shardingCondition) {
236         List<ShardingConditionValue> result = new ArrayList<>(shardingColumns.size());
237         for (ShardingConditionValue each : shardingCondition.getValues()) {
238             Optional<BindingTableRule> bindingTableRule = shardingRule.findBindingTableRule(each.getTableName());
239             if ((logicTableName.equalsIgnoreCase(each.getTableName()) || bindingTableRule.isPresent() && bindingTableRule.get().hasLogicTable(logicTableName))
240                     && new CaseInsensitiveSet<>(shardingColumns).contains(each.getColumnName())) {
241                 result.add(each);
242             }
243         }
244         return result;
245     }
246     
247     private Collection<DataNode> route0(final ShardingTable shardingTable,
248                                         final ShardingStrategy databaseShardingStrategy, final List<ShardingConditionValue> databaseShardingValues,
249                                         final ShardingStrategy tableShardingStrategy, final List<ShardingConditionValue> tableShardingValues) {
250         Collection<String> routedDataSources = routeDataSources(shardingTable, databaseShardingStrategy, databaseShardingValues);
251         Collection<DataNode> result = new LinkedList<>();
252         for (String each : routedDataSources) {
253             result.addAll(routeTables(shardingTable, each, tableShardingStrategy, tableShardingValues));
254         }
255         return result;
256     }
257     
258     private Collection<String> routeDataSources(final ShardingTable shardingTable, final ShardingStrategy databaseShardingStrategy, final List<ShardingConditionValue> databaseShardingValues) {
259         if (databaseShardingValues.isEmpty()) {
260             return shardingTable.getActualDataSourceNames();
261         }
262         Collection<String> result = databaseShardingStrategy.doSharding(shardingTable.getActualDataSourceNames(), databaseShardingValues, shardingTable.getDataSourceDataNode(), props);
263         ShardingSpherePreconditions.checkNotEmpty(result, NoShardingDatabaseRouteInfoException::new);
264         ShardingSpherePreconditions.checkState(shardingTable.getActualDataSourceNames().containsAll(result),
265                 () -> new MismatchedShardingDataSourceRouteInfoException(result, shardingTable.getActualDataSourceNames()));
266         return result;
267     }
268     
269     private Collection<DataNode> routeTables(final ShardingTable shardingTable, final String routedDataSource,
270                                              final ShardingStrategy tableShardingStrategy, final List<ShardingConditionValue> tableShardingValues) {
271         Collection<String> availableTargetTables = shardingTable.getActualTableNames(routedDataSource);
272         Collection<String> routedTables = tableShardingValues.isEmpty()
273                 ? availableTargetTables
274                 : tableShardingStrategy.doSharding(availableTargetTables, tableShardingValues, shardingTable.getTableDataNode(), props);
275         Collection<DataNode> result = new LinkedList<>();
276         for (String each : routedTables) {
277             result.add(new DataNode(routedDataSource, each));
278         }
279         return result;
280     }
281     
282     private ShardingStrategy createShardingStrategy(final ShardingStrategyConfiguration shardingStrategyConfig, final Map<String, ShardingAlgorithm> shardingAlgorithms,
283                                                     final String defaultShardingColumn) {
284         return null == shardingStrategyConfig ? new NoneShardingStrategy()
285                 : ShardingStrategyFactory.newInstance(shardingStrategyConfig, shardingAlgorithms.get(shardingStrategyConfig.getShardingAlgorithmName()), defaultShardingColumn);
286     }
287 }