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
data/lib/active_record/core.rb
CHANGED
@@ -196,7 +196,7 @@ module ActiveRecord
|
|
196
196
|
else
|
197
197
|
connected_to_stack.reverse_each do |hash|
|
198
198
|
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
199
|
-
return hash[:role] if hash[:role] && hash[:klasses].include?(
|
199
|
+
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_classes)
|
200
200
|
end
|
201
201
|
|
202
202
|
default_role
|
@@ -215,7 +215,7 @@ module ActiveRecord
|
|
215
215
|
def self.current_shard
|
216
216
|
connected_to_stack.reverse_each do |hash|
|
217
217
|
return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
|
218
|
-
return hash[:shard] if hash[:shard] && hash[:klasses].include?(
|
218
|
+
return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_classes)
|
219
219
|
end
|
220
220
|
|
221
221
|
default_shard
|
@@ -237,7 +237,7 @@ module ActiveRecord
|
|
237
237
|
else
|
238
238
|
connected_to_stack.reverse_each do |hash|
|
239
239
|
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?(
|
240
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_classes)
|
241
241
|
end
|
242
242
|
|
243
243
|
false
|
@@ -254,11 +254,23 @@ module ActiveRecord
|
|
254
254
|
end
|
255
255
|
end
|
256
256
|
|
257
|
-
def self.
|
257
|
+
def self.connection_class=(b) # :nodoc:
|
258
|
+
@connection_class = b
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.connection_class # :nodoc
|
262
|
+
@connection_class ||= false
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.connection_class? # :nodoc:
|
266
|
+
self.connection_class
|
267
|
+
end
|
268
|
+
|
269
|
+
def self.connection_classes # :nodoc:
|
258
270
|
klass = self
|
259
271
|
|
260
272
|
until klass == Base
|
261
|
-
break if klass.
|
273
|
+
break if klass.connection_class?
|
262
274
|
klass = klass.superclass
|
263
275
|
end
|
264
276
|
|
@@ -277,14 +289,14 @@ module ActiveRecord
|
|
277
289
|
self.default_role = writing_role
|
278
290
|
self.default_shard = :default
|
279
291
|
|
280
|
-
def self.strict_loading_violation!(owner:,
|
292
|
+
def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
|
281
293
|
case action_on_strict_loading_violation
|
282
294
|
when :raise
|
283
|
-
message = "`#{
|
295
|
+
message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
|
284
296
|
raise ActiveRecord::StrictLoadingViolationError.new(message)
|
285
297
|
when :log
|
286
298
|
name = "strict_loading_violation.active_record"
|
287
|
-
ActiveSupport::Notifications.instrument(name, owner: owner,
|
299
|
+
ActiveSupport::Notifications.instrument(name, owner: owner, reflection: reflection)
|
288
300
|
end
|
289
301
|
end
|
290
302
|
end
|
@@ -332,31 +344,37 @@ module ActiveRecord
|
|
332
344
|
hash = args.first
|
333
345
|
return super unless Hash === hash
|
334
346
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
347
|
+
hash = hash.each_with_object({}) do |(key, value), h|
|
348
|
+
key = key.to_s
|
349
|
+
key = attribute_aliases[key] || key
|
350
|
+
|
351
|
+
return super if reflect_on_aggregation(key)
|
352
|
+
|
353
|
+
reflection = _reflect_on_association(key)
|
354
|
+
|
355
|
+
if !reflection
|
356
|
+
value = value.id if value.respond_to?(:id)
|
357
|
+
elsif reflection.belongs_to? && !reflection.polymorphic?
|
358
|
+
key = reflection.join_foreign_key
|
359
|
+
pkey = reflection.join_primary_key
|
360
|
+
value = value.public_send(pkey) if value.respond_to?(pkey)
|
348
361
|
end
|
349
|
-
end
|
350
362
|
|
351
|
-
|
363
|
+
if !columns_hash.key?(key) || StatementCache.unsupported_value?(value)
|
364
|
+
return super
|
365
|
+
end
|
366
|
+
|
367
|
+
h[key] = value
|
368
|
+
end
|
352
369
|
|
370
|
+
keys = hash.keys
|
353
371
|
statement = cached_find_by_statement(keys) { |params|
|
354
372
|
wheres = keys.index_with { params.bind }
|
355
373
|
where(wheres).limit(1)
|
356
374
|
}
|
357
375
|
|
358
376
|
begin
|
359
|
-
statement.execute(values, connection).first
|
377
|
+
statement.execute(hash.values, connection).first
|
360
378
|
rescue TypeError
|
361
379
|
raise ActiveRecord::StatementInvalid
|
362
380
|
end
|
@@ -390,7 +408,21 @@ module ActiveRecord
|
|
390
408
|
end
|
391
409
|
|
392
410
|
# Specifies columns which shouldn't be exposed while calling +#inspect+.
|
393
|
-
|
411
|
+
def filter_attributes=(filter_attributes)
|
412
|
+
@inspection_filter = nil
|
413
|
+
@filter_attributes = filter_attributes
|
414
|
+
end
|
415
|
+
|
416
|
+
def inspection_filter # :nodoc:
|
417
|
+
if defined?(@filter_attributes)
|
418
|
+
@inspection_filter ||= begin
|
419
|
+
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
420
|
+
ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
|
421
|
+
end
|
422
|
+
else
|
423
|
+
superclass.inspection_filter
|
424
|
+
end
|
425
|
+
end
|
394
426
|
|
395
427
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
396
428
|
def inspect # :nodoc:
|
@@ -758,10 +790,7 @@ module ActiveRecord
|
|
758
790
|
private_constant :InspectionMask
|
759
791
|
|
760
792
|
def inspection_filter
|
761
|
-
|
762
|
-
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
763
|
-
ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
|
764
|
-
end
|
793
|
+
self.class.inspection_filter
|
765
794
|
end
|
766
795
|
end
|
767
796
|
end
|
@@ -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?(
|
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
|
data/lib/active_record/enum.rb
CHANGED
@@ -139,27 +139,23 @@ module ActiveRecord
|
|
139
139
|
mapping.key(subtype.deserialize(value))
|
140
140
|
end
|
141
141
|
|
142
|
-
def serializable?(value)
|
143
|
-
(value.blank? || mapping.has_key?(value) || mapping.has_value?(value)) && super
|
144
|
-
end
|
145
|
-
|
146
142
|
def serialize(value)
|
147
143
|
mapping.fetch(value, value)
|
148
144
|
end
|
149
145
|
|
150
146
|
def assert_valid_value(value)
|
151
|
-
unless
|
147
|
+
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
152
148
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
153
149
|
end
|
154
150
|
end
|
155
151
|
|
152
|
+
attr_reader :subtype
|
153
|
+
|
156
154
|
private
|
157
|
-
attr_reader :name, :mapping
|
155
|
+
attr_reader :name, :mapping
|
158
156
|
end
|
159
157
|
|
160
158
|
def enum(definitions)
|
161
|
-
klass = self
|
162
|
-
|
163
159
|
enum_prefix = definitions.delete(:_prefix)
|
164
160
|
enum_suffix = definitions.delete(:_suffix)
|
165
161
|
enum_scopes = definitions.delete(:_scopes)
|
@@ -187,55 +183,77 @@ module ActiveRecord
|
|
187
183
|
EnumType.new(attr, enum_values, subtype)
|
188
184
|
end
|
189
185
|
|
186
|
+
value_method_names = []
|
190
187
|
_enum_methods_module.module_eval do
|
188
|
+
prefix = if enum_prefix == true
|
189
|
+
"#{name}_"
|
190
|
+
elsif enum_prefix
|
191
|
+
"#{enum_prefix}_"
|
192
|
+
end
|
193
|
+
|
194
|
+
suffix = if enum_suffix == true
|
195
|
+
"_#{name}"
|
196
|
+
elsif enum_suffix
|
197
|
+
"_#{enum_suffix}"
|
198
|
+
end
|
199
|
+
|
191
200
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
192
|
-
value_method_names = []
|
193
201
|
pairs.each do |label, value|
|
194
|
-
if enum_prefix == true
|
195
|
-
prefix = "#{name}_"
|
196
|
-
elsif enum_prefix
|
197
|
-
prefix = "#{enum_prefix}_"
|
198
|
-
end
|
199
|
-
if enum_suffix == true
|
200
|
-
suffix = "_#{name}"
|
201
|
-
elsif enum_suffix
|
202
|
-
suffix = "_#{enum_suffix}"
|
203
|
-
end
|
204
|
-
|
205
|
-
method_friendly_label = label.to_s.gsub(/\W+/, "_")
|
206
|
-
value_method_name = "#{prefix}#{method_friendly_label}#{suffix}"
|
207
|
-
value_method_names << value_method_name
|
208
202
|
enum_values[label] = value
|
209
203
|
label = label.to_s
|
210
204
|
|
211
|
-
|
205
|
+
value_method_name = "#{prefix}#{label}#{suffix}"
|
206
|
+
value_method_names << value_method_name
|
207
|
+
define_enum_methods(name, value_method_name, value, enum_scopes)
|
208
|
+
|
209
|
+
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
210
|
+
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
211
|
+
|
212
|
+
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
213
|
+
value_method_names << value_method_alias
|
214
|
+
define_enum_methods(name, value_method_alias, value, enum_scopes)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
detect_negative_enum_conditions!(value_method_names) if enum_scopes != false
|
219
|
+
enum_values.freeze
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
class EnumMethods < Module # :nodoc:
|
225
|
+
def initialize(klass)
|
226
|
+
@klass = klass
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
attr_reader :klass
|
231
|
+
|
232
|
+
def define_enum_methods(name, value_method_name, value, enum_scopes)
|
233
|
+
# def active?() status_for_database == 0 end
|
212
234
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
213
|
-
define_method("#{value_method_name}?") {
|
235
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
214
236
|
|
215
237
|
# def active!() update!(status: 0) end
|
216
238
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
217
|
-
define_method("#{value_method_name}!") { update!(
|
239
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
218
240
|
|
219
241
|
# scope :active, -> { where(status: 0) }
|
220
242
|
# scope :not_active, -> { where.not(status: 0) }
|
221
243
|
if enum_scopes != false
|
222
244
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
223
|
-
klass.scope value_method_name, -> { where(
|
245
|
+
klass.scope value_method_name, -> { where(name => value) }
|
224
246
|
|
225
247
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
226
|
-
klass.scope "not_#{value_method_name}", -> { where.not(
|
248
|
+
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
227
249
|
end
|
228
250
|
end
|
229
|
-
klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
|
230
|
-
end
|
231
|
-
enum_values.freeze
|
232
251
|
end
|
233
|
-
|
252
|
+
private_constant :EnumMethods
|
234
253
|
|
235
|
-
private
|
236
254
|
def _enum_methods_module
|
237
255
|
@_enum_methods_module ||= begin
|
238
|
-
mod =
|
256
|
+
mod = EnumMethods.new(self)
|
239
257
|
include mod
|
240
258
|
mod
|
241
259
|
end
|
@@ -181,7 +181,7 @@ module ActiveRecord
|
|
181
181
|
# end
|
182
182
|
# end
|
183
183
|
#
|
184
|
-
# If you preload your test database with all fixture data (probably by running
|
184
|
+
# If you preload your test database with all fixture data (probably by running <tt>bin/rails db:fixtures:load</tt>)
|
185
185
|
# and use transactional tests, then you may omit all fixtures declarations in your test cases since
|
186
186
|
# all the data's already there and every case rolls back its changes.
|
187
187
|
#
|
@@ -773,9 +773,12 @@ module ActiveRecord
|
|
773
773
|
|
774
774
|
def find
|
775
775
|
raise FixtureClassNotFound, "No class attached to find." unless model_class
|
776
|
-
model_class.unscoped do
|
776
|
+
object = model_class.unscoped do
|
777
777
|
model_class.find(fixture[model_class.primary_key])
|
778
778
|
end
|
779
|
+
# Fixtures can't be eagerly loaded
|
780
|
+
object.instance_variable_set(:@strict_loading, false)
|
781
|
+
object
|
779
782
|
end
|
780
783
|
end
|
781
784
|
end
|
@@ -69,7 +69,11 @@ module ActiveRecord
|
|
69
69
|
attr_reader :scope_attributes
|
70
70
|
|
71
71
|
def find_unique_index_for(unique_by)
|
72
|
-
|
72
|
+
if !connection.supports_insert_conflict_target?
|
73
|
+
return if unique_by.nil?
|
74
|
+
|
75
|
+
raise ArgumentError, "#{connection.class} does not support :unique_by"
|
76
|
+
end
|
73
77
|
|
74
78
|
name_or_columns = unique_by || model.primary_key
|
75
79
|
match = Array(name_or_columns).map(&:to_s)
|
@@ -89,7 +89,9 @@ module ActiveRecord
|
|
89
89
|
|
90
90
|
begin
|
91
91
|
locking_column = self.class.locking_column
|
92
|
-
|
92
|
+
lock_attribute_was = @attributes[locking_column]
|
93
|
+
lock_value_for_database = _lock_value_for_database(locking_column)
|
94
|
+
|
93
95
|
attribute_names = attribute_names.dup if attribute_names.frozen?
|
94
96
|
attribute_names << locking_column
|
95
97
|
|
@@ -98,7 +100,7 @@ module ActiveRecord
|
|
98
100
|
affected_rows = self.class._update_record(
|
99
101
|
attributes_with_values(attribute_names),
|
100
102
|
@primary_key => id_in_database,
|
101
|
-
locking_column =>
|
103
|
+
locking_column => lock_value_for_database
|
102
104
|
)
|
103
105
|
|
104
106
|
if affected_rows != 1
|
@@ -109,7 +111,7 @@ module ActiveRecord
|
|
109
111
|
|
110
112
|
# If something went wrong, revert the locking_column value.
|
111
113
|
rescue Exception
|
112
|
-
|
114
|
+
@attributes[locking_column] = lock_attribute_was
|
113
115
|
raise
|
114
116
|
end
|
115
117
|
end
|
@@ -121,7 +123,7 @@ module ActiveRecord
|
|
121
123
|
|
122
124
|
affected_rows = self.class._delete_record(
|
123
125
|
@primary_key => id_in_database,
|
124
|
-
locking_column =>
|
126
|
+
locking_column => _lock_value_for_database(locking_column)
|
125
127
|
)
|
126
128
|
|
127
129
|
if affected_rows != 1
|
@@ -131,6 +133,14 @@ module ActiveRecord
|
|
131
133
|
affected_rows
|
132
134
|
end
|
133
135
|
|
136
|
+
def _lock_value_for_database(locking_column)
|
137
|
+
if will_save_change_to_attribute?(locking_column)
|
138
|
+
@attributes[locking_column].value_for_database
|
139
|
+
else
|
140
|
+
@attributes[locking_column].original_value_for_database
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
134
144
|
module ClassMethods
|
135
145
|
DEFAULT_LOCKING_COLUMN = "lock_version"
|
136
146
|
|
@@ -22,9 +22,10 @@ module ActiveRecord
|
|
22
22
|
def strict_loading_violation(event)
|
23
23
|
debug do
|
24
24
|
owner = event.payload[:owner]
|
25
|
-
association = event.payload[:
|
25
|
+
association = event.payload[:reflection].klass
|
26
|
+
name = event.payload[:reflection].name
|
26
27
|
|
27
|
-
color("Strict loading violation: #{
|
28
|
+
color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
@@ -56,7 +56,8 @@ module ActiveRecord
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def add_reference(table_name, ref_name, **options)
|
59
|
-
ReferenceDefinition.new(ref_name, **options)
|
59
|
+
ReferenceDefinition.new(ref_name, **options)
|
60
|
+
.add_to(connection.update_table_definition(table_name, self))
|
60
61
|
end
|
61
62
|
alias :add_belongs_to :add_reference
|
62
63
|
|
@@ -320,7 +320,7 @@ module ActiveRecord
|
|
320
320
|
# +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
|
321
321
|
# as +default_or_changes+ will make this change reversible in the migration.
|
322
322
|
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
|
323
|
-
# Sets or removes a
|
323
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on +column_name+. The +null+ flag
|
324
324
|
# indicates whether the value can be +NULL+. See
|
325
325
|
# ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
|
326
326
|
# details.
|
@@ -122,9 +122,9 @@ module ActiveRecord
|
|
122
122
|
# :singleton-method: immutable_strings_by_default=
|
123
123
|
# :call-seq: immutable_strings_by_default=(bool)
|
124
124
|
#
|
125
|
-
# Determines whether columns should infer their type as
|
126
|
-
#
|
127
|
-
#
|
125
|
+
# Determines whether columns should infer their type as +:string+ or
|
126
|
+
# +:immutable_string+. This setting does not affect the behavior of
|
127
|
+
# <tt>attribute :foo, :string</tt>. Defaults to false.
|
128
128
|
|
129
129
|
included do
|
130
130
|
mattr_accessor :primary_key_prefix_type, instance_writer: false
|
@@ -316,7 +316,7 @@ module ActiveRecord
|
|
316
316
|
# self.ignored_columns = [:category]
|
317
317
|
# end
|
318
318
|
#
|
319
|
-
# The schema still contains
|
319
|
+
# The schema still contains "category", but now the model omits it, so any meta-driven code or
|
320
320
|
# schema caching will not attempt to use the column:
|
321
321
|
#
|
322
322
|
# Project.columns_hash["category"] => nil
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
ActiveRecord::
|
4
|
-
|
5
|
-
at_exit do
|
6
|
-
ActiveRecord::Base.connection.rollback_transaction
|
3
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.set_callback(:checkout, :after) do
|
4
|
+
begin_transaction(joinable: false)
|
7
5
|
end
|
@@ -361,17 +361,23 @@ db_namespace = namespace :db do
|
|
361
361
|
|
362
362
|
# Skipped when no database
|
363
363
|
ActiveRecord::Tasks::DatabaseTasks.migrate
|
364
|
+
|
364
365
|
if ActiveRecord::Base.dump_schema_after_migration
|
365
366
|
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, ActiveRecord::Base.schema_format)
|
366
367
|
end
|
367
|
-
|
368
368
|
rescue ActiveRecord::NoDatabaseError
|
369
|
-
|
370
|
-
ActiveRecord::Tasks::DatabaseTasks.
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
369
|
+
config_name = db_config.name
|
370
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, config_name)
|
371
|
+
|
372
|
+
if File.exist?(ActiveRecord::Tasks::DatabaseTasks.dump_filename(config_name))
|
373
|
+
ActiveRecord::Tasks::DatabaseTasks.load_schema(
|
374
|
+
db_config,
|
375
|
+
ActiveRecord::Base.schema_format,
|
376
|
+
nil
|
377
|
+
)
|
378
|
+
else
|
379
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
380
|
+
end
|
375
381
|
|
376
382
|
seed = true
|
377
383
|
end
|
@@ -407,8 +413,9 @@ db_namespace = namespace :db do
|
|
407
413
|
fixture_files = if ENV["FIXTURES"]
|
408
414
|
ENV["FIXTURES"].split(",")
|
409
415
|
else
|
410
|
-
|
411
|
-
|
416
|
+
files = Dir[File.join(fixtures_dir, "**/*.{yml}")]
|
417
|
+
files.reject! { |f| f.start_with?(File.join(fixtures_dir, "files")) }
|
418
|
+
files.map! { |f| f[fixtures_dir.to_s.size..-5].delete_prefix("/") }
|
412
419
|
end
|
413
420
|
|
414
421
|
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
|
@@ -162,7 +162,7 @@ module ActiveRecord
|
|
162
162
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
163
163
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
164
164
|
def class_name
|
165
|
-
@class_name ||= -(options[:class_name]
|
165
|
+
@class_name ||= -(options[:class_name] || derive_class_name).to_s
|
166
166
|
end
|
167
167
|
|
168
168
|
# Returns a list of scopes that should be applied for this Reflection
|
@@ -310,6 +310,7 @@ module ActiveRecord
|
|
310
310
|
type_cast_calculated_value(result.cast_values.first, operation) do |value|
|
311
311
|
type = column.try(:type_caster) ||
|
312
312
|
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
|
+
type = type.subtype if Enum::EnumType === type
|
313
314
|
type.deserialize(value)
|
314
315
|
end
|
315
316
|
end
|
@@ -387,8 +388,11 @@ module ActiveRecord
|
|
387
388
|
key = key_records[key] if associated
|
388
389
|
|
389
390
|
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
390
|
-
type
|
391
|
-
|
391
|
+
unless type
|
392
|
+
type = column.try(:type_caster) ||
|
393
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
|
+
type = type.subtype if Enum::EnumType === type
|
395
|
+
end
|
392
396
|
type.deserialize(value)
|
393
397
|
end
|
394
398
|
end
|
@@ -326,7 +326,7 @@ module ActiveRecord
|
|
326
326
|
# compared to the records in memory. If the relation is unloaded, an
|
327
327
|
# efficient existence query is performed, as in #exists?.
|
328
328
|
def include?(record)
|
329
|
-
if loaded? || offset_value || limit_value
|
329
|
+
if loaded? || offset_value || limit_value || having_clause.any?
|
330
330
|
records.include?(record)
|
331
331
|
else
|
332
332
|
record.is_a?(klass) && exists?(record.id)
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def queries
|
12
|
-
[associated_table.join_foreign_key => ids]
|
12
|
+
[ associated_table.join_foreign_key => ids ]
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
@@ -31,8 +31,8 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def convert_to_id(value)
|
34
|
-
if value.respond_to?(
|
35
|
-
value.
|
34
|
+
if value.respond_to?(primary_key)
|
35
|
+
value.public_send(primary_key)
|
36
36
|
else
|
37
37
|
value
|
38
38
|
end
|
@@ -9,11 +9,13 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def queries
|
12
|
+
return [ associated_table.join_foreign_key => values ] if values.empty?
|
13
|
+
|
12
14
|
type_to_ids_mapping.map do |type, ids|
|
13
|
-
{
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
query = {}
|
16
|
+
query[associated_table.join_foreign_type] = type if type
|
17
|
+
query[associated_table.join_foreign_key] = ids
|
18
|
+
query
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
@@ -23,7 +25,7 @@ module ActiveRecord
|
|
23
25
|
def type_to_ids_mapping
|
24
26
|
default_hash = Hash.new { |hsh, key| hsh[key] = [] }
|
25
27
|
values.each_with_object(default_hash) do |value, hash|
|
26
|
-
hash[klass(value)
|
28
|
+
hash[klass(value)&.polymorphic_name] << convert_to_id(value)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -46,6 +48,8 @@ module ActiveRecord
|
|
46
48
|
value._read_attribute(primary_key(value))
|
47
49
|
when Relation
|
48
50
|
value.select(primary_key(value))
|
51
|
+
else
|
52
|
+
value
|
49
53
|
end
|
50
54
|
end
|
51
55
|
end
|
@@ -93,11 +93,8 @@ module ActiveRecord
|
|
93
93
|
# PriceEstimate.where(estimate_of: treasure)
|
94
94
|
associated_table = table.associated_table(key)
|
95
95
|
if associated_table.polymorphic_association?
|
96
|
-
|
97
|
-
|
98
|
-
value = [value] unless value.is_a?(Array)
|
99
|
-
klass = PolymorphicArrayValue
|
100
|
-
end
|
96
|
+
value = [value] unless value.is_a?(Array)
|
97
|
+
klass = PolymorphicArrayValue
|
101
98
|
elsif associated_table.through_association?
|
102
99
|
next associated_table.predicate_builder.expand_from_hash(
|
103
100
|
associated_table.primary_key => value
|
@@ -106,7 +103,9 @@ module ActiveRecord
|
|
106
103
|
|
107
104
|
klass ||= AssociationQueryValue
|
108
105
|
queries = klass.new(associated_table, value).queries.map! do |query|
|
109
|
-
|
106
|
+
# If the query produced is identical to attributes don't go any deeper.
|
107
|
+
# Prevents stack level too deep errors when association and foreign_key are identical.
|
108
|
+
query == attributes ? self[key, value] : expand_from_hash(query)
|
110
109
|
end
|
111
110
|
|
112
111
|
grouping_queries(queries)
|