switchman 1.16.0 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/switchman/shard.rb +10 -5
  3. data/db/migrate/20130328212039_create_switchman_shards.rb +2 -0
  4. data/db/migrate/20130328224244_create_default_shard.rb +3 -1
  5. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +2 -0
  6. data/db/migrate/20180828183945_add_default_shard_index.rb +2 -0
  7. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +2 -0
  8. data/db/migrate/20190114212900_add_unique_name_indexes.rb +2 -0
  9. data/lib/switchman.rb +3 -1
  10. data/lib/switchman/action_controller/caching.rb +2 -0
  11. data/lib/switchman/active_record/abstract_adapter.rb +8 -2
  12. data/lib/switchman/active_record/association.rb +10 -4
  13. data/lib/switchman/active_record/attribute_methods.rb +2 -0
  14. data/lib/switchman/active_record/base.rb +13 -11
  15. data/lib/switchman/active_record/batches.rb +2 -0
  16. data/lib/switchman/active_record/calculations.rb +2 -0
  17. data/lib/switchman/active_record/connection_handler.rb +8 -6
  18. data/lib/switchman/active_record/connection_pool.rb +2 -0
  19. data/lib/switchman/active_record/finder_methods.rb +2 -0
  20. data/lib/switchman/active_record/log_subscriber.rb +2 -0
  21. data/lib/switchman/active_record/migration.rb +3 -1
  22. data/lib/switchman/active_record/model_schema.rb +2 -0
  23. data/lib/switchman/active_record/persistence.rb +3 -1
  24. data/lib/switchman/active_record/postgresql_adapter.rb +3 -1
  25. data/lib/switchman/active_record/predicate_builder.rb +2 -0
  26. data/lib/switchman/active_record/query_cache.rb +2 -0
  27. data/lib/switchman/active_record/query_methods.rb +121 -72
  28. data/lib/switchman/active_record/reflection.rb +2 -0
  29. data/lib/switchman/active_record/relation.rb +7 -5
  30. data/lib/switchman/active_record/spawn_methods.rb +2 -0
  31. data/lib/switchman/active_record/statement_cache.rb +3 -1
  32. data/lib/switchman/active_record/table_definition.rb +4 -2
  33. data/lib/switchman/active_record/type_caster.rb +2 -0
  34. data/lib/switchman/active_record/where_clause_factory.rb +2 -0
  35. data/lib/switchman/active_support/cache.rb +4 -2
  36. data/lib/switchman/arel.rb +2 -0
  37. data/lib/switchman/call_super.rb +2 -0
  38. data/lib/switchman/connection_pool_proxy.rb +13 -11
  39. data/lib/switchman/database_server.rb +18 -16
  40. data/lib/switchman/default_shard.rb +2 -0
  41. data/lib/switchman/engine.rb +10 -8
  42. data/lib/switchman/environment.rb +2 -0
  43. data/lib/switchman/errors.rb +2 -0
  44. data/lib/switchman/{shackles.rb → guard_rail.rb} +6 -4
  45. data/lib/switchman/{shackles → guard_rail}/relation.rb +7 -5
  46. data/lib/switchman/open4.rb +2 -0
  47. data/lib/switchman/r_spec_helper.rb +7 -5
  48. data/lib/switchman/rails.rb +2 -0
  49. data/lib/switchman/schema_cache.rb +2 -0
  50. data/lib/switchman/sharded_instrumenter.rb +3 -1
  51. data/lib/switchman/standard_error.rb +2 -0
  52. data/lib/switchman/test_helper.rb +5 -3
  53. data/lib/switchman/version.rb +3 -1
  54. data/lib/tasks/switchman.rake +3 -3
  55. metadata +30 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70787591a2f55963ba4b74b816cb11428d1d764e87d3635309d98b423ed842e1
4
- data.tar.gz: 5516037d6c39f1215eac8764512d97125dbeaf2cb64aeed234f5a4d55ce18650
3
+ metadata.gz: c544d2ed8498a6aecf33823f39b304393a65f213f6a68bf04f4d36610ebe3ac2
4
+ data.tar.gz: be685b315997b103dd13376f5223196f1b66a5be490502aac6bc401408899abc
5
5
  SHA512:
6
- metadata.gz: bad00cb3a194c18f792c214caa3f47f90ac53b7186ea458775b9562e1bf2b8fa898e6317ca4514722ce3a9e8652061101e15f657556f2c95344c6ecd5284bb34
7
- data.tar.gz: '0697c43ffd9934ddfb9d0640295024e1035297ac7f14e9a3018a80f0170e427904924d4efa42892a5cf2f223aa75490c9ccd0da500c7040c4c53da4b04f8d9a7'
6
+ metadata.gz: fbdd769d90c831e1bbe6dd39def12775537989346daae7dbbab3103a330b89151e9d1c3b9fe216ab9ade75e3c01ee1b1c91062cc622707c3c2697f7dc3239ea8
7
+ data.tar.gz: 27d32eefa842a76debb1a1fc7875407b4df1398a45a71419c7cc13045fd8705ff5e4af724478a9cb854d517433ae8d16420baa13fc8e7459ab1953e626c0c80a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/database_server'
2
4
  require 'switchman/default_shard'
3
5
  require 'switchman/environment'
@@ -38,7 +40,10 @@ module Switchman
38
40
  end
39
41
 
40
42
  def default(reload_deprecated = false, reload: false, with_fallback: false)
41
- reload = reload_deprecated if reload_deprecated
43
+ if reload_deprecated
44
+ reload = reload_deprecated
45
+ ::ActiveSupport::Deprecation.warn("positional reload parameter to Switchman::Shard.default is deprecated; use `reload: true`")
46
+ end
42
47
  if !@default || reload
43
48
  # Have to create a dummy object so that several key methods still work
44
49
  # (it's easier to do this in one place here, and just assume that sharding
@@ -351,8 +356,8 @@ module Switchman
351
356
  # prune the prior connection unless it happened to be the same
352
357
  if previous_shard && shard != previous_shard && !previous_shard.database_server.shareable?
353
358
  previous_shard.activate do
354
- ::Shackles.activated_environments.each do |env|
355
- ::Shackles.activate(env) do
359
+ ::GuardRail.activated_environments.each do |env|
360
+ ::GuardRail.activate(env) do
356
361
  if ::ActiveRecord::Base.connected? && ::ActiveRecord::Base.connection.open_transactions == 0
357
362
  ::ActiveRecord::Base.connection_pool.current_pool.disconnect!
358
363
  end
@@ -659,7 +664,7 @@ module Switchman
659
664
  case adapter
660
665
  when 'mysql', 'mysql2'
661
666
  self.activate do
662
- ::Shackles.activate(:deploy) do
667
+ ::GuardRail.activate(:deploy) do
663
668
  drop_statement ||= "DROP DATABASE #{self.name}"
664
669
  Array(drop_statement).each do |stmt|
665
670
  ::ActiveRecord::Base.connection.execute(stmt)
@@ -668,7 +673,7 @@ module Switchman
668
673
  end
669
674
  when 'postgresql'
670
675
  self.activate do
671
- ::Shackles.activate(:deploy) do
676
+ ::GuardRail.activate(:deploy) do
672
677
  # Shut up, Postgres!
673
678
  conn = ::ActiveRecord::Base.connection
674
679
  old_proc = conn.raw_connection.set_notice_processor {}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateSwitchmanShards < ActiveRecord::Migration[4.2]
2
4
  def change
3
5
  create_table :switchman_shards do |t|
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateDefaultShard < ActiveRecord::Migration[4.2]
2
4
  def up
3
5
  unless Switchman::Shard.default.is_a?(Switchman::Shard)
4
6
  Switchman::Shard.reset_column_information
5
7
  Switchman::Shard.create!(:default => true)
6
- Switchman::Shard.default(true)
8
+ Switchman::Shard.default(reload: true)
7
9
  end
8
10
  end
9
11
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class AddBackDefaultStringLimitsSwitchman < ActiveRecord::Migration[4.2]
2
4
  def up
3
5
  add_string_limit_if_missing :switchman_shards, :name
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
2
4
  def change
3
5
  Switchman::Shard.where(default: nil).update_all(default: false)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class AddTimestampsToShards < ActiveRecord::Migration[4.2]
2
4
  def change
3
5
  add_timestamps :switchman_shards, null: true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class AddUniqueNameIndexes < ActiveRecord::Migration[4.2]
