activerecord 5.1.4 → 5.1.5.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 +5 -5
- data/CHANGELOG.md +151 -0
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/builder/belongs_to.rb +7 -3
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +3 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -9
- data/lib/active_record/associations/preloader/association.rb +11 -34
- data/lib/active_record/associations/preloader/through_association.rb +10 -3
- data/lib/active_record/attribute_methods/dirty.rb +3 -2
- data/lib/active_record/collection_cache_key.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +5 -4
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +28 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +5 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +11 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +3 -4
- data/lib/active_record/migration/compatibility.rb +34 -20
- data/lib/active_record/model_schema.rb +33 -33
- data/lib/active_record/persistence.rb +1 -5
- data/lib/active_record/query_cache.rb +6 -6
- data/lib/active_record/railties/databases.rake +2 -2
- data/lib/active_record/reflection.rb +24 -10
- data/lib/active_record/relation.rb +14 -2
- data/lib/active_record/relation/calculations.rb +16 -11
- data/lib/active_record/relation/finder_methods.rb +1 -11
- data/lib/active_record/relation/query_methods.rb +19 -14
- data/lib/active_record/tasks/database_tasks.rb +11 -10
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6016e93d2aaf0a0b209c596bfd75f89345d4c5e33a254d901bca0c8363e67edc
|
4
|
+
data.tar.gz: 87a1c2845000e04e863351025880efcd8a3a9c432fac308f73370817cce8b3b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78de0a487a64f1f212ee5c89ac80806f4cc21dc44af76855c856aef70525508330f1a177745811a3d6eb658ad4c5be0c2ca66e6808a40cfcb15a2c39fab73791
|
7
|
+
data.tar.gz: faac29bf1b8260798c0a1416ea73b8c45dc0ec738122a8adf68598688fa1e84d94357d3bc6ab892bcebabd39c595a377bb928fab7556bcd9ec9784609aa87a9d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,154 @@
|
|
1
|
+
## Rails 5.1.5.rc1 (February 01, 2018) ##
|
2
|
+
|
3
|
+
* Fix `count(:all)` with eager loading and having an order other than the driving table.
|
4
|
+
|
5
|
+
Fixes #31783.
|
6
|
+
|
7
|
+
*Ryuta Kamizono*
|
8
|
+
|
9
|
+
* Use `count(:all)` in `HasManyAssociation#count_records` to prevent invalid
|
10
|
+
SQL queries for association counting.
|
11
|
+
|
12
|
+
*Klas Eskilson*
|
13
|
+
|
14
|
+
* Fix to invoke callbacks when using `update_attribute`.
|
15
|
+
|
16
|
+
*Mike Busch*
|
17
|
+
|
18
|
+
* Fix `count(:all)` to correctly work `distinct` with custom SELECT list.
|
19
|
+
|
20
|
+
*Ryuta Kamizono*
|
21
|
+
|
22
|
+
* Fix conflicts `counter_cache` with `touch: true` by optimistic locking.
|
23
|
+
|
24
|
+
```
|
25
|
+
# create_table :posts do |t|
|
26
|
+
# t.integer :comments_count, default: 0
|
27
|
+
# t.integer :lock_version
|
28
|
+
# t.timestamps
|
29
|
+
# end
|
30
|
+
class Post < ApplicationRecord
|
31
|
+
end
|
32
|
+
|
33
|
+
# create_table :comments do |t|
|
34
|
+
# t.belongs_to :post
|
35
|
+
# end
|
36
|
+
class Comment < ApplicationRecord
|
37
|
+
belongs_to :post, touch: true, counter_cache: true
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Before:
|
42
|
+
```
|
43
|
+
post = Post.create!
|
44
|
+
# => begin transaction
|
45
|
+
INSERT INTO "posts" ("created_at", "updated_at", "lock_version")
|
46
|
+
VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0)
|
47
|
+
commit transaction
|
48
|
+
|
49
|
+
comment = Comment.create!(post: post)
|
50
|
+
# => begin transaction
|
51
|
+
INSERT INTO "comments" ("post_id") VALUES (1)
|
52
|
+
|
53
|
+
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1,
|
54
|
+
"lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1
|
55
|
+
|
56
|
+
UPDATE "posts" SET "updated_at" = '2017-12-11 21:27:11.398330',
|
57
|
+
"lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0
|
58
|
+
rollback transaction
|
59
|
+
# => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post.
|
60
|
+
|
61
|
+
Comment.take.destroy!
|
62
|
+
# => begin transaction
|
63
|
+
DELETE FROM "comments" WHERE "comments"."id" = 1
|
64
|
+
|
65
|
+
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1,
|
66
|
+
"lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1
|
67
|
+
|
68
|
+
UPDATE "posts" SET "updated_at" = '2017-12-11 21:42:47.785901',
|
69
|
+
"lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0
|
70
|
+
rollback transaction
|
71
|
+
# => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post.
|
72
|
+
```
|
73
|
+
|
74
|
+
After:
|
75
|
+
```
|
76
|
+
post = Post.create!
|
77
|
+
# => begin transaction
|
78
|
+
INSERT INTO "posts" ("created_at", "updated_at", "lock_version")
|
79
|
+
VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0)
|
80
|
+
commit transaction
|
81
|
+
|
82
|
+
comment = Comment.create!(post: post)
|
83
|
+
# => begin transaction
|
84
|
+
INSERT INTO "comments" ("post_id") VALUES (1)
|
85
|
+
|
86
|
+
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1,
|
87
|
+
"lock_version" = COALESCE("lock_version", 0) + 1,
|
88
|
+
"updated_at" = '2017-12-11 21:37:09.802642' WHERE "posts"."id" = 1
|
89
|
+
commit transaction
|
90
|
+
|
91
|
+
comment.destroy!
|
92
|
+
# => begin transaction
|
93
|
+
DELETE FROM "comments" WHERE "comments"."id" = 1
|
94
|
+
|
95
|
+
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1,
|
96
|
+
"lock_version" = COALESCE("lock_version", 0) + 1,
|
97
|
+
"updated_at" = '2017-12-11 21:39:02.685520' WHERE "posts"."id" = 1
|
98
|
+
commit transaction
|
99
|
+
```
|
100
|
+
|
101
|
+
Fixes #31199.
|
102
|
+
|
103
|
+
*bogdanvlviv*
|
104
|
+
|
105
|
+
* Query cache was unavailable when entering the `ActiveRecord::Base.cache` block
|
106
|
+
without being connected.
|
107
|
+
|
108
|
+
*Tsukasa Oishi*
|
109
|
+
|
110
|
+
* Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create wrong
|
111
|
+
ar_internal_metadata's data for a test database.
|
112
|
+
|
113
|
+
Before:
|
114
|
+
```
|
115
|
+
$ RAILS_ENV=test rails dbconsole
|
116
|
+
> SELECT * FROM ar_internal_metadata;
|
117
|
+
key|value|created_at|updated_at
|
118
|
+
environment|development|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679
|
119
|
+
```
|
120
|
+
|
121
|
+
After:
|
122
|
+
```
|
123
|
+
$ RAILS_ENV=test rails dbconsole
|
124
|
+
> SELECT * FROM ar_internal_metadata;
|
125
|
+
key|value|created_at|updated_at
|
126
|
+
environment|test|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679
|
127
|
+
```
|
128
|
+
|
129
|
+
Fixes #26731.
|
130
|
+
|
131
|
+
*bogdanvlviv*
|
132
|
+
|
133
|
+
* Fix longer sequence name detection for serial columns.
|
134
|
+
|
135
|
+
Fixes #28332.
|
136
|
+
|
137
|
+
*Ryuta Kamizono*
|
138
|
+
|
139
|
+
* MySQL: Don't lose `auto_increment: true` in the `db/schema.rb`.
|
140
|
+
|
141
|
+
Fixes #30894.
|
142
|
+
|
143
|
+
*Ryuta Kamizono*
|
144
|
+
|
145
|
+
* Fix `COUNT(DISTINCT ...)` for `GROUP BY` with `ORDER BY` and `LIMIT`.
|
146
|
+
|
147
|
+
Fixes #30886.
|
148
|
+
|
149
|
+
*Ryuta Kamizono*
|
150
|
+
|
151
|
+
|
1
152
|
## Rails 5.1.4 (September 07, 2017) ##
|
2
153
|
|
3
154
|
* No changes.
|
@@ -47,9 +47,9 @@ module ActiveRecord
|
|
47
47
|
def update_counters(by)
|
48
48
|
if require_counter_update? && foreign_key_present?
|
49
49
|
if target && !stale_target?
|
50
|
-
target.increment!(reflection.counter_cache_column, by)
|
50
|
+
target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
|
51
51
|
else
|
52
|
-
klass.update_counters(target_id, reflection.counter_cache_column => by)
|
52
|
+
klass.update_counters(target_id, reflection.counter_cache_column => by, touch: reflection.options[:touch])
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
@@ -114,9 +114,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
114
114
|
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
|
115
115
|
}}
|
116
116
|
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
unless reflection.counter_cache_column
|
118
|
+
model.after_create callback.(:saved_changes), if: :saved_changes?
|
119
|
+
model.after_destroy callback.(:changes_to_save)
|
120
|
+
end
|
121
|
+
|
122
|
+
model.after_update callback.(:saved_changes), if: :saved_changes?
|
123
|
+
model.after_touch callback.(:changes_to_save)
|
120
124
|
end
|
121
125
|
|
122
126
|
def self.add_default_callbacks(model, reflection)
|
@@ -126,7 +126,7 @@ module ActiveRecord
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def aliases
|
129
|
-
Aliases.new join_root.each_with_index.map { |join_part, i|
|
129
|
+
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
130
130
|
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
131
131
|
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
132
132
|
}
|
@@ -134,7 +134,7 @@ module ActiveRecord
|
|
134
134
|
}
|
135
135
|
end
|
136
136
|
|
137
|
-
def instantiate(result_set,
|
137
|
+
def instantiate(result_set, &block)
|
138
138
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
139
139
|
|
140
140
|
seen = Hash.new { |i, object_id|
|
@@ -157,7 +157,7 @@ module ActiveRecord
|
|
157
157
|
message_bus.instrument("instantiation.active_record", payload) do
|
158
158
|
result_set.each { |row_hash|
|
159
159
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
160
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
|
160
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
161
161
|
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
162
162
|
}
|
163
163
|
end
|
@@ -40,15 +40,7 @@ module ActiveRecord
|
|
40
40
|
|
41
41
|
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
42
42
|
|
43
|
-
|
44
|
-
scope_chain_items = reflection.join_scopes(table, predicate_builder)
|
45
|
-
klass_scope = reflection.klass_join_scope(table, predicate_builder)
|
46
|
-
|
47
|
-
scope_chain_items.concat [klass_scope].compact
|
48
|
-
|
49
|
-
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
|
50
|
-
left.merge right
|
51
|
-
end
|
43
|
+
rel = reflection.join_scope(table)
|
52
44
|
|
53
45
|
if rel && !rel.arel.constraints.empty?
|
54
46
|
binds += rel.bound_attributes
|
@@ -90,7 +90,11 @@ module ActiveRecord
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def key_conversion_required?
|
93
|
-
@key_conversion_required
|
93
|
+
unless defined?(@key_conversion_required)
|
94
|
+
@key_conversion_required = (association_key_type != owner_key_type)
|
95
|
+
end
|
96
|
+
|
97
|
+
@key_conversion_required
|
94
98
|
end
|
95
99
|
|
96
100
|
def convert_key(key)
|
@@ -127,42 +131,15 @@ module ActiveRecord
|
|
127
131
|
end
|
128
132
|
|
129
133
|
def build_scope
|
130
|
-
scope = klass.
|
131
|
-
|
132
|
-
values = reflection_scope.values
|
133
|
-
preload_values = preload_scope.values
|
134
|
-
|
135
|
-
scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
|
136
|
-
scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
137
|
-
|
138
|
-
if preload_values[:select] || values[:select]
|
139
|
-
scope._select!(preload_values[:select] || values[:select])
|
140
|
-
end
|
141
|
-
scope.includes! preload_values[:includes] || values[:includes]
|
142
|
-
if preload_scope.joins_values.any?
|
143
|
-
scope.joins!(preload_scope.joins_values)
|
144
|
-
else
|
145
|
-
scope.joins!(reflection_scope.joins_values)
|
146
|
-
end
|
147
|
-
|
148
|
-
if order_values = preload_values[:order] || values[:order]
|
149
|
-
scope.order!(order_values)
|
150
|
-
end
|
151
|
-
|
152
|
-
if preload_values[:reordering] || values[:reordering]
|
153
|
-
scope.reordering_value = true
|
154
|
-
end
|
155
|
-
|
156
|
-
if preload_values[:readonly] || values[:readonly]
|
157
|
-
scope.readonly!
|
158
|
-
end
|
134
|
+
scope = klass.scope_for_association
|
159
135
|
|
160
|
-
if
|
161
|
-
scope.where!(
|
136
|
+
if reflection.type
|
137
|
+
scope.where!(reflection.type => model.base_class.sti_name)
|
162
138
|
end
|
163
139
|
|
164
|
-
scope.
|
165
|
-
|
140
|
+
scope.merge!(reflection_scope)
|
141
|
+
scope.merge!(preload_scope) if preload_scope != NULL_RELATION
|
142
|
+
scope
|
166
143
|
end
|
167
144
|
end
|
168
145
|
end
|
@@ -81,17 +81,24 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
def through_scope
|
83
83
|
scope = through_reflection.klass.unscoped
|
84
|
+
values = reflection_scope.values
|
84
85
|
|
85
86
|
if options[:source_type]
|
86
87
|
scope.where! reflection.foreign_type => options[:source_type]
|
87
88
|
else
|
88
89
|
unless reflection_scope.where_clause.empty?
|
89
|
-
scope.includes_values = Array(
|
90
|
+
scope.includes_values = Array(values[:includes] || options[:source])
|
90
91
|
scope.where_clause = reflection_scope.where_clause
|
92
|
+
if joins = values[:joins]
|
93
|
+
scope.joins!(source_reflection.name => joins)
|
94
|
+
end
|
95
|
+
if left_outer_joins = values[:left_outer_joins]
|
96
|
+
scope.left_outer_joins!(source_reflection.name => left_outer_joins)
|
97
|
+
end
|
91
98
|
end
|
92
99
|
|
93
|
-
scope.references!
|
94
|
-
if scope.eager_loading? && order_values =
|
100
|
+
scope.references! values[:references]
|
101
|
+
if scope.eager_loading? && order_values = values[:order]
|
95
102
|
scope = scope.order(order_values)
|
96
103
|
end
|
97
104
|
end
|
@@ -63,7 +63,7 @@ module ActiveRecord
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def changes_internally_applied # :nodoc:
|
66
|
-
@mutations_before_last_save =
|
66
|
+
@mutations_before_last_save = mutations_from_database
|
67
67
|
forget_attribute_assignments
|
68
68
|
@mutations_from_database = AttributeMutationTracker.new(@attributes)
|
69
69
|
end
|
@@ -71,7 +71,8 @@ module ActiveRecord
|
|
71
71
|
def changes_applied # :nodoc:
|
72
72
|
@previous_mutation_tracker = mutation_tracker
|
73
73
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
74
|
-
|
74
|
+
@mutation_tracker = nil
|
75
|
+
@mutations_from_database = nil
|
75
76
|
end
|
76
77
|
|
77
78
|
def clear_changes_information # :nodoc:
|
@@ -4,8 +4,8 @@ module ActiveRecord
|
|
4
4
|
query_signature = Digest::MD5.hexdigest(collection.to_sql)
|
5
5
|
key = "#{collection.model_name.cache_key}/query-#{query_signature}"
|
6
6
|
|
7
|
-
if collection.loaded?
|
8
|
-
size = collection.size
|
7
|
+
if collection.loaded? || collection.distinct_value
|
8
|
+
size = collection.records.size
|
9
9
|
if size > 0
|
10
10
|
timestamp = collection.max_by(×tamp_column)._read_attribute(timestamp_column)
|
11
11
|
end
|
@@ -4,6 +4,7 @@ require "active_record/connection_adapters/schema_cache"
|
|
4
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
5
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
6
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
7
|
+
require "active_support/concurrency/load_interlock_aware_monitor"
|
7
8
|
require "arel/collectors/bind"
|
8
9
|
require "arel/collectors/sql_string"
|
9
10
|
|
@@ -105,7 +106,7 @@ module ActiveRecord
|
|
105
106
|
@schema_cache = SchemaCache.new self
|
106
107
|
@quoted_column_names, @quoted_table_names = {}, {}
|
107
108
|
@visitor = arel_visitor
|
108
|
-
@lock =
|
109
|
+
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
109
110
|
|
110
111
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
111
112
|
@prepared_statements = true
|
@@ -140,11 +140,11 @@ module ActiveRecord
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
143
|
-
query_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
|
143
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
144
144
|
end
|
145
145
|
|
146
146
|
def release_advisory_lock(lock_name) # :nodoc:
|
147
|
-
query_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
|
147
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
148
148
|
end
|
149
149
|
|
150
150
|
def native_database_types
|
@@ -464,12 +464,13 @@ module ActiveRecord
|
|
464
464
|
fk.constraint_name AS 'name',
|
465
465
|
rc.update_rule AS 'on_update',
|
466
466
|
rc.delete_rule AS 'on_delete'
|
467
|
-
FROM information_schema.
|
468
|
-
JOIN information_schema.
|
467
|
+
FROM information_schema.referential_constraints rc
|
468
|
+
JOIN information_schema.key_column_usage fk
|
469
469
|
USING (constraint_schema, constraint_name)
|
470
470
|
WHERE fk.referenced_column_name IS NOT NULL
|
471
471
|
AND fk.table_schema = #{scope[:schema]}
|
472
472
|
AND fk.table_name = #{scope[:name]}
|
473
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
473
474
|
AND rc.table_name = #{scope[:name]}
|
474
475
|
SQL
|
475
476
|
|