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;
19  
20  import com.cedarsoftware.util.CaseInsensitiveMap;
21  import com.google.common.base.Splitter;
22  import com.google.common.base.Strings;
23  import lombok.Getter;
24  import org.apache.shardingsphere.infra.algorithm.core.context.AlgorithmSQLContext;
25  import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
26  import org.apache.shardingsphere.infra.algorithm.keygen.core.KeyGenerateAlgorithm;
27  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
28  import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
29  import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
30  import org.apache.shardingsphere.infra.datanode.DataNode;
31  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
32  import org.apache.shardingsphere.infra.expr.core.InlineExpressionParserFactory;
33  import org.apache.shardingsphere.infra.instance.InstanceContext;
34  import org.apache.shardingsphere.infra.instance.InstanceContextAware;
35  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
36  import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
37  import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
38  import org.apache.shardingsphere.infra.rule.scope.DatabaseRule;
39  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
40  import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
41  import org.apache.shardingsphere.sharding.api.config.rule.ShardingAutoTableRuleConfiguration;
42  import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableReferenceRuleConfiguration;
43  import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
44  import org.apache.shardingsphere.sharding.api.config.strategy.audit.ShardingAuditStrategyConfiguration;
45  import org.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;
46  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ComplexShardingStrategyConfiguration;
47  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.NoneShardingStrategyConfiguration;
48  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
49  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
50  import org.apache.shardingsphere.sharding.api.sharding.ShardingAutoTableAlgorithm;
51  import org.apache.shardingsphere.sharding.cache.ShardingCache;
52  import org.apache.shardingsphere.sharding.exception.metadata.ShardingTableRuleNotFoundException;
53  import org.apache.shardingsphere.sharding.rule.attribute.ShardingDataNodeRuleAttribute;
54  import org.apache.shardingsphere.sharding.rule.attribute.ShardingTableNamesRuleAttribute;
55  import org.apache.shardingsphere.sharding.rule.checker.ShardingRuleChecker;
56  import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
57  import org.apache.shardingsphere.sharding.spi.ShardingAuditAlgorithm;
58  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
59  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BinaryOperationExpression;
60  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
61  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.AndPredicate;
62  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.WhereSegment;
63  import org.apache.shardingsphere.sql.parser.sql.common.util.ExpressionExtractUtils;
64  
65  import javax.sql.DataSource;
66  import java.util.Arrays;
67  import java.util.Collection;
68  import java.util.Collections;
69  import java.util.HashSet;
70  import java.util.LinkedHashMap;
71  import java.util.LinkedHashSet;
72  import java.util.LinkedList;
73  import java.util.List;
74  import java.util.Map;
75  import java.util.Objects;
76  import java.util.Optional;
77  import java.util.TreeSet;
78  import java.util.function.Function;
79  import java.util.stream.Collectors;
80  
81  /**
82   * Sharding rule.
83   */
84  @Getter
85  public final class ShardingRule implements DatabaseRule {
86      
87      private static final String ALGORITHM_EXPRESSION_KEY = "algorithm-expression";
88      
89      private final ShardingRuleConfiguration configuration;
90      
91      private final Collection<String> dataSourceNames;
92      
93      private final Map<String, ShardingAlgorithm> shardingAlgorithms = new CaseInsensitiveMap<>();
94      
95      private final Map<String, KeyGenerateAlgorithm> keyGenerators = new CaseInsensitiveMap<>();
96      
97      private final Map<String, ShardingAuditAlgorithm> auditors = new CaseInsensitiveMap<>();
98      
99      private final Map<String, ShardingTable> shardingTables = new CaseInsensitiveMap<>();
100     
101     private final Map<String, BindingTableRule> bindingTableRules = new CaseInsensitiveMap<>();
102     
103     private final ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig;
104     
105     private final ShardingStrategyConfiguration defaultTableShardingStrategyConfig;
106     
107     private final ShardingAuditStrategyConfiguration defaultAuditStrategy;
108     
109     private final KeyGenerateAlgorithm defaultKeyGenerateAlgorithm;
110     
111     private final String defaultShardingColumn;
112     
113     private final ShardingCache shardingCache;
114     
115     private final RuleAttributes attributes;
116     
117     private final ShardingRuleChecker shardingRuleChecker = new ShardingRuleChecker(this);
118     
119     public ShardingRule(final ShardingRuleConfiguration ruleConfig, final Map<String, DataSource> dataSources, final InstanceContext instanceContext) {
120         configuration = ruleConfig;
121         this.dataSourceNames = getDataSourceNames(ruleConfig.getTables(), ruleConfig.getAutoTables(), dataSources.keySet());
122         ruleConfig.getShardingAlgorithms().forEach((key, value) -> shardingAlgorithms.put(key, TypedSPILoader.getService(ShardingAlgorithm.class, value.getType(), value.getProps())));
123         ruleConfig.getKeyGenerators().forEach((key, value) -> keyGenerators.put(key, TypedSPILoader.getService(KeyGenerateAlgorithm.class, value.getType(), value.getProps())));
124         ruleConfig.getAuditors().forEach((key, value) -> auditors.put(key, TypedSPILoader.getService(ShardingAuditAlgorithm.class, value.getType(), value.getProps())));
125         shardingTables.putAll(createShardingTables(ruleConfig.getTables(), ruleConfig.getDefaultKeyGenerateStrategy()));
126         shardingTables.putAll(createShardingAutoTables(ruleConfig.getAutoTables(), ruleConfig.getDefaultKeyGenerateStrategy()));
127         bindingTableRules.putAll(createBindingTableRules(ruleConfig.getBindingTableGroups()));
128         defaultDatabaseShardingStrategyConfig = createDefaultDatabaseShardingStrategyConfiguration(ruleConfig);
129         defaultTableShardingStrategyConfig = createDefaultTableShardingStrategyConfiguration(ruleConfig);
130         defaultAuditStrategy = null == ruleConfig.getDefaultAuditStrategy() ? new ShardingAuditStrategyConfiguration(Collections.emptyList(), true) : ruleConfig.getDefaultAuditStrategy();
131         defaultKeyGenerateAlgorithm = null == ruleConfig.getDefaultKeyGenerateStrategy()
132                 ? TypedSPILoader.getService(KeyGenerateAlgorithm.class, null)
133                 : keyGenerators.get(ruleConfig.getDefaultKeyGenerateStrategy().getKeyGeneratorName());
134         defaultShardingColumn = ruleConfig.getDefaultShardingColumn();
135         keyGenerators.values().stream().filter(InstanceContextAware.class::isInstance).forEach(each -> ((InstanceContextAware) each).setInstanceContext(instanceContext));
136         if (defaultKeyGenerateAlgorithm instanceof InstanceContextAware && -1 == instanceContext.getWorkerId()) {
137             ((InstanceContextAware) defaultKeyGenerateAlgorithm).setInstanceContext(instanceContext);
138         }
139         shardingCache = null == ruleConfig.getShardingCache() ? null : new ShardingCache(ruleConfig.getShardingCache(), this);
140         attributes = new RuleAttributes(new ShardingDataNodeRuleAttribute(shardingTables), new ShardingTableNamesRuleAttribute(shardingTables.values()));
141         shardingRuleChecker.check(ruleConfig);
142     }
143     
144     private ShardingStrategyConfiguration createDefaultDatabaseShardingStrategyConfiguration(final ShardingRuleConfiguration ruleConfig) {
145         Optional.ofNullable(ruleConfig.getDefaultDatabaseShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), "default"));
146         return null == ruleConfig.getDefaultDatabaseShardingStrategy() ? new NoneShardingStrategyConfiguration() : ruleConfig.getDefaultDatabaseShardingStrategy();
147     }
148     
149     private ShardingStrategyConfiguration createDefaultTableShardingStrategyConfiguration(final ShardingRuleConfiguration ruleConfig) {
150         Optional.ofNullable(ruleConfig.getDefaultTableShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), "default"));
151         return null == ruleConfig.getDefaultTableShardingStrategy() ? new NoneShardingStrategyConfiguration() : ruleConfig.getDefaultTableShardingStrategy();
152     }
153     
154     private Collection<String> getDataSourceNames(final Collection<ShardingTableRuleConfiguration> tableRuleConfigs,
155                                                   final Collection<ShardingAutoTableRuleConfiguration> autoTableRuleConfigs, final Collection<String> dataSourceNames) {
156         if (tableRuleConfigs.isEmpty() && autoTableRuleConfigs.isEmpty()) {
157             return dataSourceNames;
158         }
159         if (tableRuleConfigs.stream().map(ShardingTableRuleConfiguration::getActualDataNodes).anyMatch(each -> null == each || each.isEmpty())) {
160             return dataSourceNames;
161         }
162         Collection<String> result = new LinkedHashSet<>();
163         tableRuleConfigs.forEach(each -> result.addAll(getDataSourceNames(each)));
164         autoTableRuleConfigs.forEach(each -> result.addAll(getDataSourceNames(each)));
165         return result;
166     }
167     
168     private Collection<String> getDataSourceNames(final ShardingAutoTableRuleConfiguration shardingAutoTableRuleConfig) {
169         List<String> actualDataSources = InlineExpressionParserFactory.newInstance(shardingAutoTableRuleConfig.getActualDataSources()).splitAndEvaluate();
170         return new HashSet<>(actualDataSources);
171     }
172     
173     private Collection<String> getDataSourceNames(final ShardingTableRuleConfiguration shardingTableRuleConfig) {
174         List<String> actualDataNodes = InlineExpressionParserFactory.newInstance(shardingTableRuleConfig.getActualDataNodes()).splitAndEvaluate();
175         return actualDataNodes.stream().map(each -> new DataNode(each).getDataSourceName()).collect(Collectors.toList());
176     }
177     
178     private Map<String, ShardingTable> createShardingTables(final Collection<ShardingTableRuleConfiguration> tableRuleConfigs,
179                                                             final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
180         return tableRuleConfigs.stream().map(each -> createShardingTable(each, defaultKeyGenerateStrategyConfig))
181                 .collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, CaseInsensitiveMap::new));
182     }
183     
184     private ShardingTable createShardingTable(final ShardingTableRuleConfiguration tableRuleConfig, final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
185         Optional.ofNullable(tableRuleConfig.getDatabaseShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), tableRuleConfig.getLogicTable()));
186         Optional.ofNullable(tableRuleConfig.getTableShardingStrategy()).ifPresent(optional -> checkManualShardingAlgorithm(optional.getShardingAlgorithmName(), tableRuleConfig.getLogicTable()));
187         return new ShardingTable(tableRuleConfig, dataSourceNames, getDefaultGenerateKeyColumn(defaultKeyGenerateStrategyConfig));
188     }
189     
190     private void checkManualShardingAlgorithm(final String shardingAlgorithmName, final String logicTable) {
191         ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(shardingAlgorithmName);
192         ShardingSpherePreconditions.checkState(!(shardingAlgorithm instanceof ShardingAutoTableAlgorithm),
193                 () -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` tables sharding configuration can not use auto sharding algorithm.", logicTable));
194     }
195     
196     private Map<String, ShardingTable> createShardingAutoTables(final Collection<ShardingAutoTableRuleConfiguration> autoTableRuleConfigs,
197                                                                 final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
198         return autoTableRuleConfigs.stream().map(each -> createShardingAutoTable(defaultKeyGenerateStrategyConfig, each))
199                 .collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, CaseInsensitiveMap::new));
200     }
201     
202     private ShardingTable createShardingAutoTable(final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig, final ShardingAutoTableRuleConfiguration autoTableRuleConfig) {
203         checkAutoShardingAlgorithm(autoTableRuleConfig.getShardingStrategy().getShardingAlgorithmName(), autoTableRuleConfig.getLogicTable());
204         ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(autoTableRuleConfig.getShardingStrategy().getShardingAlgorithmName());
205         return new ShardingTable(autoTableRuleConfig, dataSourceNames, (ShardingAutoTableAlgorithm) shardingAlgorithm, getDefaultGenerateKeyColumn(defaultKeyGenerateStrategyConfig));
206     }
207     
208     private void checkAutoShardingAlgorithm(final String shardingAlgorithmName, final String logicTable) {
209         ShardingAlgorithm shardingAlgorithm = shardingAlgorithms.get(shardingAlgorithmName);
210         ShardingSpherePreconditions.checkState(shardingAlgorithm instanceof ShardingAutoTableAlgorithm,
211                 () -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` autoTables sharding configuration must use auto sharding algorithm.", logicTable));
212     }
213     
214     private String getDefaultGenerateKeyColumn(final KeyGenerateStrategyConfiguration defaultKeyGenerateStrategyConfig) {
215         return Optional.ofNullable(defaultKeyGenerateStrategyConfig).map(KeyGenerateStrategyConfiguration::getColumn).orElse(null);
216     }
217     
218     private Map<String, BindingTableRule> createBindingTableRules(final Collection<ShardingTableReferenceRuleConfiguration> bindingTableGroups) {
219         Map<String, BindingTableRule> result = new LinkedHashMap<>();
220         for (ShardingTableReferenceRuleConfiguration each : bindingTableGroups) {
221             BindingTableRule bindingTableRule = createBindingTableRule(each.getReference());
222             for (String logicTable : bindingTableRule.getAllLogicTables()) {
223                 result.put(logicTable, bindingTableRule);
224             }
225         }
226         return result;
227     }
228     
229     private BindingTableRule createBindingTableRule(final String bindingTableGroup) {
230         Map<String, ShardingTable> shardingTables = Splitter.on(",").trimResults().splitToList(bindingTableGroup).stream()
231                 .map(this::getShardingTable).collect(Collectors.toMap(ShardingTable::getLogicTable, Function.identity(), (oldValue, currentValue) -> oldValue, LinkedHashMap::new));
232         BindingTableRule result = new BindingTableRule();
233         result.getShardingTables().putAll(shardingTables);
234         return result;
235     }
236     
237     /**
238      * Get database sharding strategy configuration.
239      *
240      * @param shardingTable sharding table
241      * @return database sharding strategy configuration
242      */
243     public ShardingStrategyConfiguration getDatabaseShardingStrategyConfiguration(final ShardingTable shardingTable) {
244         return getDatabaseShardingStrategyConfiguration(shardingTable, defaultDatabaseShardingStrategyConfig);
245     }
246     
247     private ShardingStrategyConfiguration getDatabaseShardingStrategyConfiguration(final ShardingTable shardingTable, final ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig) {
248         return null == shardingTable.getDatabaseShardingStrategyConfig() ? defaultDatabaseShardingStrategyConfig : shardingTable.getDatabaseShardingStrategyConfig();
249     }
250     
251     /**
252      * Get table sharding strategy configuration.
253      *
254      * @param shardingTable sharding table
255      * @return table sharding strategy configuration
256      */
257     public ShardingStrategyConfiguration getTableShardingStrategyConfiguration(final ShardingTable shardingTable) {
258         return getTableShardingStrategyConfiguration(shardingTable, defaultTableShardingStrategyConfig);
259     }
260     
261     private ShardingStrategyConfiguration getTableShardingStrategyConfiguration(final ShardingTable shardingTable, final ShardingStrategyConfiguration defaultTableShardingStrategyConfig) {
262         return null == shardingTable.getTableShardingStrategyConfig() ? defaultTableShardingStrategyConfig : shardingTable.getTableShardingStrategyConfig();
263     }
264     
265     /**
266      * Get audit strategy configuration.
267      *
268      * @param shardingTable sharding table
269      * @return audit strategy configuration
270      */
271     public ShardingAuditStrategyConfiguration getAuditStrategyConfiguration(final ShardingTable shardingTable) {
272         return null == shardingTable.getAuditStrategyConfig() ? defaultAuditStrategy : shardingTable.getAuditStrategyConfig();
273     }
274     
275     /**
276      * Find sharding table.
277      *
278      * @param logicTableName logic table name
279      * @return sharding table
280      */
281     public Optional<ShardingTable> findShardingTable(final String logicTableName) {
282         if (Strings.isNullOrEmpty(logicTableName) || !shardingTables.containsKey(logicTableName)) {
283             return Optional.empty();
284         }
285         return Optional.of(shardingTables.get(logicTableName));
286     }
287     
288     /**
289      * Find sharding table via actual table name.
290      *
291      * @param actualTableName actual table name
292      * @return sharding table
293      */
294     public Optional<ShardingTable> findShardingTableByActualTable(final String actualTableName) {
295         for (ShardingTable each : shardingTables.values()) {
296             if (each.isExisted(actualTableName)) {
297                 return Optional.of(each);
298             }
299         }
300         return Optional.empty();
301     }
302     
303     /**
304      * Get sharding table.
305      *
306      * @param logicTableName logic table name
307      * @return sharding table
308      * @throws ShardingTableRuleNotFoundException sharding table rule not found exception
309      */
310     public ShardingTable getShardingTable(final String logicTableName) {
311         Optional<ShardingTable> shardingTable = findShardingTable(logicTableName);
312         ShardingSpherePreconditions.checkState(shardingTable.isPresent(), () -> new ShardingTableRuleNotFoundException(Collections.singleton(logicTableName)));
313         return shardingTable.get();
314     }
315     
316     /**
317      * Judge whether logic table is all binding tables or not.
318      *
319      * @param logicTableNames logic table names
320      * @return whether logic table is all binding tables or not
321      */
322     public boolean isAllBindingTables(final Collection<String> logicTableNames) {
323         if (logicTableNames.isEmpty()) {
324             return false;
325         }
326         Optional<BindingTableRule> bindingTableRule = findBindingTableRule(logicTableNames);
327         if (!bindingTableRule.isPresent()) {
328             return false;
329         }
330         Collection<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
331         result.addAll(bindingTableRule.get().getAllLogicTables());
332         return !result.isEmpty() && result.containsAll(logicTableNames);
333     }
334     
335     /**
336      * Judge whether logic table is all binding tables.
337      *
338      * @param database database
339      * @param sqlStatementContext sqlStatementContext
340      * @param logicTableNames logic table names
341      * @return whether logic table is all binding tables
342      */
343     public boolean isAllBindingTables(final ShardingSphereDatabase database, final SQLStatementContext sqlStatementContext, final Collection<String> logicTableNames) {
344         if (!(sqlStatementContext instanceof SelectStatementContext && ((SelectStatementContext) sqlStatementContext).isContainsJoinQuery())) {
345             return isAllBindingTables(logicTableNames);
346         }
347         if (!isAllBindingTables(logicTableNames)) {
348             return false;
349         }
350         String defaultSchemaName = new DatabaseTypeRegistry(sqlStatementContext.getDatabaseType()).getDefaultSchemaName(database.getName());
351         ShardingSphereSchema schema = sqlStatementContext.getTablesContext().getSchemaName().map(database::getSchema).orElseGet(() -> database.getSchema(defaultSchemaName));
352         SelectStatementContext select = (SelectStatementContext) sqlStatementContext;
353         return isJoinConditionContainsShardingColumns(schema, select, logicTableNames, select.getWhereSegments());
354     }
355     
356     private Optional<BindingTableRule> findBindingTableRule(final Collection<String> logicTableNames) {
357         for (String each : logicTableNames) {
358             Optional<BindingTableRule> result = findBindingTableRule(each);
359             if (result.isPresent()) {
360                 return result;
361             }
362         }
363         return Optional.empty();
364     }
365     
366     /**
367      * Find binding table rule via logic table name.
368      *
369      * @param logicTableName logic table name
370      * @return binding table rule
371      */
372     public Optional<BindingTableRule> findBindingTableRule(final String logicTableName) {
373         return Optional.ofNullable(bindingTableRules.get(logicTableName));
374     }
375     
376     /**
377      * Judge whether logic table is all sharding table or not.
378      *
379      * @param logicTableNames logic table names
380      * @return whether logic table is all sharding table or not
381      */
382     public boolean isAllShardingTables(final Collection<String> logicTableNames) {
383         if (logicTableNames.isEmpty()) {
384             return false;
385         }
386         for (String each : logicTableNames) {
387             if (!isShardingTable(each)) {
388                 return false;
389             }
390         }
391         return true;
392     }
393     
394     /**
395      * Judge whether logic table is sharding table or not.
396      *
397      * @param logicTableName logic table name
398      * @return whether logic table is sharding table or not
399      */
400     public boolean isShardingTable(final String logicTableName) {
401         return shardingTables.containsKey(logicTableName);
402     }
403     
404     /**
405      * Judge whether all tables are in same data source or not.
406      *
407      * @param logicTableNames logic table names
408      * @return whether all tables are in same data source or not
409      */
410     public boolean isAllTablesInSameDataSource(final Collection<String> logicTableNames) {
411         Collection<String> dataSourceNames = logicTableNames.stream().map(shardingTables::get)
412                 .filter(Objects::nonNull).flatMap(each -> each.getActualDataSourceNames().stream()).collect(Collectors.toSet());
413         return 1 == dataSourceNames.size();
414     }
415     
416     /**
417      * Judge whether contains sharding table or not.
418      *
419      * @param logicTableNames logic table names
420      * @return whether contains sharding table or not
421      */
422     public boolean containsShardingTable(final Collection<String> logicTableNames) {
423         for (String each : logicTableNames) {
424             if (isShardingTable(each)) {
425                 return true;
426             }
427         }
428         return false;
429     }
430     
431     /**
432      * Find sharding column.
433      *
434      * @param columnName column name
435      * @param tableName table name
436      * @return sharding column
437      */
438     public Optional<String> findShardingColumn(final String columnName, final String tableName) {
439         return Optional.ofNullable(shardingTables.get(tableName)).flatMap(optional -> findShardingColumn(optional, columnName));
440     }
441     
442     private Optional<String> findShardingColumn(final ShardingTable shardingTable, final String columnName) {
443         Optional<String> databaseShardingColumn = findShardingColumn(getDatabaseShardingStrategyConfiguration(shardingTable), columnName);
444         if (databaseShardingColumn.isPresent()) {
445             return databaseShardingColumn;
446         }
447         return findShardingColumn(getTableShardingStrategyConfiguration(shardingTable), columnName);
448     }
449     
450     private Optional<String> findShardingColumn(final ShardingStrategyConfiguration shardingStrategyConfig, final String columnName) {
451         if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
452             String shardingColumn = null == ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn()
453                     ? defaultShardingColumn
454                     : ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn();
455             return shardingColumn.equalsIgnoreCase(columnName) ? Optional.of(shardingColumn) : Optional.empty();
456         }
457         if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
458             List<String> shardingColumns = Splitter.on(",").trimResults().splitToList(((ComplexShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumns());
459             for (String each : shardingColumns) {
460                 if (each.equalsIgnoreCase(columnName)) {
461                     return Optional.of(each);
462                 }
463             }
464         }
465         return Optional.empty();
466     }
467     
468     /**
469      * Judge whether given logic table column is generate key column or not.
470      *
471      * @param columnName column name
472      * @param tableName table name
473      * @return whether given logic table column is generate key column or not
474      */
475     public boolean isGenerateKeyColumn(final String columnName, final String tableName) {
476         return Optional.ofNullable(shardingTables.get(tableName)).filter(each -> isGenerateKeyColumn(each, columnName)).isPresent();
477     }
478     
479     private boolean isGenerateKeyColumn(final ShardingTable shardingTable, final String columnName) {
480         Optional<String> generateKeyColumn = shardingTable.getGenerateKeyColumn();
481         return generateKeyColumn.isPresent() && generateKeyColumn.get().equalsIgnoreCase(columnName);
482     }
483     
484     /**
485      * Find column name of generated key.
486      *
487      * @param logicTableName logic table name
488      * @return column name of generated key
489      */
490     public Optional<String> findGenerateKeyColumnName(final String logicTableName) {
491         return Optional.ofNullable(shardingTables.get(logicTableName)).filter(each -> each.getGenerateKeyColumn().isPresent()).flatMap(ShardingTable::getGenerateKeyColumn);
492     }
493     
494     /**
495      * Find the generated keys of logic table.
496      *
497      * @param algorithmSQLContext key generate context 
498      * @param keyGenerateCount key generate count
499      * @return generated keys
500      */
501     public Collection<? extends Comparable<?>> generateKeys(final AlgorithmSQLContext algorithmSQLContext, final int keyGenerateCount) {
502         return getKeyGenerateAlgorithm(algorithmSQLContext.getTableName()).generateKeys(algorithmSQLContext, keyGenerateCount);
503     }
504     
505     /**
506      * Judge whether support auto increment or not.
507      * 
508      * @param logicTableName logic table name
509      * @return whether support auto increment or not
510      */
511     public boolean isSupportAutoIncrement(final String logicTableName) {
512         return getKeyGenerateAlgorithm(logicTableName).isSupportAutoIncrement();
513     }
514     
515     private KeyGenerateAlgorithm getKeyGenerateAlgorithm(final String logicTableName) {
516         ShardingTable shardingTable = getShardingTable(logicTableName);
517         return null == shardingTable.getKeyGeneratorName() ? defaultKeyGenerateAlgorithm : keyGenerators.get(shardingTable.getKeyGeneratorName());
518     }
519     
520     /**
521      * Find data node by logic table name.
522      *
523      * @param logicTableName logic table name
524      * @return data node
525      */
526     public DataNode getDataNode(final String logicTableName) {
527         ShardingTable shardingTable = getShardingTable(logicTableName);
528         return shardingTable.getActualDataNodes().get(0);
529     }
530     
531     /**
532      * Get sharding logic table names.
533      *
534      * @param logicTableNames logic table names
535      * @return sharding logic table names
536      */
537     public Collection<String> getShardingLogicTableNames(final Collection<String> logicTableNames) {
538         Collection<String> result = new LinkedList<>();
539         for (String each : logicTableNames) {
540             if (isShardingTable(each)) {
541                 result.add(each);
542             }
543         }
544         return result;
545     }
546     
547     /**
548      * Get sharding rule table names.
549      *
550      * @param logicTableNames logic table names
551      * @return sharding rule table names
552      */
553     public Collection<String> getShardingRuleTableNames(final Collection<String> logicTableNames) {
554         return logicTableNames.stream().filter(this::isShardingTable).collect(Collectors.toList());
555     }
556     
557     /**
558      * Get logic and actual binding tables.
559      *
560      * @param dataSourceName data source name
561      * @param logicTable logic table name
562      * @param actualTable actual table name
563      * @param availableLogicBindingTables available logic binding table names
564      * @return logic and actual binding tables
565      */
566     public Map<String, String> getLogicAndActualTablesFromBindingTable(final String dataSourceName,
567                                                                        final String logicTable, final String actualTable, final Collection<String> availableLogicBindingTables) {
568         return findBindingTableRule(logicTable).map(optional -> optional.getLogicAndActualTables(dataSourceName, logicTable, actualTable, availableLogicBindingTables))
569                 .orElseGet(Collections::emptyMap);
570     }
571     
572     /**
573      * Is sharding cache enabled.
574      *
575      * @return is sharding cache enabled
576      */
577     public boolean isShardingCacheEnabled() {
578         return null != shardingCache;
579     }
580     
581     private boolean isJoinConditionContainsShardingColumns(final ShardingSphereSchema schema, final SelectStatementContext select,
582                                                            final Collection<String> tableNames, final Collection<WhereSegment> whereSegments) {
583         Collection<String> databaseJoinConditionTables = new HashSet<>(tableNames.size(), 1F);
584         Collection<String> tableJoinConditionTables = new HashSet<>(tableNames.size(), 1F);
585         for (WhereSegment each : whereSegments) {
586             Collection<AndPredicate> andPredicates = ExpressionExtractUtils.getAndPredicates(each.getExpr());
587             if (andPredicates.size() > 1) {
588                 return false;
589             }
590             for (AndPredicate andPredicate : andPredicates) {
591                 databaseJoinConditionTables.addAll(getJoinConditionTables(schema, select, andPredicate.getPredicates(), true));
592                 tableJoinConditionTables.addAll(getJoinConditionTables(schema, select, andPredicate.getPredicates(), false));
593             }
594         }
595         ShardingTable shardingTable = getShardingTable(tableNames.iterator().next());
596         boolean containsDatabaseShardingColumns = !(getDatabaseShardingStrategyConfiguration(shardingTable) instanceof StandardShardingStrategyConfiguration)
597                 || databaseJoinConditionTables.containsAll(tableNames);
598         boolean containsTableShardingColumns =
599                 !(getTableShardingStrategyConfiguration(shardingTable) instanceof StandardShardingStrategyConfiguration) || tableJoinConditionTables.containsAll(tableNames);
600         return containsDatabaseShardingColumns && containsTableShardingColumns;
601     }
602     
603     private Collection<String> getJoinConditionTables(final ShardingSphereSchema schema, final SelectStatementContext select,
604                                                       final Collection<ExpressionSegment> predicates, final boolean isDatabaseJoinCondition) {
605         Collection<String> result = new LinkedList<>();
606         for (ExpressionSegment each : predicates) {
607             if (!isJoinConditionExpression(each)) {
608                 continue;
609             }
610             ColumnSegment leftColumn = (ColumnSegment) ((BinaryOperationExpression) each).getLeft();
611             ColumnSegment rightColumn = (ColumnSegment) ((BinaryOperationExpression) each).getRight();
612             Map<String, String> columnExpressionTableNames = select.getTablesContext().findTableNamesByColumnSegment(Arrays.asList(leftColumn, rightColumn), schema);
613             Optional<ShardingTable> leftShardingTable = findShardingTable(columnExpressionTableNames.get(leftColumn.getExpression()));
614             Optional<ShardingTable> rightShardingTable = findShardingTable(columnExpressionTableNames.get(rightColumn.getExpression()));
615             if (!leftShardingTable.isPresent() || !rightShardingTable.isPresent()) {
616                 continue;
617             }
618             ShardingStrategyConfiguration leftConfig = isDatabaseJoinCondition
619                     ? getDatabaseShardingStrategyConfiguration(leftShardingTable.get())
620                     : getTableShardingStrategyConfiguration(leftShardingTable.get());
621             ShardingStrategyConfiguration rightConfig = isDatabaseJoinCondition
622                     ? getDatabaseShardingStrategyConfiguration(rightShardingTable.get())
623                     : getTableShardingStrategyConfiguration(rightShardingTable.get());
624             if (findShardingColumn(leftConfig, leftColumn.getIdentifier().getValue()).isPresent()
625                     && findShardingColumn(rightConfig, rightColumn.getIdentifier().getValue()).isPresent()) {
626                 result.add(columnExpressionTableNames.get(leftColumn.getExpression()));
627                 result.add(columnExpressionTableNames.get(rightColumn.getExpression()));
628             }
629         }
630         return result;
631     }
632     
633     private boolean isJoinConditionExpression(final ExpressionSegment expression) {
634         if (!(expression instanceof BinaryOperationExpression)) {
635             return false;
636         }
637         BinaryOperationExpression binaryExpression = (BinaryOperationExpression) expression;
638         return binaryExpression.getLeft() instanceof ColumnSegment && binaryExpression.getRight() instanceof ColumnSegment && "=".equals(binaryExpression.getOperator());
639     }
640 }