activerecord 5.1.5 → 5.1.7

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -0
  3. data/lib/active_record/associations/belongs_to_association.rb +1 -1
  4. data/lib/active_record/associations/builder/belongs_to.rb +2 -1
  5. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  6. data/lib/active_record/attribute_mutation_tracker.rb +9 -1
  7. data/lib/active_record/attribute_set/builder.rb +24 -22
  8. data/lib/active_record/attribute_set.rb +1 -1
  9. data/lib/active_record/attributes.rb +1 -1
  10. data/lib/active_record/callbacks.rb +4 -0
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +3 -1
  12. data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -1
  13. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -8
  14. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  15. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +11 -1
  16. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  17. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -0
  18. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
  19. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -1
  20. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +2 -1
  21. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  22. data/lib/active_record/counter_cache.rb +10 -12
  23. data/lib/active_record/errors.rb +18 -12
  24. data/lib/active_record/gem_version.rb +1 -1
  25. data/lib/active_record/locking/optimistic.rb +19 -28
  26. data/lib/active_record/model_schema.rb +5 -5
  27. data/lib/active_record/persistence.rb +34 -36
  28. data/lib/active_record/query_cache.rb +6 -6
  29. data/lib/active_record/relation/calculations.rb +1 -1
  30. data/lib/active_record/relation/finder_methods.rb +2 -2
  31. data/lib/active_record/relation/predicate_builder.rb +2 -2
  32. data/lib/active_record/relation/query_methods.rb +1 -1
  33. data/lib/active_record/relation.rb +2 -2
  34. data/lib/active_record/tasks/database_tasks.rb +1 -1
  35. data/lib/active_record/timestamp.rb +7 -0
  36. data/lib/active_record/type/serialized.rb +4 -0
  37. data/lib/active_record/validations/uniqueness.rb +1 -1
  38. metadata +9 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c959975b61990368b7c19d60de2e6ac341e8962fe5ec6612f2c7246916343200
4
- data.tar.gz: 38b6c9d5f665a16158f3e3a663c8d1935f925d086a1b0bb954f68b94ba7c1fd0
3
+ metadata.gz: 3761a48d6ad944f4726d6f454c7410ce0fbf30be0dc352eabf0e91bf8dea9944
4
+ data.tar.gz: 392232a9587e31dd74e1718943feb82f28473f64cb6fe6787fa9f9a164c582c8
5
5
  SHA512:
6
- metadata.gz: 154de32a9ce2ffabd8a5b33d067e206b5be1d3b50c556e361b5018ce91cf534df778537aa46e2c2e7d7e616bb0e0ded3d62ad29b3f133c1565c53e615aff1de7
7
- data.tar.gz: 42d3009be5bf1eb2c57fd712d4cfa414560bc3f0dd74d180aacb2d888f907203973f07cdf362d4ebbd1ecbf7855ae08aabe8ae9f49e6d361dd2cb840333e723a
6
+ metadata.gz: 4218769b0900a3b0a259ae85ab829eab4b3fad9801d3090d3ab5eca256e090a9c5b27d425d209ed38fda281308597aae4be88d0e15fc08e2eed1bedb80ae1639
7
+ data.tar.gz: 942951a6f2905cab9ee82620438e8c627bff5185f7769f4f384b049e580f39d549763c0052f6c3b7c71ee66a8f6bc7bdf9767e7108bc7267383f5b3dccd18157
data/CHANGELOG.md CHANGED
@@ -1,5 +1,84 @@
1
+ ## Rails 5.1.7 (March 27, 2019) ##
2
+
3
+ * Fix `touch` option to behave consistently with `Persistence#touch` method.
4
+
5
+ *Ryuta Kamizono*
6
+
7
+ * Back port Rails 5.2 `reverse_order` Arel SQL literal fix.
8
+
9
+ *Matt Jones*, *Brooke Kuhlmann*
10
+
11
+ * `becomes` should clear the mutation tracker which is created in `after_initialize`.
12
+
13
+ Fixes #32867.
14
+
15
+ *Ryuta Kamizono*
16
+
17
+
18
+ ## Rails 5.1.6.2 (March 11, 2019) ##
19
+
20
+ * No changes.
21
+
22
+
23
+ ## Rails 5.1.6.1 (November 27, 2018) ##
24
+
25
+ * No changes.
26
+
27
+
28
+ ## Rails 5.1.6 (March 29, 2018) ##
29
+
30
+ * MySQL: Support mysql2 0.5.x.
31
+
32
+ *Aaron Stone*
33
+
34
+ * Apply time column precision on assignment.
35
+
36
+ PR #20317 changed the behavior of datetime columns so that when they
37
+ have a specified precision then on assignment the value is rounded to
38
+ that precision. This behavior is now applied to time columns as well.
39
+
40
+ Fixes #30301.
41
+
42
+ *Andrew White*
43
+
44
+ * Normalize time column values for SQLite database.
45
+
46
+ For legacy reasons, time columns in SQLite are stored as full datetimes
47
+ because until #24542 the quoting for time columns didn't remove the date
48
+ component. To ensure that values are consistent we now normalize the
49
+ date component to 2001-01-01 on reading and writing.
50
+
51
+ *Andrew White*
52
+
53
+ * Ensure that the date component is removed when quoting times.
54
+
55
+ PR #24542 altered the quoting for time columns so that the date component
56
+ was removed however it only removed it when it was 2001-01-01. Now the
57
+ date component is removed irrespective of what the date is.
58
+
59
+ *Andrew White*
60
+
61
+ * Fix that after commit callbacks on update does not triggered when optimistic locking is enabled.
62
+
63
+ *Ryuta Kamizono*
64
+
65
+ * `ActiveRecord::Persistence#touch` does not work well when optimistic locking enabled and
66
+ `locking_column`, without default value, is null in the database.
67
+
68
+ *bogdanvlviv*
69
+
70
+ * Fix destroying existing object does not work well when optimistic locking enabled and
71
+ `locking column` is null in the database.
72
+
73
+ *bogdanvlviv*
74
+
75
+
1
76
  ## Rails 5.1.5 (February 14, 2018) ##
