switchman 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +10 -2
  3. data/app/models/switchman/shard.rb +234 -270
  4. data/app/models/switchman/unsharded_record.rb +7 -0
  5. data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
  6. data/db/migrate/20130328224244_create_default_shard.rb +5 -5
  7. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
  8. data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
  9. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +7 -5
  10. data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
  11. data/lib/switchman.rb +3 -3
  12. data/lib/switchman/action_controller/caching.rb +2 -2
  13. data/lib/switchman/active_record/abstract_adapter.rb +1 -0
  14. data/lib/switchman/active_record/association.rb +78 -85
  15. data/lib/switchman/active_record/attribute_methods.rb +58 -52
  16. data/lib/switchman/active_record/base.rb +58 -59
  17. data/lib/switchman/active_record/calculations.rb +73 -66
  18. data/lib/switchman/active_record/connection_pool.rb +14 -41
  19. data/lib/switchman/active_record/database_configurations.rb +34 -0
  20. data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
  21. data/lib/switchman/active_record/finder_methods.rb +11 -16
  22. data/lib/switchman/active_record/log_subscriber.rb +4 -8
  23. data/lib/switchman/active_record/migration.rb +6 -15
  24. data/lib/switchman/active_record/model_schema.rb +1 -1
  25. data/lib/switchman/active_record/persistence.rb +4 -6
  26. data/lib/switchman/active_record/postgresql_adapter.rb +42 -53
  27. data/lib/switchman/active_record/predicate_builder.rb +1 -1
  28. data/lib/switchman/active_record/query_cache.rb +18 -19
  29. data/lib/switchman/active_record/query_methods.rb +172 -181
  30. data/lib/switchman/active_record/reflection.rb +6 -10
  31. data/lib/switchman/active_record/relation.rb +27 -21
  32. data/lib/switchman/active_record/spawn_methods.rb +27 -29
  33. data/lib/switchman/active_record/statement_cache.rb +18 -35
  34. data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
  35. data/lib/switchman/active_support/cache.rb +3 -5
  36. data/lib/switchman/arel.rb +13 -8
  37. data/lib/switchman/database_server.rb +121 -142
  38. data/lib/switchman/default_shard.rb +52 -16
  39. data/lib/switchman/engine.rb +61 -57
  40. data/lib/switchman/environment.rb +4 -8
  41. data/lib/switchman/errors.rb +1 -0
  42. data/lib/switchman/guard_rail.rb +6 -19
  43. data/lib/switchman/guard_rail/relation.rb +5 -7
  44. data/lib/switchman/r_spec_helper.rb +29 -37
  45. data/lib/switchman/rails.rb +14 -12
  46. data/lib/switchman/schema_cache.rb +1 -1
  47. data/lib/switchman/sharded_instrumenter.rb +1 -1
  48. data/lib/switchman/standard_error.rb +15 -3
  49. data/lib/switchman/test_helper.rb +6 -10
  50. data/lib/switchman/version.rb +1 -1
  51. data/lib/tasks/switchman.rake +54 -69
  52. metadata +100 -44
  53. data/lib/switchman/active_record/batches.rb +0 -11
  54. data/lib/switchman/active_record/connection_handler.rb +0 -172
  55. data/lib/switchman/active_record/where_clause_factory.rb +0 -36
  56. data/lib/switchman/connection_pool_proxy.rb +0 -169
@@ -23,4 +23,4 @@ module Switchman
23
23
  end
24
24
  end
25
25
  end
26
- end
26
+ end
@@ -3,31 +3,30 @@
3
3
  module Switchman
4
4
  module ActiveRecord
5
5
  module QueryCache
6
-
7
6
  private
8
7
 
9
8
  def cache_sql(sql, name, binds)
10
9
  # have to include the shard id in the cache key because of switching dbs on the same connection
11
- sql = "#{self.shard.id}::#{sql}"
10
+ sql = "#{shard.id}::#{sql}"
12
11
  @lock.synchronize do
