switchman 2.0.6 → 3.0.1

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +10 -2
  3. data/app/models/switchman/shard.rb +235 -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 +2 -2
  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 -89
  15. data/lib/switchman/active_record/attribute_methods.rb +106 -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 +18 -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 +45 -144
  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 +122 -144
  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/sharded_instrumenter.rb +1 -1
  47. data/lib/switchman/standard_error.rb +15 -3
  48. data/lib/switchman/test_helper.rb +7 -11
  49. data/lib/switchman/version.rb +1 -1
  50. data/lib/tasks/switchman.rake +54 -69
  51. metadata +90 -49
  52. data/lib/switchman/active_record/batches.rb +0 -11
  53. data/lib/switchman/active_record/connection_handler.rb +0 -172
  54. data/lib/switchman/active_record/where_clause_factory.rb +0 -36
  55. data/lib/switchman/connection_pool_proxy.rb +0 -169
  56. data/lib/switchman/schema_cache.rb +0 -20
@@ -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