switchman 1.16.0 → 2.0.5

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 (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)