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.infra.route.engine;
19  
20  import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
21  import org.apache.shardingsphere.infra.binder.context.extractor.SQLStatementContextExtractor;
22  import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
23  import org.apache.shardingsphere.infra.exception.kernel.syntax.hint.DataSourceHintNotExistsException;
24  import org.apache.shardingsphere.infra.hint.HintManager;
25  import org.apache.shardingsphere.infra.hint.HintValueContext;
26  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
27  import org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
28  import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
29  import org.apache.shardingsphere.infra.route.SQLRouter;
30  import org.apache.shardingsphere.infra.route.SQLRouter.Type;
31  import org.apache.shardingsphere.infra.route.context.RouteContext;
32  import org.apache.shardingsphere.infra.route.context.RouteMapper;
33  import org.apache.shardingsphere.infra.route.context.RouteUnit;
34  import org.apache.shardingsphere.infra.route.engine.tableless.router.TablelessSQLRouter;
35  import org.apache.shardingsphere.infra.route.lifecycle.DecorateSQLRouter;
36  import org.apache.shardingsphere.infra.route.lifecycle.EntranceSQLRouter;
37  import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
38  import org.apache.shardingsphere.infra.session.query.QueryContext;
39  import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
40  
41  import java.util.Collection;
42  import java.util.Collections;
43  import java.util.LinkedHashMap;
44  import java.util.Map;
45  import java.util.Map.Entry;
46  import java.util.Optional;
47  
48  /**
49   * SQL route engine.
50   */
51  @HighFrequencyInvocation
52  public final class SQLRouteEngine {
53      
54      private final ConfigurationProperties props;
55      
56      @SuppressWarnings("rawtypes")
57      private final Map<ShardingSphereRule, SQLRouter> dataNodeRouters;
58      
59      @SuppressWarnings("rawtypes")
60      private final Map<ShardingSphereRule, SQLRouter> dataSourceRouters;
61      
62      @SuppressWarnings("rawtypes")
63      public SQLRouteEngine(final Collection<ShardingSphereRule> rules, final ConfigurationProperties props) {
64          this.props = props;
65          Map<ShardingSphereRule, SQLRouter> routers = OrderedSPILoader.getServices(SQLRouter.class, rules);
66          dataNodeRouters = filterRouters(routers, Type.DATA_NODE);
67          dataSourceRouters = filterRouters(routers, Type.DATA_SOURCE);
68      }
69      
70      @SuppressWarnings("rawtypes")
71      private Map<ShardingSphereRule, SQLRouter> filterRouters(final Map<ShardingSphereRule, SQLRouter> routers, final Type type) {
72          Map<ShardingSphereRule, SQLRouter> result = new LinkedHashMap<>();
73          for (Entry<ShardingSphereRule, SQLRouter> entry : routers.entrySet()) {
74              if (type == entry.getValue().getType()) {
75                  result.put(entry.getKey(), entry.getValue());
76              }
77          }
78          return result;
79      }
80      
81      /**
82       * Route.
83       *
84       * @param queryContext query context
85       * @param globalRuleMetaData global rule meta data
86       * @param database sharding sphere database
87       * @return route context
88       */
89      public RouteContext route(final QueryContext queryContext, final RuleMetaData globalRuleMetaData, final ShardingSphereDatabase database) {
90          RouteContext result = new RouteContext();
91          Optional<String> dataSourceName = findDataSourceByHint(queryContext.getHintValueContext(), database.getResourceMetaData().getStorageUnits());
92          if (dataSourceName.isPresent()) {
93              result.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName.get(), dataSourceName.get()), Collections.emptyList()));
94              return result;
95          }
96          Collection<String> tableNames = SQLStatementContextExtractor.getTableNames(database, queryContext.getSqlStatementContext());
97          result = route(queryContext, globalRuleMetaData, database, dataNodeRouters, tableNames, result);
98          result = new TablelessSQLRouter().route(queryContext, globalRuleMetaData, database, tableNames, result);
99          result = route(queryContext, globalRuleMetaData, database, dataSourceRouters, tableNames, result);
100         if (result.getRouteUnits().isEmpty() && 1 == database.getResourceMetaData().getStorageUnits().size()) {
101             String singleDataSourceName = database.getResourceMetaData().getStorageUnits().keySet().iterator().next();
102             result.getRouteUnits().add(new RouteUnit(new RouteMapper(singleDataSourceName, singleDataSourceName), Collections.emptyList()));
103         }
104         return result;
105     }
106     
107     @SuppressWarnings({"unchecked", "rawtypes"})
108     private RouteContext route(final QueryContext queryContext, final RuleMetaData globalRuleMetaData, final ShardingSphereDatabase database,
109                                final Map<ShardingSphereRule, SQLRouter> routers, final Collection<String> tableNames, final RouteContext routeContext) {
110         RouteContext result = routeContext;
111         for (Entry<ShardingSphereRule, SQLRouter> entry : routers.entrySet()) {
112             if (result.getRouteUnits().isEmpty() && entry.getValue() instanceof EntranceSQLRouter) {
113                 result = ((EntranceSQLRouter) entry.getValue()).createRouteContext(queryContext, globalRuleMetaData, database, entry.getKey(), tableNames, props);
114             } else if (entry.getValue() instanceof DecorateSQLRouter) {
115                 ((DecorateSQLRouter) entry.getValue()).decorateRouteContext(result, queryContext, database, entry.getKey(), tableNames, props);
116             }
117         }
118         return result;
119     }
120     
121     private Optional<String> findDataSourceByHint(final HintValueContext hintValueContext, final Map<String, StorageUnit> storageUnits) {
122         Optional<String> result = HintManager.isInstantiated() && HintManager.getDataSourceName().isPresent() ? HintManager.getDataSourceName() : hintValueContext.findHintDataSourceName();
123         if (result.isPresent() && !storageUnits.containsKey(result.get())) {
124             throw new DataSourceHintNotExistsException(result.get());
125         }
126         return result;
127     }
128 }