activerecord 6.0.0.rc2 → 6.0.1.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.

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -3
  3. data/lib/active_record/association_relation.rb +15 -6
  4. data/lib/active_record/associations.rb +1 -1
  5. data/lib/active_record/associations/association.rb +9 -1
  6. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  7. data/lib/active_record/associations/join_dependency.rb +4 -0
  8. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  9. data/lib/active_record/associations/preloader.rb +1 -1
  10. data/lib/active_record/autosave_association.rb +7 -3
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +25 -10
  12. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -0
  13. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  14. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
  15. data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -3
  16. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +13 -4
  17. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  18. data/lib/active_record/connection_adapters/mysql/database_statements.rb +3 -1
  19. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -2
  20. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -1
  21. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  22. data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -0
  23. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +3 -1
  24. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -0
  25. data/lib/active_record/connection_handling.rb +11 -4
  26. data/lib/active_record/core.rb +8 -4
  27. data/lib/active_record/database_configurations.rb +60 -31
  28. data/lib/active_record/enum.rb +9 -0
  29. data/lib/active_record/fixtures.rb +11 -6
  30. data/lib/active_record/gem_version.rb +2 -2
  31. data/lib/active_record/insert_all.rb +2 -3
  32. data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
  33. data/lib/active_record/migration.rb +13 -5
  34. data/lib/active_record/model_schema.rb +3 -0
  35. data/lib/active_record/railties/databases.rake +6 -3
  36. data/lib/active_record/relation.rb +1 -0
  37. data/lib/active_record/relation/calculations.rb +1 -3
  38. data/lib/active_record/relation/finder_methods.rb +10 -1
  39. data/lib/active_record/relation/query_methods.rb +47 -21
  40. data/lib/active_record/tasks/database_tasks.rb +35 -0
  41. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  42. data/lib/active_record/test_databases.rb +1 -16
  43. data/lib/active_record/transactions.rb +1 -1
  44. data/lib/active_record/validations.rb +1 -0
  45. data/lib/arel.rb +12 -5
  46. metadata +12 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97bc72165e9eb1685576780f439be5be8894b7c589cc890d8c6340b8125de0a4
4
- data.tar.gz: 74d58cd2f64976ef8faa42a02e94e6d9c9356fce3f0f2c219f7377a09712e7a4
3
+ metadata.gz: be3ff760985818a549e4ba996b816dc1c370e35e60ce2c608a30c5c8f6c91f7b
4
+ data.tar.gz: a5748dd47536b784c658a9af54dfe44dd6065e2f0ad9be44b64f26f44ab97255
5
5
  SHA512:
