activerecord 5.2.2 → 5.2.3

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/lib/active_record/associations/collection_association.rb +4 -5
  4. data/lib/active_record/associations/collection_proxy.rb +8 -34
  5. data/lib/active_record/associations/has_many_association.rb +1 -0
  6. data/lib/active_record/associations/has_many_through_association.rb +2 -0
  7. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -7
  8. data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
  9. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -0
  10. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -2
  11. data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
  12. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -8
  13. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
  14. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  15. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -24
  16. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -0
  17. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  18. data/lib/active_record/core.rb +2 -1
  19. data/lib/active_record/errors.rb +18 -12
  20. data/lib/active_record/gem_version.rb +1 -1
  21. data/lib/active_record/migration/compatibility.rb +15 -15
  22. data/lib/active_record/persistence.rb +3 -1
  23. data/lib/active_record/querying.rb +1 -2
  24. data/lib/active_record/relation/calculations.rb +11 -8
  25. data/lib/active_record/relation/finder_methods.rb +6 -2
  26. data/lib/active_record/relation/merger.rb +6 -3
  27. data/lib/active_record/relation/predicate_builder.rb +14 -9
  28. data/lib/active_record/relation/query_attribute.rb +5 -3
  29. data/lib/active_record/relation/query_methods.rb +30 -7
  30. metadata +9 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38bf52e024134bc78c1daa8bf44ee21d685adcc62ddc9ded7feae3111c08a8e7
4
- data.tar.gz: 89f9dbe14887a77a660722bb1a09ffbde6b11a4ab600b9a724458ac369548f09
3
+ metadata.gz: 3a6a58445af3e133df097836131d528804585b935b40025174db5e3a284351b5
4
+ data.tar.gz: 4d1b7fbc93cb5582065167d8d8a197ed887133b06170c5acf0ab6a18dea29465
5
5
  SHA512:
6
- metadata.gz: f0a17e48f08ba5082caa787efabf99f58bf0738de05a53f0fc4979ed27285f42f4f28e9406089f74845573f43d7a78718d5323bce8826a48d7bb673a77717055
7
- data.tar.gz: '03572796340d73d6e9b1cfa04c1d10d84a528615e76d9e0f700af85086d4dad11c58e21ca96b8c44bddd303dd090a25e68489391046e6cd0b8cc7565d2393da8'
6
+ metadata.gz: c91429dea93398c1c532da4f5a66435b73842887a4d0f86782dfb19ba3801dfa65e04410a26f503e18cbc434793f293c051c7844a3702a638f14443711d7d6ea
7
+ data.tar.gz: a224f223a6f2da2ac314fa2d6f0deb4befbb0a962cad19d8a5c93a769ffd45450d23df71d791dfadae0b56435207beee54e5126e2e9cee50e9678fbe233fff9b
@@ -1,3 +1,45 @@
1
+ ## Rails 5.2.3 (March 27, 2019) ##
2
+
3
+ * Fix different `count` calculation when using `size` with manual `select` with DISTINCT.
4
+
5
+ Fixes #35214.
6
+
7
+ *Juani Villarejo*
8
+
9
+ * Fix prepared statements caching to be enabled even when query caching is enabled.
10
+
11
+ *Ryuta Kamizono*
12
+
13
+ * Don't allow `where` with invalid value matches to nil values.
14
+
15
+ Fixes #33624.
16
+
17
+ *Ryuta Kamizono*
18
+
19
+ * Restore an ability that class level `update` without giving ids.
20
+
21
+ Fixes #34743.
22
+
23
+ *Ryuta Kamizono*
24
+
25
+ * Fix join table column quoting with SQLite.
26
+
27
+ *Gannon McGibbon*
28
+
29
+ * Ensure that `delete_all` on collection proxy returns affected count.
30
+
31
+ *Ryuta Kamizono*
32
+
33
+ * Reset scope after delete on collection association to clear stale offsets of removed records.
34
+
35
+ *Gannon McGibbon*
36
+
37
+
38
+ ## Rails 5.2.2.1 (March 11, 2019) ##
39
+
40
+ * No changes.
41
+
42
+
1
43
  ## Rails 5.2.2 (December 04, 2018) ##
2
44
 
3
45
  * Do not ignore the scoping with query methods in the scope block.
@@ -109,9 +109,8 @@ module ActiveRecord
109
109
  end
