activerecord 6.1.0 → 6.1.6.1

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.

Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +466 -26
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +4 -4
  6. data/lib/active_record/association_relation.rb +10 -0
  7. data/lib/active_record/associations/association.rb +13 -7
  8. data/lib/active_record/associations/association_scope.rb +7 -5
  9. data/lib/active_record/associations/belongs_to_association.rb +8 -5
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  11. data/lib/active_record/associations/builder/association.rb +23 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +2 -2
  13. data/lib/active_record/associations/collection_association.rb +16 -8
  14. data/lib/active_record/associations/has_many_association.rb +1 -1
  15. data/lib/active_record/associations/join_dependency/join_association.rb +8 -7
  16. data/lib/active_record/associations/join_dependency.rb +1 -1
  17. data/lib/active_record/associations/preloader/association.rb +1 -3
  18. data/lib/active_record/associations.rb +6 -2
  19. data/lib/active_record/attributes.rb +1 -1
  20. data/lib/active_record/coders/yaml_column.rb +13 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -5
  22. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  23. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -3
  25. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +1 -1
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -0
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +7 -1
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +14 -3
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -11
  30. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  31. data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -0
  32. data/lib/active_record/connection_adapters/mysql/quoting.rb +17 -2
  33. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -1
  34. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
  35. data/lib/active_record/connection_adapters/pool_config.rb +13 -3
  36. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  37. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -1
  38. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  39. data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -8
  40. data/lib/active_record/connection_adapters/schema_cache.rb +43 -11
  41. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  42. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -0
  43. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  44. data/lib/active_record/connection_adapters.rb +2 -0
  45. data/lib/active_record/connection_handling.rb +22 -14
  46. data/lib/active_record/core.rb +68 -31
  47. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  48. data/lib/active_record/database_configurations/hash_config.rb +1 -1
  49. data/lib/active_record/database_configurations/url_config.rb +1 -1
  50. data/lib/active_record/database_configurations.rb +2 -1
  51. data/lib/active_record/enum.rb +52 -34
  52. data/lib/active_record/fixtures.rb +5 -2
  53. data/lib/active_record/gem_version.rb +2 -2
  54. data/lib/active_record/insert_all.rb +5 -1
  55. data/lib/active_record/internal_metadata.rb +2 -0
  56. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  57. data/lib/active_record/locking/optimistic.rb +14 -4
  58. data/lib/active_record/log_subscriber.rb +3 -2
  59. data/lib/active_record/migration/compatibility.rb +9 -5
  60. data/lib/active_record/migration.rb +1 -1
  61. data/lib/active_record/model_schema.rb +5 -5
  62. data/lib/active_record/railtie.rb +18 -0
  63. data/lib/active_record/railties/console_sandbox.rb +2 -4
  64. data/lib/active_record/railties/databases.rake +24 -17
  65. data/lib/active_record/reflection.rb +1 -1
  66. data/lib/active_record/relation/calculations.rb +8 -4
  67. data/lib/active_record/relation/finder_methods.rb +2 -2
  68. data/lib/active_record/relation/predicate_builder/association_query_value.rb +3 -3
  69. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +9 -5
  70. data/lib/active_record/relation/predicate_builder.rb +5 -6
  71. data/lib/active_record/relation/query_methods.rb +11 -8
  72. data/lib/active_record/relation/where_clause.rb +19 -16
  73. data/lib/active_record/relation.rb +10 -17
  74. data/lib/active_record/scoping/default.rb +1 -3
  75. data/lib/active_record/signed_id.rb +1 -1
  76. data/lib/active_record/statement_cache.rb +2 -2
  77. data/lib/active_record/table_metadata.rb +6 -3
  78. data/lib/active_record/tasks/database_tasks.rb +2 -1
  79. data/lib/active_record/test_fixtures.rb +42 -1
  80. data/lib/active_record/transactions.rb +4 -2
  81. data/lib/active_record/validations/numericality.rb +1 -1
  82. data/lib/active_record.rb +1 -1
  83. data/lib/arel/collectors/bind.rb +2 -2
  84. data/lib/arel/collectors/composite.rb +3 -3
  85. data/lib/arel/collectors/sql_string.rb +1 -1
  86. data/lib/arel/collectors/substitute_binds.rb +1 -1
  87. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  88. data/lib/arel/predications.rb +2 -2
  89. data/lib/arel/visitors/to_sql.rb +1 -1
  90. metadata +14 -13