2
77
 
78
+ * PostgreSQL: Allow pg-1.0 gem to be used with Active Record.
79
+
80
+ *Lars Kanis*
81
+
3
82
  * Fix `count(:all)` with eager loading and having an order other than the driving table.
4
83
 
5
84
  Fixes #31783.
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  def update_counters_on_replace(record)
66
66
  if require_counter_update? && different_target?(record)
67
67
  owner.instance_variable_set :@_after_replace_counter_called, true
68
- record.increment!(reflection.counter_cache_column)
68
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
69
69
  decrement_counters
70
70
  end
71
71
  end
@@ -84,7 +84,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
84
84
  else
85
85
  klass = association.klass
86
86
  end
87
- old_record = klass.find_by(klass.primary_key => old_foreign_id)
87
+ primary_key = reflection.association_primary_key(klass)
88
+ old_record = klass.find_by(primary_key => old_foreign_id)
88
89
 
89
90
  if old_record
90
91
  if touch != true
@@ -204,12 +204,12 @@ module ActiveRecord
204
204
 
205
205
  # Alias for `changed`
206
206
  def changed_attribute_names_to_save
207
- changes_to_save.keys
207
+ mutations_from_database.changed_attribute_names
208
208
  end
209
209
 
210
210
  # Alias for `changed_attributes`
211
211
  def attributes_in_database
212
- changes_to_save.transform_values(&:first)
212
+ mutations_from_database.changed_values
213
213
  end
214
214
 
215
215
  def attribute_was(*)
@@ -8,6 +8,10 @@ module ActiveRecord
8
8
  @deprecated_forced_changes = Set.new
9
9
  end
10
10
 
11
+ def changed_attribute_names
12
+ attr_names.select { |attr_name| changed?(attr_name) }
13
+ end
14
+
11
15
  def changed_values
12
16
  attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
13
17
  if changed?(attr_name)
@@ -20,7 +24,7 @@ module ActiveRecord
20
24
  attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
21
25
  change = change_to_attribute(attr_name)
22
26
  if change
23
- result[attr_name] = change
27
+ result.merge!(attr_name => change)
24
28
  end
25
29
  end
26
30
  end
@@ -82,6 +86,10 @@ module ActiveRecord
82
86
  class NullMutationTracker # :nodoc:
83
87
  include Singleton
84
88
 
89
+ def changed_attribute_names(*)
90
+ []
91
+ end
92
+
85
93
  def changed_values(*)
86
94
  {}
87
95
  end
@@ -3,35 +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, :default
6
+ attr_reader :types, :default_attributes
7
7
 
8
- def initialize(types, always_initialized = nil, &default)
8
+ def initialize(types, default_attributes = {})
9
9
  @types = types
10
- @always_initialized = always_initialized
11
- @default = default
10
+ @default_attributes = default_attributes
12
11
  end
13
12
 
14
13
  def build_from_database(values = {}, additional_types = {})