110
110
  end
111
111
 
112
- # Add +records+ to this association. Returns +self+ so method calls may
113
- # be chained. Since << flattens its argument list and inserts each record,
114
- # +push+ and +concat+ behave identically.
112
+ # Add +records+ to this association. Since +<<+ flattens its argument list
113
+ # and inserts each record, +push+ and +concat+ behave identically.
115
114
  def concat(*records)
116
115
  records = records.flatten
117
116
  if owner.new_record?
@@ -362,7 +361,6 @@ module ActiveRecord
362
361
  add_to_target(record) do
363
362
  result = insert_record(record, true, raise) {
364
363
  @_was_loaded = loaded?
365
- @association_ids = nil
366
364
  }
367
365
  end
368
366
  raise ActiveRecord::Rollback unless result
@@ -399,6 +397,7 @@ module ActiveRecord
399
397
 
400
398
  delete_records(existing_records, method) if existing_records.any?
401
399
  records.each { |record| target.delete(record) }
400
+ @association_ids = nil
402
401
 
403
402
  records.each { |record| callback(:after_remove, record) }
404
403
  end
@@ -439,7 +438,6 @@ module ActiveRecord
439
438
  unless owner.new_record?
440
439
  result &&= insert_record(record, true, raise) {
441
440
  @_was_loaded = loaded?
442
- @association_ids = nil
443
441
  }
444
442
  end
445
443
  end
@@ -462,6 +460,7 @@ module ActiveRecord
462
460
  if index
463
461
  target[index] = record
464
462
  elsif @_was_loaded || !loaded?
463
+ @association_ids = nil
465
464
  target << record
466
465
  end
467
466
 
@@ -366,34 +366,6 @@ module ActiveRecord
366
366
  @association.create!(attributes, &block)
367
367
  end
368
368
 
369
- # Add one or more records to the collection by setting their foreign keys
370
- # to the association's primary key. Since #<< flattens its argument list and
371
- # inserts each record, +push+ and #concat behave identically. Returns +self+
372
- # so method calls may be chained.
373
- #
374
- # class Person < ActiveRecord::Base
375
- # has_many :pets
376
- # end
377
- #
378
- # person.pets.size # => 0
379
- # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
380
- # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
381
- # person.pets.size # => 3
382
- #
383
- # person.id # => 1
384
- # person.pets
385
- # # => [
386
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
387
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
388
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
389
- # # ]
390
- #
391
- # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
392
- # person.pets.size # => 5
393
- def concat(*records)
394
- @association.concat(*records)
395
- end
396
-
397
369
  # Replaces this collection with +other_array+. This will perform a diff
398
370
  # and delete/add only records that have changed.
399
371
  #
@@ -500,7 +472,7 @@ module ActiveRecord
500
472
  # Pet.find(1, 2, 3)
501
473
  # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
502
474
  def delete_all(dependent = nil)
503
- @association.delete_all(dependent)
475
+ @association.delete_all(dependent).tap { reset_scope }
504
476
  end
505
477
 
506
478
  # Deletes the records of the collection directly from the database
@@ -527,7 +499,7 @@ module ActiveRecord
527
499
  #
528
500
  # Pet.find(1) # => Couldn't find Pet with id=1
529
501
  def destroy_all
530
- @association.destroy_all
502
+ @association.destroy_all.tap { reset_scope }
531
503
  end
532
504
 
533
505
  # Deletes the +records+ supplied from the collection according to the strategy
@@ -646,7 +618,7 @@ module ActiveRecord
646
618
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
647
619
  # # ]
648
620
  def delete(*records)
649
- @association.delete(*records)
621
+ @association.delete(*records).tap { reset_scope }
650
622
  end
651
623
 
652
624
  # Destroys the +records+ supplied and removes them from the collection.
@@ -718,7 +690,7 @@ module ActiveRecord
718
690
  #
719
691
  # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
720
692
  def destroy(*records)
721
- @association.destroy(*records)
693
+ @association.destroy(*records).tap { reset_scope }
722
694
  end
723
695
 
724
696
  ##
@@ -1033,8 +1005,9 @@ module ActiveRecord
1033
1005
  end
1034
1006
 
1035
1007
  # Adds one or more +records+ to the collection by setting their foreign keys
