activerecord 5.0.0 → 5.0.7.2

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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +431 -2
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +4 -2
  5. data/lib/active_record/association_relation.rb +4 -1
  6. data/lib/active_record/associations/association.rb +11 -1
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +4 -2
  9. data/lib/active_record/associations/builder/singular_association.rb +10 -1
  10. data/lib/active_record/associations/collection_association.rb +56 -48
  11. data/lib/active_record/associations/collection_proxy.rb +38 -20
  12. data/lib/active_record/associations/has_many_association.rb +1 -7
  13. data/lib/active_record/associations/has_many_through_association.rb +2 -4
  14. data/lib/active_record/associations/join_dependency/join_association.rb +6 -11
  15. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  16. data/lib/active_record/associations/join_dependency.rb +10 -4
  17. data/lib/active_record/associations/preloader/association.rb +24 -37
  18. data/lib/active_record/associations/preloader/collection_association.rb +0 -1
  19. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  20. data/lib/active_record/associations/preloader/through_association.rb +10 -4
  21. data/lib/active_record/associations/singular_association.rb +8 -2
  22. data/lib/active_record/associations/through_association.rb +1 -1
  23. data/lib/active_record/associations.rb +38 -17
  24. data/lib/active_record/attribute.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +14 -1
  26. data/lib/active_record/attribute_methods/read.rb +1 -1
  27. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -2
  28. data/lib/active_record/attribute_methods.rb +3 -7
  29. data/lib/active_record/attribute_set/builder.rb +37 -13
  30. data/lib/active_record/attribute_set.rb +2 -0
  31. data/lib/active_record/attributes.rb +3 -3
  32. data/lib/active_record/autosave_association.rb +15 -11
  33. data/lib/active_record/base.rb +1 -1
  34. data/lib/active_record/collection_cache_key.rb +16 -6
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +41 -33
  36. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  37. data/lib/active_record/connection_adapters/abstract/query_cache.rb +37 -2
  38. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -5
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -14
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +68 -56
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -12
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -60
  43. data/lib/active_record/connection_adapters/column.rb +1 -1
  44. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  45. data/lib/active_record/connection_adapters/mysql/database_statements.rb +8 -25
  46. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  47. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -6
  48. data/lib/active_record/connection_adapters/postgresql/column.rb +28 -1
  49. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
  50. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -2
  51. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  52. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  53. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +10 -0
  54. data/lib/active_record/connection_adapters/postgresql/quoting.rb +32 -3
  55. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -8
  56. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  57. data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -19
  58. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  59. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -4
  60. data/lib/active_record/core.rb +6 -4
  61. data/lib/active_record/enum.rb +8 -5
  62. data/lib/active_record/explain.rb +20 -9
  63. data/lib/active_record/fixtures.rb +5 -5
  64. data/lib/active_record/gem_version.rb +2 -2
  65. data/lib/active_record/integration.rb +13 -10
  66. data/lib/active_record/log_subscriber.rb +19 -17
  67. data/lib/active_record/migration.rb +35 -14
  68. data/lib/active_record/model_schema.rb +161 -52
  69. data/lib/active_record/no_touching.rb +4 -0
  70. data/lib/active_record/persistence.rb +41 -20
  71. data/lib/active_record/query_cache.rb +15 -17
  72. data/lib/active_record/querying.rb +3 -3
  73. data/lib/active_record/railties/controller_runtime.rb +1 -1
  74. data/lib/active_record/railties/databases.rake +9 -21
  75. data/lib/active_record/reflection.rb +20 -0
  76. data/lib/active_record/relation/batches.rb +10 -10
  77. data/lib/active_record/relation/calculations.rb +16 -12
  78. data/lib/active_record/relation/delegation.rb +2 -1
  79. data/lib/active_record/relation/finder_methods.rb +13 -11
  80. data/lib/active_record/relation/query_methods.rb +3 -3
  81. data/lib/active_record/relation.rb +12 -5
  82. data/lib/active_record/result.rb +7 -1
  83. data/lib/active_record/sanitization.rb +11 -1
  84. data/lib/active_record/schema_dumper.rb +10 -17
  85. data/lib/active_record/scoping/default.rb +5 -1
  86. data/lib/active_record/scoping/named.rb +18 -6
  87. data/lib/active_record/scoping.rb +4 -3
  88. data/lib/active_record/serialization.rb +1 -1
  89. data/lib/active_record/statement_cache.rb +2 -2
  90. data/lib/active_record/table_metadata.rb +4 -3
  91. data/lib/active_record/tasks/database_tasks.rb +14 -11
  92. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  93. data/lib/active_record/touch_later.rb +6 -1
  94. data/lib/active_record/transactions.rb +1 -1
  95. data/lib/active_record/type/internal/abstract_json.rb +5 -1
  96. data/lib/active_record/validations/uniqueness.rb +3 -4
  97. data/lib/active_record.rb +3 -2
  98. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  99. data/lib/rails/generators/active_record/migration.rb +8 -0
  100. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  101. metadata +7 -8
