activerecord 5.2.2.1 → 5.2.3.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/lib/active_record/associations/collection_association.rb +4 -5
- data/lib/active_record/associations/collection_proxy.rb +8 -34
- data/lib/active_record/associations/has_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_through_association.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -8
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -24
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/core.rb +2 -1
- data/lib/active_record/errors.rb +18 -12
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration/compatibility.rb +15 -15
- data/lib/active_record/persistence.rb +3 -1
- data/lib/active_record/querying.rb +1 -2
- data/lib/active_record/relation/calculations.rb +11 -8
- data/lib/active_record/relation/finder_methods.rb +6 -2
- data/lib/active_record/relation/merger.rb +6 -3
- data/lib/active_record/relation/predicate_builder.rb +14 -9
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +30 -7
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ca71be00762ccaa95e792a4ee196a15112bfa3e69ab05a9649c3454bd400ede
|
4
|
+
data.tar.gz: f28ceda28e1e45b48f0f35c9bdb0b3050424300843ce36b0072040480d0977c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29db48c061d39784d3c9d8b4f239f7d4895d3e54190ccfb8c5c89f84f7a5259768bec0852a87e08108c6b931c358aa086daaa691e1629f2a0d49e66ceae9e275
|
7
|
+
data.tar.gz: 51118719fb1aefcbb39517ed43b852f1f7406976ef320b328ba3bcde75f8036d296bd725f7f86b16dbd38b7214175791c5574a6241d6350cc2b18eaba962f757
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,40 @@
|
|
1
|
+
## Rails 5.2.3.rc1 (March 21, 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
|
+
|
1
38
|
## Rails 5.2.2.1 (March 11, 2019) ##
|
2
39
|
|
3
40
|
* No changes.
|
@@ -109,9 +109,8 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
# Add +records+ to this association.
|
113
|
-
#
|
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.
|
1037
|
-
#
|
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"
|
@@ -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 =
|
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
|
-
|
24
|
-
|
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
|
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,
|
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,
|
902
|
+
remove_foreign_key(table_name, foreign_key_options)
|
903
903
|
end
|
904
904
|
|
905
905
|
remove_column(table_name, "#{ref_name}_id")
|
@@ -804,15 +804,25 @@ module ActiveRecord
|
|
804
804
|
end
|
805
805
|
|
806
806
|
def mismatched_foreign_key(message)
|
807
|
-
|
808
|
-
|
809
|
-
|
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
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
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:
|
@@ -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
|
-
|
710
|
-
|
711
|
-
sqls
|
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
|
-
|
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)
|
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
|
data/lib/active_record/core.rb
CHANGED
@@ -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
|
|
data/lib/active_record/errors.rb
CHANGED
@@ -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(
|
121
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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.
|
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.
|
@@ -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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
22
|
-
|
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
|
-
|
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
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
connection.quote_table_name(field
|
1058
|
-
|
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
|
-
|
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
|
-
|
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.
|
4
|
+
version: 5.2.3.rc1
|
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: 2019-03-
|
11
|
+
date: 2019-03-22 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.
|
19
|
+
version: 5.2.3.rc1
|
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.
|
26
|
+
version: 5.2.3.rc1
|
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.
|
33
|
+
version: 5.2.3.rc1
|
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.
|
40
|
+
version: 5.2.3.rc1
|
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.
|
311
|
-
changelog_uri: https://github.com/rails/rails/blob/v5.2.
|
310
|
+
source_code_uri: https://github.com/rails/rails/tree/v5.2.3.rc1/activerecord
|
311
|
+
changelog_uri: https://github.com/rails/rails/blob/v5.2.3.rc1/activerecord/CHANGELOG.md
|
312
312
|
post_install_message:
|
313
313
|
rdoc_options:
|
314
314
|
- "--main"
|
@@ -322,9 +322,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
322
322
|
version: 2.2.2
|
323
323
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
324
324
|
requirements:
|
325
|
-
- - "
|
325
|
+
- - ">"
|
326
326
|
- !ruby/object:Gem::Version
|
327
|
-
version:
|
327
|
+
version: 1.3.1
|
328
328
|
requirements: []
|
329
329
|
rubygems_version: 3.0.1
|
330
330
|
signing_key:
|