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.single.route.engine;
19  
20  import lombok.RequiredArgsConstructor;
21  import org.apache.shardingsphere.infra.exception.dialect.exception.syntax.table.TableExistsException;
22  import org.apache.shardingsphere.infra.datanode.DataNode;
23  import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedTable;
24  import org.apache.shardingsphere.infra.route.context.RouteContext;
25  import org.apache.shardingsphere.infra.route.context.RouteMapper;
26  import org.apache.shardingsphere.infra.route.context.RouteUnit;
27  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
28  import org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
29  import org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
30  import org.apache.shardingsphere.single.exception.SingleTableNotFoundException;
31  import org.apache.shardingsphere.single.rule.SingleRule;
32  import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
33  import org.apache.shardingsphere.sql.parser.sql.common.statement.ddl.CreateTableStatement;
34  import org.apache.shardingsphere.sql.parser.sql.common.statement.ddl.DDLStatement;
35  import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
36  import org.apache.shardingsphere.sql.parser.sql.dialect.handler.ddl.CreateTableStatementHandler;
37  
38  import java.util.Collection;
39  import java.util.Collections;
40  import java.util.LinkedList;
41  import java.util.Map;
42  import java.util.Map.Entry;
43  import java.util.Optional;
44  import java.util.function.Function;
45  import java.util.stream.Collectors;
46  
47  /**
48   * Single standard route engine.
49   */
50  @RequiredArgsConstructor
51  public final class SingleStandardRouteEngine implements SingleRouteEngine {
52      
53      private final Collection<QualifiedTable> singleTables;
54      
55      private final SQLStatement sqlStatement;
56      
57      @Override
58      public void route(final RouteContext routeContext, final SingleRule singleRule) {
59          if (routeContext.getRouteUnits().isEmpty() || sqlStatement instanceof SelectStatement) {
60              routeStatement(routeContext, singleRule);
61          } else {
62              RouteContext newRouteContext = new RouteContext();
63              routeStatement(newRouteContext, singleRule);
64              combineRouteContext(routeContext, newRouteContext);
65          }
66      }
67      
68      private void routeStatement(final RouteContext routeContext, final SingleRule rule) {
69          if (sqlStatement instanceof DDLStatement) {
70              routeDDLStatement(routeContext, rule);
71          } else {
72              boolean allTablesInSameComputeNode = rule.isAllTablesInSameComputeNode(getDataNodes(routeContext), singleTables);
73              ShardingSpherePreconditions.checkState(allTablesInSameComputeNode, () -> new UnsupportedSQLOperationException("all tables must be in the same compute node"));
74              fillRouteContext(rule, routeContext, singleTables);
75          }
76      }
77      
78      private Collection<DataNode> getDataNodes(final RouteContext routeContext) {
79          Collection<DataNode> result = new LinkedList<>();
80          for (Collection<DataNode> each : routeContext.getOriginalDataNodes()) {
81              result.addAll(each);
82          }
83          return result;
84      }
85      
86      private void routeDDLStatement(final RouteContext routeContext, final SingleRule rule) {
87          if (sqlStatement instanceof CreateTableStatement) {
88              QualifiedTable table = singleTables.iterator().next();
89              Optional<DataNode> dataNode = rule.getAttributes().getAttribute(MutableDataNodeRuleAttribute.class).findTableDataNode(table.getSchemaName(), table.getTableName());
90              boolean containsIfNotExists = CreateTableStatementHandler.ifNotExists((CreateTableStatement) sqlStatement);
91              if (dataNode.isPresent() && containsIfNotExists) {
92                  String dataSourceName = dataNode.map(DataNode::getDataSourceName).orElse(null);
93                  routeContext.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName, dataSourceName), Collections.singleton(new RouteMapper(table.getTableName(), table.getTableName()))));
94              } else if (dataNode.isPresent()) {
95                  throw new TableExistsException(table.getTableName());
96              } else {
97                  String dataSourceName = rule.assignNewDataSourceName();
98                  routeContext.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName, dataSourceName), Collections.singleton(new RouteMapper(table.getTableName(), table.getTableName()))));
99              }
100         } else {
101             fillRouteContext(rule, routeContext, singleTables);
102         }
103     }
104     
105     private void fillRouteContext(final SingleRule singleRule, final RouteContext routeContext, final Collection<QualifiedTable> logicTables) {
106         for (QualifiedTable each : logicTables) {
107             String tableName = each.getTableName();
108             Optional<DataNode> dataNode = singleRule.getAttributes().getAttribute(MutableDataNodeRuleAttribute.class).findTableDataNode(each.getSchemaName(), tableName);
109             ShardingSpherePreconditions.checkState(dataNode.isPresent(), () -> new SingleTableNotFoundException(tableName));
110             String dataSource = dataNode.get().getDataSourceName();
111             routeContext.putRouteUnit(new RouteMapper(dataSource, dataSource), Collections.singletonList(new RouteMapper(tableName, tableName)));
112         }
113     }
114     
115     private void combineRouteContext(final RouteContext routeContext, final RouteContext newRouteContext) {
116         Map<String, RouteUnit> dataSourceRouteUnits = getDataSourceRouteUnits(newRouteContext);
117         routeContext.getRouteUnits().removeIf(each -> !dataSourceRouteUnits.containsKey(each.getDataSourceMapper().getLogicName()));
118         for (Entry<String, RouteUnit> entry : dataSourceRouteUnits.entrySet()) {
119             routeContext.putRouteUnit(entry.getValue().getDataSourceMapper(), entry.getValue().getTableMappers());
120         }
121     }
122     
123     private Map<String, RouteUnit> getDataSourceRouteUnits(final RouteContext newRouteContext) {
124         return newRouteContext.getRouteUnits().stream().collect(Collectors.toMap(each -> each.getDataSourceMapper().getLogicName(), Function.identity()));
125     }
126 }