switchman 4.0.0 → 4.2.0
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.
- checksums.yaml +4 -4
- data/lib/switchman/active_record/abstract_adapter.rb +1 -7
- data/lib/switchman/active_record/associations.rb +44 -60
- data/lib/switchman/active_record/attribute_methods.rb +34 -27
- data/lib/switchman/active_record/base.rb +12 -61
- data/lib/switchman/active_record/calculations.rb +37 -48
- data/lib/switchman/active_record/connection_pool.rb +37 -23
- data/lib/switchman/active_record/database_configurations.rb +7 -19
- data/lib/switchman/active_record/finder_methods.rb +21 -45
- data/lib/switchman/active_record/log_subscriber.rb +1 -10
- data/lib/switchman/active_record/migration.rb +12 -24
- data/lib/switchman/active_record/persistence.rb +1 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +26 -12
- data/lib/switchman/active_record/query_cache.rb +22 -42
- data/lib/switchman/active_record/query_methods.rb +63 -22
- data/lib/switchman/active_record/reflection.rb +9 -2
- data/lib/switchman/active_record/relation.rb +56 -3
- data/lib/switchman/active_record/spawn_methods.rb +1 -5
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +71 -35
- data/lib/switchman/arel.rb +0 -25
- data/lib/switchman/database_server.rb +5 -9
- data/lib/switchman/engine.rb +10 -5
- data/lib/switchman/shard.rb +9 -11
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +1 -1
- metadata +33 -19
|
@@ -3,29 +3,11 @@
|
|
|
3
3
|
module Switchman
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
module ConnectionPool
|
|
6
|
-
if ::Rails.version < "7.1"
|
|
7
|
-
def get_schema_cache(connection)
|
|
8
|
-
self.schema_cache ||= SharedSchemaCache.get_schema_cache(connection)
|
|
9
|
-
self.schema_cache.connection = connection
|
|
10
|
-
|
|
11
|
-
self.schema_cache
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# rubocop:disable Naming/AccessorMethodName override method
|
|
15
|
-
def set_schema_cache(cache)
|
|
16
|
-
schema_cache = get_schema_cache(cache.connection)
|
|
17
|
-
|
|
18
|
-
cache.instance_variables.each do |x|
|
|
19
|
-
schema_cache.instance_variable_set(x, cache.instance_variable_get(x))
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
# rubocop:enable Naming/AccessorMethodName override method
|
|
23
|
-
end
|
|
24
|
-
|
|
25
6
|
def default_schema
|
|
26
|
-
connection
|
|
7
|
+
connection_method = (::Rails.version < "7.2") ? :connection : :lease_connection
|
|
8
|
+
send(connection_method) unless @schemas
|
|
27
9
|
# default shard will not switch databases immediately, so it won't be set yet
|
|
28
|
-
@schemas ||=
|
|
10
|
+
@schemas ||= send(connection_method).current_schemas
|
|
29
11
|
@schemas.first
|
|
30
12
|
end
|
|
31
13
|
|
|
@@ -43,8 +25,36 @@ module Switchman
|
|
|
43
25
|
conn
|
|
44
26
|
end
|
|
45
27
|
|
|
28
|
+
unless ::Rails.version < "7.2"
|
|
29
|
+
def active_connection(switch_shard: true)
|
|
30
|
+
conn = super()
|
|
31
|
+
return nil if conn.nil?
|
|
32
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
|
33
|
+
|
|
34
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
|
35
|
+
conn
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def lease_connection(switch_shard: true)
|
|
39
|
+
conn = super()
|
|
40
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
|
41
|
+
|
|
42
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
|
43
|
+
conn
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def with_connection(switch_shard: true, **kwargs)
|
|
47
|
+
super(**kwargs) do |conn|
|
|
48
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
|
49
|
+
|
|
50
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
|
51
|
+
yield conn
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
46
56
|
def release_connection(with_id = Thread.current)
|
|
47
|
-
super
|
|
57
|
+
super
|
|
48
58
|
|
|
49
59
|
flush
|
|
50
60
|
end
|
|
@@ -60,7 +70,11 @@ module Switchman
|
|
|
60
70
|
private
|
|
61
71
|
|
|
62
72
|
def current_shard
|
|
63
|
-
|
|
73
|
+
if ::Rails.version < "8.0"
|
|
74
|
+
connection_class.current_switchman_shard
|
|
75
|
+
else
|
|
76
|
+
connection_descriptor.name.constantize.current_switchman_shard
|
|
77
|
+
end
|
|
64
78
|
end
|
|
65
79
|
|
|
66
80
|
def tls_key
|
|
@@ -7,26 +7,14 @@ module Switchman
|
|
|
7
7
|
# since all should point to the same data, even if multiple are writable
|
|
8
8
|
# (Picks 'primary' since it is guaranteed to exist and switchman handles activating
|
|
9
9
|
# deploy through other means)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return res.select { |config| config.name.end_with?("primary") }
|
|
17
|
-
end
|
|
18
|
-
res
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
def configs_for(include_hidden: false, name: nil, **)
|
|
22
|
-
res = super
|
|
23
|
-
if name && !include_hidden
|
|
24
|
-
return nil unless name.end_with?("primary")
|
|
25
|
-
elsif !include_hidden
|
|
26
|
-
return res.select { |config| config.name.end_with?("primary") }
|
|
27
|
-
end
|
|
28
|
-
res
|
|
10
|
+
def configs_for(include_hidden: false, name: nil, **)
|
|
11
|
+
res = super
|
|
12
|
+
if name && !include_hidden
|
|
13
|
+
return nil unless name.end_with?("primary")
|
|
14
|
+
elsif !include_hidden
|
|
15
|
+
return res.select { |config| config.name.end_with?("primary") }
|
|
29
16
|
end
|
|
17
|
+
res
|
|
30
18
|
end
|
|
31
19
|
|
|
32
20
|
private
|
|
@@ -4,7 +4,7 @@ module Switchman
|
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
module FinderMethods
|
|
6
6
|
def find_one(id)
|
|
7
|
-
return super
|
|
7
|
+
return super unless klass.integral_id?
|
|
8
8
|
|
|
9
9
|
if shard_source_value != :implicit
|
|
10
10
|
current_shard = Shard.current(klass.connection_class_for_self)
|
|
@@ -28,70 +28,46 @@ module Switchman
|
|
|
28
28
|
if shard
|
|
29
29
|
shard.activate { super(local_id) }
|
|
30
30
|
else
|
|
31
|
-
super
|
|
31
|
+
super
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def find_some_ordered(ids)
|
|
36
36
|
current_shard = Shard.current(klass.connection_class_for_self)
|
|
37
37
|
ids = ids.map { |id| Shard.relative_id_for(id, current_shard, current_shard) }
|
|
38
|
-
super
|
|
38
|
+
super
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def find_or_instantiator_by_attributes(match, attributes, *args)
|
|
42
42
|
primary_shard.activate { super }
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
conditions = conditions.id if ::ActiveRecord::Base === conditions
|
|
48
|
-
return false unless conditions
|
|
45
|
+
def exists?(conditions = :none)
|
|
46
|
+
return false if @none
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
case conditions
|
|
56
|
-
when Array, Hash
|
|
57
|
-
relation = relation.where(conditions)
|
|
58
|
-
else
|
|
59
|
-
relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
relation.activate do |shard_rel|
|
|
63
|
-
return true if connection.select_value(shard_rel.arel, "#{name} Exists")
|
|
64
|
-
end
|
|
65
|
-
false
|
|
48
|
+
if Base === conditions
|
|
49
|
+
raise ArgumentError, <<-TEXT.squish
|
|
50
|
+
You are passing an instance of ActiveRecord::Base to `exists?`.
|
|
51
|
+
Please pass the id of the object by calling `.id`.
|
|
52
|
+
TEXT
|
|
66
53
|
end
|
|
67
|
-
else
|
|
68
|
-
def exists?(conditions = :none)
|
|
69
|
-
return false if @none
|
|
70
54
|
|
|
71
|
-
|
|
72
|
-
raise ArgumentError, <<-TEXT.squish
|
|
73
|
-
You are passing an instance of ActiveRecord::Base to `exists?`.
|
|
74
|
-
Please pass the id of the object by calling `.id`.
|
|
75
|
-
TEXT
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
return false if !conditions || limit_value == 0 # rubocop:disable Style/NumericPredicate
|
|
55
|
+
return false if !conditions || limit_value == 0 # rubocop:disable Style/NumericPredicate
|
|
79
56
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
57
|
+
if eager_loading?
|
|
58
|
+
relation = apply_join_dependency(eager_loading: false)
|
|
59
|
+
return relation.exists?(conditions)
|
|
60
|
+
end
|
|
84
61
|
|
|
85
|
-
|
|
86
|
-
|
|
62
|
+
relation = construct_relation_for_exists(conditions)
|
|
63
|
+
return false if relation.where_clause.contradiction?
|
|
87
64
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
end
|
|
65
|
+
relation.activate do |shard_rel|
|
|
66
|
+
return true if skip_query_cache_if_necessary do
|
|
67
|
+
connection.select_rows(shard_rel.arel, "#{name} Exists?").size == 1
|
|
92
68
|
end
|
|
93
|
-
false
|
|
94
69
|
end
|
|
70
|
+
false
|
|
95
71
|
end
|
|
96
72
|
end
|
|
97
73
|
end
|
|
@@ -5,11 +5,6 @@ module Switchman
|
|
|
5
5
|
module LogSubscriber
|
|
6
6
|
# sadly, have to completely replace this
|
|
7
7
|
def sql(event)
|
|
8
|
-
if ::Rails.version < "7.1"
|
|
9
|
-
self.class.runtime += event.duration
|
|
10
|
-
return unless logger.debug?
|
|
11
|
-
end
|
|
12
|
-
|
|
13
8
|
payload = event.payload
|
|
14
9
|
|
|
15
10
|
return if ::ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
|
@@ -29,11 +24,7 @@ module Switchman
|
|
|
29
24
|
end
|
|
30
25
|
|
|
31
26
|
name = colorize_payload_name(name, payload[:name])
|
|
32
|
-
sql =
|
|
33
|
-
color(sql, sql_color(sql), true)
|
|
34
|
-
else
|
|
35
|
-
color(sql, sql_color(sql), bold: true)
|
|
36
|
-
end
|
|
27
|
+
sql = color(sql, sql_color(sql), bold: true)
|
|
37
28
|
|
|
38
29
|
debug " #{name} #{sql}#{binds}#{shard}"
|
|
39
30
|
end
|
|
@@ -29,11 +29,7 @@ module Switchman
|
|
|
29
29
|
db_name_hash = Zlib.crc32(Shard.current.name)
|
|
30
30
|
shard_name_hash = ::ActiveRecord::Migrator::MIGRATOR_SALT * db_name_hash
|
|
31
31
|
# Store in internalmetadata to allow other tools to be able to lock out migrations
|
|
32
|
-
|
|
33
|
-
::ActiveRecord::InternalMetadata[:migrator_advisory_lock_id] = shard_name_hash
|
|
34
|
-
else
|
|
35
|
-
::ActiveRecord::InternalMetadata.new(connection)[:migrator_advisory_lock_id] = shard_name_hash
|
|
36
|
-
end
|
|
32
|
+
@internal_metadata[:migrator_advisory_lock_id] = shard_name_hash.to_s
|
|
37
33
|
shard_name_hash
|
|
38
34
|
end
|
|
39
35
|
|
|
@@ -51,31 +47,23 @@ module Switchman
|
|
|
51
47
|
|
|
52
48
|
module MigrationContext
|
|
53
49
|
def migrate(...)
|
|
54
|
-
connection = ::ActiveRecord::Base.connection
|
|
55
50
|
schema_cache_holder = ::ActiveRecord::Base.connection_pool
|
|
56
|
-
schema_cache_holder = schema_cache_holder.schema_reflection
|
|
57
|
-
previous_schema_cache =
|
|
58
|
-
schema_cache_holder.get_schema_cache(connection)
|
|
59
|
-
else
|
|
60
|
-
schema_cache_holder.instance_variable_get(:@cache)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
if ::Rails.version < "7.1"
|
|
64
|
-
temporary_schema_cache = ::ActiveRecord::ConnectionAdapters::SchemaCache.new(connection)
|
|
51
|
+
schema_cache_holder = schema_cache_holder.schema_reflection
|
|
52
|
+
previous_schema_cache = schema_cache_holder.instance_variable_get(:@cache)
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
schema_cache_holder.set_schema_cache(temporary_schema_cache)
|
|
68
|
-
else
|
|
69
|
-
schema_cache_holder.instance_variable_get(:@cache)
|
|
54
|
+
schema_cache_holder.instance_variable_get(:@cache)
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
end
|
|
56
|
+
reset_column_information
|
|
57
|
+
schema_cache_holder.clear!
|
|
74
58
|
|
|
75
59
|
begin
|
|
76
|
-
super
|
|
60
|
+
super
|
|
77
61
|
ensure
|
|
78
|
-
|
|
62
|
+
if ::Rails.version < "7.2"
|
|
63
|
+
schema_cache_holder.set_schema_cache(previous_schema_cache)
|
|
64
|
+
else
|
|
65
|
+
schema_cache_holder.instance_variable_set(:@cache, previous_schema_cache)
|
|
66
|
+
end
|
|
79
67
|
reset_column_information
|
|
80
68
|
end
|
|
81
69
|
end
|
|
@@ -73,23 +73,37 @@ module Switchman
|
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
module ClassMethods
|
|
77
|
+
def quote_local_table_name(name)
|
|
78
|
+
# postgres quotes tables and columns the same; just pass through
|
|
79
|
+
# (differs from quote_table_name_with_shard below by no logic to
|
|
80
|
+
# explicitly qualify the table)
|
|
81
|
+
quote_column_name(name)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def quote_table_name(name, shard: nil)
|
|
85
|
+
# This looks kind of weird at first glance, but older Rails versions do not actually import
|
|
86
|
+
# these methods as class methods.
|
|
87
|
+
shard = self.shard if shard.nil? && ::Rails.version < "7.2" && !@use_local_table_name
|
|
88
|
+
|
|
89
|
+
return quote_local_table_name(name) unless shard
|
|
90
|
+
|
|
91
|
+
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
|
92
|
+
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
|
93
|
+
name.quoted
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
76
97
|
def quote_local_table_name(name)
|
|
77
|
-
|
|
78
|
-
# (differs from quote_table_name_with_shard below by no logic to
|
|
79
|
-
# explicitly qualify the table)
|
|
80
|
-
quote_column_name(name)
|
|
98
|
+
self.class.quote_local_table_name(name)
|
|
81
99
|
end
|
|
82
100
|
|
|
83
101
|
def quote_table_name(name)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
|
87
|
-
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
|
88
|
-
name.quoted
|
|
102
|
+
self.class.quote_table_name(name, shard: @use_local_table_name ? nil : shard)
|
|
89
103
|
end
|
|
90
104
|
|
|
91
|
-
def with_global_table_name(&
|
|
92
|
-
with_local_table_name(false, &
|
|
105
|
+
def with_global_table_name(&)
|
|
106
|
+
with_local_table_name(false, &)
|
|
93
107
|
end
|
|
94
108
|
|
|
95
109
|
def with_local_table_name(enable = true) # rubocop:disable Style/OptionalBooleanParameter
|
|
@@ -129,7 +143,7 @@ module Switchman
|
|
|
129
143
|
end
|
|
130
144
|
|
|
131
145
|
def columns(*)
|
|
132
|
-
|
|
146
|
+
with_global_table_name { super }
|
|
133
147
|
end
|
|
134
148
|
end
|
|
135
149
|
end
|
|
@@ -5,41 +5,15 @@ module Switchman
|
|
|
5
5
|
module QueryCache
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
type_casted_binds: -> { type_casted_binds(binds) }
|
|
22
|
-
}
|
|
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
|
|
31
|
-
result.dup
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
else
|
|
35
|
-
def cache_sql(sql, name, binds)
|
|
36
|
-
# have to include the shard id in the cache key because of switching dbs on the same connection
|
|
37
|
-
sql = "#{shard.id}::#{sql}"
|
|
38
|
-
key = binds.empty? ? sql : [sql, binds]
|
|
39
|
-
result = nil
|
|
40
|
-
hit = false
|
|
8
|
+
def cache_sql(sql, name, binds)
|
|
9
|
+
# have to include the shard id in the cache key because of switching dbs on the same connection
|
|
10
|
+
sql = "#{shard.id}::#{sql}"
|
|
11
|
+
key = binds.empty? ? sql : [sql, binds]
|
|
12
|
+
result = nil
|
|
13
|
+
hit = false
|
|
41
14
|
|
|
42
|
-
|
|
15
|
+
@lock.synchronize do
|
|
16
|
+
if ::Rails.version < "7.2"
|
|
43
17
|
if (result = @query_cache.delete(key))
|
|
44
18
|
hit = true
|
|
45
19
|
@query_cache[key] = result
|
|
@@ -47,17 +21,23 @@ module Switchman
|
|
|
47
21
|
result = @query_cache[key] = yield
|
|
48
22
|
@query_cache.shift if @query_cache_max_size && @query_cache.size > @query_cache_max_size
|
|
49
23
|
end
|
|
24
|
+
else
|
|
25
|
+
hit = true
|
|
26
|
+
result = @query_cache.compute_if_absent(key) do
|
|
27
|
+
hit = false
|
|
28
|
+
yield
|
|
29
|
+
end
|
|
50
30
|
end
|
|
31
|
+
end
|
|
51
32
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
result.dup
|
|
33
|
+
if hit
|
|
34
|
+
::ActiveSupport::Notifications.instrument(
|
|
35
|
+
"sql.active_record",
|
|
36
|
+
cache_notification_info(sql, name, binds)
|
|
37
|
+
)
|
|
60
38
|
end
|
|
39
|
+
|
|
40
|
+
result.dup
|
|
61
41
|
end
|
|
62
42
|
end
|
|
63
43
|
end
|
|
@@ -34,13 +34,29 @@ module Switchman
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def shard_value=(value)
|
|
37
|
-
|
|
37
|
+
if @loaded
|
|
38
|
+
error_class = if ::Rails.version < "7.2"
|
|
39
|
+
::ActiveRecord::ImmutableRelation
|
|
40
|
+
else
|
|
41
|
+
::ActiveRecord::UnmodifiableRelation
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
raise error_class
|
|
45
|
+
end
|
|
38
46
|
|
|
39
47
|
@values[:shard] = value
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
def shard_source_value=(value)
|
|
43
|
-
|
|
51
|
+
if @loaded
|
|
52
|
+
error_class = if ::Rails.version < "7.2"
|
|
53
|
+
::ActiveRecord::ImmutableRelation
|
|
54
|
+
else
|
|
55
|
+
::ActiveRecord::UnmodifiableRelation
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
raise error_class
|
|
59
|
+
end
|
|
44
60
|
|
|
45
61
|
@values[:shard_source] = value
|
|
46
62
|
end
|
|
@@ -107,6 +123,10 @@ module Switchman
|
|
|
107
123
|
|
|
108
124
|
protected
|
|
109
125
|
|
|
126
|
+
def arel_columns(columns)
|
|
127
|
+
connection.with_local_table_name { super }
|
|
128
|
+
end
|
|
129
|
+
|
|
110
130
|
def remove_nonlocal_primary_keys!
|
|
111
131
|
each_transposable_predicate_value do |value, predicate, _relation, _column, type|
|
|
112
132
|
next value unless
|
|
@@ -243,31 +263,49 @@ module Switchman
|
|
|
243
263
|
end
|
|
244
264
|
end
|
|
245
265
|
|
|
246
|
-
def arel_columns(columns)
|
|
247
|
-
connection.with_local_table_name { super }
|
|
248
|
-
end
|
|
249
|
-
|
|
250
266
|
def arel_column(columns)
|
|
251
267
|
connection.with_local_table_name { super }
|
|
252
268
|
end
|
|
253
269
|
|
|
254
270
|
def table_name_matches?(from)
|
|
255
|
-
|
|
271
|
+
if ::Rails.version < "7.2"
|
|
272
|
+
connection.with_global_table_name { super }
|
|
273
|
+
else
|
|
274
|
+
connection.with_global_table_name do
|
|
275
|
+
table_name = Regexp.escape(table.name)
|
|
276
|
+
# INST: adapter_class -> connection
|
|
277
|
+
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
|
278
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
unless ::Rails.version < "7.2"
|
|
284
|
+
def order_column(field)
|
|
285
|
+
arel_column(field) do |attr_name|
|
|
286
|
+
if attr_name == "count" && !group_values.empty?
|
|
287
|
+
table[attr_name]
|
|
288
|
+
else
|
|
289
|
+
# INST: adapter_class -> connection
|
|
290
|
+
::Arel.sql(connection.quote_table_name(attr_name), retryable: true)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
256
294
|
end
|
|
257
295
|
|
|
258
|
-
def each_predicate(predicates = nil, &
|
|
259
|
-
return predicates.map(&
|
|
296
|
+
def each_predicate(predicates = nil, &)
|
|
297
|
+
return predicates.map(&) if predicates
|
|
260
298
|
|
|
261
|
-
each_predicate_cb(:having_clause, :having_clause=, &
|
|
262
|
-
each_predicate_cb(:where_clause, :where_clause=, &
|
|
299
|
+
each_predicate_cb(:having_clause, :having_clause=, &)
|
|
300
|
+
each_predicate_cb(:where_clause, :where_clause=, &)
|
|
263
301
|
end
|
|
264
302
|
|
|
265
|
-
def each_predicate_cb(clause_getter, clause_setter, &
|
|
303
|
+
def each_predicate_cb(clause_getter, clause_setter, &)
|
|
266
304
|
old_clause = send(clause_getter)
|
|
267
305
|
old_predicates = old_clause.send(:predicates)
|
|
268
306
|
return if old_predicates.empty?
|
|
269
307
|
|
|
270
|
-
new_predicates = old_predicates.map(&
|
|
308
|
+
new_predicates = old_predicates.map(&)
|
|
271
309
|
return if new_predicates == old_predicates
|
|
272
310
|
|
|
273
311
|
new_clause = old_clause.dup
|
|
@@ -289,7 +327,10 @@ module Switchman
|
|
|
289
327
|
|
|
290
328
|
next predicate if new_left == old_left && new_right == old_right
|
|
291
329
|
|
|
292
|
-
next predicate.class.new predicate.expr.class.new(new_left, new_right)
|
|
330
|
+
next predicate.class.new predicate.expr.class.new(new_left, new_right) if ::Rails.version < "7.2"
|
|
331
|
+
|
|
332
|
+
next predicate.class.new predicate.expr.class.new([new_left, new_right])
|
|
333
|
+
|
|
293
334
|
when ::Arel::Nodes::SelectStatement
|
|
294
335
|
new_cores = predicate.cores.map do |core|
|
|
295
336
|
next core unless core.is_a?(::Arel::Nodes::SelectCore) # just in case something weird is going on
|
|
@@ -344,33 +385,33 @@ module Switchman
|
|
|
344
385
|
end
|
|
345
386
|
end
|
|
346
387
|
|
|
347
|
-
def each_transposable_predicate_value_cb(node, original_block, &
|
|
388
|
+
def each_transposable_predicate_value_cb(node, original_block, &)
|
|
348
389
|
case node
|
|
349
390
|
when Array
|
|
350
|
-
node.filter_map { |val| each_transposable_predicate_value_cb(val, original_block, &
|
|
391
|
+
node.filter_map { |val| each_transposable_predicate_value_cb(val, original_block, &).presence }
|
|
351
392
|
when ::ActiveModel::Attribute
|
|
352
393
|
old_value = node.value_before_type_cast
|
|
353
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
394
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
354
395
|
|
|
355
396
|
(old_value == new_value) ? node : node.class.new(node.name, new_value, node.type)
|
|
356
397
|
when ::Arel::Nodes::And
|
|
357
398
|
old_value = node.children
|
|
358
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
399
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
359
400
|
|
|
360
401
|
(old_value == new_value) ? node : node.class.new(new_value)
|
|
361
402
|
when ::Arel::Nodes::BindParam
|
|
362
403
|
old_value = node.value
|
|
363
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
404
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
364
405
|
|
|
365
406
|
(old_value == new_value) ? node : node.class.new(new_value)
|
|
366
407
|
when ::Arel::Nodes::Casted
|
|
367
408
|
old_value = node.value
|
|
368
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
409
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
369
410
|
|
|
370
411
|
(old_value == new_value) ? node : node.class.new(new_value, node.attribute)
|
|
371
412
|
when ::Arel::Nodes::HomogeneousIn
|
|
372
413
|
old_value = node.values
|
|
373
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
414
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
374
415
|
|
|
375
416
|
# switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
|
|
376
417
|
if new_value.empty?
|
|
@@ -381,7 +422,7 @@ module Switchman
|
|
|
381
422
|
end
|
|
382
423
|
when ::Arel::Nodes::Binary
|
|
383
424
|
old_value = node.right
|
|
384
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
|
425
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
|
385
426
|
|
|
386
427
|
(old_value == new_value) ? node : node.class.new(node.left, new_value)
|
|
387
428
|
when ::Arel::Nodes::SelectStatement
|
|
@@ -28,11 +28,18 @@ module Switchman
|
|
|
28
28
|
# this technically belongs on AssociationReflection, but we put it on
|
|
29
29
|
# ThroughReflection as well, instead of delegating to its internal
|
|
30
30
|
# HasManyAssociation, losing its proper `klass`
|
|
31
|
-
def association_scope_cache(klass, owner, &
|
|
31
|
+
def association_scope_cache(klass, owner, &)
|
|
32
32
|
key = self
|
|
33
33
|
key = [key, owner._read_attribute(@foreign_type)] if polymorphic?
|
|
34
34
|
key = [key, shard(owner).id].flatten
|
|
35
|
-
|
|
35
|
+
|
|
36
|
+
if ::Rails.version < "7.2"
|
|
37
|
+
klass.cached_find_by_statement(key, &)
|
|
38
|
+
else
|
|
39
|
+
klass.with_connection do |connection|
|
|
40
|
+
klass.cached_find_by_statement(connection, key, &)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|