13
12
  result =
14
- if query_cache[sql].key?(binds)
15
- args = {
16
- sql: sql,
17
- binds: binds,
18
- name: name,
19
- connection_id: object_id,
20
- cached: true
21
- }
22
- args[:type_casted_binds] = -> { type_casted_binds(binds) } if ::Rails.version >= '5.1.5'
23
- ::ActiveSupport::Notifications.instrument(
24
- "sql.active_record",
25
- args
26
- )
27
- query_cache[sql][binds]
28
- else
29
- query_cache[sql][binds] = yield
30
- end
13
+ if query_cache[sql].key?(binds)
14
+ args = {
15
+ sql: sql,
16
+ binds: binds,
17
+ name: name,
18
+ connection_id: object_id,
19
+ cached: true,
20
+ type_casted_binds: -> { type_casted_binds(binds) }
21
+ }
22
+ ::ActiveSupport::Notifications.instrument(
23
+ 'sql.active_record',
24
+ args
25
+ )
26
+ query_cache[sql][binds]
27
+ else
28
+ query_cache[sql][binds] = yield
29
+ end
31
30
  result.dup
32
31
  end
33
32
  end
@@ -17,15 +17,20 @@ module Switchman
17
17
  def shard_value
18
18
  @values[:shard]
19
19
  end
20
+
20
21
  def shard_source_value
21
22
  @values[:shard_source]
22
23
  end
24
+
23
25
  def shard_value=(value)
24
26
  raise ::ActiveRecord::ImmutableRelation if @loaded
27
+
25
28
  @values[:shard] = value
26
29
  end
30
+
27
31
  def shard_source_value=(value)
28
32
  raise ::ActiveRecord::ImmutableRelation if @loaded
33
+
29
34
  @values[:shard_source] = value
30
35
  end
31
36
 
@@ -35,11 +40,13 @@ module Switchman
35
40
 
36
41
  def shard!(value, source = :explicit)
37
42
  raise ArgumentError, "shard can't be nil" unless value
38
- old_primary_shard = self.primary_shard
43
+
44
+ old_primary_shard = primary_shard
39
45
  self.shard_value = value
40
46
  self.shard_source_value = source
41
- if (old_primary_shard != self.primary_shard || source == :to_a)
42
- transpose_clauses(old_primary_shard, self.primary_shard, source == :to_a)
47
+ if old_primary_shard != primary_shard || source == :to_a
48
+ transpose_clauses(old_primary_shard, primary_shard,
49
+ remove_nonlocal_primary_keys: source == :to_a)
43
50
  end
44
51
  self
45
52
  end
@@ -58,7 +65,7 @@ module Switchman
58
65
  when ::ActiveRecord::Relation
59
66
  Shard.default
60
67
  when nil
61
- Shard.current(klass.shard_category)
68
+ Shard.current(klass.connection_classes)
62
69
  else
63
70
  raise ArgumentError, "invalid shard value #{shard_value}"
64
71
  end
@@ -72,114 +79,88 @@ module Switchman
72
79
  when ::ActiveRecord::Base
73
80
  shard_value.respond_to?(:associated_shards) ? shard_value.associated_shards : [shard_value.shard]
74
81
  when nil
75
- [Shard.current(klass.shard_category)]
82
+ [Shard.current(klass.connection_classes)]
76
83
  else
77
84
  shard_value
78
85
  end
79
86
  end
80
87
 
88
+ def or(other)
89
+ super(other.shard(primary_shard))
90
+ end
91
+
81
92
  private
82
93
 