1036
- # to the association's primary key. Returns +self+, so several appends may be
1037
- # chained together.
1008
+ # to the association's primary key. Since +<<+ flattens its argument list and
1009
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
+ # so several appends may be chained together.
1038
1011
  #
1039
1012
  # class Person < ActiveRecord::Base
1040
1013
  # has_many :pets
@@ -1057,6 +1030,7 @@ module ActiveRecord
1057
1030
  end
1058
1031
  alias_method :push, :<<
1059
1032
  alias_method :append, :<<
1033
+ alias_method :concat, :<<
1060
1034
 
1061
1035
  def prepend(*args)
1062
1036
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
@@ -99,6 +99,7 @@ module ActiveRecord
99
99
  def delete_or_nullify_all_records(method)
100
100
  count = delete_count(method, scope)
101
101
  update_counter(-count)
102
+ count
102
103
  end
103
104
 
104
105
  # Deletes the records according to the <tt>:dependent</tt> option.
@@ -161,6 +161,8 @@ module ActiveRecord
161
161
  else
162
162
  update_counter(-count)
163
163
  end
164
+
165
+ count
164
166
  end
165
167
 
166
168
  def difference(a, b)
@@ -915,6 +915,16 @@ module ActiveRecord
915
915
  # about the model. The model needs to pass a specification name to the handler,
916
916
  # in order to look up the correct connection pool.
917
917
  class ConnectionHandler
918
+ def self.create_owner_to_pool # :nodoc:
919
+ Concurrent::Map.new(initial_capacity: 2) do |h, k|
920
+ # Discard the parent's connection pools immediately; we have no need
921
+ # of them
922
+ discard_unowned_pools(h)
923
+
924
+ h[k] = Concurrent::Map.new(initial_capacity: 2)
925
+ end
926
+ end
927
+
918
928
  def self.unowned_pool_finalizer(pid_map) # :nodoc:
919
929
  lambda do |_|
920
930
  discard_unowned_pools(pid_map)
@@ -929,13 +939,7 @@ module ActiveRecord
929
939
 
930
940
  def initialize
931
941
  # These caches are keyed by spec.name (ConnectionSpecification#name).
932
- @owner_to_pool = Concurrent::Map.new(initial_capacity: 2) do |h, k|
933
- # Discard the parent's connection pools immediately; we have no need
934
- # of them
935
- ConnectionHandler.discard_unowned_pools(h)
936
-
937
- h[k] = Concurrent::Map.new(initial_capacity: 2)
938
- end
942
+ @owner_to_pool = ConnectionHandler.create_owner_to_pool
939
943
 
940
944
  # Backup finalizer: if the forked child never needed a pool, the above
941
945
  # early discard has not occurred
@@ -20,9 +20,22 @@ module ActiveRecord
20
20
  raise "Passing bind parameters with an arel AST is forbidden. " \
21
21
  "The values must be stored on the AST directly"
22
22
  end
23
- sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
24
- [sql.freeze, binds || []]
23
+
24
+ if prepared_statements
25
+ sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
26
+
27
+ if binds.length > bind_params_length
28
+ unprepared_statement do
29
+ sql, binds = to_sql_and_binds(arel_or_sql_string)
30
+ visitor.preparable = false
31
+ end
32
+ end
33
+ else
34
+ sql = visitor.accept(arel_or_sql_string.ast, collector).value
35
+ end
36
+ [sql.freeze, binds]
25
37
  else
38
+ visitor.preparable = false if prepared_statements
26
39
  [arel_or_sql_string.dup.freeze, binds]
27
40
  end
28
41
  end
@@ -47,13 +60,8 @@ module ActiveRecord
47
60
  arel = arel_from_relation(arel)
48
61
  sql, binds = to_sql_and_binds(arel, binds)
49
62
 
50
- if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
51
- preparable = false
52
- elsif binds.length > bind_params_length
53
- sql, binds = unprepared_statement { to_sql_and_binds(arel) }
54
- preparable = false
55
- else
56
- preparable = visitor.preparable
63
+ if preparable.nil?
64
+ preparable = prepared_statements ? visitor.preparable : false
57
65
  end
58
66
 
59
67
  if prepared_statements && preparable
@@ -96,6 +96,11 @@ module ActiveRecord
96
96
  if @query_cache_enabled && !locked?(arel)
97
97
  arel = arel_from_relation(arel)