15
- if always_initialized && !values.key?(always_initialized)
16
- values[always_initialized] = nil
17
- end
18
-
19
- attributes = LazyAttributeHash.new(types, values, additional_types, &default)
14
+ attributes = LazyAttributeHash.new(types, values, additional_types, default_attributes)
20
15
  AttributeSet.new(attributes)
21
16
  end
22
17
  end
23
18
  end
24
19
 
25
20
  class LazyAttributeHash # :nodoc:
26
- delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize
21
+ delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
27
22
 
28
- def initialize(types, values, additional_types, &default)
23
+ def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
29
24
  @types = types
30
25
  @values = values
31
26
  @additional_types = additional_types
32
27
  @materialized = false
33
- @delegate_hash = {}
34
- @default = default || proc {}
28
+ @delegate_hash = delegate_hash
29
+ @default_attributes = default_attributes
35
30
  end
36
31
 
37
32
  def key?(key)
@@ -79,22 +74,24 @@ module ActiveRecord
79
74
  end
80
75
 
81
76
  def marshal_dump
82
- materialize
77
+ [@types, @values, @additional_types, @default_attributes, @delegate_hash]
83
78
  end
84
79
 
85
- def marshal_load(delegate_hash)
86
- @delegate_hash = delegate_hash
87
- @types = {}
88
- @values = {}
89
- @additional_types = {}
90
- @materialized = true
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
91
88
  end
92
89
 
93
90
  # TODO Change this to private once we've dropped Ruby 2.2 support.
94
91
  # Workaround for Ruby 2.2 "private attribute?" warning.
95
92
  protected
96
93
 
97
- attr_reader :types, :values, :additional_types, :delegate_hash, :default
94
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
98
95
 
99
96
  def materialize
100
97
  unless @materialized
@@ -117,7 +114,12 @@ module ActiveRecord
117
114
  if value_present
118
115
  delegate_hash[name] = Attribute.from_database(name, value, type)
119
116
  elsif types.key?(name)
120
- delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
117
+ attr = default_attributes[name]
118
+ if attr
119
+ delegate_hash[name] = attr.dup
120
+ else
121
+ delegate_hash[name] = Attribute.uninitialized(name, type)
122
+ end
121
123
  end
122
124
  end
123
125
  end
@@ -3,7 +3,7 @@ require "active_record/attribute_set/yaml_encoder"
3
3
 
4
4
  module ActiveRecord
5
5
  class AttributeSet # :nodoc:
6
- delegate :each_value, :fetch, to: :attributes
6
+ delegate :each_value, :fetch, :except, to: :attributes
7
7
 
8
8
  def initialize(attributes)
9
9
  @attributes = attributes
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  # store_listing = StoreListing.new(price_in_cents: '10.1')
57
57
  #
58
58
  # # before
59
- # store_listing.price_in_cents # => BigDecimal.new(10.1)
59
+ # store_listing.price_in_cents # => BigDecimal(10.1)
60
60
  #
61
61
  # class StoreListing < ActiveRecord::Base
62
62
  # attribute :price_in_cents, :integer
@@ -330,6 +330,10 @@ module ActiveRecord
330
330
  _run_touch_callbacks { super }
331
331
  end
332
332
 
333
+ def increment!(attribute, by = 1, touch: nil) # :nodoc:
334
+ touch ? _run_touch_callbacks { super } : super
335
+ end
336
+
333
337
  private
334
338
 
335
339
  def create_or_update(*)
@@ -191,7 +191,9 @@ module ActiveRecord
191
191
  t0 = Time.now
192
192
  elapsed = 0
193
193
  loop do
194
- @cond.wait(timeout - elapsed)
194
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
195
+ @cond.wait(timeout - elapsed)
196
+ end
195
197
 
196
198
  return remove if any?
197
199
 
@@ -152,7 +152,8 @@ module ActiveRecord
152
152
  end
153
153
 
154
154
  def quoted_time(value) # :nodoc:
155
- quoted_date(value).sub(/\A2000-01-01 /, "")
155
+ value = value.change(year: 2000, month: 1, day: 1)
156
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "")
156
157
  end
157
158
 
158
159
  def quoted_binary(value) # :nodoc:
@@ -873,15 +873,25 @@ module ActiveRecord
873
873
  end
874
874
 
875
875
  def mismatched_foreign_key(message)
