activerecord 6.1.0 → 6.1.2
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 +125 -15
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +4 -4
- data/lib/active_record/association_relation.rb +10 -0
- data/lib/active_record/associations/association.rb +7 -7
- data/lib/active_record/associations/association_scope.rb +7 -5
- data/lib/active_record/associations/belongs_to_association.rb +7 -3
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +23 -2
- data/lib/active_record/associations/builder/belongs_to.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +8 -7
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations.rb +6 -2
- data/lib/active_record/attributes.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -6
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -1
- data/lib/active_record/connection_adapters/pool_config.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_handling.rb +11 -3
- data/lib/active_record/core.rb +42 -24
- data/lib/active_record/database_configurations/url_config.rb +1 -1
- data/lib/active_record/enum.rb +48 -28
- data/lib/active_record/fixtures.rb +4 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +14 -4
- data/lib/active_record/log_subscriber.rb +3 -2
- data/lib/active_record/migration/compatibility.rb +2 -1
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +13 -7
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +9 -5
- data/lib/active_record/relation/predicate_builder.rb +2 -5
- data/lib/active_record/relation/query_methods.rb +8 -5
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/table_metadata.rb +6 -3
- data/lib/active_record/tasks/database_tasks.rb +1 -0
- data/lib/active_record/transactions.rb +4 -2
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4112bd4b4d7a00b26db10332f2f6452d2b6ad1fdf3f2ad50988a8229abac9192
|
4
|
+
data.tar.gz: 6fbcb021ae1942e307730ef7aaad54d1f594cd52b749d407f8aafe052bee6794
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb1830fe0587253ecede381d53dd0be5c8de80070559458653d4a6e149c002a03a3ba37a8723e0e869693500b55ec9956f9f18d2d556884f40d28ac9af0e34f6
|
7
|
+
data.tar.gz: d78aeab68b5c4d4d0e2d8a315b7175a0257b6e1bb6db82c1ae04551f44540d72ffd122881af9cfb93cf8c0d8d57b6310a5bd303e612e24795f54fc07b777f307
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,113 @@
|
|
1
|
+
## Rails 6.1.2 (February 09, 2021) ##
|
2
|
+
|
3
|
+
* Fix timestamp type for sqlite3.
|
4
|
+
|
5
|
+
*Eileen M. Uchitelle*
|
6
|
+
|
7
|
+
* Make destroy async transactional.
|
8
|
+
|
9
|
+
An active record rollback could occur while enqueuing a job. In this
|
10
|
+
case the job would enqueue even though the database deletion
|
11
|
+
rolledback putting things in a funky state.
|
12
|
+
|
13
|
+
Now the jobs are only enqueued until after the db transaction has been committed.
|
14
|
+
|
15
|
+
*Cory Gwin*
|
16
|
+
|
17
|
+
* Fix malformed packet error in MySQL statement for connection configuration.
|
18
|
+
|
19
|
+
*robinroestenburg*
|
20
|
+
|
21
|
+
* Connection specification now passes the "url" key as a configuration for the
|
22
|
+
adapter if the "url" protocol is "jdbc", "http", or "https". Previously only
|
23
|
+
urls with the "jdbc" prefix were passed to the Active Record Adapter, others
|
24
|
+
are assumed to be adapter specification urls.
|
25
|
+
|
26
|
+
Fixes #41137.
|
27
|
+
|
28
|
+
*Jonathan Bracy*
|
29
|
+
|
30
|
+
* Fix granular connection swapping when there are multiple abstract classes.
|
31
|
+
|
32
|
+
*Eileen M. Uchitelle*
|
33
|
+
|
34
|
+
* Fix `find_by` with custom primary key for belongs_to association.
|
35
|
+
|
36
|
+
*Ryuta Kamizono*
|
37
|
+
|
38
|
+
* Add support for `rails console --sandbox` for multiple database applications.
|
39
|
+
|
40
|
+
*alpaca-tc*
|
41
|
+
|
42
|
+
* Fix `where` on polymorphic association with empty array.
|
43
|
+
|
44
|
+
*Ryuta Kamizono*
|
45
|
+
|
46
|
+
* Fix preventing writes for `ApplicationRecord`.
|
47
|
+
|
48
|
+
*Eileen M. Uchitelle*
|
49
|
+
|
50
|
+
|
51
|
+
## Rails 6.1.1 (January 07, 2021) ##
|
52
|
+
|
53
|
+
* Fix fixtures loading when strict loading is enabled for the association.
|
54
|
+
|
55
|
+
*Alex Ghiculescu*
|
56
|
+
|
57
|
+
* Fix `where` with custom primary key for belongs_to association.
|
58
|
+
|
59
|
+
*Ryuta Kamizono*
|
60
|
+
|
61
|
+
* Fix `where` with aliased associations.
|
62
|
+
|
63
|
+
*Ryuta Kamizono*
|
64
|
+
|
65
|
+
* Fix `composed_of` with symbol mapping.
|
66
|
+
|
67
|
+
*Ryuta Kamizono*
|
68
|
+
|
69
|
+
* Don't skip money's type cast for pluck and calculations.
|
70
|
+
|
71
|
+
*Ryuta Kamizono*
|
72
|
+
|
73
|
+
* Fix `where` on polymorphic association with non Active Record object.
|
74
|
+
|
75
|
+
*Ryuta Kamizono*
|
76
|
+
|
77
|
+
* Make sure `db:prepare` works even the schema file doesn't exist.
|
78
|
+
|
79
|
+
*Rafael Mendonça França*
|
80
|
+
|
81
|
+
* Fix complicated `has_many :through` with nested where condition.
|
82
|
+
|
83
|
+
*Ryuta Kamizono*
|
84
|
+
|
85
|
+
* Handle STI models for `has_many dependent: :destroy_async`.
|
86
|
+
|
87
|
+
*Muhammad Usman*
|
88
|
+
|
89
|
+
* Restore possibility of passing `false` to :polymorphic option of `belongs_to`.
|
90
|
+
|
91
|
+
Previously, passing `false` would trigger the option validation logic
|
92
|
+
to throw an error saying :polymorphic would not be a valid option.
|
93
|
+
|
94
|
+
*glaszig*
|
95
|
+
|
96
|
+
* Allow adding nonnamed expression indexes to be revertible.
|
97
|
+
|
98
|
+
Fixes #40732.
|
99
|
+
|
100
|
+
Previously, the following code would raise an error, when executed while rolling back,
|
101
|
+
and the index name should be specified explicitly. Now, the index name is inferred
|
102
|
+
automatically.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
add_index(:items, "to_tsvector('english', description)")
|
106
|
+
```
|
107
|
+
|
108
|
+
*fatkodima*
|
109
|
+
|
110
|
+
|
1
111
|
## Rails 6.1.0 (December 09, 2020) ##
|
2
112
|
|
3
113
|
* Only warn about negative enums if a positive form that would cause conflicts exists.
|
@@ -38,21 +148,21 @@
|
|
38
148
|
|
39
149
|
Before:
|
40
150
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
151
|
+
AnimalsRecord.connected_to(role: :reading) do
|
152
|
+
MealsRecord.connected_to(role: :reading) do
|
153
|
+
Dog.first # read from animals replica
|
154
|
+
Dinner.first # read from meals replica
|
155
|
+
Person.first # read from primary writer
|
156
|
+
end
|
46
157
|
end
|
47
|
-
end
|
48
158
|
|
49
159
|
After:
|
50
160
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
161
|
+
ActiveRecord::Base.connected_to_many([AnimalsRecord, MealsRecord], role: :reading) do
|
162
|
+
Dog.first # read from animals replica
|
163
|
+
Dinner.first # read from meals replica
|
164
|
+
Person.first # read from primary writer
|
165
|
+
end
|
56
166
|
|
57
167
|
*Eileen M. Uchitelle*, *John Crepezzi*
|
58
168
|
|
@@ -170,13 +280,13 @@
|
|
170
280
|
|
171
281
|
Before:
|
172
282
|
|
173
|
-
|
174
|
-
|
283
|
+
User.where.not(name: "Jon", role: "admin")
|
284
|
+
# SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
|
175
285
|
|
176
286
|
After:
|
177
287
|
|
178
|
-
|
179
|
-
|
288
|
+
User.where.not(name: "Jon", role: "admin")
|
289
|
+
# SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
|
180
290
|
|
181
291
|
*Rafael Mendonça França*
|
182
292
|
|
data/README.rdoc
CHANGED
@@ -194,7 +194,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
194
194
|
|
195
195
|
Source code can be downloaded as part of the Rails project on GitHub:
|
196
196
|
|
197
|
-
* https://github.com/rails/rails/tree/
|
197
|
+
* https://github.com/rails/rails/tree/main/activerecord
|
198
198
|
|
199
199
|
|
200
200
|
== License
|
@@ -244,8 +244,8 @@ module ActiveRecord
|
|
244
244
|
private
|
245
245
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
246
246
|
define_method(name) do
|
247
|
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !
|
248
|
-
attrs = mapping.collect { |key, _|
|
247
|
+
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !read_attribute(key).nil? })
|
248
|
+
attrs = mapping.collect { |key, _| read_attribute(key) }
|
249
249
|
object = constructor.respond_to?(:call) ?
|
250
250
|
constructor.call(*attrs) :
|
251
251
|
class_name.constantize.send(constructor, *attrs)
|
@@ -271,10 +271,10 @@ module ActiveRecord
|
|
271
271
|
end
|
272
272
|
|
273
273
|
if part.nil? && allow_nil
|
274
|
-
mapping.each { |key, _|
|
274
|
+
mapping.each { |key, _| write_attribute(key, nil) }
|
275
275
|
@aggregation_cache[name] = nil
|
276
276
|
else
|
277
|
-
mapping.each { |key, value|
|
277
|
+
mapping.each { |key, value| write_attribute(key, part.send(value)) }
|
278
278
|
@aggregation_cache[name] = part.freeze
|
279
279
|
end
|
280
280
|
end
|
@@ -27,6 +27,16 @@ module ActiveRecord
|
|
27
27
|
RUBY
|
28
28
|
end
|
29
29
|
|
30
|
+
def build(attributes = nil, &block)
|
31
|
+
if attributes.is_a?(Array)
|
32
|
+
attributes.collect { |attr| build(attr, &block) }
|
33
|
+
else
|
34
|
+
block = current_scope_restoring_block(&block)
|
35
|
+
scoping { _new(attributes, &block) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias new build
|
39
|
+
|
30
40
|
private
|
31
41
|
def _new(attributes, &block)
|
32
42
|
@association.build(attributes, &block)
|
@@ -211,12 +211,8 @@ module ActiveRecord
|
|
211
211
|
|
212
212
|
private
|
213
213
|
def find_target
|
214
|
-
if owner.strict_loading? && owner.validation_context.nil?
|
215
|
-
Base.strict_loading_violation!(owner: owner.class,
|
216
|
-
end
|
217
|
-
|
218
|
-
if reflection.strict_loading? && owner.validation_context.nil?
|
219
|
-
Base.strict_loading_violation!(owner: owner.class, association: reflection.name)
|
214
|
+
if (owner.strict_loading? || reflection.strict_loading?) && owner.validation_context.nil?
|
215
|
+
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
220
216
|
end
|
221
217
|
|
222
218
|
scope = self.scope
|
@@ -331,7 +327,11 @@ module ActiveRecord
|
|
331
327
|
end
|
332
328
|
|
333
329
|
def enqueue_destroy_association(options)
|
334
|
-
owner.class.destroy_association_async_job
|
330
|
+
job_class = owner.class.destroy_association_async_job
|
331
|
+
|
332
|
+
if job_class
|
333
|
+
owner._after_commit_jobs.push([job_class, options])
|
334
|
+
end
|
335
335
|
end
|
336
336
|
|
337
337
|
def inversable?(record)
|
@@ -131,11 +131,13 @@ module ActiveRecord
|
|
131
131
|
if scope_chain_item == chain_head.scope
|
132
132
|
scope.merge! item.except(:where, :includes, :unscope, :order)
|
133
133
|
elsif !item.references_values.empty?
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
scope.merge! item.only(:joins, :left_outer_joins)
|
135
|
+
|
136
|
+
associations = item.eager_load_values | item.includes_values
|
137
|
+
|
138
|
+
unless associations.empty?
|
139
|
+
scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
140
|
+
end
|
139
141
|
end
|
140
142
|
|
141
143
|
reflection.all_includes do
|
@@ -80,7 +80,7 @@ module ActiveRecord
|
|
80
80
|
@updated = true
|
81
81
|
end
|
82
82
|
|
83
|
-
replace_keys(record)
|
83
|
+
replace_keys(record, force: true)
|
84
84
|
|
85
85
|
self.target = record
|
86
86
|
end
|
@@ -108,8 +108,12 @@ module ActiveRecord
|
|
108
108
|
reflection.counter_cache_column && owner.persisted?
|
109
109
|
end
|
110
110
|
|
111
|
-
def replace_keys(record)
|
112
|
-
|
111
|
+
def replace_keys(record, force: false)
|
112
|
+
target_key = record ? record._read_attribute(primary_key(record.class)) : nil
|
113
|
+
|
114
|
+
if force || owner[reflection.foreign_key] != target_key
|
115
|
+
owner[reflection.foreign_key] = target_key
|
116
|
+
end
|
113
117
|
end
|
114
118
|
|
115
119
|
def primary_key(klass)
|
@@ -14,9 +14,14 @@ module ActiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
-
def replace_keys(record)
|
17
|
+
def replace_keys(record, force: false)
|
18
18
|
super
|
19
|
-
|
19
|
+
|
20
|
+
target_type = record ? record.class.polymorphic_name : nil
|
21
|
+
|
22
|
+
if force || owner[reflection.foreign_type] != target_type
|
23
|
+
owner[reflection.foreign_type] = target_type
|
24
|
+
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def inverse_reflection_for(record)
|
@@ -76,6 +76,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
76
76
|
if dependent = reflection.options[:dependent]
|
77
77
|
check_dependent_options(dependent, model)
|
78
78
|
add_destroy_callbacks(model, reflection)
|
79
|
+
add_after_commit_jobs_callback(model, dependent)
|
79
80
|
end
|
80
81
|
|
81
82
|
Association.extensions.each do |extension|
|
@@ -132,11 +133,31 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
132
133
|
|
133
134
|
def self.add_destroy_callbacks(model, reflection)
|
134
135
|
name = reflection.name
|
135
|
-
model.before_destroy
|
136
|
+
model.before_destroy(->(o) { o.association(name).handle_dependency })
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.add_after_commit_jobs_callback(model, dependent)
|
140
|
+
if dependent == :destroy_async
|
141
|
+
mixin = model.generated_association_methods
|
142
|
+
|
143
|
+
unless mixin.method_defined?(:_after_commit_jobs)
|
144
|
+
model.after_commit(-> do
|
145
|
+
_after_commit_jobs.each do |job_class, job_arguments|
|
146
|
+
job_class.perform_later(**job_arguments)
|
147
|
+
end
|
148
|
+
end)
|
149
|
+
|
150
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
151
|
+
def _after_commit_jobs
|
152
|
+
@_after_commit_jobs ||= []
|
153
|
+
end
|
154
|
+
CODE
|
155
|
+
end
|
156
|
+
end
|
136
157
|
end
|
137
158
|
|
138
159
|
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
139
160
|
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
140
|
-
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
|
161
|
+
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks, :add_after_commit_jobs_callback
|
141
162
|
end
|
142
163
|
end
|
@@ -7,8 +7,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:counter_cache, :optional, :default]
|
11
|
-
valid += [:
|
10
|
+
valid = super + [:polymorphic, :counter_cache, :optional, :default]
|
11
|
+
valid += [:foreign_type] if options[:polymorphic]
|
12
12
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
13
|
valid
|
14
14
|
end
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
enqueue_destroy_association(
|
43
43
|
owner_model_name: owner.class.to_s,
|
44
44
|
owner_id: owner.id,
|
45
|
-
association_class:
|
45
|
+
association_class: reflection.klass.to_s,
|
46
46
|
association_ids: ids,
|
47
47
|
association_primary_key_column: primary_key_column,
|
48
48
|
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
@@ -42,16 +42,17 @@ module ActiveRecord
|
|
42
42
|
chain.reverse_each do |reflection, table|
|
43
43
|
klass = reflection.klass
|
44
44
|
|
45
|
-
|
45
|
+
scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
46
46
|
|
47
|
-
unless
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
unless scope.references_values.empty?
|
48
|
+
associations = scope.eager_load_values | scope.includes_values
|
49
|
+
|
50
|
+
unless associations.empty?
|
51
|
+
scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
52
|
+
end
|
52
53
|
end
|
53
54
|
|
54
|
-
arel =
|
55
|
+
arel = scope.arel(alias_tracker.aliases)
|
55
56
|
nodes = arel.constraints.first
|
56
57
|
|
57
58
|
if nodes.is_a?(Arel::Nodes::And)
|
@@ -195,7 +195,7 @@ module ActiveRecord
|
|
195
195
|
next table, true
|
196
196
|
end
|
197
197
|
|
198
|
-
table_name = @references[reflection.name.to_sym]
|
198
|
+
table_name = @references[reflection.name.to_sym]&.to_s
|
199
199
|
|
200
200
|
table = alias_tracker.aliased_table_for(reflection.klass.arel_table, table_name) do
|
201
201
|
name = reflection.alias_candidate(parent.table_name)
|
@@ -1371,7 +1371,9 @@ module ActiveRecord
|
|
1371
1371
|
#
|
1372
1372
|
# * <tt>nil</tt> do nothing (default).
|
1373
1373
|
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
|
1374
|
-
# * <tt>:destroy_async</tt> destroys all the associated objects in a background job.
|
1374
|
+
# * <tt>:destroy_async</tt> destroys all the associated objects in a background job. <b>WARNING:</b> Do not use
|
1375
|
+
# this option if the association is backed by foreign key constraints in your database. The foreign key
|
1376
|
+
# constraint actions will occur inside the same transaction that deletes its owner.
|
1375
1377
|
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
|
1376
1378
|
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
|
1377
1379
|
# on polymorphic associations. Callbacks are not executed.
|
@@ -1523,7 +1525,9 @@ module ActiveRecord
|
|
1523
1525
|
#
|
1524
1526
|
# * <tt>nil</tt> do nothing (default).
|
1525
1527
|
# * <tt>:destroy</tt> causes the associated object to also be destroyed
|
1526
|
-
# * <tt>:destroy_async</tt> causes
|
1528
|
+
# * <tt>:destroy_async</tt> causes the associated object to be destroyed in a background job. <b>WARNING:</b> Do not use
|
1529
|
+
# this option if the association is backed by foreign key constraints in your database. The foreign key
|
1530
|
+
# constraint actions will occur inside the same transaction that deletes its owner.
|
1527
1531
|
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
|
1528
1532
|
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
|
1529
1533
|
# on polymorphic associations. Callbacks are not executed.
|
@@ -173,7 +173,7 @@ module ActiveRecord
|
|
173
173
|
# class Money < Struct.new(:amount, :currency)
|
174
174
|
# end
|
175
175
|
#
|
176
|
-
# class MoneyType < Type::Value
|
176
|
+
# class MoneyType < ActiveRecord::Type::Value
|
177
177
|
# def initialize(currency_converter:)
|
178
178
|
# @currency_converter = currency_converter
|
179
179
|
# end
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
attr_accessor :schema_cache
|
26
26
|
|
27
|
-
def
|
27
|
+
def connection_klass
|
28
28
|
nil
|
29
29
|
end
|
30
30
|
end
|
@@ -360,7 +360,7 @@ module ActiveRecord
|
|
360
360
|
include ConnectionAdapters::AbstractPool
|
361
361
|
|
362
362
|
attr_accessor :automatic_reconnect, :checkout_timeout
|
363
|
-
attr_reader :db_config, :size, :reaper, :pool_config, :
|
363
|
+
attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
|
364
364
|
|
365
365
|
delegate :schema_cache, :schema_cache=, to: :pool_config
|
366
366
|
|
@@ -375,7 +375,7 @@ module ActiveRecord
|
|
375
375
|
|
376
376
|
@pool_config = pool_config
|
377
377
|
@db_config = pool_config.db_config
|
378
|
-
@
|
378
|
+
@connection_klass = pool_config.connection_klass
|
379
379
|
|
380
380
|
@checkout_timeout = db_config.checkout_timeout
|
381
381
|
@idle_timeout = db_config.idle_timeout
|
@@ -1040,7 +1040,7 @@ module ActiveRecord
|
|
1040
1040
|
end
|
1041
1041
|
alias :connection_pools :connection_pool_list
|
1042
1042
|
|
1043
|
-
def establish_connection(config, owner_name: Base
|
1043
|
+
def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
|
1044
1044
|
owner_name = config.to_s if config.is_a?(Symbol)
|
1045
1045
|
|
1046
1046
|
pool_config = resolve_pool_config(config, owner_name)
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
module QueryCache
|
8
8
|
class << self
|
9
9
|
def included(base) #:nodoc:
|
10
|
-
dirties_query_cache base, :insert, :update, :delete, :truncate, :truncate_tables,
|
10
|
+
dirties_query_cache base, :create, :insert, :update, :delete, :truncate, :truncate_tables,
|
11
11
|
:rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
|
12
12
|
|
13
13
|
base.set_callback :checkout, :after, :configure_query_cache!
|
@@ -1387,8 +1387,14 @@ module ActiveRecord
|
|
1387
1387
|
|
1388
1388
|
checks = []
|
1389
1389
|
|
1390
|
+
if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
|
1391
|
+
options[:name] = index_name(table_name, column_name)
|
1392
|
+
column_names = []
|
1393
|
+
else
|
1394
|
+
column_names = index_column_names(column_name || options[:column])
|
1395
|
+
end
|
1396
|
+
|
1390
1397
|
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1391
|
-
column_names = index_column_names(column_name || options[:column])
|
1392
1398
|
|
1393
1399
|
if column_names.present?
|
1394
1400
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
@@ -111,7 +111,7 @@ module ActiveRecord
|
|
111
111
|
@config.fetch(:use_metadata_table, true)
|
112
112
|
end
|
113
113
|
|
114
|
-
# Determines whether writes are currently being
|
114
|
+
# Determines whether writes are currently being prevented.
|
115
115
|
#
|
116
116
|
# Returns true if the connection is a replica.
|
117
117
|
#
|
@@ -123,10 +123,9 @@ module ActiveRecord
|
|
123
123
|
def preventing_writes?
|
124
124
|
return true if replica?
|
125
125
|
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord::Base.legacy_connection_handling
|
126
|
-
return false if
|
126
|
+
return false if connection_klass.nil?
|
127
127
|
|
128
|
-
|
129
|
-
klass&.current_preventing_writes
|
128
|
+
connection_klass.current_preventing_writes
|
130
129
|
end
|
131
130
|
|
132
131
|
def migrations_paths # :nodoc:
|
@@ -202,8 +201,8 @@ module ActiveRecord
|
|
202
201
|
@owner = Thread.current
|
203
202
|
end
|
204
203
|
|
205
|
-
def
|
206
|
-
@pool.
|
204
|
+
def connection_klass # :nodoc:
|
205
|
+
@pool.connection_klass
|
207
206
|
end
|
208
207
|
|
209
208
|
def schema_cache
|
@@ -751,6 +751,11 @@ module ActiveRecord
|
|
751
751
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
752
752
|
variables["wait_timeout"] = wait_timeout
|
753
753
|
|
754
|
+
# Set the collation of the connection character set.
|
755
|
+
if @config[:collation]
|
756
|
+
variables["collation_connection"] = @config[:collation]
|
757
|
+
end
|
758
|
+
|
754
759
|
defaults = [":default", :default].to_set
|
755
760
|
|
756
761
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
@@ -770,15 +775,6 @@ module ActiveRecord
|
|
770
775
|
end
|
771
776
|
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
772
777
|
|
773
|
-
# NAMES does not have an equals sign, see
|
774
|
-
# https://dev.mysql.com/doc/refman/en/set-names.html
|
775
|
-
# (trailing comma because variable_assignments will always have content)
|
776
|
-
if @config[:encoding]
|
777
|
-
encoding = +"NAMES #{@config[:encoding]}"
|
778
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
779
|
-
encoding << ", "
|
780
|
-
end
|
781
|
-
|
782
778
|
# Gather up all of the SET variables...
|
783
779
|
variable_assignments = variables.map do |k, v|
|
784
780
|
if defaults.include?(v)
|
@@ -790,7 +786,7 @@ module ActiveRecord
|
|
790
786
|
end.compact.join(", ")
|
791
787
|
|
792
788
|
# ...and send them all in one query
|
793
|
-
execute("SET #{
|
789
|
+
execute("SET #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
794
790
|
end
|
795
791
|
|
796
792
|
def column_definitions(table_name) # :nodoc:
|
@@ -79,7 +79,10 @@ module ActiveRecord
|
|
79
79
|
" WHERE table_schema = #{scope[:schema]}" \
|
80
80
|
" AND table_name = #{scope[:name]}" \
|
81
81
|
" AND column_name = #{column_name}"
|
82
|
-
|
82
|
+
# Calling .inspect leads into issues with the query result
|
83
|
+
# which already returns escaped quotes.
|
84
|
+
# We remove the escape sequence from the result in order to deal with double escaping issues.
|
85
|
+
@connection.query_value(sql, "SCHEMA").gsub("\\'", "'").inspect
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class PoolConfig # :nodoc:
|
6
6
|
include Mutex_m
|
7
7
|
|
8
|
-
attr_reader :db_config, :
|
8
|
+
attr_reader :db_config, :connection_klass
|
9
9
|
attr_accessor :schema_cache
|
10
10
|
|
11
11
|
INSTANCES = ObjectSpace::WeakMap.new
|
@@ -17,14 +17,24 @@ module ActiveRecord
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(
|
20
|
+
def initialize(connection_klass, db_config)
|
21
21
|
super()
|
22
|
-
@
|
22
|
+
@connection_klass = connection_klass
|
23
23
|
@db_config = db_config
|
24
24
|
@pool = nil
|
25
25
|
INSTANCES[self] = self
|
26
26
|
end
|
27
27
|
|
28
|
+
def connection_specification_name
|
29
|
+
if connection_klass.is_a?(String)
|
30
|
+
connection_klass
|
31
|
+
elsif connection_klass.primary_class?
|
32
|
+
"ActiveRecord::Base"
|
33
|
+
else
|
34
|
+
connection_klass.name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
28
38
|
def disconnect!
|
29
39
|
ActiveSupport::ForkTracker.check!
|
30
40
|
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
ftype = result.ftype i
|
58
58
|
fmod = result.fmod i
|
59
59
|
case type = get_oid_type(ftype, fmod, fname)
|
60
|
-
when Type::Integer, Type::Float,
|
60
|
+
when Type::Integer, Type::Float, OID::Decimal, Type::String, Type::DateTime, Type::Boolean
|
61
61
|
# skip if a column has already been type casted by pg decoders
|
62
62
|
else types[fname] = type
|
63
63
|
end
|