activerecord 6.1.0 → 6.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +305 -17
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +4 -4
- data/lib/active_record/association_relation.rb +10 -0
- data/lib/active_record/associations/association.rb +13 -7
- data/lib/active_record/associations/association_scope.rb +7 -5
- data/lib/active_record/associations/belongs_to_association.rb +8 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +23 -2
- data/lib/active_record/associations/builder/belongs_to.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +8 -7
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations.rb +6 -2
- data/lib/active_record/attributes.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +11 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/abstract/transaction.rb +14 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -11
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +17 -2
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -1
- data/lib/active_record/connection_adapters/pool_config.rb +13 -3
- data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -8
- data/lib/active_record/connection_adapters/schema_cache.rb +9 -1
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +2 -0
- data/lib/active_record/connection_handling.rb +20 -12
- data/lib/active_record/core.rb +58 -29
- data/lib/active_record/database_configurations/url_config.rb +1 -1
- data/lib/active_record/enum.rb +52 -34
- data/lib/active_record/fixtures.rb +5 -2
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +14 -4
- data/lib/active_record/log_subscriber.rb +3 -2
- data/lib/active_record/migration/compatibility.rb +2 -1
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/model_schema.rb +4 -4
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +16 -9
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/calculations.rb +6 -2
- data/lib/active_record/relation/finder_methods.rb +1 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +9 -5
- data/lib/active_record/relation/predicate_builder.rb +5 -6
- data/lib/active_record/relation/query_methods.rb +9 -6
- data/lib/active_record/relation/where_clause.rb +17 -14
- data/lib/active_record/relation.rb +10 -17
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/table_metadata.rb +6 -3
- data/lib/active_record/tasks/database_tasks.rb +1 -0
- data/lib/active_record/test_fixtures.rb +42 -1
- data/lib/active_record/transactions.rb +4 -2
- data/lib/active_record/validations/numericality.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/composite.rb +3 -3
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/homogeneous_in.rb +4 -0
- data/lib/arel/predications.rb +2 -2
- data/lib/arel/visitors/to_sql.rb +1 -1
- metadata +10 -10
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
def load(yaml)
|
24
24
|
return object_class.new if object_class != Object && yaml.nil?
|
25
25
|
return yaml unless yaml.is_a?(String) && yaml.start_with?("---")
|
26
|
-
obj =
|
26
|
+
obj = yaml_load(yaml)
|
27
27
|
|
28
28
|
assert_valid_value(obj, action: "load")
|
29
29
|
obj ||= object_class.new if object_class != Object
|
@@ -44,6 +44,16 @@ module ActiveRecord
|
|
44
44
|
rescue ArgumentError
|
45
45
|
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
46
46
|
end
|
47
|
+
|
48
|
+
if YAML.respond_to?(:unsafe_load)
|
49
|
+
def yaml_load(payload)
|
50
|
+
YAML.unsafe_load(payload)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
def yaml_load(payload)
|
54
|
+
YAML.load(payload)
|
55
|
+
end
|
56
|
+
end
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
attr_accessor :schema_cache
|
26
26
|
|
27
|
-
def
|
27
|
+
def connection_klass
|
28
28
|
nil
|
29
29
|
end
|
30
30
|
end
|
@@ -360,7 +360,7 @@ module ActiveRecord
|
|
360
360
|
include ConnectionAdapters::AbstractPool
|
361
361
|
|
362
362
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
363
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
363
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
|
364
364
|
|
365
365
|
delegate :schema_cache, :schema_cache=, to: :pool_config
|
366
366
|
|
@@ -375,7 +375,7 @@ module ActiveRecord
|
|
375
375
|
|
376
376
|
@pool_config = pool_config
|
377
377
|
@db_config = pool_config.db_config
|
378
|
-
@
|
378
|
+
@connection_klass = pool_config.connection_klass
|
379
379
|
|
380
380
|
@checkout_timeout = db_config.checkout_timeout
|
381
381
|
@idle_timeout = db_config.idle_timeout
|
@@ -1040,7 +1040,7 @@ module ActiveRecord
|
|
1040
1040
|
end
|
1041
1041
|
alias :connection_pools :connection_pool_list
|
1042
1042
|
|
1043
|
-
def establish_connection(config, owner_name: Base
|
1043
|
+
def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
|
1044
1044
|
owner_name = config.to_s if config.is_a?(Symbol)
|
1045
1045
|
|
1046
1046
|
pool_config = resolve_pool_config(config, owner_name)
|
@@ -395,7 +395,7 @@ module ActiveRecord
|
|
395
395
|
|
396
396
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
397
397
|
# something beyond a simple insert (e.g. Oracle).
|
398
|
-
# Most of adapters should implement
|
398
|
+
# Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
|
399
399
|
# We keep this method to provide fallback
|
400
400
|
# for databases like sqlite that do not support bulk inserts.
|
401
401
|
def insert_fixture(fixture, table_name)
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
module QueryCache
|
8
8
|
class << self
|
9
9
|
def included(base) #:nodoc:
|
10
|
-
dirties_query_cache base, :insert, :update, :delete, :truncate, :truncate_tables,
|
10
|
+
dirties_query_cache base, :create, :insert, :update, :delete, :truncate, :truncate_tables,
|
11
11
|
:rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
|
12
12
|
|
13
13
|
base.set_callback :checkout, :after, :configure_query_cache!
|
@@ -94,8 +94,8 @@ module ActiveRecord
|
|
94
94
|
sql = ["CREATE"]
|
95
95
|
sql << "UNIQUE" if index.unique
|
96
96
|
sql << "INDEX"
|
97
|
-
sql << "IF NOT EXISTS" if o.if_not_exists
|
98
97
|
sql << o.algorithm if o.algorithm
|
98
|
+
sql << "IF NOT EXISTS" if o.if_not_exists
|
99
99
|
sql << index.type if index.type
|
100
100
|
sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
|
101
101
|
sql << "USING #{index.using}" if supports_index_using? && index.using
|
@@ -1387,8 +1387,14 @@ module ActiveRecord
|
|
1387
1387
|
|
1388
1388
|
checks = []
|
1389
1389
|
|
1390
|
+
if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
|
1391
|
+
options[:name] = index_name(table_name, column_name)
|
1392
|
+
column_names = []
|
1393
|
+
else
|
1394
|
+
column_names = index_column_names(column_name || options[:column])
|
1395
|
+
end
|
1396
|
+
|
1390
1397
|
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1391
|
-
column_names = index_column_names(column_name || options[:column])
|
1392
1398
|
|
1393
1399
|
if column_names.present?
|
1394
1400
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
@@ -33,6 +33,10 @@ module ActiveRecord
|
|
33
33
|
@state == :fully_rolledback
|
34
34
|
end
|
35
35
|
|
36
|
+
def invalidated?
|
37
|
+
@state == :invalidated
|
38
|
+
end
|
39
|
+
|
36
40
|
def fully_completed?
|
37
41
|
completed?
|
38
42
|
end
|
@@ -51,6 +55,11 @@ module ActiveRecord
|
|
51
55
|
@state = :fully_rolledback
|
52
56
|
end
|
53
57
|
|
58
|
+
def invalidate!
|
59
|
+
@children&.each { |c| c.invalidate! }
|
60
|
+
@state = :invalidated
|
61
|
+
end
|
62
|
+
|
54
63
|
def commit!
|
55
64
|
@state = :committed
|
56
65
|
end
|
@@ -299,7 +308,7 @@ module ActiveRecord
|
|
299
308
|
def rollback_transaction(transaction = nil)
|
300
309
|
@connection.lock.synchronize do
|
301
310
|
transaction ||= @stack.pop
|
302
|
-
transaction.rollback
|
311
|
+
transaction.rollback unless transaction.state.invalidated?
|
303
312
|
transaction.rollback_records
|
304
313
|
end
|
305
314
|
end
|
@@ -312,15 +321,17 @@ module ActiveRecord
|
|
312
321
|
ret
|
313
322
|
rescue Exception => error
|
314
323
|
if transaction
|
324
|
+
transaction.state.invalidate! if error.is_a? ActiveRecord::TransactionRollbackError
|
315
325
|
rollback_transaction
|
316
326
|
after_failure_actions(transaction, error)
|
317
327
|
end
|
328
|
+
|
318
329
|
raise
|
319
330
|
ensure
|
320
331
|
if transaction
|
321
332
|
if error
|
322
|
-
# @connection still holds an open transaction, so we must not
|
323
|
-
# put it back in the pool for reuse
|
333
|
+
# @connection still holds an open or invalid transaction, so we must not
|
334
|
+
# put it back in the pool for reuse.
|
324
335
|
@connection.throw_away! unless transaction.state.rolledback?
|
325
336
|
else
|
326
337
|
if Thread.current.status == "aborting"
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require "active_record/connection_adapters/schema_cache"
|
5
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
6
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
7
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
@@ -111,22 +110,21 @@ module ActiveRecord
|
|
111
110
|
@config.fetch(:use_metadata_table, true)
|
112
111
|
end
|
113
112
|
|
114
|
-
# Determines whether writes are currently being
|
113
|
+
# Determines whether writes are currently being prevented.
|
115
114
|
#
|
116
115
|
# Returns true if the connection is a replica.
|
117
116
|
#
|
118
117
|
# If the application is using legacy handling, returns
|
119
|
-
# true if
|
118
|
+
# true if +connection_handler.prevent_writes+ is set.
|
120
119
|
#
|
121
120
|
# If the application is using the new connection handling
|
122
|
-
# will return true based on
|
121
|
+
# will return true based on +current_preventing_writes+.
|
123
122
|
def preventing_writes?
|
124
123
|
return true if replica?
|
125
124
|
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord::Base.legacy_connection_handling
|
126
|
-
return false if
|
125
|
+
return false if connection_klass.nil?
|
127
126
|
|
128
|
-
|
129
|
-
klass&.current_preventing_writes
|
127
|
+
connection_klass.current_preventing_writes
|
130
128
|
end
|
131
129
|
|
132
130
|
def migrations_paths # :nodoc:
|
@@ -155,9 +153,10 @@ module ActiveRecord
|
|
155
153
|
end
|
156
154
|
end
|
157
155
|
|
158
|
-
def prepared_statements
|
156
|
+
def prepared_statements?
|
159
157
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
160
158
|
end
|
159
|
+
alias :prepared_statements :prepared_statements?
|
161
160
|
|
162
161
|
def prepared_statements_disabled_cache # :nodoc:
|
163
162
|
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
@@ -202,8 +201,8 @@ module ActiveRecord
|
|
202
201
|
@owner = Thread.current
|
203
202
|
end
|
204
203
|
|
205
|
-
def
|
206
|
-
@pool.
|
204
|
+
def connection_klass # :nodoc:
|
205
|
+
@pool.connection_klass
|
207
206
|
end
|
208
207
|
|
209
208
|
def schema_cache
|
@@ -251,7 +250,7 @@ module ActiveRecord
|
|
251
250
|
end
|
252
251
|
|
253
252
|
def unprepared_statement
|
254
|
-
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
253
|
+
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
255
254
|
yield
|
256
255
|
ensure
|
257
256
|
cache&.delete(object_id)
|
@@ -23,8 +23,12 @@ module ActiveRecord
|
|
23
23
|
@name_to_pool_config[shard]
|
24
24
|
end
|
25
25
|
|
26
|
-
def set_pool_config(
|
27
|
-
|
26
|
+
def set_pool_config(role, shard, pool_config)
|
27
|
+
if pool_config
|
28
|
+
@name_to_pool_config[shard] = pool_config
|
29
|
+
else
|
30
|
+
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
31
|
+
end
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/time_with_zone"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module MySQL
|
@@ -69,10 +71,23 @@ module ActiveRecord
|
|
69
71
|
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
70
72
|
|
71
73
|
private
|
74
|
+
# Override +_type_cast+ we pass to mysql2 Date and Time objects instead
|
75
|
+
# of Strings since mysql2 is able to handle those classes more efficiently.
|
72
76
|
def _type_cast(value)
|
73
77
|
case value
|
74
|
-
when
|
75
|
-
|
78
|
+
when ActiveSupport::TimeWithZone
|
79
|
+
# We need to check explicitly for ActiveSupport::TimeWithZone because
|
80
|
+
# we need to transform it to Time objects but we don't want to
|
81
|
+
# transform Time objects to themselves.
|
82
|
+
if ActiveRecord::Base.default_timezone == :utc
|
83
|
+
value.getutc
|
84
|
+
else
|
85
|
+
value.getlocal
|
86
|
+
end
|
87
|
+
when Date, Time
|
88
|
+
value
|
89
|
+
else
|
90
|
+
super
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|
@@ -79,7 +79,10 @@ module ActiveRecord
|
|
79
79
|
" WHERE table_schema = #{scope[:schema]}" \
|
80
80
|
" AND table_name = #{scope[:name]}" \
|
81
81
|
" AND column_name = #{column_name}"
|
82
|
-
|
82
|
+
# Calling .inspect leads into issues with the query result
|
83
|
+
# which already returns escaped quotes.
|
84
|
+
# We remove the escape sequence from the result in order to deal with double escaping issues.
|
85
|
+
@connection.query_value(sql, "SCHEMA").gsub("\\'", "'").inspect
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class PoolConfig # :nodoc:
|
6
6
|
include Mutex_m
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
8
|
+
attr_reader :db_config, :connection_klass
|
9
9
|
attr_accessor :schema_cache
|
10
10
|
|
11
11
|
INSTANCES = ObjectSpace::WeakMap.new
|
@@ -17,14 +17,24 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(
|
20
|
+
def initialize(connection_klass, db_config)
|
21
21
|
super()
|
22
|
-
@
|
22
|
+
@connection_klass = connection_klass
|
23
23
|
@db_config = db_config
|
24
24
|
@pool = nil
|
25
25
|
INSTANCES[self] = self
|
26
26
|
end
|
27
27
|
|
28
|
+
def connection_specification_name
|
29
|
+
if connection_klass.is_a?(String)
|
30
|
+
connection_klass
|
31
|
+
elsif connection_klass.primary_class?
|
32
|
+
"ActiveRecord::Base"
|
33
|
+
else
|
34
|
+
connection_klass.name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
28
38
|
def disconnect!
|
29
39
|
ActiveSupport::ForkTracker.check!
|
30
40
|
|
@@ -36,7 +36,11 @@ module ActiveRecord
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def set_pool_config(role, shard, pool_config)
|
39
|
-
|
39
|
+
if pool_config
|
40
|
+
@name_to_role_mapping[role][shard] = pool_config
|
41
|
+
else
|
42
|
+
raise ArgumentError, "The `pool_config` for the :#{role} role and :#{shard} shard was `nil`. Please check your configuration. If you want your writing role to be something other than `:writing` set `config.active_record.writing_role` in your application configuration. The same setting should be applied for the `reading_role` if applicable."
|
43
|
+
end
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
ftype = result.ftype i
|
58
58
|
fmod = result.fmod i
|
59
59
|
case type = get_oid_type(ftype, fmod, fname)
|
60
|
-
when Type::Integer, Type::Float,
|
60
|
+
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
61
61
|
# skip if a column has already been type casted by pg decoders
|
62
62
|
else types[fname] = type
|
63
63
|
end
|
@@ -26,9 +26,9 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
28
28
|
case value
|
29
|
-
when /^-?\D
|
29
|
+
when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
|
30
30
|
value.gsub!(/[^-\d.]/, "")
|
31
|
-
when /^-?\D
|
31
|
+
when /^-?\D*+[\d.]+,\d{2}$/ # (2)
|
32
32
|
value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
|
33
33
|
end
|
34
34
|
|
@@ -227,11 +227,7 @@ module ActiveRecord
|
|
227
227
|
end
|
228
228
|
|
229
229
|
def next_key
|
230
|
-
"a#{@counter
|
231
|
-
end
|
232
|
-
|
233
|
-
def []=(sql, key)
|
234
|
-
super.tap { @counter += 1 }
|
230
|
+
"a#{@counter += 1}"
|
235
231
|
end
|
236
232
|
|
237
233
|
private
|
@@ -649,9 +645,7 @@ module ActiveRecord
|
|
649
645
|
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
650
646
|
end
|
651
647
|
|
652
|
-
if without_prepared_statement?(binds)
|
653
|
-
result = exec_no_cache(sql, name, [])
|
654
|
-
elsif !prepare
|
648
|
+
if !prepare || without_prepared_statement?(binds)
|
655
649
|
result = exec_no_cache(sql, name, binds)
|
656
650
|
else
|
657
651
|
result = exec_cache(sql, name, binds)
|
@@ -9,7 +9,15 @@ module ActiveRecord
|
|
9
9
|
return unless File.file?(filename)
|
10
10
|
|
11
11
|
read(filename) do |file|
|
12
|
-
filename.include?(".dump")
|
12
|
+
if filename.include?(".dump")
|
13
|
+
Marshal.load(file)
|
14
|
+
else
|
15
|
+
if YAML.respond_to?(:unsafe_load)
|
16
|
+
YAML.unsafe_load(file)
|
17
|
+
else
|
18
|
+
YAML.load(file)
|
19
|
+
end
|
20
|
+
end
|
13
21
|
end
|
14
22
|
end
|
15
23
|
|
@@ -271,7 +271,7 @@ module ActiveRecord
|
|
271
271
|
def change_column(table_name, column_name, type, **options) #:nodoc:
|
272
272
|
alter_table(table_name) do |definition|
|
273
273
|
definition[column_name].instance_eval do
|
274
|
-
self.type = type
|
274
|
+
self.type = aliased_types(type.to_s, type)
|
275
275
|
self.options.merge!(options)
|
276
276
|
end
|
277
277
|
end
|
@@ -12,6 +12,8 @@ module ActiveRecord
|
|
12
12
|
autoload :PoolConfig
|
13
13
|
autoload :PoolManager
|
14
14
|
autoload :LegacyPoolManager
|
15
|
+
autoload :SchemaCache
|
16
|
+
autoload :Deduplicable
|
15
17
|
|
16
18
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
17
19
|
autoload :IndexDefinition
|
@@ -91,6 +91,7 @@ module ActiveRecord
|
|
91
91
|
db_config, owner_name = resolve_config_for_connection(database_key)
|
92
92
|
handler = lookup_connection_handler(role.to_sym)
|
93
93
|
|
94
|
+
self.connection_class = true
|
94
95
|
connections << handler.establish_connection(db_config, owner_name: owner_name, role: role)
|
95
96
|
end
|
96
97
|
|
@@ -99,6 +100,7 @@ module ActiveRecord
|
|
99
100
|
db_config, owner_name = resolve_config_for_connection(database_key)
|
100
101
|
handler = lookup_connection_handler(role.to_sym)
|
101
102
|
|
103
|
+
self.connection_class = true
|
102
104
|
connections << handler.establish_connection(db_config, owner_name: owner_name, role: role, shard: shard.to_sym)
|
103
105
|
end
|
104
106
|
end
|
@@ -112,7 +114,7 @@ module ActiveRecord
|
|
112
114
|
#
|
113
115
|
# If only a role is passed, Active Record will look up the connection
|
114
116
|
# based on the requested role. If a non-established role is requested
|
115
|
-
# an
|
117
|
+
# an +ActiveRecord::ConnectionNotEstablished+ error will be raised:
|
116
118
|
#
|
117
119
|
# ActiveRecord::Base.connected_to(role: :writing) do
|
118
120
|
# Dog.create! # creates dog using dog writing connection
|
@@ -123,7 +125,7 @@ module ActiveRecord
|
|
123
125
|
# end
|
124
126
|
#
|
125
127
|
# When swapping to a shard, the role must be passed as well. If a non-existent
|
126
|
-
# shard is passed, an
|
128
|
+
# shard is passed, an +ActiveRecord::ConnectionNotEstablished+ error will be
|
127
129
|
# raised.
|
128
130
|
#
|
129
131
|
# When a shard and role is passed, Active Record will first lookup the role,
|
@@ -143,6 +145,10 @@ module ActiveRecord
|
|
143
145
|
if self != Base && !abstract_class
|
144
146
|
raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
|
145
147
|
end
|
148
|
+
|
149
|
+
if name != connection_specification_name && !primary_class?
|
150
|
+
raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
|
151
|
+
end
|
146
152
|
end
|
147
153
|
|
148
154
|
if database && (role || shard)
|
@@ -172,20 +178,22 @@ module ActiveRecord
|
|
172
178
|
end
|
173
179
|
end
|
174
180
|
|
175
|
-
# Connects a role and/or shard to the provided connection names. Optionally
|
176
|
-
# can be passed to block writes on a connection.
|
177
|
-
#
|
181
|
+
# Connects a role and/or shard to the provided connection names. Optionally +prevent_writes+
|
182
|
+
# can be passed to block writes on a connection. +reading+ will automatically set
|
183
|
+
# +prevent_writes+ to true.
|
178
184
|
#
|
179
|
-
#
|
185
|
+
# +connected_to_many+ is an alternative to deeply nested +connected_to+ blocks.
|
180
186
|
#
|
181
187
|
# Usage:
|
182
188
|
#
|
183
|
-
# ActiveRecord::Base.
|
189
|
+
# ActiveRecord::Base.connected_to_many(AnimalsRecord, MealsRecord, role: :reading) do
|
184
190
|
# Dog.first # Read from animals replica
|
185
191
|
# Dinner.first # Read from meals replica
|
186
192
|
# Person.first # Read from primary writer
|
187
193
|
# end
|
188
|
-
def connected_to_many(classes, role:, shard: nil, prevent_writes: false)
|
194
|
+
def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
|
195
|
+
classes = classes.flatten
|
196
|
+
|
189
197
|
if legacy_connection_handling
|
190
198
|
raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
|
191
199
|
end
|
@@ -208,7 +216,7 @@ module ActiveRecord
|
|
208
216
|
# being used. For example, when booting a console in readonly mode.
|
209
217
|
#
|
210
218
|
# It is not recommended to use this method in a request since it
|
211
|
-
# does not yield to a block like
|
219
|
+
# does not yield to a block like +connected_to+.
|
212
220
|
def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
|
213
221
|
if legacy_connection_handling
|
214
222
|
raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
|
@@ -222,13 +230,13 @@ module ActiveRecord
|
|
222
230
|
# Prevent writing to the database regardless of role.
|
223
231
|
#
|
224
232
|
# In some cases you may want to prevent writes to the database
|
225
|
-
# even if you are on a database that can write.
|
233
|
+
# even if you are on a database that can write. +while_preventing_writes+
|
226
234
|
# will prevent writes to the database for the duration of the block.
|
227
235
|
#
|
228
236
|
# This method does not provide the same protection as a readonly
|
229
237
|
# user and is meant to be a safeguard against accidental writes.
|
230
238
|
#
|
231
|
-
# See
|
239
|
+
# See +READ_QUERY+ for the queries that are blocked by this
|
232
240
|
# method.
|
233
241
|
def while_preventing_writes(enabled = true, &block)
|
234
242
|
if legacy_connection_handling
|
@@ -357,7 +365,7 @@ module ActiveRecord
|
|
357
365
|
self.connection_specification_name = owner_name
|
358
366
|
|
359
367
|
db_config = Base.configurations.resolve(config_or_env)
|
360
|
-
[db_config,
|
368
|
+
[db_config, self]
|
361
369
|
end
|
362
370
|
|
363
371
|
def with_handler(handler_key, &blk)
|