876
- parts = message.scan(/`(\w+)`[ $)]/).flatten
877
- MismatchedForeignKey.new(
878
- self,
876
+ match = %r/
877
+ (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
878
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
879
+ REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
880
+ /xmi.match(message)
881
+
882
+ options = {
879
883
  message: message,
880
- table: parts[0],
881
- foreign_key: parts[1],
882
- target_table: parts[2],
883
- primary_key: parts[3],
884
- )
884
+ }
885
+
886
+ if match
887
+ options[:table] = match[:table]
888
+ options[:foreign_key] = match[:foreign_key]
889
+ options[:target_table] = match[:target_table]
890
+ options[:primary_key] = match[:primary_key]
891
+ options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
892
+ end
893
+
894
+ MismatchedForeignKey.new(options)
885
895
  end
886
896
 
887
897
  def integer_to_sql(limit) # :nodoc:
@@ -1,7 +1,7 @@
1
1
  require "active_record/connection_adapters/abstract_mysql_adapter"
2
2
  require "active_record/connection_adapters/mysql/database_statements"
3
3
 
4
- gem "mysql2", ">= 0.3.18", "< 0.5"
4
+ gem "mysql2", ">= 0.3.18", "< 0.6.0"
5
5
  require "mysql2"
6
6
  raise "mysql2 0.4.3 is not supported. Please upgrade to 0.4.4+" if Mysql2::VERSION == "0.4.3"
7
7
 
@@ -31,7 +31,13 @@ module ActiveRecord
31
31
 
32
32
  def cast(value)
33
33
  if value.is_a?(::String)
34
- value = @pg_decoder.decode(value)
34
+ value = begin
35
+ @pg_decoder.decode(value)
36
+ rescue TypeError
37
+ # malformed array string is treated as [], will raise in PG 2.0 gem
38
+ # this keeps a consistent implementation
39
+ []
40
+ end
35
41
  end
36
42
  type_cast_array(value, :cast)
37
43
  end
@@ -64,6 +70,10 @@ module ActiveRecord
64
70
  deserialize(raw_old_value) != new_value
65
71
  end
66
72
 
73
+ def force_equality?(value)
74
+ value.is_a?(::Array)
75
+ end
76
+
67
77
  private
68
78
 
69
79
  def type_cast_array(value, method)
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module OID # :nodoc:
5
5
  class Decimal < Type::Decimal # :nodoc:
6
6
  def infinity(options = {})
7
- BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1)
7
+ BigDecimal("Infinity") * (options[:negative] ? -1 : 1)
8
8
  end
9
9
  end
10
10
  end
@@ -51,6 +51,10 @@ module ActiveRecord
51
51
  ::Range.new(new_begin, new_end, value.exclude_end?)
52
52
  end
53
53
 
54
+ def force_equality?(value)
55
+ value.is_a?(::Range)
56
+ end
57
+
54
58
  private
55
59
 
56
60
  def type_cast_single(value)
@@ -122,7 +122,7 @@ module ActiveRecord
122
122
  comment = row[5]
123
123
  opclass = row[6]
124
124
 
125
- using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
125
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
126
126
 
127
127
  if indkey.include?(0) || opclass > 0
128
128
  columns = expressions
@@ -2,6 +2,14 @@
2
2
  gem "pg", ">= 0.18", "< 2.0"
3
3
  require "pg"
4
4
 
5
+ # Use async_exec instead of exec_params on pg versions before 1.1
6
+ class ::PG::Connection
7
+ unless self.public_method_defined?(:async_exec_params)
8
+ remove_method :exec_params
9
+ alias exec_params async_exec
10
+ end
11
+ end
12
+
5
13
  require "active_record/connection_adapters/abstract_adapter"
6
14
  require "active_record/connection_adapters/statement_pool"
7
15
  require "active_record/connection_adapters/postgresql/column"
@@ -613,7 +621,7 @@ module ActiveRecord
613
621
  type_casted_binds = type_casted_binds(binds)
614
622
  log(sql, name, binds, type_casted_binds) do
615
623
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
616
- @connection.async_exec(sql, type_casted_binds)
624
+ @connection.exec_params(sql, type_casted_binds)
617
625
  end
618
626
  end
619
627
  end
@@ -15,7 +15,8 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def quoted_time(value)
18
- quoted_date(value)
18
+ value = value.change(year: 2000, month: 1, day: 1)
19
+ quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
19
20
  end
20
21
 
21
22
  def quoted_binary(value)
@@ -7,7 +7,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
7
7
  require "active_record/connection_adapters/sqlite3/schema_dumper"
8
8
  require "active_record/connection_adapters/sqlite3/schema_statements"
9
9
 
10
- gem "sqlite3", "~> 1.3.6"
10
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
11
11
  require "sqlite3"
12
12
 
13
13
  module ActiveRecord
@@ -45,8 +45,12 @@ module ActiveRecord
45
45
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
46
46
  counter_name = reflection.counter_cache_column
47
47
 
