switchman 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
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
|
|
@@ -25,7 +25,7 @@ module Switchman
|
|
25
25
|
# we can make some assumptions about the shard source
|
26
26
|
# (e.g. infer from the primary key or use the current shard)
|
27
27
|
|
28
|
-
def execute(*args)
|
28
|
+
def execute(*args, &block)
|
29
29
|
params, connection = args
|
30
30
|
klass = @klass
|
31
31
|
target_shard = nil
|
@@ -40,7 +40,7 @@ module Switchman
|
|
40
40
|
|
41
41
|
target_shard.activate(klass.connection_class_for_self) do
|
42
42
|
sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
|
43
|
-
klass.find_by_sql(sql, bind_values)
|
43
|
+
klass.find_by_sql(sql, bind_values, &block)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -66,7 +66,11 @@ module Switchman
|
|
66
66
|
|
67
67
|
def primary_value_index
|
68
68
|
primary_ba_index = @bound_attributes.index do |ba|
|
69
|
-
ba.is_a?(::ActiveRecord::
|
69
|
+
if ba.value.is_a?(::ActiveRecord::StatementCache::Substitute)
|
70
|
+
ba.is_a?(::ActiveRecord::Relation::QueryAttribute) && ba.value.primary
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
70
74
|
end
|
71
75
|
@indexes.index(primary_ba_index) if primary_ba_index
|
72
76
|
end
|
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
|