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.condition;
19
20 import lombok.Getter;
21 import lombok.ToString;
22 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
23 import org.apache.shardingsphere.infra.binder.context.statement.dml.InsertStatementContext;
24 import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
25 import org.apache.shardingsphere.sharding.api.config.strategy.sharding.HintShardingStrategyConfiguration;
26 import org.apache.shardingsphere.sharding.api.config.strategy.sharding.NoneShardingStrategyConfiguration;
27 import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
28 import org.apache.shardingsphere.sharding.route.engine.condition.value.ListShardingConditionValue;
29 import org.apache.shardingsphere.sharding.route.engine.condition.value.RangeShardingConditionValue;
30 import org.apache.shardingsphere.sharding.route.engine.condition.value.ShardingConditionValue;
31 import org.apache.shardingsphere.sharding.rule.BindingTableRule;
32 import org.apache.shardingsphere.sharding.rule.ShardingRule;
33 import org.apache.shardingsphere.sharding.rule.ShardingTable;
34 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
35 import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
36 import org.apache.shardingsphere.sql.parser.sql.common.util.SafeNumberOperationUtils;
37
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.LinkedList;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Optional;
45
46
47
48
49 @Getter
50 @ToString
51 public final class ShardingConditions {
52
53 private final List<ShardingCondition> conditions;
54
55 private final SQLStatementContext sqlStatementContext;
56
57 private final ShardingRule rule;
58
59 private final boolean subqueryContainsShardingCondition;
60
61 public ShardingConditions(final List<ShardingCondition> conditions, final SQLStatementContext sqlStatementContext, final ShardingRule rule) {
62 this.conditions = conditions;
63 this.sqlStatementContext = sqlStatementContext;
64 this.rule = rule;
65 subqueryContainsShardingCondition = isSubqueryContainsShardingCondition(conditions, sqlStatementContext);
66 }
67
68
69
70
71
72
73 public boolean isAlwaysFalse() {
74 if (conditions.isEmpty()) {
75 return false;
76 }
77 for (ShardingCondition each : conditions) {
78 if (!(each instanceof AlwaysFalseShardingCondition)) {
79 return false;
80 }
81 }
82 return true;
83 }
84
85
86
87
88 public void merge() {
89 if (conditions.size() > 1) {
90 Collection<ShardingCondition> result = new LinkedList<>();
91 result.add(conditions.remove(conditions.size() - 1));
92 while (!conditions.isEmpty()) {
93 findUniqueShardingCondition(result, conditions.remove(conditions.size() - 1)).ifPresent(result::add);
94 }
95 conditions.addAll(result);
96 }
97 }
98
99 private Optional<ShardingCondition> findUniqueShardingCondition(final Collection<ShardingCondition> conditions, final ShardingCondition condition) {
100 for (ShardingCondition each : conditions) {
101 if (isSameShardingCondition(rule, condition, each)) {
102 return Optional.empty();
103 }
104 }
105 return Optional.of(condition);
106 }
107
108
109
110
111
112
113 public boolean isNeedMerge() {
114 boolean selectContainsSubquery = sqlStatementContext instanceof SelectStatementContext && ((SelectStatementContext) sqlStatementContext).isContainsSubquery();
115 boolean insertSelectContainsSubquery = sqlStatementContext instanceof InsertStatementContext && null != ((InsertStatementContext) sqlStatementContext).getInsertSelectContext()
116 && ((InsertStatementContext) sqlStatementContext).getInsertSelectContext().getSelectStatementContext().isContainsSubquery();
117 return (selectContainsSubquery || insertSelectContainsSubquery) && !rule.getShardingLogicTableNames(sqlStatementContext.getTablesContext().getTableNames()).isEmpty();
118 }
119
120 private boolean isSubqueryContainsShardingCondition(final List<ShardingCondition> conditions, final SQLStatementContext sqlStatementContext) {
121 Collection<SelectStatement> selectStatements = getSelectStatements(sqlStatementContext);
122 if (selectStatements.size() > 1) {
123 Map<Integer, List<ShardingCondition>> startIndexShardingConditions = new HashMap<>(conditions.size(), 1F);
124 for (ShardingCondition each : conditions) {
125 startIndexShardingConditions.computeIfAbsent(each.getStartIndex(), unused -> new LinkedList<>()).add(each);
126 }
127 for (SelectStatement each : selectStatements) {
128 if (each.getFrom().isPresent() && each.getFrom().get() instanceof SubqueryTableSegment) {
129 continue;
130 }
131 if (!each.getWhere().isPresent() || !startIndexShardingConditions.containsKey(each.getWhere().get().getExpr().getStartIndex())) {
132 return false;
133 }
134 }
135 }
136 return true;
137 }
138
139 private Collection<SelectStatement> getSelectStatements(final SQLStatementContext sqlStatementContext) {
140 Collection<SelectStatement> result = new LinkedList<>();
141 if (sqlStatementContext instanceof SelectStatementContext) {
142 result.add(((SelectStatementContext) sqlStatementContext).getSqlStatement());
143 for (SelectStatementContext each : ((SelectStatementContext) sqlStatementContext).getSubqueryContexts().values()) {
144 result.add(each.getSqlStatement());
145 }
146 }
147 if (sqlStatementContext instanceof InsertStatementContext && null != ((InsertStatementContext) sqlStatementContext).getInsertSelectContext()) {
148 SelectStatementContext selectStatementContext = ((InsertStatementContext) sqlStatementContext).getInsertSelectContext().getSelectStatementContext();
149 result.add(selectStatementContext.getSqlStatement());
150 for (SelectStatementContext each : selectStatementContext.getSubqueryContexts().values()) {
151 result.add(each.getSqlStatement());
152 }
153 }
154 return result;
155 }
156
157
158
159
160
161
162 public boolean isSameShardingCondition() {
163 Collection<String> hintStrategyTables = findHintStrategyTables(sqlStatementContext);
164 return 1 == hintStrategyTables.size() || subqueryContainsShardingCondition && 1 == conditions.size();
165 }
166
167 private boolean isSameShardingCondition(final ShardingRule shardingRule, final ShardingCondition shardingCondition1, final ShardingCondition shardingCondition2) {
168 if (shardingCondition1.getValues().size() != shardingCondition2.getValues().size()) {
169 return false;
170 }
171 for (int i = 0; i < shardingCondition1.getValues().size(); i++) {
172 ShardingConditionValue shardingValue1 = shardingCondition1.getValues().get(i);
173 ShardingConditionValue shardingValue2 = shardingCondition2.getValues().get(i);
174 if (!isSameShardingConditionValue(shardingRule, shardingValue1, shardingValue2)) {
175 return false;
176 }
177 }
178 return true;
179 }
180
181 private boolean isSameShardingCondition(final ShardingConditionValue shardingValue1, final ShardingConditionValue shardingValue2) {
182 return shardingValue1.getTableName().equals(shardingValue2.getTableName()) && shardingValue1.getColumnName().equals(shardingValue2.getColumnName());
183 }
184
185 private Collection<String> findHintStrategyTables(final SQLStatementContext sqlStatementContext) {
186 Collection<String> result = new HashSet<>(sqlStatementContext.getTablesContext().getTableNames().size(), 1F);
187 for (String each : sqlStatementContext.getTablesContext().getTableNames()) {
188 Optional<ShardingTable> shardingTable = rule.findShardingTable(each);
189 if (!shardingTable.isPresent()) {
190 continue;
191 }
192 ShardingStrategyConfiguration databaseHintStrategy = rule.getDatabaseShardingStrategyConfiguration(shardingTable.get());
193 ShardingStrategyConfiguration tableHintStrategy = rule.getTableShardingStrategyConfiguration(shardingTable.get());
194 boolean isDatabaseTableHintStrategy = databaseHintStrategy instanceof HintShardingStrategyConfiguration && tableHintStrategy instanceof HintShardingStrategyConfiguration;
195 boolean isDatabaseHintStrategy = databaseHintStrategy instanceof HintShardingStrategyConfiguration && tableHintStrategy instanceof NoneShardingStrategyConfiguration;
196 boolean isTableHintStrategy = databaseHintStrategy instanceof NoneShardingStrategyConfiguration && tableHintStrategy instanceof HintShardingStrategyConfiguration;
197 if (isDatabaseTableHintStrategy || isDatabaseHintStrategy || isTableHintStrategy) {
198 result.add(each);
199 }
200 }
201 return result;
202 }
203
204 private boolean isBindingTable(final ShardingRule shardingRule, final ShardingConditionValue shardingValue1, final ShardingConditionValue shardingValue2) {
205 Optional<BindingTableRule> bindingRule = shardingRule.findBindingTableRule(shardingValue1.getTableName());
206 return bindingRule.isPresent() && bindingRule.get().hasLogicTable(shardingValue2.getTableName());
207 }
208
209 private boolean isSameShardingConditionValue(final ShardingRule shardingRule, final ShardingConditionValue shardingValue1, final ShardingConditionValue shardingValue2) {
210 if (isBindingTable(shardingRule, shardingValue1, shardingValue2)) {
211 return true;
212 }
213 return isSameShardingCondition(shardingValue1, shardingValue2) && isSameShardingValue(shardingValue1, shardingValue2);
214 }
215
216 @SuppressWarnings({"rawtypes", "unchecked"})
217 private boolean isSameShardingValue(final ShardingConditionValue shardingConditionValue1, final ShardingConditionValue shardingConditionValue2) {
218 if (shardingConditionValue1 instanceof ListShardingConditionValue && shardingConditionValue2 instanceof ListShardingConditionValue) {
219 return SafeNumberOperationUtils.safeCollectionEquals(
220 ((ListShardingConditionValue) shardingConditionValue1).getValues(), ((ListShardingConditionValue) shardingConditionValue2).getValues());
221 }
222 if (shardingConditionValue1 instanceof RangeShardingConditionValue && shardingConditionValue2 instanceof RangeShardingConditionValue) {
223 return SafeNumberOperationUtils.safeRangeEquals(
224 ((RangeShardingConditionValue) shardingConditionValue1).getValueRange(), ((RangeShardingConditionValue) shardingConditionValue2).getValueRange());
225 }
226 return false;
227 }
228 }