switchman 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/switchman/active_record/query_methods.rb +121 -127
- data/lib/switchman/active_record/relation.rb +5 -2
- data/lib/switchman/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0730763611a9f47cf3b9a39d5e3a9969189067772c743bad82729c670d18e545
|
4
|
+
data.tar.gz: 4136ab04f9eac6c48ae4ad7a70b284845f095e3fc4153a8d5a5023f2bc5e312d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad391b862e5428999a5260278e767dae4a1266eb99fd2bab18619b26302542d67d50fee3277ff27711ce4eaec68fbf5428773d17b03b7d235e501baeb06a1623
|
7
|
+
data.tar.gz: 87cee001748bcea7ce0f46a4d673a2c7ac3aefb16b99f84d463fbaff3804a98cd53184dd271a3b19261f89baef91151d85728076b76484646d5be95af1f101cb
|
@@ -12,8 +12,6 @@ module Switchman
|
|
12
12
|
# :explicit - explicit set on the relation
|
13
13
|
# :association - a special value that scopes from associations use to use slightly different logic
|
14
14
|
# for foreign key transposition
|
15
|
-
# :to_a - a special value that Relation#to_a uses when querying multiple shards to
|
16
|
-
# remove primary keys from conditions that aren't applicable to the current shard
|
17
15
|
def shard_value
|
18
16
|
@values[:shard]
|
19
17
|
end
|
@@ -44,10 +42,7 @@ module Switchman
|
|
44
42
|
old_primary_shard = primary_shard
|
45
43
|
self.shard_value = value
|
46
44
|
self.shard_source_value = source
|
47
|
-
|
48
|
-
transpose_clauses(old_primary_shard, primary_shard,
|
49
|
-
remove_nonlocal_primary_keys: source == :to_a)
|
50
|
-
end
|
45
|
+
transpose_predicates(nil, old_primary_shard, primary_shard) if old_primary_shard != primary_shard
|
51
46
|
self
|
52
47
|
end
|
53
48
|
|
@@ -89,29 +84,21 @@ module Switchman
|
|
89
84
|
super(other.shard(primary_shard))
|
90
85
|
end
|
91
86
|
|
92
|
-
|
87
|
+
protected
|
93
88
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
if new_predicates != predicates
|
103
|
-
#{type}_clause.instance_variable_set(:@predicates, new_predicates)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
89
|
+
def remove_nonlocal_primary_keys!
|
90
|
+
each_transposable_predicate_value do |value, predicate, _relation, _column, type|
|
91
|
+
next value unless
|
92
|
+
type == :primary &&
|
93
|
+
predicate.left.relation.klass == klass &&
|
94
|
+
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
|
95
|
+
|
96
|
+
value.is_a?(Integer) && value > Shard::IDS_PER_SHARD ? [] : value
|
107
97
|
end
|
108
|
-
|
98
|
+
self
|
109
99
|
end
|
110
100
|
|
111
|
-
|
112
|
-
transpose_where_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
113
|
-
transpose_having_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
114
|
-
end
|
101
|
+
private
|
115
102
|
|
116
103
|
def infer_shards_from_primary_key(predicates)
|
117
104
|
return unless klass.integral_id?
|
@@ -145,7 +132,7 @@ module Switchman
|
|
145
132
|
return
|
146
133
|
else
|
147
134
|
id_shards = id_shards.to_a
|
148
|
-
|
135
|
+
transpose_predicates(nil, primary_shard, id_shards.first)
|
149
136
|
self.shard_value = id_shards
|
150
137
|
return
|
151
138
|
end
|
@@ -162,7 +149,7 @@ module Switchman
|
|
162
149
|
|
163
150
|
return if !id_shard || id_shard == primary_shard
|
164
151
|
|
165
|
-
|
152
|
+
transpose_predicates(nil, primary_shard, id_shard)
|
166
153
|
self.shard_value = id_shard
|
167
154
|
end
|
168
155
|
|
@@ -246,122 +233,129 @@ module Switchman
|
|
246
233
|
connection.with_global_table_name { super }
|
247
234
|
end
|
248
235
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
transpose_single_predicate(predicate, source_shard, target_shard,
|
255
|
-
remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
256
|
-
end
|
236
|
+
def each_predicate(predicates = nil, &block)
|
237
|
+
return predicates.map(&block) if predicates
|
238
|
+
|
239
|
+
each_predicate_cb(:having_clause, :having_clause=, &block)
|
240
|
+
each_predicate_cb(:where_clause, :where_clause=, &block)
|
257
241
|
end
|
258
242
|
|
259
|
-
def
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
if predicate.is_a?(::Arel::Nodes::Grouping)
|
264
|
-
return predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
265
|
-
|
266
|
-
# Dang, we have an OR. OK, that means we have other epxressions below this
|
267
|
-
# level, perhaps many, that may need transposition.
|
268
|
-
# the left side and right side must each be treated as predicate lists and
|
269
|
-
# transformed in kind, if neither of them changes we can just return the grouping as is.
|
270
|
-
# hold on, it's about to get recursive...
|
271
|
-
or_expr = predicate.expr
|
272
|
-
left_node = or_expr.left
|
273
|
-
right_node = or_expr.right
|
274
|
-
new_left_predicates = transpose_single_predicate(left_node, source_shard,
|
275
|
-
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
276
|
-
new_right_predicates = transpose_single_predicate(right_node, source_shard,
|
277
|
-
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
278
|
-
return predicate if new_left_predicates == left_node && new_right_predicates == right_node
|
279
|
-
|
280
|
-
return ::Arel::Nodes::Grouping.new ::Arel::Nodes::Or.new(new_left_predicates, new_right_predicates)
|
281
|
-
end
|
282
|
-
return predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
283
|
-
return predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
284
|
-
|
285
|
-
relation, column = relation_and_column(predicate.left)
|
286
|
-
return predicate unless (type = transposable_attribute_type(relation, column))
|
287
|
-
|
288
|
-
remove = true if type == :primary &&
|
289
|
-
remove_nonlocal_primary_keys &&
|
290
|
-
predicate.left.relation.klass == klass &&
|
291
|
-
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
|
292
|
-
|
293
|
-
current_source_shard =
|
294
|
-
if source_shard
|
295
|
-
source_shard
|
296
|
-
elsif type == :primary
|
297
|
-
Shard.current(klass.connection_class_for_self)
|
298
|
-
elsif type == :foreign
|
299
|
-
source_shard_for_foreign_key(relation, column)
|
300
|
-
end
|
243
|
+
def each_predicate_cb(clause_getter, clause_setter, &block)
|
244
|
+
old_clause = send(clause_getter)
|
245
|
+
old_predicates = old_clause.send(:predicates)
|
246
|
+
return if old_predicates.empty?
|
301
247
|
|
302
|
-
|
303
|
-
|
304
|
-
else
|
305
|
-
predicate.right
|
306
|
-
end
|
248
|
+
new_predicates = old_predicates.map(&block)
|
249
|
+
return if new_predicates == old_predicates
|
307
250
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
251
|
+
new_clause = old_clause.dup
|
252
|
+
new_clause.instance_variable_set(:@predicates, new_predicates)
|
253
|
+
|
254
|
+
send(clause_setter, new_clause)
|
255
|
+
end
|
256
|
+
|
257
|
+
def each_transposable_predicate(predicates = nil, &block)
|
258
|
+
each_predicate(predicates) do |predicate|
|
259
|
+
if predicate.is_a?(::Arel::Nodes::Grouping)
|
260
|
+
next predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
261
|
+
|
262
|
+
or_expr = predicate.expr
|
263
|
+
old_left = or_expr.left
|
264
|
+
old_right = or_expr.right
|
265
|
+
new_left, new_right = each_transposable_predicate([old_left, old_right], &block)
|
266
|
+
|
267
|
+
next predicate if new_left == old_left && new_right == old_right
|
268
|
+
|
269
|
+
next predicate.class.new predicate.expr.class.new(new_left, new_right)
|
314
270
|
end
|
315
271
|
|
316
|
-
|
317
|
-
predicate
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
272
|
+
next predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
273
|
+
next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
274
|
+
|
275
|
+
relation, column = relation_and_column(predicate.left)
|
276
|
+
next predicate unless (type = transposable_attribute_type(relation, column))
|
277
|
+
|
278
|
+
yield(predicate, relation, column, type)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def each_transposable_predicate_value(predicates = nil)
|
283
|
+
each_transposable_predicate(predicates) do |predicate, relation, column, type|
|
284
|
+
each_transposable_predicate_value_cb(predicate) do |value|
|
285
|
+
yield(value, predicate, relation, column, type)
|
323
286
|
end
|
324
|
-
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def each_transposable_predicate_value_cb(node, &block)
|
291
|
+
case node
|
292
|
+
when Array
|
293
|
+
node.map { |val| each_transposable_predicate_value_cb(val, &block).presence }.compact
|
294
|
+
when ::ActiveModel::Attribute
|
295
|
+
old_value = node.value_before_type_cast
|
296
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
297
|
+
|
298
|
+
old_value == new_value ? node : node.class.new(node.name, new_value, node.type)
|
299
|
+
when ::Arel::Nodes::And
|
300
|
+
old_value = node.children
|
301
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
302
|
+
|
303
|
+
old_value == new_value ? node : node.class.new(new_value)
|
304
|
+
when ::Arel::Nodes::BindParam
|
305
|
+
old_value = node.value
|
306
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
307
|
+
|
308
|
+
old_value == new_value ? node : node.class.new(new_value)
|
309
|
+
when ::Arel::Nodes::Casted
|
310
|
+
old_value = node.value
|
311
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
312
|
+
|
313
|
+
old_value == new_value ? node : node.class.new(new_value, node.attribute)
|
314
|
+
when ::Arel::Nodes::HomogeneousIn
|
315
|
+
old_value = node.values
|
316
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
317
|
+
|
325
318
|
# switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
|
326
|
-
if
|
327
|
-
klass =
|
328
|
-
klass.new(
|
319
|
+
if new_value.empty?
|
320
|
+
klass = node.type == :in ? ::Arel::Nodes::In : ::Arel::Nodes::NotIn
|
321
|
+
klass.new(node.attribute, new_value)
|
329
322
|
else
|
330
|
-
|
323
|
+
old_value == new_value ? node : node.class.new(new_value, node.attribute, node.type)
|
331
324
|
end
|
325
|
+
when ::Arel::Nodes::Binary
|
326
|
+
old_value = node.right
|
327
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
328
|
+
|
329
|
+
old_value == new_value ? node : node.class.new(node.left, new_value)
|
332
330
|
else
|
333
|
-
|
331
|
+
yield(node)
|
334
332
|
end
|
335
333
|
end
|
336
334
|
|
337
|
-
def
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
|
349
|
-
if current_id == local_id
|
350
|
-
# make a new bind param
|
351
|
-
value
|
352
|
-
else
|
353
|
-
new_att = query_att.class.new(query_att.name, local_id, query_att.type)
|
354
|
-
if value.is_a?(::ActiveModel::Attribute)
|
355
|
-
new_att
|
356
|
-
else
|
357
|
-
::Arel::Nodes::BindParam.new(new_att)
|
358
|
-
end
|
335
|
+
def transpose_predicates(predicates,
|
336
|
+
source_shard,
|
337
|
+
target_shard)
|
338
|
+
each_transposable_predicate_value(predicates) do |value, _predicate, relation, column, type|
|
339
|
+
current_source_shard =
|
340
|
+
if source_shard
|
341
|
+
source_shard
|
342
|
+
elsif type == :primary
|
343
|
+
Shard.current(klass.connection_class_for_self)
|
344
|
+
elsif type == :foreign
|
345
|
+
source_shard_for_foreign_key(relation, column)
|
359
346
|
end
|
360
|
-
|
347
|
+
|
348
|
+
transpose_predicate_value(value, current_source_shard, target_shard, type)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def transpose_predicate_value(value, current_shard, target_shard, attribute_type)
|
353
|
+
if value.is_a?(::ActiveRecord::StatementCache::Substitute)
|
354
|
+
value.sharded = true # mark for transposition later
|
355
|
+
value.primary = true if attribute_type == :primary
|
356
|
+
value
|
361
357
|
else
|
362
|
-
|
363
|
-
local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
|
364
|
-
local_id
|
358
|
+
Shard.relative_id_for(value, current_shard, target_shard) || value
|
365
359
|
end
|
366
360
|
end
|
367
361
|
end
|
@@ -103,7 +103,9 @@ module Switchman
|
|
103
103
|
def activate(unordered: false, &block)
|
104
104
|
shards = all_shards
|
105
105
|
if Array === shards && shards.length == 1
|
106
|
-
if
|
106
|
+
if !loaded? && shard_value != shards.first
|
107
|
+
shard(shards.first).activate(&block)
|
108
|
+
elsif shards.first == DefaultShard || shards.first == Shard.current(klass.connection_class_for_self)
|
107
109
|
yield(self, shards.first)
|
108
110
|
else
|
109
111
|
shards.first.activate(klass.connection_class_for_self) { yield(self, shards.first) }
|
@@ -115,7 +117,8 @@ module Switchman
|
|
115
117
|
# don't even query other shards if we're already past the limit
|
116
118
|
next if limit_value && result_count >= limit_value && order_values.empty?
|
117
119
|
|
118
|
-
relation = shard(Shard.current(klass.connection_class_for_self)
|
120
|
+
relation = shard(Shard.current(klass.connection_class_for_self))
|
121
|
+
relation.remove_nonlocal_primary_keys!
|
119
122
|
# do a minimal query if possible
|
120
123
|
relation = relation.limit(limit_value - result_count) if limit_value && !result_count.zero? && order_values.empty?
|
121
124
|
|
data/lib/switchman/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
13
|
+
date: 2022-12-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|