98
98
  sql, binds = to_sql_and_binds(arel, binds)
99
+
100
+ if preparable.nil?
101
+ preparable = prepared_statements ? visitor.preparable : false
102
+ end
103
+
99
104
  cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
100
105
  else
101
106
  super
@@ -305,7 +305,7 @@ module ActiveRecord
305
305
  yield td if block_given?
306
306
 
307
307
  if options[:force]
308
- drop_table(table_name, **options, if_exists: true)
308
+ drop_table(table_name, options.merge(if_exists: true))
309
309
  end
310
310
 
311
311
  result = execute schema_creation.accept td
@@ -899,7 +899,7 @@ module ActiveRecord
899
899
  foreign_key_options = { to_table: reference_name }
900
900
  end
901
901
  foreign_key_options[:column] ||= "#{ref_name}_id"
902
- remove_foreign_key(table_name, **foreign_key_options)
902
+ remove_foreign_key(table_name, foreign_key_options)
903
903
  end
904
904
 
905
905
  remove_column(table_name, "#{ref_name}_id")
@@ -81,7 +81,9 @@ module ActiveRecord
81
81
  alias :in_use? :owner
82
82
 
83
83
  def self.type_cast_config_to_integer(config)
84
- if config =~ SIMPLE_INT
84
+ if config.is_a?(Integer)
85
+ config
86
+ elsif config =~ SIMPLE_INT
85
87
  config.to_i
86
88
  else
87
89
  config
@@ -804,15 +804,25 @@ module ActiveRecord
804
804
  end
805
805
 
806
806
  def mismatched_foreign_key(message)