48
- updates = { counter_name.to_sym => object.send(counter_association).count(:all) }
49
- updates.merge!(touch_updates(touch)) if touch
48
+ updates = { counter_name => object.send(counter_association).count(:all) }
49
+
50
+ if touch
51
+ names = touch if touch != true
52
+ updates.merge!(touch_attributes_with_time(*names))
53
+ end
50
54
 
51
55
  unscoped.where(primary_key => object.id).update_all(updates)
52
56
  end
@@ -66,8 +70,8 @@ module ActiveRecord
66
70
  # * +counters+ - A Hash containing the names of the fields
67
71
  # to update as keys and the amount to update the field by as values.
68
72
  # * <tt>:touch</tt> option - Touch timestamp columns when updating.
69
- # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
70
- # touch that column or an array of symbols to touch just those ones.
73
+ # If attribute names are passed, they are updated along with updated_at/on
74
+ # attributes.
71
75
  #
72
76
  # ==== Examples
73
77
  #
@@ -105,7 +109,8 @@ module ActiveRecord
105
109
  end
106
110
 
107
111
  if touch
108
- touch_updates = touch_updates(touch)
112
+ names = touch if touch != true
113
+ touch_updates = touch_attributes_with_time(*names)
109
114
  updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
110
115
  end
111
116
 
@@ -163,13 +168,6 @@ module ActiveRecord
163
168
  def decrement_counter(counter_name, id, touch: nil)
164
169
  update_counters(id, counter_name => -1, touch: touch)
165
170
  end
166
-
167
- private
168
- def touch_updates(touch)
169
- touch = timestamp_attributes_for_update_in_model if touch == true
170
- touch_time = current_time_from_proper_timezone
171
- Array(touch).map { |column| [ column, touch_time ] }.to_h
172
- end
173
171
  end
174
172
 
175
173
  private
@@ -115,16 +115,27 @@ module ActiveRecord
115
115
 
116
116
  # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
117
117
  class MismatchedForeignKey < StatementInvalid
118
- def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
119
- @adapter = adapter
118
+ def initialize(
119
+ adapter = nil,
120
+ message: nil,
121
+ sql: nil,
122
+ binds: nil,
123
+ table: nil,
124
+ foreign_key: nil,
125
+ target_table: nil,
126
+ primary_key: nil,
127
+ primary_key_column: nil
128
+ )
120
129
  if table
121
- msg = <<-EOM.strip_heredoc
122
- Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
123
- This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
124
- To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
130
+ type = primary_key_column.bigint? ? :bigint : primary_key_column.type
131
+ msg = <<-EOM.squish
132
+ Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
133
+ which has type `#{primary_key_column.sql_type}`.
134
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
135
+ (For example `t.#{type} :#{foreign_key}`).
125
136
  EOM
126
137
  else
127
- msg = <<-EOM
138
+ msg = <<-EOM.squish
128
139
  There is a mismatch between the foreign key and primary key column types.
129
140
  Verify that the foreign key column type and the primary key of the associated table match types.
130
141
  EOM
@@ -134,11 +145,6 @@ module ActiveRecord
134
145
  end
135
146
  super(msg)
136
147
  end
137
-
138
- private
139
- def column_type(table, column)
140
- @adapter.columns(table).detect { |c| c.name == column }.sql_type
141
- end
142
148
  end
143
149
 
144
150
  # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 5
9
9
  MINOR = 1
10
- TINY = 5
10
+ TINY = 7
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -60,13 +60,6 @@ module ActiveRecord
60
60
  end
61
61
 
62
62
  private
63
-
64
- def increment_lock
65
- lock_col = self.class.locking_column
66
- previous_lock_value = send(lock_col).to_i
67
- send(lock_col + "=", previous_lock_value + 1)
68
- end
69
-
70
63
  def _create_record(attribute_names = self.attribute_names, *)
71
64
  if locking_enabled?
72
65
  # We always want to persist the locking version, even if we don't detect
@@ -76,39 +69,37 @@ module ActiveRecord
76
69
  super
77
70
  end
78
71
 
79
- def _update_record(attribute_names = self.attribute_names)
72
+ def _touch_row(attribute_names, time)
73
+ super
74
+ ensure
75
+ clear_attribute_change(self.class.locking_column) if locking_enabled?
76
+ end
77
+
78
+ def _update_row(attribute_names, attempted_action = "update")
80
79
  return super unless locking_enabled?
81
- return 0 if attribute_names.empty?
82
80
 
83
81
  begin