@@ -197,13 +197,6 @@ module ActiveRecord
197
197
  autoload :CollectionAssociation
198
198
  autoload :ForeignAssociation
199
199
  autoload :CollectionProxy
200
-
201
- autoload :BelongsToAssociation
202
- autoload :BelongsToPolymorphicAssociation
203
- autoload :HasManyAssociation
204
- autoload :HasManyThroughAssociation
205
- autoload :HasOneAssociation
206
- autoload :HasOneThroughAssociation
207
200
  autoload :ThroughAssociation
208
201
 
209
202
  module Builder #:nodoc:
@@ -218,12 +211,24 @@ module ActiveRecord
218
211
  end
219
212
 
220
213
  eager_autoload do
214
+ autoload :BelongsToAssociation
215
+ autoload :BelongsToPolymorphicAssociation
216
+ autoload :HasManyAssociation
217
+ autoload :HasManyThroughAssociation
218
+ autoload :HasOneAssociation
219
+ autoload :HasOneThroughAssociation
220
+
221
221
  autoload :Preloader
222
222
  autoload :JoinDependency
223
223
  autoload :AssociationScope
224
224
  autoload :AliasTracker
225
225
  end
226
226
 
227
+ def self.eager_load!
228
+ super
229
+ Preloader.eager_load!
230
+ end
231
+
227
232
  # Returns the association instance for the given name, instantiating it if it doesn't already exist
228
233
  def association(name) #:nodoc:
229
234
  association = association_instance_get(name)
@@ -312,17 +317,18 @@ module ActiveRecord
312
317
  # | | belongs_to |
313
318
  # generated methods | belongs_to | :polymorphic | has_one
314
319
  # ----------------------------------+------------+--------------+---------
315
- # other(force_reload=false) | X | X | X
320
+ # other | X | X | X
316
321
  # other=(other) | X | X | X
317
322
  # build_other(attributes={}) | X | | X
318
323
  # create_other(attributes={}) | X | | X
319
324
  # create_other!(attributes={}) | X | | X
325
+ # reload_other | X | X | X
320
326
  #
321
327
  # === Collection associations (one-to-many / many-to-many)
322
328
  # | | | has_many
323
329
  # generated methods | habtm | has_many | :through
324
330
  # ----------------------------------+-------+----------+----------
325
- # others(force_reload=false) | X | X | X
331
+ # others | X | X | X
326
332
  # others=(other,other,...) | X | X | X
327
333
  # other_ids | X | X | X
328
334
  # other_ids=(id,id,...) | X | X | X
@@ -346,6 +352,7 @@ module ActiveRecord
346
352
  # others.exists? | X | X | X
347
353
  # others.distinct | X | X | X
348
354
  # others.reset | X | X | X
355
+ # others.reload | X | X | X
349
356
  #
350
357
  # === Overriding generated methods
351
358
  #
@@ -1157,9 +1164,9 @@ module ActiveRecord
1157
1164
  # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
1158
1165
  # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
1159
1166
  #
1160
- # [collection(force_reload = false)]
1161
- # Returns an array of all the associated objects.
1162
- # An empty array is returned if none are found.
1167
+ # [collection]
1168
+ # Returns a Relation of all the associated objects.
1169
+ # An empty Relation is returned if none are found.
1163
1170
  # [collection<<(object, ...)]
1164
1171
  # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
1165
1172
  # Note that this operation instantly fires update SQL without waiting for the save or update call on the
@@ -1216,6 +1223,9 @@ module ActiveRecord
1216
1223
  # [collection.create!(attributes = {})]
1217
1224
  # Does the same as <tt>collection.create</tt>, but raises ActiveRecord::RecordInvalid
1218
1225
  # if the record is invalid.
