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.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -15
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +4 -4
  5. data/lib/active_record/association_relation.rb +10 -0
  6. data/lib/active_record/associations/association.rb +7 -7
  7. data/lib/active_record/associations/association_scope.rb +7 -5
  8. data/lib/active_record/associations/belongs_to_association.rb +7 -3
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +2 -2
  12. data/lib/active_record/associations/has_many_association.rb +1 -1
  13. data/lib/active_record/associations/join_dependency/join_association.rb +8 -7
  14. data/lib/active_record/associations/join_dependency.rb +1 -1
  15. data/lib/active_record/associations.rb +6 -2
  16. data/lib/active_record/attributes.rb +1 -1
  17. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +4 -4
  18. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +7 -1
  21. data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -6
  22. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +6 -10
  23. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -1
  24. data/lib/active_record/connection_adapters/pool_config.rb +13 -3
  25. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
  26. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -3
  27. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  28. data/lib/active_record/connection_handling.rb +11 -3
  29. data/lib/active_record/core.rb +42 -24
  30. data/lib/active_record/database_configurations/url_config.rb +1 -1
  31. data/lib/active_record/enum.rb +48 -28
  32. data/lib/active_record/fixtures.rb +4 -1
  33. data/lib/active_record/gem_version.rb +1 -1
  34. data/lib/active_record/locking/optimistic.rb +14 -4
  35. data/lib/active_record/log_subscriber.rb +3 -2
  36. data/lib/active_record/migration/compatibility.rb +2 -1
  37. data/lib/active_record/railties/console_sandbox.rb +2 -4
  38. data/lib/active_record/railties/databases.rake +13 -7
  39. data/lib/active_record/reflection.rb +1 -1
  40. data/lib/active_record/relation/predicate_builder/association_query_value.rb +3 -3
  41. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +9 -5
  42. data/lib/active_record/relation/predicate_builder.rb +2 -5
  43. data/lib/active_record/relation/query_methods.rb +8 -5
  44. data/lib/active_record/signed_id.rb +1 -1
  45. data/lib/active_record/table_metadata.rb +6 -3
  46. data/lib/active_record/tasks/database_tasks.rb +1 -0
  47. data/lib/active_record/transactions.rb +4 -2
  48. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 418812373f3ff5fd16133fe0928b94b80b78b8a366abfc23a205fcb2ccc5a3e3
4
- data.tar.gz: 70db1902a8fc82b1c46f8a7731ffb358357bc8cac29564a57881a732dbd2a65a
3
+ metadata.gz: 4112bd4b4d7a00b26db10332f2f6452d2b6ad1fdf3f2ad50988a8229abac9192
4
+ data.tar.gz: 6fbcb021ae1942e307730ef7aaad54d1f594cd52b749d407f8aafe052bee6794
5
5
  SHA512:
6
- metadata.gz: 31ae64f629cb62f63bc37c74a888d557f9f0b102d401f2f9cf62c7d31655d99e5161ae9bc7f8be874c7a9cf27ec879dde3c04075df5cb2b5868891c0db144a50
7
- data.tar.gz: cfc9585f3a6f53187707a9d96503269e3d4b1c5f1fe8c5f4db3bdefe8ce636042b6cf1fe9fb233491d1e8197f52293a7018a2f9925f1e34644ad1e30b134d59f
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
- AnimalsRecord.connected_to(role: :reading) do
42
- MealsRecord.connected_to(role: :reading) do
43
- Dog.first # read from animals replica
44
- Dinner.first # read from meals replica
45
- Person.first # read from primary writer
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
- ActiveRecord::Base.connected_to_many([AnimalsRecord, MealsRecord], role: :reading) do
52
- Dog.first # read from animals replica
53
- Dinner.first # read from meals replica
54
- Person.first # read from primary writer
55
- end
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
- User.where.not(name: "Jon", role: "admin")
174
- # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
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
- User.where.not(name: "Jon", role: "admin")
179
- # SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
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/master/activerecord
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, _| !_read_attribute(key).nil? })
248
- attrs = mapping.collect { |key, _| _read_attribute(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, _| self[key] = nil }
274
+ mapping.each { |key, _| write_attribute(key, nil) }
275
275
  @aggregation_cache[name] = nil
276
276
  else
277
- mapping.each { |key, value| self[key] = part.send(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, association: klass)
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&.perform_later(**options)
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
- join_dependency = item.construct_join_dependency(
135
- item.eager_load_values | item.includes_values, Arel::Nodes::OuterJoin
136
- )
137
- scope.joins!(*item.joins_values, join_dependency)
138
- scope.left_outer_joins!(*item.left_outer_joins_values)
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
- owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
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
- owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
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 lambda { |o| o.association(name).handle_dependency }
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 += [:polymorphic, :foreign_type] if options[:polymorphic]
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: association_class.to_s,
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
- join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
45
+ scope = reflection.join_scope(table, foreign_table, foreign_klass)
46
46
 
47
- unless join_scope.references_values.empty?
48
- join_dependency = join_scope.construct_join_dependency(
49
- join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
50
- )
51
- join_scope.joins!(join_dependency)
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 = join_scope.arel(alias_tracker.aliases)
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 all the associated object to be destroyed in a background job.
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 owner_name
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, :owner_name
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
- @owner_name = pool_config.connection_specification_name
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.name, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
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!
@@ -71,6 +71,10 @@ module ActiveRecord
71
71
  end
72
72
  CODE
73
73
  end
74
+
75
+ def aliased_types(name, fallback)
76
+ "timestamp" == name ? :datetime : fallback
77
+ end
74
78
  end
75
79
 
76
80
  AddColumnDefinition = Struct.new(:column) # :nodoc:
@@ -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 prevents.
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 owner_name.nil?
126
+ return false if connection_klass.nil?
127
127
 
128
- klass = self.owner_name.safe_constantize
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 owner_name # :nodoc:
206
- @pool.owner_name
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 #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
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
- @connection.query_value(sql, "SCHEMA").inspect
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, :connection_specification_name
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(connection_specification_name, db_config)
20
+ def initialize(connection_klass, db_config)
21
21
  super()
22
- @connection_specification_name = connection_specification_name
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, Type::Decimal, Type::String, Type::DateTime, Type::Boolean
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