2
4
  def change
3
5
  add_index :switchman_shards, [:database_server_id, :name], unique: true
data/lib/switchman.rb CHANGED
@@ -1,4 +1,6 @@
1
- require "shackles"
1
+ # frozen_string_literal: true
2
+
3
+ require "guard_rail"
2
4
  require "switchman/open4"
3
5
  require "switchman/engine"
4
6
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActionController
3
5
  module Caching
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/sharded_instrumenter'
2
4
 
3
5
  module Switchman
4
6
  module ActiveRecord
5
7
  module AbstractAdapter
6
8
  module ForeignKeyCheck
7
- def add_column(table, name, type, options = {})
8
- Engine.foreign_key_check(name, type, options)
9
+ def add_column(table, name, type, limit: nil, **)
10
+ Engine.foreign_key_check(name, type, limit: limit)
9
11
  super
10
12
  end
11
13
  end
@@ -27,6 +29,10 @@ module Switchman
27
29
  quote_table_name(name)
28
30
  end
29
31
 
32
+ def schema_migration
33
+ ::ActiveRecord::SchemaMigration
34
+ end
35
+
30
36
  protected
31
37
 
32
38
  def log(*args, &block)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Association
@@ -75,7 +77,7 @@ module Switchman
75
77
  module Association
76
78
  if ::Rails.version >= "5.2" and ::Rails.version < "6.0"
77
79
  def run(preloader)
78
- associated_records_by_owner(preloader).each do |owner, records|
80
+ associated_records_by_owner.each do |owner, records|
79
81
  associate_records_to_owner(owner, records)
80
82
  end
81
83
  end
@@ -102,6 +104,7 @@ module Switchman
102
104
  end
103
105
 
104
106
  def associated_records_by_owner(preloader = nil)
107
+ return @associated_records_by_owner if defined?(@associated_records_by_owner)
105
108
  owners_map = owners_by_key
106
109
 
107
110
  if klass.nil? || owners_map.empty?
@@ -149,10 +152,13 @@ module Switchman
149
152
  records.flatten!
150
153
  end
151
154
 
155
+ # This ivar may look unused, but remember this is an extension of
156
+ # rails' AR::Associations::Preloader::Association class. It gets used
157
+ # by that class (and its subclasses).
152
158
  @preloaded_records = records
153
159
 
154
160
  # Each record may have multiple owners, and vice-versa
155
- records_by_owner = owners.each_with_object({}) do |owner,h|
161
+ @associated_records_by_owner = owners.each_with_object({}) do |owner,h|
156
162
  h[owner] = []
157
163
  end
158
164
  records.each do |record|
@@ -161,10 +167,10 @@ module Switchman
161
167
 
162
168
  owners_map[owner_key.to_s].each do |owner|
163
169
  owner.association(reflection.name).set_inverse_instance(record)
164
- records_by_owner[owner] << record
170
+ @associated_records_by_owner[owner] << record
165
171
  end
166
172
  end
167
- records_by_owner
173
+ @associated_records_by_owner
168
174
  end
169
175
 
170
176
  def owners_by_key
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module AttributeMethods
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Base
@@ -31,20 +33,20 @@ module Switchman
31
33
  @integral_id
32
34
  end
33
35
 
34
- def transaction(*args)
36
+ def transaction(**)
35
37
  if self != ::ActiveRecord::Base && current_scope
36
38
  current_scope.activate do
37
39
  db = Shard.current(shard_category).database_server
38
- if ::Shackles.environment != db.shackles_environment
39
- db.unshackle { super }
40
+ if ::GuardRail.environment != db.guard_rail_environment
41
+ db.unguard { super }
40
42
  else
41
43
  super
42
44
  end
43
45
  end
44
46
  else
45
47
  db = Shard.current(shard_category).database_server
46
- if ::Shackles.environment != db.shackles_environment
47
- db.unshackle { super }
48
+ if ::GuardRail.environment != db.guard_rail_environment
49
+ db.unguard { super }
48
50
  else
49
51
  super
50
52
  end
@@ -105,12 +107,12 @@ module Switchman
105
107
  end
106
108
  end
107
109
 
108
- def save(*args)
110
+ def save(*, **)
109
111
  @shard_set_in_stone = true