1226
+ # [collection.reload]
1227
+ # Returns a Relation of all of the associated objects, forcing a database read.
1228
+ # An empty Relation is returned if none are found.
1219
1229
  #
1220
1230
  # === Example
1221
1231
  #
@@ -1235,6 +1245,7 @@ module ActiveRecord
1235
1245
  # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
1236
1246
  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
1237
1247
  # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
1248
+ # * <tt>Firm#clients.reload</tt>
1238
1249
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1239
1250
  #
1240
1251
  # === Scopes
@@ -1371,7 +1382,7 @@ module ActiveRecord
1371
1382
  # +association+ is a placeholder for the symbol passed as the +name+ argument, so
1372
1383
  # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
1373
1384
  #
1374
- # [association(force_reload = false)]
1385
+ # [association]
1375
1386
  # Returns the associated object. +nil+ is returned if none is found.
1376
1387
  # [association=(associate)]
1377
1388
  # Assigns the associate object, extracts the primary key, sets it as the foreign key,
@@ -1388,6 +1399,8 @@ module ActiveRecord
1388
1399
  # [create_association!(attributes = {})]
1389
1400
  # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
1390
1401
  # if the record is invalid.
1402
+ # [reload_association]
1403
+ # Returns the associated object, forcing a database read.
1391
1404
  #
1392
1405
  # === Example
1393
1406
  #
@@ -1397,6 +1410,7 @@ module ActiveRecord
1397
1410
  # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
1398
1411
  # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
1399
1412
  # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
1413
+ # * <tt>Account#reload_beneficiary</tt>
1400
1414
  #
1401
1415
  # === Scopes
1402
1416
  #
@@ -1503,7 +1517,7 @@ module ActiveRecord
1503
1517
  # +association+ is a placeholder for the symbol passed as the +name+ argument, so
1504
1518
  # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
1505
1519
  #
1506
- # [association(force_reload = false)]
1520
+ # [association]
1507
1521
  # Returns the associated object. +nil+ is returned if none is found.
1508
1522
  # [association=(associate)]
1509
1523
  # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
@@ -1517,6 +1531,8 @@ module ActiveRecord
1517
1531
  # [create_association!(attributes = {})]
1518
1532
  # Does the same as <tt>create_association</tt>, but raises ActiveRecord::RecordInvalid
1519
1533
  # if the record is invalid.
1534
+ # [reload_association]
1535
+ # Returns the associated object, forcing a database read.
1520
1536
  #
1521
1537
  # === Example
1522
1538
  #
@@ -1526,6 +1542,7 @@ module ActiveRecord
1526
1542
  # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
1527
1543
  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1528
1544
  # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
1545
+ # * <tt>Post#reload_author</tt>
1529
1546
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1530
1547
  #
1531
1548
  # === Scopes
@@ -1661,9 +1678,9 @@ module ActiveRecord
1661
1678
  # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
1662
1679
  # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
1663
1680
  #
1664
- # [collection(force_reload = false)]
1665
- # Returns an array of all the associated objects.
1666
- # An empty array is returned if none are found.
1681
+ # [collection]
1682
+ # Returns a Relation of all the associated objects.
1683
+ # An empty Relation is returned if none are found.
1667
1684
  # [collection<<(object, ...)]
1668
1685
  # Adds one or more objects to the collection by creating associations in the join table
1669
1686
  # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
@@ -1701,6 +1718,9 @@ module ActiveRecord
1701
1718
  # Returns a new object of the collection type that has been instantiated
1702
1719
  # with +attributes+, linked to this object through the join table, and that has already been
1703
1720
  # saved (if it passed the validation).
1721
+ # [collection.reload]
1722
+ # Returns a Relation of all of the associated objects, forcing a database read.
1723
+ # An empty Relation is returned if none are found.
1704
1724
  #
1705
1725
  # === Example
1706
1726
  #
@@ -1719,6 +1739,7 @@ module ActiveRecord
1719
1739
  # * <tt>Developer#projects.exists?(...)</tt>
1720
1740
  # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
1721
1741
  # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
1742
+ # * <tt>Developer#projects.reload</tt>
1722
1743
  # The declaration may include an +options+ hash to specialize the behavior of the association.
1723
1744
  #
1724
1745
  # === Scopes
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
 
66
66
  def with_value_from_user(value)
67
67
  type.assert_valid_value(value)