@@ -167,6 +167,9 @@ module ActiveRecord
167
167
  elsif type_metadata.extra == "DEFAULT_GENERATED"
168
168
  default = +"(#{default})" unless default.start_with?("(")
169
169
  default, default_function = nil, default
170
+ elsif type_metadata.type == :text && default
171
+ # strip and unescape quotes
172
+ default = default[1...-1].gsub("\\'", "'")
170
173
  end
171
174
 
172
175
  MySQL::Column.new(
@@ -203,7 +206,7 @@ module ActiveRecord
203
206
  def data_source_sql(name = nil, type: nil)
204
207
  scope = quoted_scope(name, type: type)
205
208
 
206
- sql = +"SELECT table_name FROM (SELECT * FROM information_schema.tables "
209
+ sql = +"SELECT table_name FROM (SELECT table_name, table_type FROM information_schema.tables "
207
210
  sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
208
211
  if scope[:type] || scope[:name]
209
212
  conditions = []
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  class PoolConfig # :nodoc:
6
6
  include Mutex_m
7
7
 
8
- attr_reader :db_config, :connection_specification_name
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(connection_specification_name, db_config)
20
+ def initialize(connection_klass, db_config)
21
21
  super()
22
- @connection_specification_name = connection_specification_name
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
- @name_to_role_mapping[role][shard] = pool_config
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
@@ -28,6 +28,8 @@ module ActiveRecord
28
28
 
29
29
  def write_query?(sql) # :nodoc:
30
30
  !READ_QUERY.match?(sql)
31
+ rescue ArgumentError # Invalid encoding
32
+ !READ_QUERY.match?(sql.b)
31
33
  end
32
34
 
33
35
  # Executes an SQL statement, returning a PG::Result object on success
@@ -57,7 +59,7 @@ module ActiveRecord
57
59
  ftype = result.ftype i
58
60
  fmod = result.fmod i
59
61
  case type = get_oid_type(ftype, fmod, fname)
60
- when Type::Integer, Type::Float, Type::Decimal, Type::String, Type::DateTime, Type::Boolean
62
+ when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
61
63
  # skip if a column has already been type casted by pg decoders
62
64
  else types[fname] = type
63
65
  end
@@ -26,9 +26,9 @@ module ActiveRecord
26
26
 
27
27
  value = value.sub(/^\((.+)\)$/, '-\1') # (4)
28
28
  case value
29
- when /^-?\D*[\d,]+\.\d{2}$/ # (1)
29
+ when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
30
30
  value.gsub!(/[^-\d.]/, "")
31
- when /^-?\D*[\d.]+,\d{2}$/ # (2)
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 + 1}"
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") ? Marshal.load(file) : YAML.load(file)
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
 
@@ -190,16 +198,40 @@ module ActiveRecord
190
198
  @indexes = deep_deduplicate(@indexes)
191
199
  end
192
200
 