84
- lock_col = self.class.locking_column
85
-
86
- previous_lock_value = read_attribute_before_type_cast(lock_col)
87
-
88
- increment_lock
89
-
90
- attribute_names.push(lock_col)
82
+ locking_column = self.class.locking_column
83
+ previous_lock_value = read_attribute_before_type_cast(locking_column)
84
+ attribute_names << locking_column
91
85
 
92
- relation = self.class.unscoped
86
+ self[locking_column] += 1
93
87
 
94
- affected_rows = relation.where(
95
- self.class.primary_key => id,
96
- lock_col => previous_lock_value
97
- ).update_all(
98
- attributes_for_update(attribute_names).map do |name|
99
- [name, _read_attribute(name)]
100
- end.to_h
88
+ affected_rows = self.class.unscoped._update_record(
89
+ arel_attributes_with_values(attribute_names),
90
+ self.class.primary_key => id_in_database,
91
+ locking_column => previous_lock_value
101
92
  )
102
93
 
103
- unless affected_rows == 1
104
- raise ActiveRecord::StaleObjectError.new(self, "update")
94
+ if affected_rows != 1
95
+ raise ActiveRecord::StaleObjectError.new(self, attempted_action)
105
96
  end
106
97
 
107
98
  affected_rows
108
99
 
109
100
  # If something went wrong, revert the locking_column value.
110
101
  rescue Exception
111
- send(lock_col + "=", previous_lock_value.to_i)
102
+ self[locking_column] = previous_lock_value.to_i
112
103
  raise
113
104
  end
114
105
  end
@@ -128,7 +119,7 @@ module ActiveRecord
128
119
 
129
120
  if locking_enabled?
130
121
  locking_column = self.class.locking_column
131
- relation = relation.where(locking_column => _read_attribute(locking_column))
122
+ relation = relation.where(locking_column => read_attribute_before_type_cast(locking_column))
132
123
  end
133
124
 
134
125
  relation
@@ -332,11 +332,11 @@ module ActiveRecord
332
332
  end
333
333
 
334
334
  def attributes_builder # :nodoc:
335
- @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key) do |name|
336
- unless columns_hash.key?(name)
337
- _default_attributes[name].dup
338
- end
335
+ unless defined?(@attributes_builder) && @attributes_builder
336
+ defaults = _default_attributes.except(*(column_names - [primary_key]))
337
+ @attributes_builder = AttributeSet::Builder.new(attribute_types, defaults)
339
338
  end
339
+ @attributes_builder
340
340
  end
341
341
 
342
342
  def columns_hash # :nodoc:
@@ -381,7 +381,7 @@ module ActiveRecord
381
381
  # default values when instantiating the Active Record object for this table.
382
382
  def column_defaults
383
383
  load_schema
384
- _default_attributes.to_hash
384
+ _default_attributes.deep_dup.to_hash
385
385
  end
386
386
 
387
387
  def _default_attributes # :nodoc:
@@ -226,7 +226,8 @@ module ActiveRecord
226
226
  def becomes(klass)
227
227
  became = klass.new
228
228
  became.instance_variable_set("@attributes", @attributes)
229
- became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
229
+ became.instance_variable_set("@mutation_tracker", @mutation_tracker ||= nil)
230
+ became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
230
231
  became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
231
232
  became.instance_variable_set("@new_record", new_record?)
232
233
  became.instance_variable_set("@destroyed", destroyed?)
@@ -505,36 +506,12 @@ module ActiveRecord
505
506
  MSG
506
507
  end
507
508
 
508
- time ||= current_time_from_proper_timezone
509
- attributes = timestamp_attributes_for_update_in_model
510
- attributes.concat(names)
511
-
512
- unless attributes.empty?
513
- changes = {}
514
-
515
- attributes.each do |column|
516
- column = column.to_s
517
- changes[column] = write_attribute(column, time)
518
- end
519
-
520
- primary_key = self.class.primary_key
521
- scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))
522
-
523
- if locking_enabled?
524
- locking_column = self.class.locking_column
525
- scope = scope.where(locking_column => _read_attribute(locking_column))
526
- changes[locking_column] = increment_lock
527
- end
528
-
529
- clear_attribute_changes(changes.keys)
530
- result = scope.update_all(changes) == 1
531
-
532
- if !result && locking_enabled?
533
- raise ActiveRecord::StaleObjectError.new(self, "touch")
534
- end
509
+ attribute_names = timestamp_attributes_for_update_in_model
510
+ attribute_names |= names.map(&:to_s)
535
511
 
536
- @_trigger_update_callback = result
537
- result
512
+ unless attribute_names.empty?
513
+ affected_rows = _touch_row(attribute_names, time)
514
+ @_trigger_update_callback = affected_rows == 1
538
515
  else
539
516
  true