83
- if ::Rails.version >= '5.2'
84
- [:where, :having].each do |type|
85
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
86
- def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
87
- unless (predicates = #{type}_clause.send(:predicates)).empty?
88
- new_predicates, _binds = transpose_predicates(predicates, source_shard,
89
- target_shard, remove_nonlocal_primary_keys)
94
+ %i[where having].each do |type|
95
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
96
+ def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys:)
97
+ unless (predicates = #{type}_clause.send(:predicates)).empty?
98
+ new_predicates = transpose_predicates(predicates, source_shard,
99
+ target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
100
+ if new_predicates != predicates
101
+ self.#{type}_clause = #{type}_clause.dup
90
102
  if new_predicates != predicates
91
- self.#{type}_clause = #{type}_clause.dup
92
- if new_predicates != predicates
93
- #{type}_clause.instance_variable_set(:@predicates, new_predicates)
94
- end
103
+ #{type}_clause.instance_variable_set(:@predicates, new_predicates)
95
104
  end
96
105
  end
97
106
  end
98
- RUBY
99
- end
100
- else
101
- [:where, :having].each do |type|
102
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
103
- def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
104
- unless (predicates = #{type}_clause.send(:predicates)).empty?
105
- new_predicates, new_binds = transpose_predicates(predicates, source_shard,
106
- target_shard, remove_nonlocal_primary_keys,
107
- binds: #{type}_clause.binds,
108
- dup_binds_on_mutation: true)
109
- if new_predicates != predicates || !new_binds.equal?(#{type}_clause.binds)
110
- self.#{type}_clause = #{type}_clause.dup
111
- if new_predicates != predicates
112
- #{type}_clause.instance_variable_set(:@predicates, new_predicates)
113
- end
114
- if !new_binds.equal?(#{type}_clause.binds)
115
- #{type}_clause.instance_variable_set(:@binds, new_binds)
116
- end
117
- end
118
- end
119
- end
120
- RUBY
121
107
  end
108
+ RUBY
122
109
  end
123
110
 
124
- def transpose_clauses(source_shard, target_shard, remove_nonlocal_primary_keys = false)
125
- transpose_where_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
126
- transpose_having_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
111
+ def transpose_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: false)
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)
127
114
  end
128
115
 
129
- def infer_shards_from_primary_key(predicates, binds = nil)
116
+ def infer_shards_from_primary_key(predicates)
130
117
  return unless klass.integral_id?
131
118
 
132
119
  primary_key = predicates.detect do |predicate|
133
- predicate.is_a?(::Arel::Nodes::Binary) && predicate.left.is_a?(::Arel::Attributes::Attribute) &&
134
- predicate.left.relation.is_a?(::Arel::Table) && predicate.left.relation.model == klass &&
120
+ (predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)) &&
121
+ predicate.left.is_a?(::Arel::Attributes::Attribute) &&
122
+ predicate.left.relation.is_a?(::Arel::Table) && predicate.left.relation.klass == klass &&
135
123
  klass.primary_key == predicate.left.name
136
124
  end
137
- if primary_key
138
- case primary_key.right
139
- when Array
140
- id_shards = Set.new
141
- primary_key.right.each do |value|
142
- local_id, id_shard = Shard.local_id_for(value)
143
- id_shard ||= Shard.current(klass.shard_category) if local_id
144
- id_shards << id_shard if id_shard
145
- end
146
- if id_shards.empty?
147
- return
148
- elsif id_shards.length == 1
149
- id_shard = id_shards.first
150
- # prefer to not change the shard
151
- elsif id_shards.include?(primary_shard)
152
- id_shards.delete(primary_shard)
153
- self.shard_value = [primary_shard] + id_shards.to_a
154
- return
155
- else
156
- id_shards = id_shards.to_a
157
- transpose_clauses(primary_shard, id_shards.first)
158
- self.shard_value = id_shards
159
- return
160
- end
161
- when ::Arel::Nodes::BindParam
162
- if ::Rails.version >= "5.2"
163
- local_id, id_shard = Shard.local_id_for(primary_key.right.value.value_before_type_cast)
164
- id_shard ||= Shard.current(klass.shard_category) if local_id
165
- else
166
- # look for a bind param with a matching column name
167
- if binds && bind = binds.detect{|b| b&.name.to_s == klass.primary_key.to_s}
168
- unless bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
169
- local_id, id_shard = Shard.local_id_for(bind.value)
170
- id_shard ||= Shard.current(klass.shard_category) if local_id
171
- end
172
- end
173
- end
174
- else
175
- local_id, id_shard = Shard.local_id_for(primary_key.right)
176
- id_shard ||= Shard.current(klass.shard_category) if local_id
125
+ return unless primary_key
126
+
127
+ right = primary_key.is_a?(::Arel::Nodes::HomogeneousIn) ? primary_key.values : primary_key.right
128
+
129
+ case right
130
+ when Array
131
+ id_shards = Set.new
132
+ right.each do |value|
133
+ local_id, id_shard = Shard.local_id_for(value)
134
+ id_shard ||= Shard.current(klass.connection_classes) if local_id
135
+ id_shards << id_shard if id_shard
177
136
  end
137
+ return if id_shards.empty?
178
138
 
179
- return if !id_shard || id_shard == primary_shard
180
- transpose_clauses(primary_shard, id_shard)
181
- self.shard_value = id_shard
139
+ if id_shards.length == 1
140
+ id_shard = id_shards.first
141
+ # prefer to not change the shard
142
+ elsif id_shards.include?(primary_shard)
143
+ id_shards.delete(primary_shard)
144
+ self.shard_value = [primary_shard] + id_shards.to_a
145
+ return
146
+ else
147
+ id_shards = id_shards.to_a
148
+ transpose_clauses(primary_shard, id_shards.first)
149
+ self.shard_value = id_shards
150
+ return
151
+ end
152
+ when ::Arel::Nodes::BindParam
153
+ local_id, id_shard = Shard.local_id_for(right.value.value_before_type_cast)
154
+ id_shard ||= Shard.current(klass.connection_classes) if local_id
155
+ else
156
+ local_id, id_shard = Shard.local_id_for(right)
157
+ id_shard ||= Shard.current(klass.connection_classes) if local_id
182
158
  end
159
+
160
+ return if !id_shard || id_shard == primary_shard
161
+
162
+ transpose_clauses(primary_shard, id_shard)
163
+ self.shard_value = id_shard
183
164
  end
184
165
 
185
166
  def transposable_attribute_type(relation, column)
@@ -201,8 +182,9 @@ module Switchman
201
182
 
202
183
  def sharded_primary_key?(relation, column)
203
184
  column = column.to_s
204
- return column == 'id' if relation.model == ::ActiveRecord::Base
205
- relation.model.primary_key == column && relation.model.integral_id?
185
+ return column == 'id' if relation.klass == ::ActiveRecord::Base
186
+
187
+ relation.klass.primary_key == column && relation.klass.integral_id?
206
188
  end
207
189
 
208
190
  def source_shard_for_foreign_key(relation, column)
@@ -211,8 +193,9 @@ module Switchman
211
193
  reflection = model.send(:reflection_for_integer_attribute, column)
212
194
  break if reflection
213
195
  end
214
- return Shard.current(klass.shard_category) if reflection.options[:polymorphic]
215
- Shard.current(reflection.klass.shard_category)
196
+ return Shard.current(klass.connection_classes) if reflection.options[:polymorphic]
197
+
198
+ Shard.current(reflection.klass.connection_classes)
216
199
  end
217
200
 
218
201
  def relation_and_column(attribute)
@@ -221,8 +204,31 @@ module Switchman
221
204
  [attribute.relation, column]
222
205
  end
223
206
 
224
- def where_clause_factory
225
- super.tap { |factory| factory.scope = self }
207
+ def build_where_clause(opts, rest = [])
208
+ opts = sanitize_forbidden_attributes(opts)
209
+
210
+ case opts
211
+ when String, Array
212
+ values = Hash === rest.first ? rest.first.values : rest
213
+
214
+ values.grep(ActiveRecord::Relation) do |rel|
215
+ # serialize subqueries against the same shard as the outer query is currently
216
+ # targeted to run against
217
+ rel.shard!(primary_shard) if rel.shard_source_value == :implicit && rel.primary_shard != primary_shard
218
+ end
219
+
220
+ super
221
+ when Hash, ::Arel::Nodes::Node
222
+ where_clause = super
223
+
224
+ predicates = where_clause.send(:predicates)
225
+ infer_shards_from_primary_key(predicates) if shard_source_value == :implicit && shard_value.is_a?(Shard)
226
+ predicates = transpose_predicates(predicates, nil, primary_shard)
227
+ where_clause.instance_variable_set(:@predicates, predicates)
228
+ where_clause
229
+ else
230
+ super
231
+ end
226
232
  end
227
233
 
228
234
  def arel_columns(columns)
@@ -233,101 +239,86 @@ module Switchman
233
239
  connection.with_local_table_name { super }
234
240
  end
235
241
 
236
- # semi-private
237
- public
238
242
  def transpose_predicates(predicates,
239
243
  source_shard,
240
244
  target_shard,
241
- remove_nonlocal_primary_keys = false,
242
- binds: nil,
243
- dup_binds_on_mutation: false)
244
- result = predicates.map do |predicate|
245
- next predicate unless predicate.is_a?(::Arel::Nodes::Binary)
246
- next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
247
- relation, column = relation_and_column(predicate.left)
248
- next predicate unless (type = transposable_attribute_type(relation, column))
249
-
250
- remove = true if type == :primary &&
251
- remove_nonlocal_primary_keys &&
252
- predicate.left.relation.model == klass &&
253
- predicate.is_a?(::Arel::Nodes::Equality)
254
-
255
- current_source_shard =
256
- if source_shard
257
- source_shard
258
- elsif type == :primary
259
- Shard.current(klass.shard_category)
260
- elsif type == :foreign
261
- source_shard_for_foreign_key(relation, column)
262
- end
245
+ remove_nonlocal_primary_keys: false)
246
+ predicates.map do |predicate|
247
+ transpose_single_predicate(predicate, source_shard, target_shard,
248
+ remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
249
+ end
250
+ end
263
251
 
264
- if ::Rails.version >= "5.2"
265
- new_right_value =
266
- case predicate.right
267
- when Array
268
- predicate.right.map {|val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
269
- else
270
- transpose_predicate_value(predicate.right, current_source_shard, target_shard, type, remove)
271
- end
272
- else
273
- new_right_value = case predicate.right
274
- when Array
275
- local_ids = []
276
- predicate.right.each do |value|
277
- local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
278
- next unless local_id
279
- unless remove && local_id > Shard::IDS_PER_SHARD
280
- if value.is_a?(::Arel::Nodes::Casted)
281
- if local_id == value.val
282
- local_id = value
283
- elsif local_id != value
284
- local_id = value.class.new(local_id, value.attribute)
285
- end
286
- end
287
- local_ids << local_id
288
- end
289
- end
290
- local_ids
291
- when ::Arel::Nodes::BindParam
292
- # look for a bind param with a matching column name
293
- if binds && bind = binds.detect{|b| b&.name.to_s == predicate.left.name.to_s}
294
- # before we mutate, dup
295
- if dup_binds_on_mutation
296
- binds = binds.map(&:dup)
297
- dup_binds_on_mutation = false
298
- bind = binds.find { |b| b&.name.to_s == predicate.left.name.to_s }
299
- end
300
- if bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
301
- bind.value.sharded = true # mark for transposition later
302
- bind.value.primary = true if type == :primary
252
+ def transpose_single_predicate(predicate,
253
+ source_shard,
254
+ target_shard,
255
+ remove_nonlocal_primary_keys: false)
256
+ if predicate.is_a?(::Arel::Nodes::Grouping)
257
+ return predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
258
+
259
+ # Dang, we have an OR. OK, that means we have other epxressions below this
260
+ # level, perhaps many, that may need transposition.
261
+ # the left side and right side must each be treated as predicate lists and
262
+ # transformed in kind, if neither of them changes we can just return the grouping as is.
263
+ # hold on, it's about to get recursive...
264
+ or_expr = predicate.expr
265
+ left_node = or_expr.left
266
+ right_node = or_expr.right
267
+ new_left_predicates = transpose_single_predicate(left_node, source_shard,
268
+ target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
269
+ or_expr.instance_variable_set(:@left, new_left_predicates) if new_left_predicates != left_node
270
+ new_right_predicates = transpose_single_predicate(right_node, source_shard,
271
+ target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
272
+ or_expr.instance_variable_set(:@right, new_right_predicates) if new_right_predicates != right_node
273
+ return predicate
274
+ end
275
+ return predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
276
+ return predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
277
+
278
+ relation, column = relation_and_column(predicate.left)
279
+ return predicate unless (type = transposable_attribute_type(relation, column))
280
+
281
+ remove = true if type == :primary &&
282
+ remove_nonlocal_primary_keys &&
283
+ predicate.left.relation.klass == klass &&
284
+ predicate.is_a?(::Arel::Nodes::Equality)
285
+
286
+ current_source_shard =
287
+ if source_shard
288
+ source_shard
289
+ elsif type == :primary
290
+ Shard.current(klass.connection_classes)
291
+ elsif type == :foreign
292
+ source_shard_for_foreign_key(relation, column)
293
+ end
294
+
295
+ right = if predicate.is_a?(::Arel::Nodes::HomogeneousIn)
296
+ predicate.values
303
297
  else
304
- local_id = Shard.relative_id_for(bind.value, current_source_shard, target_shard)
305
- local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
306
- bind.instance_variable_set(:@value, local_id)
307
- bind.instance_variable_set(:@value_for_database, nil)
298
+ predicate.right
308
299
  end
309
- end
310
- predicate.right
311
- else
312
- local_id = Shard.relative_id_for(predicate.right, current_source_shard, target_shard) || predicate.right
313
- local_id = [] if remove && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
314
- local_id
315
- end
300
+
301
+ new_right_value =
302
+ case right
303
+ when Array
304
+ right.map { |val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
305
+ else
306
+ transpose_predicate_value(right, current_source_shard, target_shard, type, remove)
316
307
  end
317
- if new_right_value == predicate.right
308
+
309
+ if new_right_value == right
310
+ predicate
311
+ elsif predicate.right.is_a?(::Arel::Nodes::Casted)
312
+ if new_right_value == right.value
318
313
  predicate
319
- elsif predicate.right.is_a?(::Arel::Nodes::Casted)
320
- if new_right_value == predicate.right.val
321
- predicate
322
- else
323
- predicate.class.new(predicate.left, predicate.right.class.new(new_right_value, predicate.right.attribute))
324
- end
325
314
  else
326
- predicate.class.new(predicate.left, new_right_value)
315
+ predicate.class.new(predicate.left, right.class.new(new_right_value, right.attribute))
327
316
  end
317
+ elsif predicate.is_a?(::Arel::Nodes::HomogeneousIn)
318
+ predicate.class.new(new_right_value, predicate.attribute, predicate.type)
319
+ else
320
+ predicate.class.new(predicate.left, new_right_value)
328
321
  end
329
- result = [result, binds]
330
- result
331
322
  end
332
323
 
333
324
  def transpose_predicate_value(value, current_shard, target_shard, attribute_type, remove_non_local_ids)
@@ -341,11 +332,11 @@ module Switchman
341
332
  else
342
333
  local_id = Shard.relative_id_for(current_id, current_shard, target_shard) || current_id
343
334
  local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
344
- if current_id != local_id
335
+ if current_id == local_id
345
336
  # make a new bind param
346
- ::Arel::Nodes::BindParam.new(query_att.class.new(query_att.name, local_id, query_att.type))
347
- else
348
337
  value
338
+ else
339
+ ::Arel::Nodes::BindParam.new(query_att.class.new(query_att.name, local_id, query_att.type))
349
340
  end
350
341
  end
351
342
  else