6
- metadata.gz: 5306d17e736a64c87fc0115299b055c58343df4d18a53d532b09e570d6cc799abf84b4149deef9c03f56563ed23ee563cf57d03fc6c65529e9a6aa0324ca1614
7
- data.tar.gz: 6fe8094ad7c0d0433c23691e372417124bddf17659c4bcb64b0bca5af0513d2c9b8599cfe0c244d28649529ab64b99c4a74b638bdf5ab9b4932c276a287d1e78
6
+ metadata.gz: ae9991aa99daa69c7a493ca957ec586f5def10a74b768225ee2fcbd6d20136d16b87d5ef88de7f4a9cef15f7e67df860530965f1b43f080741ee22ca94d89879
7
+ data.tar.gz: '098af13801ae75f56682d5aabbcc5318dbdbaada30512850e9b0ab4db15b6dd7168fb2ad48b994af04d2554195a062a84ac0caf17ffd1c31bc622a8a30e48884'
@@ -1,3 +1,101 @@
1
+ ## Rails 6.0.1.rc1 (October 31, 2019) ##
2
+
3
+ * Common Table Expressions are allowed on read-only Postgresql connections.
4
+
5
+ *Chris Morris*
6
+
7
+ * New record instantiation respects `unscope`.
8
+
9
+ *Ryuta Kamizono*
10
+
11
+ * Fixed a case where `find_in_batches` could halt too early.
12
+
13
+ *Takayuki Nakata*
14
+
15
+ * Autosaved associations always perform validations when a custom validation
16
+ context is used.
17
+
18
+ *Tekin Suleyman*
19
+
20
+ * `sql.active_record` notifications now include the `:connection` in
21
+ their payloads.
22
+
23
+ *Eugene Kenny*
24
+
25
+ * A rollback encountered in an `after_commit` callback does not reset
26
+ previously-committed record state.
27
+
28
+ *Ryuta Kamizono*
29
+
30
+ * Fixed that join order was lost when eager-loading.
31
+
32
+ *Ryuta Kamizono*
33
+
34
+ * `DESCRIBE` queries are allowed on read-only connections.
35
+
36
+ *Dylan Thacker-Smith*
37
+
38
+ * Fixed that records that had been `inspect`ed could not be marshaled.
39
+
40
+ *Eugene Kenny*
41
+
42
+ * The connection pool reaper thread is respawned in forked processes. This
43
+ fixes that idle connections in forked processes wouldn't be reaped.
44
+
45
+ *John Hawthorn*
46
+
47
+ * The memoized result of `ActiveRecord::Relation#take` is properly cleared
48
+ when `ActiveRecord::Relation#reset` or `ActiveRecord::Relation#reload`
49
+ is called.
50
+
51
+ *Anmol Arora*
52
+
53
+ * Fixed the performance regression for `primary_keys` introduced MySQL 8.0.
54
+
55
+ *Hiroyuki Ishii*
56
+
57
+ * `insert`, `insert_all`, `upsert`, and `upsert_all` now clear the query cache.
58
+
59
+ *Eugene Kenny*
60
+
61
+ * Call `while_preventing_writes` directly from `connected_to`.
62
+
63
+ In some cases application authors want to use the database switching middleware and make explicit calls with `connected_to`. It's possible for an app to turn off writes and not turn them back on by the time we call `connected_to(role: :writing)`.
64
+
65
+ This change allows apps to fix this by assuming if a role is writing we want to allow writes, except in the case it's explicitly turned off.
66
+
67
+ *Eileen M. Uchitelle*
68
+
69
+ * Improve detection of ActiveRecord::StatementTimeout with mysql2 adapter in the edge case when the query is terminated during filesort.
70
+
71
+ *Kir Shatrov*
72
+
73
+
74
+ ## Rails 6.0.0 (August 16, 2019) ##
75
+
76
+ * Preserve user supplied joins order as much as possible.
77
+
78
+ Fixes #36761, #34328, #24281, #12953.
79
+
80
+ *Ryuta Kamizono*
81
+
82
+ * Make the DATABASE_URL env variable only affect the primary connection. Add new env variables for multiple databases.
83
+
84
+ *John Crepezzi*, *Eileen Uchitelle*
85
+
86
+ * Add a warning for enum elements with 'not_' prefix.
87
+
88
+ class Foo
89
+ enum status: [:sent, :not_sent]
90
+ end
91
+
92
+ *Edu Depetris*
93
+
94
+ * Make currency symbols optional for money column type in PostgreSQL
95
+
96
+ *Joel Schneider*
97
+
98
+
1
99
  ## Rails 6.0.0.rc2 (July 22, 2019) ##
2
100
 
3
101
  * Add database_exists? method to connection adapters to check if a database exists.
@@ -386,7 +484,7 @@
386
484
  `GET` and `HEAD` requests will read from the replica unless there was
387
485
  a write in the last 2 seconds, otherwise they will read from the primary.
388
486
  Non-get requests will always write to the primary. The middleware accepts
389
- an argument for a Resolver class and a Operations class where you are able
487
+ an argument for a Resolver class and an Operations class where you are able
390
488
  to change how the auto-switcher works to be most beneficial for your
391
489
  application.
392
490
 
@@ -453,7 +551,7 @@
453
551
 
454
552
  *Rafael Mendonça França*
455
553
 
456
- * Deprecate `config.activerecord.sqlite3.represent_boolean_as_integer`.
554
+ * Deprecate `config.active_record.sqlite3.represent_boolean_as_integer`.
457
555
 
458
556
  *Rafael Mendonça França*
459
557
 
@@ -820,7 +918,7 @@
820
918
  *Darwin Wu*
821
919
 