540
517
  end
@@ -554,6 +531,24 @@ module ActiveRecord
554
531
  self.class.unscoped.where(self.class.primary_key => id)
555
532
  end
556
533
 
534
+ def _touch_row(attribute_names, time)
535
+ time ||= current_time_from_proper_timezone
536
+
537
+ attribute_names.each do |attr_name|
538
+ write_attribute(attr_name, time)
539
+ clear_attribute_change(attr_name)
540
+ end
541
+
542
+ _update_row(attribute_names, "touch")
543
+ end
544
+
545
+ def _update_row(attribute_names, attempted_action = "update")
546
+ self.class.unscoped._update_record(
547
+ arel_attributes_with_values(attribute_names),
548
+ self.class.primary_key => id_in_database
549
+ )
550
+ end
551
+
557
552
  def create_or_update(*args, &block)
558
553
  _raise_readonly_record_error if readonly?
559
554
  result = new_record? ? _create_record(&block) : _update_record(*args, &block)
@@ -563,23 +558,26 @@ module ActiveRecord
563
558
  # Updates the associated record with values matching those of the instance attributes.
564
559
  # Returns the number of affected rows.
565
560
  def _update_record(attribute_names = self.attribute_names)
566
- attributes_values = arel_attributes_with_values_for_update(attribute_names)
567
- if attributes_values.empty?
568
- rows_affected = 0
561
+ attribute_names &= self.class.column_names
562
+ attribute_names = attributes_for_update(attribute_names)
563
+
564
+ if attribute_names.empty?
565
+ affected_rows = 0
569
566
  @_trigger_update_callback = true
570
567
  else
571
- rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
572
- @_trigger_update_callback = rows_affected > 0
568
+ affected_rows = _update_row(attribute_names)
569
+ @_trigger_update_callback = affected_rows == 1
573
570
  end
574
571
 
575
572
  yield(self) if block_given?
576
573
 
577
- rows_affected
574
+ affected_rows
578
575
  end
579
576
 
580
577
  # Creates a record with values matching those of the instance attributes
581
578
  # and returns its id.
582
579
  def _create_record(attribute_names = self.attribute_names)
580
+ attribute_names &= self.class.column_names
583
581
  attributes_values = arel_attributes_with_values_for_create(attribute_names)
584
582
 
585
583
  new_id = self.class.unscoped.insert attributes_values
@@ -5,20 +5,20 @@ module ActiveRecord
5
5
  # Enable the query cache within the block if Active Record is configured.
6
6
  # If it's not, it will execute the given block.
7
7
  def cache(&block)
8
- if configurations.empty?
9
- yield
10
- else
8
+ if connected? || !configurations.empty?
11
9
  connection.cache(&block)
10
+ else
11
+ yield
12
12
  end
13
13
  end
14
14
 
15
15
  # Disable the query cache within the block if Active Record is configured.
16
16
  # If it's not, it will execute the given block.
17
17
  def uncached(&block)
18
- if configurations.empty?
19
- yield
20
- else
18
+ if connected? || !configurations.empty?
21
19
  connection.uncached(&block)
20
+ else
21
+ yield
22
22
  end
23
23
  end
24
24
  end
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  if has_include?(column_name)
113
113
  relation = construct_relation_for_association_calculations
114
114
 
115
- if operation.to_s.downcase == "count" && !distinct_value
115
+ if operation.to_s.downcase == "count"
116
116
  relation.distinct!
117
117
  # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
118
118
  if (column_name == :all || column_name.nil?) && select_values.empty?
@@ -379,7 +379,7 @@ module ActiveRecord
379
379
 
380
380
  case conditions
381
381
  when Array, Hash
382
- relation.where!(conditions)
382
+ relation.where!(conditions) unless conditions.empty?
383
383
  else
384
384
  relation.where!(primary_key => conditions) unless conditions == :none
385
385
  end
@@ -429,7 +429,7 @@ module ActiveRecord
429
429
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
430
430
 
431
431
  expects_array = ids.first.kind_of?(Array)
432
- return ids.first if expects_array && ids.first.empty?
432
+ return [] if expects_array && ids.first.empty?
433
433
 
434
434
  ids = ids.flatten.compact.uniq
435
435
 
@@ -95,7 +95,7 @@ module ActiveRecord
95
95
  next
96
96
  when value.is_a?(Relation)
97
97
  binds += value.bound_attributes
98
- when value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype)
98
+ when value.is_a?(Range) && !table.type(column_name).force_equality?(value)
99
99
  first = value.begin
100
100
  last = value.end
101
101
  unless first.respond_to?(:infinite?) && first.infinite?
