1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.sharding.route.engine.type.unicast;
19
20 import com.google.common.collect.Sets;
21 import lombok.RequiredArgsConstructor;
22 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
23 import org.apache.shardingsphere.infra.binder.context.statement.ddl.AlterViewStatementContext;
24 import org.apache.shardingsphere.infra.binder.context.statement.ddl.CreateViewStatementContext;
25 import org.apache.shardingsphere.infra.binder.context.statement.ddl.DropViewStatementContext;
26 import org.apache.shardingsphere.infra.binder.context.type.CursorAvailable;
27 import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
28 import org.apache.shardingsphere.infra.datanode.DataNode;
29 import org.apache.shardingsphere.infra.route.context.RouteContext;
30 import org.apache.shardingsphere.infra.route.context.RouteMapper;
31 import org.apache.shardingsphere.infra.route.context.RouteUnit;
32 import org.apache.shardingsphere.sharding.exception.syntax.DataSourceIntersectionNotFoundException;
33 import org.apache.shardingsphere.sharding.route.engine.type.ShardingRouteEngine;
34 import org.apache.shardingsphere.sharding.rule.ShardingRule;
35 import org.apache.shardingsphere.sharding.rule.ShardingTable;
36
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.LinkedHashSet;
41 import java.util.List;
42 import java.util.Set;
43 import java.util.concurrent.ThreadLocalRandom;
44 import java.util.stream.Collectors;
45
46
47
48
49 @RequiredArgsConstructor
50 public final class ShardingUnicastRoutingEngine implements ShardingRouteEngine {
51
52 private final SQLStatementContext sqlStatementContext;
53
54 private final Collection<String> logicTables;
55
56 private final ConnectionContext connectionContext;
57
58 @Override
59 public RouteContext route(final ShardingRule shardingRule) {
60 RouteContext result = new RouteContext();
61 String dataSourceName = getDataSourceName(shardingRule.getDataSourceNames());
62 RouteMapper dataSourceMapper = new RouteMapper(dataSourceName, dataSourceName);
63 if (logicTables.isEmpty()) {
64 result.getRouteUnits().add(new RouteUnit(dataSourceMapper, Collections.emptyList()));
65 } else if (1 == logicTables.size()) {
66 String logicTableName = logicTables.iterator().next();
67 if (!shardingRule.findShardingTable(logicTableName).isPresent()) {
68 result.getRouteUnits().add(new RouteUnit(dataSourceMapper, Collections.emptyList()));
69 return result;
70 }
71 DataNode dataNode = shardingRule.getDataNode(logicTableName);
72 result.getRouteUnits().add(new RouteUnit(new RouteMapper(dataNode.getDataSourceName(), dataNode.getDataSourceName()),
73 Collections.singletonList(new RouteMapper(logicTableName, dataNode.getTableName()))));
74 } else {
75 routeWithMultipleTables(result, shardingRule);
76 }
77 return result;
78 }
79
80 private String getDataSourceName(final Collection<String> dataSourceNames) {
81 return sqlStatementContext instanceof CursorAvailable || isViewStatementContext(sqlStatementContext) ? dataSourceNames.iterator().next() : getRandomDataSourceName(dataSourceNames);
82 }
83
84 private boolean isViewStatementContext(final SQLStatementContext sqlStatementContext) {
85 return sqlStatementContext instanceof CreateViewStatementContext || sqlStatementContext instanceof AlterViewStatementContext || sqlStatementContext instanceof DropViewStatementContext;
86 }
87
88 private void routeWithMultipleTables(final RouteContext routeContext, final ShardingRule shardingRule) {
89 List<RouteMapper> tableMappers = new ArrayList<>(logicTables.size());
90 Set<String> availableDataSourceNames = Collections.emptySet();
91 boolean first = true;
92 for (String each : logicTables) {
93 ShardingTable shardingTable = shardingRule.getShardingTable(each);
94 DataNode dataNode = shardingTable.getActualDataNodes().get(0);
95 tableMappers.add(new RouteMapper(each, dataNode.getTableName()));
96 Set<String> currentDataSourceNames = shardingTable.getActualDataNodes().stream().map(DataNode::getDataSourceName).collect(
97 Collectors.toCollection(() -> new LinkedHashSet<>(shardingTable.getActualDataSourceNames().size(), 1F)));
98 if (first) {
99 availableDataSourceNames = currentDataSourceNames;
100 first = false;
101 } else {
102 availableDataSourceNames = Sets.intersection(availableDataSourceNames, currentDataSourceNames);
103 }
104 }
105 if (availableDataSourceNames.isEmpty()) {
106 throw new DataSourceIntersectionNotFoundException(logicTables);
107 }
108 String dataSourceName = getDataSourceName(availableDataSourceNames);
109 routeContext.getRouteUnits().add(new RouteUnit(new RouteMapper(dataSourceName, dataSourceName), tableMappers));
110 }
111
112 private String getRandomDataSourceName(final Collection<String> dataSourceNames) {
113 Collection<String> usedDataSourceNames = connectionContext.getUsedDataSourceNames();
114 List<String> availableDataSourceNames = new ArrayList<>(usedDataSourceNames.isEmpty() ? dataSourceNames : usedDataSourceNames);
115 return availableDataSourceNames.get(ThreadLocalRandom.current().nextInt(availableDataSourceNames.size()));
116 }
117 }