822
920
  * Configuration item `config.filter_parameters` could also filter out
823
- sensitive values of database columns when call `#inspect`.
921
+ sensitive values of database columns when calling `#inspect`.
824
922
  We also added `ActiveRecord::Base::filter_attributes`/`=` in order to
825
923
  specify sensitive attributes to specific model.
826
924
 
@@ -15,17 +15,26 @@ module ActiveRecord
15
15
  other == records
16
16
  end
17
17
 
18
- def build(*args, &block)
19
- scoping { @association.build(*args, &block) }
18
+ def build(attributes = nil, &block)
19
+ block = _deprecated_scope_block("new", &block)
20
+ @association.scoping(self) do
21
+ @association.build(attributes, &block)
22
+ end
20
23
  end
21
24
  alias new build
22
25
 
23
- def create(*args, &block)
24
- scoping { @association.create(*args, &block) }
26
+ def create(attributes = nil, &block)
27
+ block = _deprecated_scope_block("create", &block)
28
+ @association.scoping(self) do
29
+ @association.create(attributes, &block)
30
+ end
25
31
  end
26
32
 
27
- def create!(*args, &block)
28
- scoping { @association.create!(*args, &block) }
33
+ def create!(attributes = nil, &block)
34
+ block = _deprecated_scope_block("create!", &block)
35
+ @association.scoping(self) do
36
+ @association.create!(attributes, &block)
37
+ end
29
38
  end
30
39
 
31
40
  private
@@ -92,7 +92,7 @@ module ActiveRecord
92
92
  through_reflection = reflection.through_reflection
93
93
  source_reflection_names = reflection.source_reflection_names
94
94
  source_associations = reflection.through_reflection.klass._reflections.keys
95
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ', locale: :en)}?")
95
+ super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
96
96
  else
97
97
  super("Could not find the source association(s).")
98
98
  end
@@ -43,6 +43,7 @@ module ActiveRecord
43
43
  reflection.check_validity!
44
44
 
45
45
  @owner, @reflection = owner, reflection
46
+ @_scope = nil
46
47
 
47
48
  reset
48
49
  reset_scope
@@ -95,7 +96,7 @@ module ActiveRecord
95
96
  end
96
97
 
97
98
  def scope
98
- target_scope.merge!(association_scope)
99
+ @_scope&.spawn || target_scope.merge!(association_scope)
99
100
  end
100
101
 
101
102
  def reset_scope
@@ -195,6 +196,13 @@ module ActiveRecord
195
196
  _create_record(attributes, true, &block)
196
197
  end
197
198
 
199
+ def scoping(relation, &block)
200
+ @_scope = relation
201
+ relation.scoping(&block)
202
+ ensure
203
+ @_scope = nil
204
+ end
205
+
198
206
  private
199
207
  def find_target
200
208
  scope = self.scope
@@ -63,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
63
63
 
64
64
  def middle_reflection(join_model)
65
65
  middle_name = [lhs_model.name.downcase.pluralize,
66
- association_name].join("_").gsub("::", "_").to_sym
66
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
67
67
  middle_options = middle_options join_model
68
68
 