@@ -158,7 +158,7 @@ module ActiveRecord
158
158
  return if table.associated_with?(column_name)
159
159
  case value
160
160
  when Array, Range
161
- table.type(column_name).respond_to?(:subtype)
161
+ table.type(column_name).force_equality?(value)
162
162
  else
163
163
  !value.nil? && handler_for(value).is_a?(BasicObjectHandler)
164
164
  end
@@ -1079,7 +1079,7 @@ module ActiveRecord
1079
1079
  end
1080
1080
  o.split(",").map! do |s|
1081
1081
  s.strip!
1082
- s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || s.concat(" DESC")
1082
+ s.gsub!(/\sasc\Z/i, " DESC") || s.gsub!(/\sdesc\Z/i, " ASC") || (s << " DESC")
1083
1083
  end
1084
1084
  else
1085
1085
  o
@@ -67,7 +67,7 @@ module ActiveRecord
67
67
  binds)
68
68
  end
69
69
 
70
- def _update_record(values, id, id_was) # :nodoc:
70
+ def _update_record(values, constraints) # :nodoc:
71
71
  substitutes, binds = substitute_values values
72
72
 
73
73
  scope = @klass.unscoped
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
  scope.unscope!(where: @klass.inheritance_column)
77
77
  end
78
78
 
79
- relation = scope.where(@klass.primary_key => (id_was || id))
79
+ relation = scope.where(constraints)
80
80
  bvs = binds + relation.bound_attributes
81
81
  um = relation
82
82
  .arel
@@ -120,7 +120,7 @@ module ActiveRecord
120
120
  $stderr.puts "Database '#{configuration['database']}' already exists"
121
121
  rescue Exception => error
122
122
  $stderr.puts error
123
- $stderr.puts "Couldn't create database for #{configuration.inspect}"
123
+ $stderr.puts "Couldn't create '#{configuration['database']}' database. Please check your configuration."
124
124
  raise
125
125
  end
126
126
 
@@ -53,6 +53,13 @@ module ActiveRecord
53
53
  end
54
54
 
55
55
  class_methods do
56
+ def touch_attributes_with_time(*names, time: nil)
57
+ attribute_names = timestamp_attributes_for_update_in_model
58
+ attribute_names |= names.map(&:to_s)
59
+ time ||= current_time_from_proper_timezone
60
+ attribute_names.each_with_object({}) { |attr_name, result| result[attr_name] = time }
61
+ end
62
+
56
63
  private
57
64
  def timestamp_attributes_for_create_in_model
58
65
  timestamp_attributes_for_create.select { |c| column_names.include?(c) }
@@ -49,6 +49,10 @@ module ActiveRecord
49
49
  end
50
50
  end
51
51
 
52
+ def force_equality?(value)
53
+ coder.respond_to?(:object_class) && value.is_a?(coder.object_class)
54
+ end
55
+
52
56
  private
53
57
 
54
58
  def default_value?(value)
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  relation = build_relation(finder_class, attribute, value)
18
18
  if record.persisted?
19
19
  if finder_class.primary_key
20
- relation = relation.where.not(finder_class.primary_key => record.id_in_database || record.id)
20
+ relation = relation.where.not(finder_class.primary_key => record.id_in_database)
21
21
  else
22
22
  raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.5
4
+ version: 5.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-14 00:00:00.000000000 Z
11
+ date: 2019-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.1.5
19
+ version: 5.1.7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.1.5
26
+ version: 5.1.7
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 5.1.5
33
+ version: 5.1.7
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 5.1.5
40
+ version: 5.1.7
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -320,8 +320,8 @@ homepage: http://rubyonrails.org
320
320
  licenses:
321
321
  - MIT
322
322
  metadata:
323
- source_code_uri: https://github.com/rails/rails/tree/v5.1.5/activerecord
324
- changelog_uri: https://github.com/rails/rails/blob/v5.1.5/activerecord/CHANGELOG.md
323
+ source_code_uri: https://github.com/rails/rails/tree/v5.1.7/activerecord
324
+ changelog_uri: https://github.com/rails/rails/blob/v5.1.7/activerecord/CHANGELOG.md
325
325
  post_install_message:
326
326
  rdoc_options:
327
327
  - "--main"
@@ -339,8 +339,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
339
339
  - !ruby/object:Gem::Version
340
340
  version: '0'
341
341
  requirements: []
342
- rubyforge_project:
343
- rubygems_version: 2.7.3
342
+ rubygems_version: 3.0.1
344
343
  signing_key:
345
344
  specification_version: 4
346
345
  summary: Object-relational mapper framework (part of Rails).