68
- self.class.from_user(name, value, type, self)
68
+ self.class.from_user(name, value, type, original_attribute || self)
69
69
  end
70
70
 
71
71
  def with_value_from_database(value)
@@ -132,7 +132,7 @@ module ActiveRecord
132
132
  end
133
133
 
134
134
  def _original_value_for_database
135
- value_for_database
135
+ type.serialize(original_value)
136
136
  end
137
137
 
138
138
  class FromDatabase < Attribute # :nodoc:
@@ -160,7 +160,7 @@ module ActiveRecord
160
160
  value
161
161
  end
162
162
 
163
- def changed_in_place_from?(old_value)
163
+ def changed_in_place?
164
164
  false
165
165
  end
166
166
  end
@@ -95,7 +95,8 @@ module ActiveRecord
95
95
  base_name.foreign_key
96
96
  else
97
97
  if ActiveRecord::Base != self && table_exists?
98
- connection.schema_cache.primary_keys(table_name)
98
+ pk = connection.schema_cache.primary_keys(table_name)
99
+ suppress_composite_primary_key(pk)
99
100
  else
100
101
  'id'
101
102
  end
@@ -122,6 +123,18 @@ module ActiveRecord
122
123
  @quoted_primary_key = nil
123
124
  @attributes_builder = nil
124
125
  end
126
+
127
+ private
128
+
129
+ def suppress_composite_primary_key(pk)
130
+ return pk unless pk.is_a?(Array)
131
+
132
+ warn <<-WARNING.strip_heredoc
133
+ WARNING: Active Record does not support composite primary key.
134
+
135
+ #{table_name} has composite primary key. Composite primary key is ignored.
136
+ WARNING
137
+ end
125
138
  end
126
139
  end
127
140
  end
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  # to a date object, like Date.new(2004, 12, 12)).
50
50
  def read_attribute(attr_name, &block)
51
51
  name = attr_name.to_s
52
- name = self.class.primary_key if name == 'id'.freeze
52
+ name = self.class.primary_key if name == "id".freeze && self.class.primary_key
53
53
  _read_attribute(name, &block)
54
54
  end
55
55
 
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def set_time_zone_without_conversion(value)
42
- ::Time.zone.local_to_utc(value).in_time_zone
42
+ ::Time.zone.local_to_utc(value).in_time_zone if value
43
43
  end
44
44
 
45
45
  def map_avoiding_infinite_recursion(value)
@@ -102,7 +102,7 @@ module ActiveRecord
102
102
 
103
103
  config.active_record.time_zone_aware_types = [:datetime]
104
104
 
105
- To silence this deprecation warning, add the following:
105
+ To use the new behavior, add the following:
106
106
 
107
107
  config.active_record.time_zone_aware_types = [:datetime, :time]
108
108
  MESSAGE
@@ -279,9 +279,8 @@ module ActiveRecord
279
279
  # Returns an <tt>#inspect</tt>-like string for the value of the
280
280
  # attribute +attr_name+. String attributes are truncated up to 50
281
281
  # characters, Date and Time attributes are returned in the
282
- # <tt>:db</tt> format, Array attributes are truncated up to 10 values.
283
- # Other attributes return the value of <tt>#inspect</tt> without
284
- # modification.
282
+ # <tt>:db</tt> format. Other attributes return the value of
283
+ # <tt>#inspect</tt> without modification.
285
284
  #
286
285
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
287
286
  #
@@ -292,7 +291,7 @@ module ActiveRecord
292
291
  # # => "\"2012-10-22 00:15:07\""
293
292
  #
294
293
  # person.attribute_for_inspect(:tag_ids)
295
- # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
294
+ # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
296
295
  def attribute_for_inspect(attr_name)
297
296
  value = read_attribute(attr_name)
298
297
 
@@ -300,9 +299,6 @@ module ActiveRecord
300
299
  "#{value[0, 50]}...".inspect
301
300
  elsif value.is_a?(Date) || value.is_a?(Time)
302
301
  %("#{value.to_s(:db)}")