193
- def deep_deduplicate(value)
194
- case value
195
- when Hash
196
- value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
197
- when Array
198
- value.map { |i| deep_deduplicate(i) }
199
- when String, Deduplicable
200
- -value
201
- else
202
- value
201
+ if RUBY_VERSION < "2.7"
202
+ def deep_deduplicate(value)
203
+ case value
204
+ when Hash
205
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
206
+ when Array
207
+ value.map { |i| deep_deduplicate(i) }
208
+ when String
209
+ if value.tainted?
210
+ # Ruby 2.6 and 2.7 have slightly different implementations of the String#-@ method.
211
+ # In Ruby 2.6, the receiver of the String#-@ method is modified under certain
212
+ # circumstances, and this was later identified as a bug
213
+ # (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
214
+ value = value.dup
215
+ end
216
+ -value
217
+ when Deduplicable
218
+ -value
219
+ else
220
+ value
221
+ end
222
+ end
223
+ else
224
+ def deep_deduplicate(value)
225
+ case value
226
+ when Hash
227
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
228
+ when Array
229
+ value.map { |i| deep_deduplicate(i) }
230
+ when String, Deduplicable
231
+ -value
232
+ else
233
+ value
234
+ end
203
235
  end
204
236
  end
205
237
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record/connection_adapters/deduplicable"
4
-
5
3
  module ActiveRecord
6
4
  # :stopdoc:
7
5
  module ConnectionAdapters
@@ -11,6 +11,8 @@ module ActiveRecord
11
11
 
12
12
  def write_query?(sql) # :nodoc:
13
13
  !READ_QUERY.match?(sql)
14
+ rescue ArgumentError # Invalid encoding
15
+ !READ_QUERY.match?(sql.b)
14
16
  end
15
17
 
16
18
  def explain(arel, binds = [])
@@ -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 `ActiveRecord::ConnectionNotEstablished` error will be raised:
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 `ActiveRecord::ConnectionNotEstablished` error will be
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,
@@ -133,7 +135,7 @@ module ActiveRecord
133
135
  # Dog.first # finds first Dog record stored on the shard one replica
134
136
  # end
135
137
  #
136
- # The database kwarg is deprecated and will be removed in 6.2.0 without replacement.
138
+ # The database kwarg is deprecated and will be removed in Rails 7.0.0 without replacement.
137
139
  def connected_to(database: nil, role: nil, shard: nil, prevent_writes: false, &blk)
138
140
  if legacy_connection_handling
139
141
  if self != Base
@@ -143,12 +145,16 @@ 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)
149
155
  raise ArgumentError, "`connected_to` cannot accept a `database` argument with any other arguments."
150
156
  elsif database
151
- ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 6.2.0 without replacement.")
157
+ ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 7.0.0 without replacement.")
152
158
 
153
159
  if database.is_a?(Hash)
154
160
  role, database = database.first
@@ -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 `prevent_writes`
176
- # can be passed to block writes on a connection. `reading` will automatically set
177
- # `prevent_writes` to true.
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
- # `connected_to_many` is an alternative to deeply nested `connected_to` blocks.
185
+ # +connected_to_many+ is an alternative to deeply nested +connected_to+ blocks.
180
186
  #
181
187
  # Usage:
182
188
  #
183
- # ActiveRecord::Base.connected_to(AnimalsRecord, MealsRecord], role: :reading) do
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 `connected_to`.
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. `while_preventing_writes`
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 `READ_QUERY` for the queries that are blocked by this
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, owner_name]
368
+ [db_config, self]
361
369
  end
362
370
 
363
371
  def with_handler(handler_key, &blk)
@@ -155,6 +155,14 @@ module ActiveRecord
155
155
 
156
156
  mattr_accessor :legacy_connection_handling, instance_writer: false, default: true
157
157
 
158
+ # Application configurable boolean that instructs the YAML Coder to use
159
+ # an unsafe load if set to true.
160
+ mattr_accessor :use_yaml_unsafe_load, instance_writer: false, default: false
161
+
162
+ # Application configurable array that provides additional permitted classes
163
+ # to Psych safe_load in the YAML Coder
164
+ mattr_accessor :yaml_column_permitted_classes, instance_writer: false, default: []
165
+
158
166
  self.filter_attributes = []
159
167
 
160
168
  def self.connection_handler
@@ -196,7 +204,7 @@ module ActiveRecord
196
204
  else