807
- parts = message.scan(/`(\w+)`[ $)]/).flatten
808
- MismatchedForeignKey.new(
809
- self,
807
+ match = %r/
808
+ (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
809
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
810
+ REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
811
+ /xmi.match(message)
812
+
813
+ options = {
810
814
  message: message,
811
- table: parts[0],
812
- foreign_key: parts[1],
813
- target_table: parts[2],
814
- primary_key: parts[3],
815
- )
815
+ }
816
+
817
+ if match
818
+ options[:table] = match[:table]
819
+ options[:foreign_key] = match[:foreign_key]
820
+ options[:target_table] = match[:target_table]
821
+ options[:primary_key] = match[:primary_key]
822
+ options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
823
+ end
824
+
825
+ MismatchedForeignKey.new(options)
816
826
  end
817
827
 
818
828
  def integer_to_sql(limit) # :nodoc:
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module DetermineIfPreparableVisitor
6
- attr_reader :preparable
6
+ attr_accessor :preparable
7
7
 
8
8
  def accept(*)
9
9
  @preparable = true
@@ -17,6 +17,42 @@ module ActiveRecord
17
17
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
18
18
  end
19
19
 
20
+ def visit_ChangeColumnDefinition(o)
21
+ column = o.column
22
+ column.sql_type = type_to_sql(column.type, column.options)
23
+ quoted_column_name = quote_column_name(o.name)
24
+
25
+ change_column_sql = "ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}".dup
26
+
27
+ options = column_options(column)
28
+
29
+ if options[:collation]
30
+ change_column_sql << " COLLATE \"#{options[:collation]}\""
31
+ end
32
+
33
+ if options[:using]
34
+ change_column_sql << " USING #{options[:using]}"
35
+ elsif options[:cast_as]
36
+ cast_as_type = type_to_sql(options[:cast_as], options)
37
+ change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
38
+ end
39
+
40
+ if options.key?(:default)
41
+ if options[:default].nil?
42
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
43
+ else
44
+ quoted_default = quote_default_expression(options[:default], column)
45
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
46
+ end
47
+ end
48
+
49
+ if options.key?(:null)
50
+ change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
51
+ end
52
+
53
+ change_column_sql
54
+ end
55
+
20
56
  def add_column_options!(sql, options)
21
57
  if options[:collation]
22
58
  sql << " COLLATE \"#{options[:collation]}\""
@@ -683,38 +683,20 @@ module ActiveRecord
683
683
  end
684
684
  end
685
685
 
686
- def change_column_sql(table_name, column_name, type, options = {})
687
- quoted_column_name = quote_column_name(column_name)
688
- sql_type = type_to_sql(type, options)
689
- sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup
690
- if options[:collation]
691
- sql << " COLLATE \"#{options[:collation]}\""
692
- end
693
- if options[:using]
694
- sql << " USING #{options[:using]}"
695
- elsif options[:cast_as]
696
- cast_as_type = type_to_sql(options[:cast_as], options)
697
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
- end
699
-
700
- sql
701
- end
702
-
703
686
  def add_column_for_alter(table_name, column_name, type, options = {})
704
687
  return super unless options.key?(:comment)
705
688
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
706
689
  end
707
690
 
708
691
  def change_column_for_alter(table_name, column_name, type, options = {})
709
- sqls = [change_column_sql(table_name, column_name, type, options)]
710
- sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
711
- sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
692
+ td = create_table_definition(table_name)
693
+ cd = td.new_column_definition(column_name, type, options)
694
+ sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
712
695
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
713
696
  sqls
714
697
  end
715
698
 
716
- # Changes the default value of a table column.
717
- def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
699
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
718
700
  column = column_for(table_name, column_name)
719
701
  return unless column
720
702
 
@@ -729,8 +711,8 @@ module ActiveRecord
729
711
  end
730
712
  end
731
713
 
732
- def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
733
- "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
714
+ def change_column_null_for_alter(table_name, column_name, null, default = nil)
715
+ "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
734
716
  end
735
717
 
736
718
  def add_timestamps_for_alter(table_name, options = {})
@@ -12,6 +12,10 @@ module ActiveRecord
12
12
  quote_column_name(attr)
13
13
  end
14
14
 
15
+ def quote_table_name(name)
16
+ @quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
17
+ end
18
+
15
19
  def quote_column_name(name)
16
20
  @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
17
21
  end
@@ -9,7 +9,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
9
9
  require "active_record/connection_adapters/sqlite3/schema_dumper"
10
10
  require "active_record/connection_adapters/sqlite3/schema_statements"
11
11
 
12
- gem "sqlite3", "~> 1.3.6"
12
+ gem "sqlite3", "~> 1.3", ">= 1.3.6"
13
13
  require "sqlite3"
14
14
 
15
15
  module ActiveRecord
@@ -184,7 +184,8 @@ module ActiveRecord
184
184
  end
185
185
 
186
186
  def find_by(*args) # :nodoc:
187
- return super if scope_attributes? || reflect_on_all_aggregations.any?
187
+ return super if scope_attributes? || reflect_on_all_aggregations.any? ||
188
+ columns_hash.key?(inheritance_column) && base_class != self
188
189
 
189
190
  hash = args.first
190
191
 
@@ -117,16 +117,27 @@ module ActiveRecord
117
117
 
118
118
  # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
119
119
  class MismatchedForeignKey < StatementInvalid
120
- def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
121
- @adapter = adapter
120
+ def initialize(
121
+ adapter = nil,
122
+ message: nil,
123
+ sql: nil,
124
+ binds: nil,
125
+ table: nil,
126
+ foreign_key: nil,
127
+ target_table: nil,
128
+ primary_key: nil,
129
+ primary_key_column: nil
130
+ )
122
131
  if table
123
- msg = <<-EOM.strip_heredoc
124
- Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
125
- This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
126
- To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
132
+ type = primary_key_column.bigint? ? :bigint : primary_key_column.type
133
+ msg = <<-EOM.squish
134
+ Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
135
+ which has type `#{primary_key_column.sql_type}`.
136
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
137
+ (For example `t.#{type} :#{foreign_key}`).
127
138
  EOM
128
139
  else
129
- msg = <<-EOM.strip_heredoc
140
+ msg = <<-EOM.squish
130
141
  There is a mismatch between the foreign key and primary key column types.
131
142
  Verify that the foreign key column type and the primary key of the associated table match types.
132
143
  EOM
@@ -136,11 +147,6 @@ module ActiveRecord
136
147
  end
137
148
  super(msg)
138
149
  end
139
-
140
- private
141
- def column_type(table, column)
142
- @adapter.columns(table).detect { |c| c.name == column }.sql_type
143
- end
144
150
  end
145
151
 
146
152
  # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 5
11
11
  MINOR = 2
12
- TINY = 2
12
+ TINY = 3
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -17,20 +17,18 @@ module ActiveRecord
17
17
 
18
18
  class V5_1 < V5_2
19
19
  def change_column(table_name, column_name, type, options = {})
20
- if adapter_name == "PostgreSQL"
21
- clear_cache!
22
- sql = connection.send(:change_column_sql, table_name, column_name, type, options)
23
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
24
- change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
25
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
26
- change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
20
+ if connection.adapter_name == "PostgreSQL"
21
+ super(table_name, column_name, type, options.except(:default, :null, :comment))
22
+ connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
23
+ connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
24
+ connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
27
25
  else
28
26
  super
29
27
  end
30
28
  end
31
29
 
32
30
  def create_table(table_name, options = {})
33
- if adapter_name == "Mysql2"
31
+ if connection.adapter_name == "Mysql2"
34
32
  super(table_name, options: "ENGINE=InnoDB", **options)
35
33
  else
36
34
  super
@@ -52,13 +50,13 @@ module ActiveRecord
52
50
  end
53
51
 
54
52
  def create_table(table_name, options = {})
55
- if adapter_name == "PostgreSQL"
53
+ if connection.adapter_name == "PostgreSQL"
56
54
  if options[:id] == :uuid && !options.key?(:default)
57
55
  options[:default] = "uuid_generate_v4()"
58
56
  end
59
57
  end
60
58
 
61
- unless adapter_name == "Mysql2" && options[:id] == :bigint
59
+ unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
62
60
  if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
63
61
  options[:default] = nil
64
62
  end
@@ -175,7 +173,7 @@ module ActiveRecord
175
173
  if options[:name].present?
176
174
  options[:name].to_s
177
175
  else
178
- index_name(table_name, column: column_names)
176
+ connection.index_name(table_name, column: column_names)
179
177
  end
180
178
  super
181
179
  end
@@ -195,15 +193,17 @@ module ActiveRecord
195
193
  end
196
194
 
197
195
  def index_name_for_remove(table_name, options = {})
198
- index_name = index_name(table_name, options)
196
+ index_name = connection.index_name(table_name, options)
199
197
 
200
- unless index_name_exists?(table_name, index_name)
198
+ unless connection.index_name_exists?(table_name, index_name)
201
199
  if options.is_a?(Hash) && options.has_key?(:name)
202
200
  options_without_column = options.dup
203
201
  options_without_column.delete :column
204
- index_name_without_column = index_name(table_name, options_without_column)
202
+ index_name_without_column = connection.index_name(table_name, options_without_column)
205
203
 
206
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column)
204
+ if connection.index_name_exists?(table_name, index_name_without_column)
205
+ return index_name_without_column
206
+ end
207
207
  end