303
- elsif value.is_a?(Array) && value.size > 10
304
- inspected = value.first(10).inspect
305
- %(#{inspected[0...-1]}, ...])
306
302
  else
307
303
  value.inspect
308
304
  end
@@ -3,33 +3,30 @@ require 'active_record/attribute'
3
3
  module ActiveRecord
4
4
  class AttributeSet # :nodoc:
5
5
  class Builder # :nodoc:
6
- attr_reader :types, :always_initialized
6
+ attr_reader :types, :default_attributes
7
7
 
8
- def initialize(types, always_initialized = nil)
8
+ def initialize(types, default_attributes = {})
9
9
  @types = types
10
- @always_initialized = always_initialized
10
+ @default_attributes = default_attributes
11
11
  end
12
12
 
13
13
  def build_from_database(values = {}, additional_types = {})
14
- if always_initialized && !values.key?(always_initialized)
15
- values[always_initialized] = nil
16
- end
17
-
18
- attributes = LazyAttributeHash.new(types, values, additional_types)
14
+ attributes = LazyAttributeHash.new(types, values, additional_types, default_attributes)
19
15
  AttributeSet.new(attributes)
20
16
  end
21
17
  end
22
18
  end
23
19
 
24
20
  class LazyAttributeHash # :nodoc:
25
- delegate :transform_values, :each_key, to: :materialize
21
+ delegate :transform_values, :each_key, :fetch, :except, to: :materialize
26
22
 
27
- def initialize(types, values, additional_types)
23
+ def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
28
24
  @types = types
29
25
  @values = values
30
26
  @additional_types = additional_types
31
27
  @materialized = false
32
- @delegate_hash = {}
28
+ @delegate_hash = delegate_hash
29
+ @default_attributes = default_attributes
33
30
  end
34
31
 
35
32
  def key?(key)
@@ -76,9 +73,31 @@ module ActiveRecord
76
73
  end
77
74
  end
78
75
 
76
+ def marshal_dump
77
+ [@types, @values, @additional_types, @default_attributes, @delegate_hash]
78
+ end
79
+
80
+ def marshal_load(values)
81
+ if values.is_a?(Hash)
82
+ empty_hash = {}.freeze
83
+ initialize(empty_hash, empty_hash, empty_hash, empty_hash, values)
84
+ @materialized = true
85
+ else
86
+ initialize(*values)
87
+ end
88
+ end
89
+
90
+ def encode_with(coder)
91
+ coder["delegate_hash"] = materialize
92
+ end
93
+
94
+ def init_with(coder)
95
+ marshal_load(coder["delegate_hash"])
96
+ end
97
+
79
98
  protected
80
99
 
81
- attr_reader :types, :values, :additional_types, :delegate_hash
100
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
82
101
 
83
102
  def materialize
84
103
  unless @materialized
@@ -101,7 +120,12 @@ module ActiveRecord
101
120
  if value_present
102
121
  delegate_hash[name] = Attribute.from_database(name, value, type)
103
122
  elsif types.key?(name)
104
- delegate_hash[name] = Attribute.uninitialized(name, type)
123
+ attr = default_attributes[name]
124
+ if attr
125
+ delegate_hash[name] = attr.dup
126
+ else
127
+ delegate_hash[name] = Attribute.uninitialized(name, type)
128
+ end
105
129
  end
106
130
  end
107
131
  end
@@ -2,6 +2,8 @@ require 'active_record/attribute_set/builder'
2
2
 
3
3
  module ActiveRecord
4
4
  class AttributeSet # :nodoc:
5
+ delegate :fetch, :except, to: :attributes
6
+
5
7
  def initialize(attributes)
6
8
  @attributes = attributes
7
9
  end
@@ -34,10 +34,10 @@ module ActiveRecord
34
34
  # is not passed, the previous default value (if any) will be used.
35
35
  # Otherwise, the default will be +nil+.
36
36
  #
37
- # +array+ (PG only) specifies that the type should be an array (see the
37
+ # +array+ (PostgreSQL only) specifies that the type should be an array (see the
38
38
  # examples below).
39
39
  #
40
- # +range+ (PG only) specifies that the type should be a range (see the
40
+ # +range+ (PostgreSQL only) specifies that the type should be a range (see the
41
41
  # examples below).
42
42
  #
43
43
  # ==== Examples
@@ -253,7 +253,7 @@ module ActiveRecord
253
253
  name,
254
254
  value,
255
255
  type,
256
- _default_attributes[name],
256
+ _default_attributes.fetch(name.to_s) { nil },
257
257
  )
258
258
  else
259
259
  default_attribute = Attribute.from_database(name, value, type)
@@ -329,26 +329,20 @@ module ActiveRecord
329
329
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
330
330
 
331
331
  validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
332
+
332
333
  unless valid = record.valid?(validation_context)