197
205
  connected_to_stack.reverse_each do |hash|
198
206
  return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
199
- return hash[:role] if hash[:role] && hash[:klasses].include?(abstract_base_class)
207
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_classes)
200
208
  end
201
209
 
202
210
  default_role
@@ -215,7 +223,7 @@ module ActiveRecord
215
223
  def self.current_shard
216
224
  connected_to_stack.reverse_each do |hash|
217
225
  return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
218
- return hash[:shard] if hash[:shard] && hash[:klasses].include?(abstract_base_class)
226
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_classes)
219
227
  end
220
228
 
221
229
  default_shard
@@ -237,7 +245,7 @@ module ActiveRecord
237
245
  else
238
246
  connected_to_stack.reverse_each do |hash|
239
247
  return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
240
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(abstract_base_class)
248
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_classes)
241
249
  end
242
250
 
243
251
  false
@@ -254,11 +262,23 @@ module ActiveRecord
254
262
  end
255
263
  end
256
264
 
257
- def self.abstract_base_class # :nodoc:
265
+ def self.connection_class=(b) # :nodoc:
266
+ @connection_class = b
267
+ end
268
+
269
+ def self.connection_class # :nodoc
270
+ @connection_class ||= false
271
+ end
272
+
273
+ def self.connection_class? # :nodoc:
274
+ self.connection_class
275
+ end
276
+
277
+ def self.connection_classes # :nodoc:
258
278
  klass = self
259
279
 
260
280
  until klass == Base
261
- break if klass.abstract_class?
281
+ break if klass.connection_class?
262
282
  klass = klass.superclass
263
283
  end
264
284
 
@@ -266,25 +286,25 @@ module ActiveRecord
266
286
  end
267
287
 
268
288
  def self.allow_unsafe_raw_sql # :nodoc:
269
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 6.2")
289
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 7.0")
270
290
  end
271
291
 
272
292
  def self.allow_unsafe_raw_sql=(value) # :nodoc:
273
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 6.2")
293
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 7.0")
274
294
  end
275
295
 
276
296
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
277
297
  self.default_role = writing_role
278
298
  self.default_shard = :default
279
299
 
280
- def self.strict_loading_violation!(owner:, association:) # :nodoc:
300
+ def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
281
301
  case action_on_strict_loading_violation
282
302
  when :raise
283
- message = "`#{association}` called on `#{owner}` is marked for strict_loading and cannot be lazily loaded."
303
+ message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
284
304
  raise ActiveRecord::StrictLoadingViolationError.new(message)
285
305
  when :log
286
306
  name = "strict_loading_violation.active_record"
287
- ActiveSupport::Notifications.instrument(name, owner: owner, association: association)
307
+ ActiveSupport::Notifications.instrument(name, owner: owner, reflection: reflection)
288
308
  end
289
309
  end
290
310
  end
@@ -332,31 +352,37 @@ module ActiveRecord
332
352
  hash = args.first
333
353
  return super unless Hash === hash
334
354
 
335
- values = hash.values.map! { |value| value.respond_to?(:id) ? value.id : value }
336
- return super if values.any? { |v| StatementCache.unsupported_value?(v) }
337
-
338
- keys = hash.keys.map! do |key|
339
- attribute_aliases[name = key.to_s] || begin
340
- reflection = _reflect_on_association(name)
341
- if reflection&.belongs_to? && !reflection.polymorphic?
342
- reflection.join_foreign_key
343
- elsif reflect_on_aggregation(name)
344
- return super
345
- else
346
- name
347
- end
355
+ hash = hash.each_with_object({}) do |(key, value), h|
356
+ key = key.to_s
357
+ key = attribute_aliases[key] || key
358
+
359
+ return super if reflect_on_aggregation(key)
360
+
361
+ reflection = _reflect_on_association(key)
362
+
363
+ if !reflection
364
+ value = value.id if value.respond_to?(:id)
365
+ elsif reflection.belongs_to? && !reflection.polymorphic?
366
+ key = reflection.join_foreign_key
367
+ pkey = reflection.join_primary_key
368
+ value = value.public_send(pkey) if value.respond_to?(pkey)
369
+ end
370
+
371
+ if !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
372
+ return super
348
373
  end
