activerecord 5.1.4 → 5.1.5
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/join_association.rb +1 -9
- data/lib/active_record/associations/join_dependency.rb +3 -3
- 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 +1 -1
- 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/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/relation.rb +14 -2
- data/lib/active_record/tasks/database_tasks.rb +11 -10
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c959975b61990368b7c19d60de2e6ac341e8962fe5ec6612f2c7246916343200
|
4
|
+
data.tar.gz: 38b6c9d5f665a16158f3e3a663c8d1935f925d086a1b0bb954f68b94ba7c1fd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 154de32a9ce2ffabd8a5b33d067e206b5be1d3b50c556e361b5018ce91cf534df778537aa46e2c2e7d7e616bb0e0ded3d62ad29b3f133c1565c53e615aff1de7
|
7
|
+
data.tar.gz: 42d3009be5bf1eb2c57fd712d4cfa414560bc3f0dd74d180aacb2d888f907203973f07cdf362d4ebbd1ecbf7855ae08aabe8ae9f49e6d361dd2cb840333e723a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,154 @@
|
|
1
|
+
## Rails 5.1.5 (February 14, 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)
|
@@ -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
|
@@ -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
|
@@ -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
|
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
14
14
|
# +sql_type_metadata+ is various information about the type of the column
|
15
15
|
# +null+ determines if this column allows +NULL+ values.
|
16
|
-
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
|
16
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
|
17
17
|
@name = name.freeze
|
18
18
|
@table_name = table_name
|
19
19
|
@sql_type_metadata = sql_type_metadata
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
5
5
|
def prepare_column_options(column)
|
6
6
|
spec = super
|
7
7
|
spec[:unsigned] = "true" if column.unsigned?
|
8
|
+
spec[:auto_increment] = "true" if column.auto_increment?
|
8
9
|
|
9
10
|
if supports_virtual_columns? && column.virtual?
|
10
11
|
spec[:as] = extract_expression_for_virtual_column(column)
|
@@ -15,6 +16,12 @@ module ActiveRecord
|
|
15
16
|
spec
|
16
17
|
end
|
17
18
|
|
19
|
+
def column_spec_for_primary_key(column)
|
20
|
+
spec = super
|
21
|
+
spec.delete(:auto_increment) if column.type == :integer && column.auto_increment?
|
22
|
+
spec
|
23
|
+
end
|
24
|
+
|
18
25
|
def migration_keys
|
19
26
|
super + [:unsigned]
|
20
27
|
end
|
@@ -5,11 +5,38 @@ module ActiveRecord
|
|
5
5
|
delegate :array, :oid, :fmod, to: :sql_type_metadata
|
6
6
|
alias :array? :array
|
7
7
|
|
8
|
+
def initialize(*, max_identifier_length: 63, **)
|
9
|
+
super
|
10
|
+
@max_identifier_length = max_identifier_length
|
11
|
+
end
|
12
|
+
|
8
13
|
def serial?
|
9
14
|
return unless default_function
|
10
15
|
|
11
|
-
%r{\Anextval\('"
|
16
|
+
if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
|
17
|
+
sequence_name_from_parts(table_name, name, suffix) == sequence_name
|
18
|
+
end
|
12
19
|
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
attr_reader :max_identifier_length
|
23
|
+
|
24
|
+
private
|
25
|
+
def sequence_name_from_parts(table_name, column_name, suffix)
|
26
|
+
over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
|
27
|
+
|
28
|
+
if over_length > 0
|
29
|
+
column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
|
30
|
+
over_length -= column_name.length - column_name_length
|
31
|
+
column_name = column_name[0, column_name_length - [over_length, 0].min]
|
32
|
+
end
|
33
|
+
|
34
|
+
if over_length > 0
|
35
|
+
table_name = table_name[0, table_name.length - over_length]
|
36
|
+
end
|
37
|
+
|
38
|
+
"#{table_name}_#{column_name}_#{suffix}"
|
39
|
+
end
|
13
40
|
end
|
14
41
|
end
|
15
42
|
end
|
@@ -62,7 +62,7 @@ module ActiveRecord
|
|
62
62
|
def quote_default_expression(value, column) # :nodoc:
|
63
63
|
if value.is_a?(Proc)
|
64
64
|
value.call
|
65
|
-
elsif column.type == :uuid && /\(\)/.match?(value)
|
65
|
+
elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
|
66
66
|
value # Does not quote function default values for UUID columns
|
67
67
|
elsif column.respond_to?(:array?)
|
68
68
|
value = type_cast_from_column(column, value)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
|
2
|
-
gem "pg", "
|
2
|
+
gem "pg", ">= 0.18", "< 2.0"
|
3
3
|
require "pg"
|
4
4
|
|
5
5
|
require "active_record/connection_adapters/abstract_adapter"
|
@@ -198,6 +198,7 @@ module ActiveRecord
|
|
198
198
|
|
199
199
|
def dealloc(key)
|
200
200
|
@connection.query "DEALLOCATE #{key}" if connection_active?
|
201
|
+
rescue PG::Error
|
201
202
|
end
|
202
203
|
|
203
204
|
def connection_active?
|
@@ -364,10 +365,11 @@ module ActiveRecord
|
|
364
365
|
end
|
365
366
|
|
366
367
|
# Returns the configured supported identifier length supported by PostgreSQL
|
367
|
-
def
|
368
|
+
def max_identifier_length
|
368
369
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
369
370
|
end
|
370
|
-
alias
|
371
|
+
alias table_alias_length max_identifier_length
|
372
|
+
alias index_name_length max_identifier_length
|
371
373
|
|
372
374
|
# Set the authorized user for this session
|
373
375
|
def session_auth=(user)
|
@@ -340,7 +340,7 @@ module ActiveRecord
|
|
340
340
|
end
|
341
341
|
|
342
342
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
343
|
-
if valid_alter_table_type?(type)
|
343
|
+
if valid_alter_table_type?(type) && !options[:primary_key]
|
344
344
|
super(table_name, column_name, type, options)
|
345
345
|
else
|
346
346
|
alter_table(table_name) do |definition|
|
@@ -440,18 +440,21 @@ module ActiveRecord
|
|
440
440
|
options[:id] = false
|
441
441
|
create_table(to, options) do |definition|
|
442
442
|
@definition = definition
|
443
|
-
|
443
|
+
if from_primary_key.is_a?(Array)
|
444
|
+
@definition.primary_keys from_primary_key
|
445
|
+
end
|
444
446
|
columns(from).each do |column|
|
445
447
|
column_name = options[:rename] ?
|
446
448
|
(options[:rename][column.name] ||
|
447
449
|
options[:rename][column.name.to_sym] ||
|
448
450
|
column.name) : column.name
|
449
|
-
next if column_name == from_primary_key
|
450
451
|
|
451
452
|
@definition.column(column_name, column.type,
|
452
453
|
limit: column.limit, default: column.default,
|
453
454
|
precision: column.precision, scale: column.scale,
|
454
|
-
null: column.null, collation: column.collation
|
455
|
+
null: column.null, collation: column.collation,
|
456
|
+
primary_key: column_name == from_primary_key
|
457
|
+
)
|
455
458
|
end
|
456
459
|
yield @definition if block_given?
|
457
460
|
end
|
@@ -464,6 +467,9 @@ module ActiveRecord
|
|
464
467
|
def copy_table_indexes(from, to, rename = {})
|
465
468
|
indexes(from).each do |index|
|
466
469
|
name = index.name
|
470
|
+
# indexes sqlite creates for internal use start with `sqlite_` and
|
471
|
+
# don't need to be copied
|
472
|
+
next if name.starts_with?("sqlite_")
|
467
473
|
if to == "a#{from}"
|
468
474
|
name = "t#{name}"
|
469
475
|
elsif from == "a#{to}"
|
@@ -479,6 +485,7 @@ module ActiveRecord
|
|
479
485
|
# index name can't be the same
|
480
486
|
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
481
487
|
opts[:unique] = true if index.unique
|
488
|
+
opts[:where] = index.where if index.where
|
482
489
|
add_index(to, columns, opts)
|
483
490
|
end
|
484
491
|
end
|
@@ -60,7 +60,7 @@ module ActiveRecord
|
|
60
60
|
# the locked record.
|
61
61
|
def lock!(lock = true)
|
62
62
|
if persisted?
|
63
|
-
if
|
63
|
+
if has_changes_to_save?
|
64
64
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
65
65
|
Locking a record with unpersisted changes is deprecated and will raise an
|
66
66
|
exception in Rails 5.2. Use `save` to persist the changes, or `reload` to
|