333
334
  if reflection.options[:autosave]
334
335
  indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
335
336
 
336
337
  record.errors.each do |attribute, message|
337
- if indexed_attribute
338
- attribute = "#{reflection.name}[#{index}].#{attribute}"
339
- else
340
- attribute = "#{reflection.name}.#{attribute}"
341
- end
338
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
342
339
  errors[attribute] << message
343
340
  errors[attribute].uniq!
344
341
  end
345
342
 
346
343
  record.errors.details.each_key do |attribute|
347
- if indexed_attribute
348
- reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
349
- else
350
- reflection_attribute = "#{reflection.name}.#{attribute}"
351
- end
344
+ reflection_attribute =
345
+ normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
352
346
 
353
347
  record.errors.details[attribute].each do |error|
354
348
  errors.details[reflection_attribute] << error
@@ -362,6 +356,14 @@ module ActiveRecord
362
356
  valid
363
357
  end
364
358
 
359
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
360
+ if indexed_attribute
361
+ "#{reflection.name}[#{index}].#{attribute}"
362
+ else
363
+ "#{reflection.name}.#{attribute}"
364
+ end
365
+ end
366
+
365
367
  # Is used as a before_save callback to check while saving a collection
366
368
  # association whether or not the parent was a new record before saving.
367
369
  def before_save_collection_association
@@ -457,7 +459,9 @@ module ActiveRecord
457
459
  # In addition, it will destroy the association if it was marked for destruction.
458
460
  def save_belongs_to_association(reflection)
459
461
  association = association_instance_get(reflection.name)
460
- record = association && association.load_target
462
+ return unless association && association.loaded? && !association.stale_target?
463
+
464
+ record = association.load_target
461
465
  if record && !record.destroyed?
462
466
  autosave = reflection.options[:autosave]
463
467
 
@@ -312,8 +312,8 @@ module ActiveRecord #:nodoc:
312
312
  include NestedAttributes
313
313
  include Aggregations
314
314
  include Transactions
315
- include NoTouching
316
315
  include TouchLater
316
+ include NoTouching
317
317
  include Reflection
318
318
  include Serialization
319
319
  include Store
@@ -8,17 +8,27 @@ module ActiveRecord
8
8
  if collection.loaded?
9
9
  size = collection.size
10
10
  if size > 0
11
- timestamp = collection.max_by(&timestamp_column).public_send(timestamp_column)
11
+ timestamp = collection.max_by(&timestamp_column)._read_attribute(timestamp_column)
12
12
  end
13
13
  else
14
14
  column_type = type_for_attribute(timestamp_column.to_s)
15
15
  column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
16
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
16
17
 
17
- query = collection
18
- .unscope(:select)
19
- .select("COUNT(*) AS #{connection.quote_column_name("size")}", "MAX(#{column}) AS timestamp")
20
- .unscope(:order)
21
- result = connection.select_one(query)
18
+ if collection.limit_value || collection.offset_value
19
+ query = collection.spawn
20
+ query.select_values = [column]
21
+ subquery_alias = "subquery_for_cache_key"
22
+ subquery_column = "#{subquery_alias}.#{timestamp_column}"
23
+ subquery = query.arel.as(subquery_alias)
24
+ arel = Arel::SelectManager.new(query.engine).project(select_values % subquery_column).from(subquery)
25
+ else
26
+ query = collection.unscope(:order)
27
+ query.select_values = [select_values % column]
28
+ arel = query.arel
29
+ end
30
+
31
+ result = connection.select_one(arel, nil, query.bound_attributes)
22
32
 
23
33
  if result.blank?
24
34
  size = 0
@@ -307,6 +307,7 @@ module ActiveRecord
307
307
  end
308
308
 
309
309
  include MonitorMixin
310
+ include QueryCache::ConnectionPoolConfiguration
310
311
 
311
312
  attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
312
313
  attr_reader :spec, :connections, :size, :reaper
@@ -349,8 +350,7 @@ module ActiveRecord
349
350
  # currently in the process of independently establishing connections to the DB.
350
351
  @now_connecting = 0
351
352
 
352
- # A boolean toggle that allows/disallows new connections.
353
- @new_cons_enabled = true
353
+ @threads_blocking_new_connections = 0
354
354
 
355
355
  @available = ConnectionLeasingQueue.new self
356
356
  end