349
- end
350
374
 
351
- return super unless keys.all? { |k| columns_hash.key?(k) }
375
+ h[key] = value
376
+ end
352
377
 
378
+ keys = hash.keys
353
379
  statement = cached_find_by_statement(keys) { |params|
354
380
  wheres = keys.index_with { params.bind }
355
381
  where(wheres).limit(1)
356
382
  }
357
383
 
358
384
  begin
359
- statement.execute(values, connection).first
385
+ statement.execute(hash.values, connection).first
360
386
  rescue TypeError
361
387
  raise ActiveRecord::StatementInvalid
362
388
  end
@@ -390,7 +416,21 @@ module ActiveRecord
390
416
  end
391
417
 
392
418
  # Specifies columns which shouldn't be exposed while calling +#inspect+.
393
- attr_writer :filter_attributes
419
+ def filter_attributes=(filter_attributes)
420
+ @inspection_filter = nil
421
+ @filter_attributes = filter_attributes
422
+ end
423
+
424
+ def inspection_filter # :nodoc:
425
+ if defined?(@filter_attributes)
426
+ @inspection_filter ||= begin
427
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
428
+ ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
429
+ end
430
+ else
431
+ superclass.inspection_filter
432
+ end
433
+ end
394
434
 
395
435
  # Returns a string like 'Post(id:integer, title:string, body:text)'
396
436
  def inspect # :nodoc:
@@ -758,10 +798,7 @@ module ActiveRecord
758
798
  private_constant :InspectionMask
759
799
 
760
800
  def inspection_filter
761
- @inspection_filter ||= begin
762
- mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
763
- ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
764
- end
801
+ self.class.inspection_filter
765
802
  end
766
803
  end
767
804
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
3
4
  require "active_support/core_ext/enumerable"
4
5
 
5
6
  module ActiveRecord
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  end
33
33
 
34
34
  def config
35
- ActiveSupport::Deprecation.warn("DatabaseConfig#config will be removed in 6.2.0 in favor of DatabaseConfigurations#configuration_hash which returns a hash with symbol keys")
35
+ ActiveSupport::Deprecation.warn("DatabaseConfig#config will be removed in 7.0.0 in favor of DatabaseConfig#configuration_hash which returns a hash with symbol keys")
36
36
  configuration_hash.stringify_keys
37
37
  end
38
38
 
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
  # Return a Hash that can be merged into the main config that represents
43
43
  # the passed in url
44
44
  def build_url_hash
45
- if url.nil? || url.start_with?("jdbc:")
45
+ if url.nil? || %w(jdbc: http: https:).any? { |protocol| url.start_with?(protocol) }
46
46
  { url: url }
47
47
  else
48
48
  ConnectionUrlResolver.new(url).to_hash
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
3
4
  require "active_record/database_configurations/database_config"
4
5
  require "active_record/database_configurations/hash_config"
5
6
  require "active_record/database_configurations/url_config"
@@ -40,7 +41,7 @@ module ActiveRecord
40
41
  def configs_for(env_name: nil, spec_name: nil, name: nil, include_replicas: false)
41
42
  if spec_name
42
43
  name = spec_name
43
- ActiveSupport::Deprecation.warn("The kwarg `spec_name` is deprecated in favor of `name`. `spec_name` will be removed in Rails 6.2")
44
+ ActiveSupport::Deprecation.warn("The kwarg `spec_name` is deprecated in favor of `name`. `spec_name` will be removed in Rails 7.0")
44
45
  end
45
46
 
46
47
  env_name ||= default_env if name