69
69
  HasMany.create_reflection(lhs_model,
@@ -70,6 +70,10 @@ module ActiveRecord
70
70
  @join_type = join_type
71
71
  end
72
72
 
73
+ def base_klass
74
+ join_root.base_klass
75
+ end
76
+
73
77
  def reflections
74
78
  join_root.drop(1).map!(&:reflection)
75
79
  end
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  nodes = arel.constraints.first
38
38
 
39
39
  others = nodes.children.extract! do |node|
40
- Arel.fetch_attribute(node) { |attr| attr.relation.name != table.name }
40
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
41
41
  end
42
42
 
43
43
  joins << table.create_join(table, table.create_on(nodes), join_type)
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  association.flat_map { |parent, child|
113
113
  grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
114
  loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
- recs = loaders.flat_map(&:preloaded_records)
115
+ recs = loaders.flat_map(&:preloaded_records).uniq
116
116
  child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
117
  loaders.concat Array.wrap(child).flat_map { |assoc|
118
118
  preloaders_on assoc, recs, scope, child_polymorphic_parent
@@ -272,7 +272,7 @@ module ActiveRecord
272
272
  # or saved. If +autosave+ is +false+ only new records will be returned,
273
273
  # unless the parent is/was a new record itself.
274
274
  def associated_records_to_validate_or_save(association, new_record, autosave)
275
- if new_record
275
+ if new_record || custom_validation_context?
276
276
  association && association.target
277
277
  elsif autosave
278
278
  association.target.find_all(&:changed_for_autosave?)
@@ -304,7 +304,7 @@ module ActiveRecord
304
304
  def validate_single_association(reflection)
305
305
  association = association_instance_get(reflection.name)
306
306
  record = association && association.reader
307
- association_valid?(reflection, record) if record
307
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
308
308
  end
309
309
 
310
310
  # Validate the associated records if <tt>:validate</tt> or
@@ -324,7 +324,7 @@ module ActiveRecord
324
324
  def association_valid?(reflection, record, index = nil)
325
325
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
326
326
 
327
- context = validation_context unless [:create, :update].include?(validation_context)
327
+ context = validation_context if custom_validation_context?
328
328
 
329
329
  unless valid = record.valid?(context)
330
330
  if reflection.options[:autosave]
@@ -499,6 +499,10 @@ module ActiveRecord
499
499
  end
500
500
  end
501
501
 
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
505
+
502
506
  def _ensure_no_duplicate_errors
503
507
  errors.messages.each_key do |attribute|
504
508
  errors[attribute].uniq!
@@ -317,14 +317,15 @@ module ActiveRecord
317
317
 
318
318
  @mutex = Mutex.new
319
319
  @pools = {}
320
+ @threads = {}
320
321
 
321
322
  class << self
322
323
  def register_pool(pool, frequency) # :nodoc:
323
324
  @mutex.synchronize do
324
- unless @pools.key?(frequency)
325
- @pools[frequency] = []
326
- spawn_thread(frequency)
325
+ unless @threads[frequency]&.alive?
326
+ @threads[frequency] = spawn_thread(frequency)
327
327
  end
328
+ @pools[frequency] ||= []
328
329
  @pools[frequency] << WeakRef.new(pool)
329
330
  end
330
331
  end
@@ -333,7 +334,8 @@ module ActiveRecord
333
334
 
334
335
  def spawn_thread(frequency)
335
336
  Thread.new(frequency) do |t|
336
- loop do
337
+ running = true
338
+ while running
337
339
  sleep t
338
340
  @mutex.synchronize do
339
341
  @pools[frequency].select!(&:weakref_alive?)
@@ -342,6 +344,12 @@ module ActiveRecord
342
344
  p.flush
343
345
  rescue WeakRef::RefError
344
346
  end
347
+
348
+ if @pools[frequency].empty?
349
+ @pools.delete(frequency)
350
+ @threads.delete(frequency)
351
+ running = false
352
+ end
345
353
  end
346
354
  end
347
355
  end
@@ -642,6 +650,7 @@ module ActiveRecord
642
650
  # or a thread dies unexpectedly.
643
651
  def reap
644
652
  stale_connections = synchronize do
653
+ return unless @connections
645
654
  @connections.select do |conn|
646
655
  conn.in_use? && !conn.owner.alive?
647
656
  end.each do |conn|
@@ -666,6 +675,7 @@ module ActiveRecord
666
675
  return if minimum_idle.nil?
667
676
 
668
677
  idle_connections = synchronize do
678
+ return unless @connections
669
679
  @connections.select do |conn|
670
680
  !conn.in_use? && conn.seconds_idle >= minimum_idle
671
681
  end.each do |conn|
@@ -1005,28 +1015,33 @@ module ActiveRecord
1005
1015
  end
1006
1016
  end
1007
1017
 
1008
- attr_reader :prevent_writes
1009
-
1010
1018
  def initialize
1011
1019
  # These caches are keyed by spec.name (ConnectionSpecification#name).
1012
1020
  @owner_to_pool = ConnectionHandler.create_owner_to_pool
1013
- @prevent_writes = false
1014
1021
 
1015
1022
  # Backup finalizer: if the forked child never needed a pool, the above
1016
1023
  # early discard has not occurred
1017
1024
  ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
1018
1025
  end
1019
1026
 
1027
+ def prevent_writes # :nodoc:
1028
+ Thread.current[:prevent_writes]
1029
+ end
1030
+
1031
+ def prevent_writes=(prevent_writes) # :nodoc:
1032
+ Thread.current[:prevent_writes] = prevent_writes
1033
+ end
1034
+
1020
1035
  # Prevent writing to the database regardless of role.
1021
1036
  #
1022
1037
  # In some cases you may want to prevent writes to the database
1023
1038
  # even if you are on a database that can write. `while_preventing_writes`
1024
1039
  # will prevent writes to the database for the duration of the block.
1025
- def while_preventing_writes
1026
- original, @prevent_writes = @prevent_writes, true
1040
+ def while_preventing_writes(enabled = true)
1041
+ original, self.prevent_writes = self.prevent_writes, enabled
1027
1042
  yield
1028
1043
  ensure
1029
- @prevent_writes = original
1044
+ self.prevent_writes = original
1030
1045
  end
1031
1046
 
1032
1047
  def connection_pool_list
@@ -149,6 +149,10 @@ module ActiveRecord
149
149
  exec_query(sql, name, binds)
150
150
  end
151
151
 
152
+ def exec_insert_all(sql, name) # :nodoc:
153
+ exec_query(sql, name)
154
+ end
155
+
152
156
  # Executes an INSERT query and returns the new record's ID
153
157
  #
154
158
  # +id_value+ will be returned unless the value is +nil+, in
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  class << self
9
9
  def included(base) #:nodoc:
10
10
  dirties_query_cache base, :insert, :update, :delete, :truncate, :truncate_tables,
11
- :rollback_to_savepoint, :rollback_db_transaction
11
+ :rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
12
12
 
13
13
  base.set_callback :checkout, :after, :configure_query_cache!
14
14
  base.set_callback :checkin, :after, :disable_query_cache!
@@ -135,6 +135,7 @@ module ActiveRecord
135
135
  type_casted_binds: -> { type_casted_binds(binds) },
136
136
  name: name,
137
137
  connection_id: object_id,
138
+ connection: self,
138
139
  cached: true
139
140
  }
140
141
  end
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  def index_exists?(table_name, column_name, options = {})
102
102
  column_names = Array(column_name).map(&:to_s)
103
103
  checks = []
104
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
105
105
  checks << lambda { |i| i.unique } if options[:unique]
106
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
107
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
3
4
  require "active_record/connection_adapters/determine_if_preparable_visitor"
4
5
  require "active_record/connection_adapters/schema_cache"
5
6
  require "active_record/connection_adapters/sql_type_metadata"
@@ -78,7 +79,7 @@ module ActiveRecord
78
79
  SIMPLE_INT = /\A\d+\z/
79
80
 
80
81
  attr_accessor :pool
81
- attr_reader :visitor, :owner, :logger, :lock, :prepared_statements
82
+ attr_reader :visitor, :owner, :logger, :lock
82
83
  alias :in_use? :owner
83
84
 
84
85
  set_callback :checkin, :after, :enable_lazy_transactions!
@@ -175,6 +176,14 @@ module ActiveRecord
175
176
  end
176
177
  end
177
178
 
179
+ def prepared_statements
180
+ @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
181
+ end
182
+
183
+ def prepared_statements_disabled_cache # :nodoc:
184
+ Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
185
+ end
186
+
178
187
  class Version
179
188
  include Comparable
180
189
 
@@ -259,10 +268,10 @@ module ActiveRecord
259
268
  end
260
269
 
261
270
  def unprepared_statement
262
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
263
272
  yield
264
273
  ensure
265
- @prepared_statements = old_prepared_statements
274
+ cache&.delete(object_id)
266
275
  end
267
276
 
268
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -407,6 +416,10 @@ module ActiveRecord
407
416
  false
408
417
  end
409
418
 
419
+ def supports_common_table_expressions?
420
+ false
421
+ end
422
+
410
423
  def supports_lazy_transactions?
411
424
  false
412
425
  end