@@ -415,7 +415,10 @@ module ActiveRecord
415
415
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
416
416
  synchronize do
417
417
  @connections.each do |conn|
418
- checkin conn
418
+ if conn.in_use?
419
+ conn.steal!
420
+ checkin conn
421
+ end
419
422
  conn.disconnect!
420
423
  end
421
424
  @connections = []
@@ -442,33 +445,19 @@ module ActiveRecord
442
445
  # connections in the pool within a timeout interval (default duration is
443
446
  # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
444
447
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
445
- num_new_conns_required = 0
446
-
447
448
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
448
449
  synchronize do
449
450
  @connections.each do |conn|
450
- checkin conn
451
+ if conn.in_use?
452
+ conn.steal!
453
+ checkin conn
454
+ end
451
455
  conn.disconnect! if conn.requires_reloading?
452
456
  end
453
457
  @connections.delete_if(&:requires_reloading?)
454
-
455
458
  @available.clear
456
-
457
- if @connections.size < @size
458
- # because of the pruning done by this method, we might be running
459
- # low on connections, while threads stuck in queue are helpless
460
- # (not being able to establish new connections for themselves),
461
- # see also more detailed explanation in +remove+
462
- num_new_conns_required = num_waiting_in_queue - @connections.size
463
- end
464
-
465
- @connections.each do |conn|
466
- @available.add conn
467
- end
468
459
  end
469
460
  end
470
-
471
- bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
472
461
  end
473
462
 
474
463
  # Clears the cache which maps classes and re-connects connections that
@@ -556,17 +545,17 @@ module ActiveRecord
556
545
  stale_connections = synchronize do
557
546
  @connections.select do |conn|
558
547
  conn.in_use? && !conn.owner.alive?
548
+ end.each do |conn|
549
+ conn.steal!
559
550
  end
560
551
  end
561
552
 
562
553
  stale_connections.each do |conn|
563
- synchronize do
564
- if conn.active?
565
- conn.reset!
566
- checkin conn
567
- else
568
- remove conn
569
- end
554
+ if conn.active?
555
+ conn.reset!
556
+ checkin conn
557
+ else
558
+ remove conn
570
559
  end
571
560
  end
572
561
  end
@@ -675,13 +664,32 @@ module ActiveRecord
675
664
  end
676
665
 
677
666
  def with_new_connections_blocked
678
- previous_value = nil
679
667
  synchronize do
680
- previous_value, @new_cons_enabled = @new_cons_enabled, false
668
+ @threads_blocking_new_connections += 1
681
669
  end
670
+
682
671
  yield
683
672
  ensure
684
- synchronize { @new_cons_enabled = previous_value }
673
+ num_new_conns_required = 0
674
+
675
+ synchronize do
676
+ @threads_blocking_new_connections -= 1
677
+
678
+ if @threads_blocking_new_connections.zero?
679
+ @available.clear
680
+
681
+ num_new_conns_required = num_waiting_in_queue
682
+
683
+ @connections.each do |conn|
684
+ next if conn.in_use?
685
+
686
+ @available.add conn
687
+ num_new_conns_required -= 1
688
+ end
689
+ end
690
+ end
691
+
692
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
685
693
  end
686
694
 
687
695
  # Acquire a connection by one of 1) immediately removing one
@@ -733,7 +741,7 @@ module ActiveRecord
733
741
  # and increment @now_connecting, to prevent overstepping this pool's @size
734
742
  # constraint
735
743
  do_checkout = synchronize do
736
- if @new_cons_enabled && (@connections.size + @now_connecting) < @size
744
+ if @threads_blocking_new_connections.zero? && (@connections.size + @now_connecting) < @size
737
745
  @now_connecting += 1
738
746
  end
739
747
  end
@@ -927,7 +935,7 @@ module ActiveRecord
927
935
  end
928
936
 
929
937
  def pool_from_any_process_for(spec_name)
930
- owner_to_pool = @owner_to_pool.values.find { |v| v[spec_name] }
938
+ owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
931
939
  owner_to_pool && owner_to_pool[spec_name]
932
940
  end
933
941
  end
@@ -244,7 +244,7 @@ module ActiveRecord
244
244
  end
245
245
 
246
246
  def reset_transaction #:nodoc:
247
- @transaction_manager = TransactionManager.new(self)
247
+ @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
248
248
  end
249
249
 
250
250
  # Register a record with the current transaction so that its after_commit and after_rollback callbacks