110
112
  (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
111
113
  end
112
114
 
113
- def save!(*args)
115
+ def save!(*, **)
114
116
  @shard_set_in_stone = true
115
117
  (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
116
118
  end
@@ -128,9 +130,9 @@ module Switchman
128
130
  result
129
131
  end
130
132
 
131
- def transaction(options={}, &block)
133
+ def transaction(**kwargs, &block)
132
134
  shard.activate(self.class.shard_category) do
133
- self.class.transaction(options, &block)
135
+ self.class.transaction(**kwargs, &block)
134
136
  end
135
137
  end
136
138
 
@@ -157,8 +159,8 @@ module Switchman
157
159
 
158
160
  def update_columns(*)
159
161
  db = Shard.current(self.class.shard_category).database_server
160
- if ::Shackles.environment != db.shackles_environment
161
- return db.unshackle { super }
162
+ if ::GuardRail.environment != db.guard_rail_environment
163
+ return db.unguard { super }
162
164
  else
163
165
  super
164
166
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Batches
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Calculations
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/connection_pool_proxy'
2
4
 
3
5
  module Switchman
@@ -55,7 +57,7 @@ module Switchman
55
57
  Shard.default.remove_instance_variable(:@name) if Shard.default.instance_variable_defined?(:@name)
56
58
  end
57
59
 
58
- @shard_connection_pools ||= { [:master, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
60
+ @shard_connection_pools ||= { [:primary, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
59
61
 
60
62
  category = pool.spec.name.to_sym
61
63
  proxy = ConnectionPoolProxy.new(category,
@@ -64,13 +66,13 @@ module Switchman
64
66
  owner_to_pool[pool.spec.name] = proxy
65
67
 
66
68
  if first_time
67
- if Shard.default.database_server.config[:prefer_slave]
68
- Shard.default.database_server.shackle!
69
+ if Shard.default.database_server.config[:prefer_secondary]
70
+ Shard.default.database_server.guard!
69
71
  end
70
72
 
71
- if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:slave]
72
- Shard.default.database_server.shackle!
73
- Shard.default(true)
73
+ if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:secondary]
74
+ Shard.default.database_server.guard!
75
+ Shard.default(reload: true)
74
76
  end
75
77
  end
76
78
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'switchman/errors'
2
4
 
3
5
  module Switchman
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module FinderMethods
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module LogSubscriber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Migration
@@ -29,7 +31,7 @@ module Switchman
29
31
 
30
32
  module Migrator
31
33
  def generate_migrator_advisory_lock_id
32
- shard_name_hash = Zlib.crc32(Shard.current.name)
34
+ shard_name_hash = Zlib.crc32("#{Shard.current.id}:#{Shard.current.name}")
33
35
  ::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
34
36
  end
35
37
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module ModelSchema
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module Persistence
4
6
  # touch reads the id attribute directly, so it's not relative to the current shard
5
- def touch(*)
7
+ def touch(*, **)
6
8
  shard.activate(self.class.shard_category) { super }
7
9
  end
8
10
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module PostgreSQLAdapter
@@ -192,7 +194,7 @@ module Switchman
192
194
  end
193
195
  end
194
196
 
195
- def add_index_options(_table_name, _column_name, _options = {})
197
+ def add_index_options(_table_name, _column_name, **)
196
198
  index_name, index_type, index_columns, index_options, algorithm, using = super
197
199
  algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
198
200
  [index_name, index_type, index_columns, index_options, algorithm, using]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module PredicateBuilder
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module QueryCache
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module QueryMethods
@@ -76,6 +78,10 @@ module Switchman
76
78
  end
77
79
  end
78
80
 
81
+ def or(other)
82
+ super(other.shard(self.primary_shard))
83
+ end
84
+
79
85
  private
80
86
 
81
87
  if ::Rails.version >= '5.2'
@@ -240,92 +246,135 @@ module Switchman
240
246
  binds: nil,
241
247
  dup_binds_on_mutation: false)
242
248
  result = predicates.map do |predicate|
243
- next predicate unless predicate.is_a?(::Arel::Nodes::Binary)
244
- next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
245
- relation, column = relation_and_column(predicate.left)
246
- next predicate unless (type = transposable_attribute_type(relation, column))
249
+ transposed, binds = transpose_single_predicate(predicate, source_shard, target_shard, remove_nonlocal_primary_keys,
250
+ binds: binds, dup_binds_on_mutation: dup_binds_on_mutation)
251
+ transposed
252
+ end
253
+ result = [result, binds]
254
+ result
255
+ end
256
+
257
+ def transpose_single_predicate(predicate,
258
+ source_shard,
259
+ target_shard,
260
+ remove_nonlocal_primary_keys = false,
261
+ binds: nil,
262
+ dup_binds_on_mutation: false)
263
+ if predicate.is_a?(::Arel::Nodes::Grouping)
264
+ return predicate, binds unless predicate.expr.is_a?(::Arel::Nodes::Or)
265
+ # Dang, we have an OR. OK, that means we have other epxressions below this
266
+ # level, perhaps many, that may need transposition.
267
+ # the left side and right side must each be treated as predicate lists and
268
+ # transformed in kind, if neither of them changes we can just return the grouping as is.
269
+ # hold on, it's about to get recursive...
270
+ #
271
+ # TODO: "binds" is getting passed up and down
272
+ # this stack purely because of the necessary handling for rails <5.2
273
+ # Dropping support for 5.2 means we can remove the "binds" argument from
274
+ # all of this and yank the conditional below where we monkey with their instance state.
275
+ or_expr = predicate.expr
276
+ left_node = or_expr.left
277
+ right_node = or_expr.right
278
+ left_predicates = left_node.children
279
+ right_predicates = right_node.children
280
+ new_left_predicates, binds = transpose_predicates(left_predicates, source_shard,
281
+ target_shard, remove_nonlocal_primary_keys,
282
+ binds: binds, dup_binds_on_mutation: dup_binds_on_mutation)
283
+ new_right_predicates, binds = transpose_predicates(right_predicates, source_shard,
284
+ target_shard, remove_nonlocal_primary_keys,
285
+ binds: binds, dup_binds_on_mutation: dup_binds_on_mutation)
286
+ if new_left_predicates != left_predicates
287
+ left_node.instance_variable_set(:@children, new_left_predicates)
288
+ end
289
+ if new_right_predicates != right_predicates
290
+ right_node.instance_variable_set(:@children, new_right_predicates)
291
+ end
292
+ return predicate, binds
293
+ end
294
+ return predicate, binds unless predicate.is_a?(::Arel::Nodes::Binary)
295
+ return predicate, binds unless predicate.left.is_a?(::Arel::Attributes::Attribute)
296
+ relation, column = relation_and_column(predicate.left)
297
+ return predicate, binds unless (type = transposable_attribute_type(relation, column))
247
298
 
248
- remove = true if type == :primary &&
249
- remove_nonlocal_primary_keys &&
250
- predicate.left.relation.model == klass &&
251
- predicate.is_a?(::Arel::Nodes::Equality)
299
+ remove = true if type == :primary &&
300
+ remove_nonlocal_primary_keys &&
301
+ predicate.left.relation.model == klass &&
302
+ predicate.is_a?(::Arel::Nodes::Equality)
252
303
 
253
- current_source_shard =
254
- if source_shard
255
- source_shard
256
- elsif type == :primary
257
- Shard.current(klass.shard_category)
258
- elsif type == :foreign
259
- source_shard_for_foreign_key(relation, column)
260
- end
304
+ current_source_shard =
305
+ if source_shard
306
+ source_shard
307
+ elsif type == :primary
308
+ Shard.current(klass.shard_category)
309
+ elsif type == :foreign
310
+ source_shard_for_foreign_key(relation, column)
311
+ end
261
312
 
262
- if ::Rails.version >= "5.2"
263
- new_right_value =
264
- case predicate.right
265
- when Array
266
- predicate.right.map {|val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
267
- else
268
- transpose_predicate_value(predicate.right, current_source_shard, target_shard, type, remove)
269
- end
270
- else
271
- new_right_value = case predicate.right
313
+ if ::Rails.version >= "5.2"
314
+ new_right_value =
315
+ case predicate.right
272
316
  when Array
273
- local_ids = []
274
- predicate.right.each do |value|
275
- local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
276
- next unless local_id
277
- unless remove && local_id > Shard::IDS_PER_SHARD
278
- if value.is_a?(::Arel::Nodes::Casted)
279
- if local_id == value.val
280
- local_id = value
281
- elsif local_id != value
282
- local_id = value.class.new(local_id, value.attribute)
283
- end
317
+ predicate.right.map {|val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
318
+ else
319
+ transpose_predicate_value(predicate.right, current_source_shard, target_shard, type, remove)
320
+ end
321
+ else
322
+ new_right_value = case predicate.right
323
+ when Array
324
+ local_ids = []
325
+ predicate.right.each do |value|
326
+ local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
327
+ next unless local_id
328
+ unless remove && local_id > Shard::IDS_PER_SHARD
329
+ if value.is_a?(::Arel::Nodes::Casted)
330
+ if local_id == value.val
331
+ local_id = value
332
+ elsif local_id != value
333
+ local_id = value.class.new(local_id, value.attribute)
284
334
  end
285
- local_ids << local_id
286
335
  end
336
+ local_ids << local_id
287
337
  end
288
- local_ids
289
- when ::Arel::Nodes::BindParam
290
- # look for a bind param with a matching column name
291
- if binds && bind = binds.detect{|b| b&.name.to_s == predicate.left.name.to_s}
292
- # before we mutate, dup
293
- if dup_binds_on_mutation
294
- binds = binds.map(&:dup)
295
- dup_binds_on_mutation = false
296
- bind = binds.find { |b| b&.name.to_s == predicate.left.name.to_s }
297
- end
298
- if bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
299
- bind.value.sharded = true # mark for transposition later
300
- bind.value.primary = true if type == :primary
301
- else
302
- local_id = Shard.relative_id_for(bind.value, current_source_shard, target_shard)
303
- local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
304
- bind.instance_variable_set(:@value, local_id)
305
- bind.instance_variable_set(:@value_for_database, nil)
306
- end
338
+ end
339
+ local_ids
340
+ when ::Arel::Nodes::BindParam
341
+ # look for a bind param with a matching column name
342
+ if binds && bind = binds.detect{|b| b&.name.to_s == predicate.left.name.to_s}
343
+ # before we mutate, dup
344
+ if dup_binds_on_mutation
345
+ binds = binds.map(&:dup)
346
+ dup_binds_on_mutation = false
347
+ bind = binds.find { |b| b&.name.to_s == predicate.left.name.to_s }
348
+ end
349
+ if bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
350
+ bind.value.sharded = true # mark for transposition later
351
+ bind.value.primary = true if type == :primary
352
+ else
353
+ local_id = Shard.relative_id_for(bind.value, current_source_shard, target_shard)
354
+ local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
355
+ bind.instance_variable_set(:@value, local_id)
356
+ bind.instance_variable_set(:@value_for_database, nil)
307
357
  end
308
- predicate.right
309
- else
310
- local_id = Shard.relative_id_for(predicate.right, current_source_shard, target_shard) || predicate.right
311
- local_id = [] if remove && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
312
- local_id
313
358
  end
359
+ predicate.right
360
+ else
361
+ local_id = Shard.relative_id_for(predicate.right, current_source_shard, target_shard) || predicate.right
362
+ local_id = [] if remove && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
363
+ local_id
314
364
  end
315
- if new_right_value == predicate.right
365
+ end
366
+ out_predicate = if new_right_value == predicate.right
367
+ predicate
368
+ elsif predicate.right.is_a?(::Arel::Nodes::Casted)
369
+ if new_right_value == predicate.right.val
316
370
  predicate
317
- elsif predicate.right.is_a?(::Arel::Nodes::Casted)
318
- if new_right_value == predicate.right.val
319
- predicate
320
- else
321
- predicate.class.new(predicate.left, predicate.right.class.new(new_right_value, predicate.right.attribute))
322
- end
323
371
  else
324
- predicate.class.new(predicate.left, new_right_value)
372
+ predicate.class.new(predicate.left, predicate.right.class.new(new_right_value, predicate.right.attribute))
325
373
  end
374
+ else
375
+ predicate.class.new(predicate.left, new_right_value)
326
376
  end
327
- result = [result, binds]
328
- result
377
+ return out_predicate, binds
329
378
  end
330
379
 
331
380
  def transpose_predicate_value(value, current_shard, target_shard, attribute_type, remove_non_local_ids)