208
208
 
209
209
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
@@ -97,11 +97,13 @@ module ActiveRecord
97
97
  # When running callbacks is not needed for each record update,
98
98
  # it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
99
99
  # for updating all records in a single query.
100
- def update(id, attributes)
100
+ def update(id = :all, attributes)
101
101
  if id.is_a?(Array)
102
102
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
103
103
  object.update(attributes[idx])
104
104
  }
105
+ elsif id == :all
106
+ all.each { |record| record.update(attributes) }
105
107
  else
106
108
  if ActiveRecord::Base === id
107
109
  raise ArgumentError,
@@ -40,8 +40,7 @@ module ActiveRecord
40
40
  def find_by_sql(sql, binds = [], preparable: nil, &block)
41
41
  result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
42
42
  column_types = result_set.column_types.dup
43
- cached_columns_hash = connection.schema_cache.columns_hash(table_name)
44
- cached_columns_hash.each_key { |k| column_types.delete k }
43
+ attribute_types.each_key { |k| column_types.delete k }
45
44
  message_bus = ActiveSupport::Notifications.instrumenter
46
45
 
47
46
  payload = {
@@ -190,11 +190,9 @@ module ActiveRecord
190
190
  relation = apply_join_dependency
191
191
  relation.pluck(*column_names)
192
192
  else
193
- enforce_raw_sql_whitelist(column_names)
193
+ klass.enforce_raw_sql_whitelist(column_names)
194
194
  relation = spawn
195
- relation.select_values = column_names.map { |cn|
196
- @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
197
- }
195
+ relation.select_values = column_names
198
196
  result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
199
197
  result.cast_values(klass.attribute_types)
200
198
  end
@@ -209,7 +207,6 @@ module ActiveRecord
209
207
  end
210
208
 
211
209
  private
212
-
213
210
  def has_include?(column_name)
214
211
  eager_loading? || (includes_values.present? && column_name && column_name != :all)
215
212
  end
@@ -224,10 +221,12 @@ module ActiveRecord
224
221
  if operation == "count"
225
222
  column_name ||= select_for_count
226
223
  if column_name == :all
227
- if distinct && (group_values.any? || select_values.empty? && order_values.empty?)
224
+ if !distinct
225
+ distinct = distinct_select?(select_for_count) if group_values.empty?
226
+ elsif group_values.any? || select_values.empty? && order_values.empty?
228
227
  column_name = primary_key
229
228
  end
230
- elsif column_name =~ /\s*DISTINCT[\s(]+/i
229
+ elsif distinct_select?(column_name)
231
230
  distinct = nil
232
231
  end
233
232
  end
@@ -239,6 +238,10 @@ module ActiveRecord
239
238
  end
240
239
  end
241
240
 
241
+ def distinct_select?(column_name)
242
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
243
+ end
244
+
242
245
  def aggregate_column(column_name)
243
246
  return column_name if Arel::Expressions === column_name
244
247
 
@@ -383,7 +386,7 @@ module ActiveRecord
383
386
  case operation
384
387
  when "count" then value.to_i
385
388
  when "sum" then type.deserialize(value || 0)
386
- when "average" then value.respond_to?(:to_d) ? value.to_d : value
389
+ when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
387
390
  else type.deserialize(value)
388
391
  end
389
392
  end
@@ -319,7 +319,7 @@ module ActiveRecord
319
319
 
320
320
  relation = construct_relation_for_exists(conditions)
321
321
 
322
- skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
322
+ skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists") } ? true : false
323
323
  rescue ::RangeError
324
324
  false
325
325
  end
@@ -359,7 +359,11 @@ module ActiveRecord
359
359
  end
360
360
 
361
361
  def construct_relation_for_exists(conditions)
362
- relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
362
+ if distinct_value && offset_value
363
+ relation = limit(1)
364
+ else
365
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
366
+ end
363
367
 
364
368
  case conditions
365
369
  when Array, Hash
@@ -175,9 +175,7 @@ module ActiveRecord
175
175
  end
176
176
 
177
177
  def merge_clauses
178
- if relation.from_clause.empty? && !other.from_clause.empty?
179
- relation.from_clause = other.from_clause
180
- end
178
+ relation.from_clause = other.from_clause if replace_from_clause?
181
179
 
182
180
  where_clause = relation.where_clause.merge(other.where_clause)
183
181
  relation.where_clause = where_clause unless where_clause.empty?
@@ -185,6 +183,11 @@ module ActiveRecord
185
183
  having_clause = relation.having_clause.merge(other.having_clause)
186
184
  relation.having_clause = having_clause unless having_clause.empty?
187
185
  end
186
+
187
+ def replace_from_clause?
188
+ relation.from_clause.empty? && !other.from_clause.empty? &&
189
+ relation.klass.base_class == other.klass.base_class
190
+ end
188
191
  end
189
192
  end
190
193
  end
@@ -93,16 +93,21 @@ module ActiveRecord
93
93
  queries.reduce(&:or)
94
94
  elsif table.aggregated_with?(key)
95
95
  mapping = table.reflect_on_aggregation(key).mapping
96
- queries = Array.wrap(value).map do |object|
97
- mapping.map do |field_attr, aggregate_attr|
98
- if mapping.size == 1 && !object.respond_to?(aggregate_attr)
99
- build(table.arel_attribute(field_attr), object)
100
- else
101
- build(table.arel_attribute(field_attr), object.send(aggregate_attr))
102
- end
103
- end.reduce(&:and)
96
+ values = value.nil? ? [nil] : Array.wrap(value)
97
+ if mapping.length == 1 || values.empty?
98
+ column_name, aggr_attr = mapping.first
99
+ values = values.map do |object|
100
+ object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
101
+ end
102
+ build(table.arel_attribute(column_name), values)
103
+ else
104
+ queries = values.map do |object|
105
+ mapping.map do |field_attr, aggregate_attr|
106
+ build(table.arel_attribute(field_attr), object.try!(aggregate_attr))
107
+ end.reduce(&:and)
108
+ end
109
+ queries.reduce(&:or)
104
110
  end
105
- queries.reduce(&:or)
106
111
  else
107
112
  build(table.arel_attribute(key), value)
108
113
  end
@@ -18,13 +18,15 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def nil?
21
- !value_before_type_cast.is_a?(StatementCache::Substitute) &&
22
- (value_before_type_cast.nil? || value_for_database.nil?)
21
+ unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
+ value_before_type_cast.nil? ||
23
+ type.respond_to?(:subtype, true) && value_for_database.nil?
24
+ end
23
25
  end
24
26
 
25
27
  def boundable?
26
28
  return @_boundable if defined?(@_boundable)
27
- nil?
29
+ value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
28
30
  @_boundable = true
29
31
  rescue ::RangeError
30
32
  @_boundable = false
@@ -1051,11 +1051,13 @@ module ActiveRecord
1051
1051
 
1052
1052
  def arel_columns(columns)
1053
1053
  columns.flat_map do |field|
1054
- if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
1055
- arel_attribute(field)
1056
- elsif Symbol === field
1057
- connection.quote_table_name(field.to_s)
1058
- elsif Proc === field
1054
+ case field
1055
+ when Symbol
1056
+ field = field.to_s
1057
+ arel_column(field) { connection.quote_table_name(field) }
1058
+ when String
1059
+ arel_column(field) { field }
1060
+ when Proc
1059
1061
  field.call
1060
1062
  else
1061
1063
  field
@@ -1063,6 +1065,21 @@ module ActiveRecord
1063
1065
  end
1064
1066
  end
1065
1067
 
1068
+ def arel_column(field)
1069
+ field = klass.attribute_alias(field) if klass.attribute_alias?(field)
1070
+ from = from_clause.name || from_clause.value
1071
+
1072
+ if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
1073
+ arel_attribute(field)
1074
+ else
1075
+ yield
1076
+ end
1077
+ end
1078
+
1079
+ def table_name_matches?(from)
1080
+ /(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
1081
+ end
1082
+
1066
1083
  def reverse_sql_order(order_query)
1067
1084
  if order_query.empty?
1068
1085
  return [arel_attribute(primary_key).desc] if primary_key
@@ -1144,14 +1161,20 @@ module ActiveRecord
1144
1161
  order_args.map! do |arg|
1145
1162
  case arg
1146
1163
  when Symbol
1147
- arel_attribute(arg).asc
1164
+ arg = arg.to_s
1165
+ arel_column(arg) {
1166
+ Arel.sql(connection.quote_table_name(arg))
1167
+ }.asc
1148
1168
  when Hash
1149
1169
  arg.map { |field, dir|
1150
1170
  case field
1151
1171
  when Arel::Nodes::SqlLiteral
1152
1172
  field.send(dir.downcase)
1153
1173
  else
1154
- arel_attribute(field).send(dir.downcase)
1174
+ field = field.to_s
1175
+ arel_column(field) {
1176
+ Arel.sql(connection.quote_table_name(field))
1177
+ }.send(dir.downcase)
1155
1178
  end
1156
1179
  }
1157
1180
  else
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.2.2
4
+ version: 5.2.3
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-12-04 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.2.2
19
+ version: 5.2.3
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.2.2
26
+ version: 5.2.3
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.2.2
33
+ version: 5.2.3
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.2.2
40
+ version: 5.2.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -307,8 +307,8 @@ homepage: http://rubyonrails.org
307
307
  licenses:
308
308
  - MIT
309
309
  metadata:
310
- source_code_uri: https://github.com/rails/rails/tree/v5.2.2/activerecord
311
- changelog_uri: https://github.com/rails/rails/blob/v5.2.2/activerecord/CHANGELOG.md
310
+ source_code_uri: https://github.com/rails/rails/tree/v5.2.3/activerecord
311
+ changelog_uri: https://github.com/rails/rails/blob/v5.2.3/activerecord/CHANGELOG.md
312
312
  post_install_message:
313
313
  rdoc_options:
314
314
  - "--main"
@@ -326,8 +326,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
326
326
  - !ruby/object:Gem::Version
327
327
  version: '0'
328
328
  requirements: []
329
- rubyforge_project:
330
- rubygems_version: 2.7.6
329
+ rubygems_version: 3.0.1
331
330
  signing_key:
332
331
  specification_version: 4
333
332
  summary: Object-relational mapper framework (part of Rails).