activerecord 6.0.0 → 6.0.1

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  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/join_dependency.rb +4 -0
  7. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  8. data/lib/active_record/associations/preloader.rb +1 -1
  9. data/lib/active_record/autosave_association.rb +7 -3
  10. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +14 -4
  11. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -0
  12. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  13. data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -5
  14. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +13 -4
  15. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  16. data/lib/active_record/connection_adapters/mysql/database_statements.rb +3 -1
  17. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -1
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -0
  19. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +3 -1
  20. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -0
  21. data/lib/active_record/connection_handling.rb +11 -4
  22. data/lib/active_record/core.rb +8 -4
  23. data/lib/active_record/gem_version.rb +1 -1
  24. data/lib/active_record/insert_all.rb +1 -1
  25. data/lib/active_record/middleware/database_selector/resolver.rb +8 -12
  26. data/lib/active_record/model_schema.rb +3 -0
  27. data/lib/active_record/relation.rb +1 -0
  28. data/lib/active_record/relation/finder_methods.rb +10 -1
  29. data/lib/active_record/relation/query_methods.rb +15 -3
  30. data/lib/active_record/transactions.rb +1 -1
  31. data/lib/arel.rb +12 -5
  32. metadata +12 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed17e948626d108075b2951ac71abbac3075ef55b055abe64e2b352e91755fd9
4
- data.tar.gz: 2c031642e021041f9fdf278888b580fd446879008a23f12d33c8b33aafbddc59
3
+ metadata.gz: 74e749ea0406c6090f479a06ed90ab6d7be8a341b3662329c623d212652ef0d3
4
+ data.tar.gz: 9daac4d5a5f794386f5580724c8a9f27c9a42c3db561d5a37ec314ac5715f0f8
5
5
  SHA512:
6
- metadata.gz: 6b208a5cbee77c2a057649b762a92ec4ca5be3b8fbd1b2123caf485579abbc602bcb2aee455a503b74f33efa9ce364ab644dc8958b9e1edd94fee3397bf17cc8
7
- data.tar.gz: 751ecf72279d478887665f6a3efc5ad366b70284fb3f4ef6d2f3b072faf7c325be660116a7d3ac9427ac44dcf6f8c345e145921692d0465e24a70f1c2ca3b1bd
6
+ metadata.gz: 5ae5468a2a52b707a9f111b1f416d6d3a63ebbfe1658f29f2d64062bbd0cd5408e7b620a4da8cabd8000ea484bbeecd2c70ecb36a94c9a2f91afac32a032ee73
7
+ data.tar.gz: 455c884ff4d115d562aa05df54beda3aea735b6b3fb0e29202280eca45fe9f4fad6d2b34a033fd6175048286fadd3ec90a975b02c54653f5a93fd9ac4a8f2d0b
@@ -1,3 +1,76 @@
1
+ ## Rails 6.0.1 (November 5, 2019) ##
2
+
3
+ * Common Table Expressions are allowed on read-only 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
+
1
74
  ## Rails 6.0.0 (August 16, 2019) ##
2
75
 
3
76
  * Preserve user supplied joins order as much as possible.
@@ -478,7 +551,7 @@
478
551
 
479
552
  *Rafael Mendonça França*
480
553
 
481
- * Deprecate `config.activerecord.sqlite3.represent_boolean_as_integer`.
554
+ * Deprecate `config.active_record.sqlite3.represent_boolean_as_integer`.
482
555
 
483
556
  *Rafael Mendonça França*
484
557
 
@@ -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
@@ -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 && record.changed_for_autosave?
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|
@@ -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
@@ -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"
@@ -11,7 +12,6 @@ require "arel/collectors/bind"
11
12
  require "arel/collectors/composite"
12
13
  require "arel/collectors/sql_string"
13
14
  require "arel/collectors/substitute_binds"
14
- require "concurrent/atomic/thread_local_var"
15
15
 
16
16
  module ActiveRecord
17
17
  module ConnectionAdapters # :nodoc:
@@ -130,10 +130,10 @@ module ActiveRecord
130
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
131
131
 
132
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
133
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
133
+ @prepared_statements = true
134
134
  @visitor.extend(DetermineIfPreparableVisitor)
135
135
  else
136
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
136
+ @prepared_statements = false
137
137
  end
138
138
 
139
139
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
@@ -177,7 +177,11 @@ module ActiveRecord
177
177
  end
178
178
 
179
179
  def prepared_statements
180
- @prepared_statement_status.value
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
181
185
  end
182
186
 
183
187
  class Version
@@ -264,7 +268,10 @@ module ActiveRecord
264
268
  end
265
269
 
266
270
  def unprepared_statement
267
- @prepared_statement_status.bind(false) { yield }
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
272
+ yield
273
+ ensure
274
+ cache&.delete(object_id)
268
275
  end
269
276
 
270
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -409,6 +416,10 @@ module ActiveRecord
409
416
  false
410
417
  end
411
418
 
419
+ def supports_common_table_expressions?
420
+ false
421
+ end
422
+
412
423
  def supports_lazy_transactions?
413
424
  false
414
425
  end
@@ -110,6 +110,14 @@ module ActiveRecord
110
110
  !mariadb? && database_version >= "5.7.7"
111
111
  end
112
112
 
113
+ def supports_common_table_expressions?
114
+ if mariadb?
115
+ database_version >= "10.2.1"
116
+ else
117
+ database_version >= "8.0.1"
118
+ end
119
+ end
120
+
113
121
  def supports_advisory_locks?
114
122
  true
115
123
  end
@@ -440,11 +448,11 @@ module ActiveRecord
440
448
 
441
449
  query_values(<<~SQL, "SCHEMA")
442
450
  SELECT column_name
443
- FROM information_schema.key_column_usage
444
- WHERE constraint_name = 'PRIMARY'
451
+ FROM information_schema.statistics
452
+ WHERE index_name = 'PRIMARY'
445
453
  AND table_schema = #{scope[:schema]}
446
454
  AND table_name = #{scope[:name]}
447
- ORDER BY ordinal_position
455
+ ORDER BY seq_in_index
448
456
  SQL
449
457
  end
450
458
 
@@ -581,6 +589,7 @@ module ActiveRecord
581
589
  end
582
590
 
583
591
  # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
592
+ ER_FILSORT_ABORT = 1028
584
593
  ER_DUP_ENTRY = 1062
585
594
  ER_NOT_NULL_VIOLATION = 1048
586
595
  ER_NO_REFERENCED_ROW = 1216
@@ -622,7 +631,7 @@ module ActiveRecord
622
631
  Deadlocked.new(message, sql: sql, binds: binds)
623
632
  when ER_LOCK_WAIT_TIMEOUT
624
633
  LockWaitTimeout.new(message, sql: sql, binds: binds)
625
- when ER_QUERY_TIMEOUT
634
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
626
635
  StatementTimeout.new(message, sql: sql, binds: binds)
627
636
  when ER_QUERY_INTERRUPTED
628
637
  QueryCanceled.new(message, sql: sql, binds: binds)
@@ -186,7 +186,7 @@ module ActiveRecord
186
186
  adapter_method = "#{spec[:adapter]}_connection"
187
187
 
188
188
  unless ActiveRecord::Base.respond_to?(adapter_method)
189
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
189
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
190
190
  end
191
191
 
192
192
  ConnectionSpecification.new(spec.delete(:name) || "primary", spec, adapter_method)
@@ -19,7 +19,9 @@ module ActiveRecord
19
19
  execute(sql, name).to_a
20
20
  end
21
21
 
22
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
22
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
23
+ :begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :describe, :desc, :with
24
+ ) # :nodoc:
23
25
  private_constant :READ_QUERY
24
26
 
25
27
  def write_query?(sql) # :nodoc:
@@ -67,7 +67,9 @@ module ActiveRecord
67
67
  end
68
68
  end
69
69
 
70
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
70
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
71
+ :begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
72
+ ) # :nodoc:
71
73
  private_constant :READ_QUERY
72
74
 
73
75
  def write_query?(sql) # :nodoc:
@@ -361,6 +361,10 @@ module ActiveRecord
361
361
  @has_pg_hint_plan
362
362
  end
363
363
 
364
+ def supports_common_table_expressions?
365
+ true
366
+ end
367
+
364
368
  def supports_lazy_transactions?
365
369
  true
366
370
  end
@@ -4,7 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  module DatabaseStatements
7
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc:
7
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
8
+ :begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback, :with
9
+ ) # :nodoc:
8
10
  private_constant :READ_QUERY
9
11
 
10
12
  def write_query?(sql) # :nodoc:
@@ -144,6 +144,10 @@ module ActiveRecord
144
144
  true
145
145
  end
146
146
 
147
+ def supports_common_table_expressions?
148
+ database_version >= "3.8.3"
149
+ end
150
+
147
151
  def supports_insert_on_conflict?
148
152
  database_version >= "3.24.0"
149
153
  end
@@ -113,8 +113,9 @@ module ActiveRecord
113
113
  # Dog.run_a_long_query
114
114
  # end
115
115
  #
116
- # When using the database key a new connection will be established every time.
117
- def connected_to(database: nil, role: nil, &blk)
116
+ # When using the database key a new connection will be established every time. It is not
117
+ # recommended to use this outside of one-off scripts.
118
+ def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
118
119
  if database && role
119
120
  raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
120
121
  elsif database
@@ -130,7 +131,13 @@ module ActiveRecord
130
131
 
131
132
  with_handler(role, &blk)
132
133
  elsif role
133
- with_handler(role.to_sym, &blk)
134
+ if role == writing_role
135
+ with_handler(role.to_sym) do
136
+ connection_handler.while_preventing_writes(prevent_writes, &blk)
137
+ end
138
+ else
139
+ with_handler(role.to_sym, &blk)
140
+ end
134
141
  else
135
142
  raise ArgumentError, "must provide a `database` or a `role`."
136
143
  end
@@ -204,7 +211,7 @@ module ActiveRecord
204
211
  # Return the specification name from the current class or its parent.
205
212
  def connection_specification_name
206
213
  if !defined?(@connection_specification_name) || @connection_specification_name.nil?
207
- return primary_class? ? "primary" : superclass.connection_specification_name
214
+ return self == Base ? "primary" : superclass.connection_specification_name
208
215
  end
209
216
  @connection_specification_name
210
217
  end
@@ -586,12 +586,16 @@ module ActiveRecord
586
586
  self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
587
587
  end
588
588
 
589
+ class InspectionMask < DelegateClass(::String)
590
+ def pretty_print(pp)
591
+ pp.text __getobj__
592
+ end
593
+ end
594
+ private_constant :InspectionMask
595
+
589
596
  def inspection_filter
590
597
  @inspection_filter ||= begin
591
- mask = DelegateClass(::String).new(ActiveSupport::ParameterFilter::FILTERED)
592
- def mask.pretty_print(pp)
593
- pp.text __getobj__
594
- end
598
+ mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
595
599
  ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
596
600
  end
597
601
  end
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 6
11
11
  MINOR = 0
12
- TINY = 0
12
+ TINY = 1
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  message = +"#{model} "
25
25
  message << "Bulk " if inserts.many?
26
26
  message << (on_duplicate == :update ? "Upsert" : "Insert")
27
- connection.exec_query to_sql, message
27
+ connection.exec_insert_all to_sql, message
28
28
  end
29
29
 
30
30
  def updatable_columns
@@ -46,11 +46,9 @@ module ActiveRecord
46
46
  private
47
47
 
48
48
  def read_from_primary(&blk)
49
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
50
- ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
51
- instrumenter.instrument("database_selector.active_record.read_from_primary") do
52
- yield
53
- end
49
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
50
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
51
+ yield
54
52
  end
55
53
  end
56
54
  end
@@ -64,13 +62,11 @@ module ActiveRecord
64
62
  end
65
63
 
66
64
  def write_to_primary(&blk)
67
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
68
- ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
69
- instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
70
- yield
71
- ensure
72
- context.update_last_write_timestamp
73
- end
65
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
66
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
67
+ yield
68
+ ensure
69
+ context.update_last_write_timestamp
74
70
  end
75
71
  end
76
72
  end
@@ -480,6 +480,9 @@ module ActiveRecord
480
480
  load_schema!
481
481
 
482
482
  @schema_loaded = true
483
+ rescue
484
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
485
+ raise
483
486
  end
484
487
  end
485
488
 
@@ -629,6 +629,7 @@ module ActiveRecord
629
629
  @to_sql = @arel = @loaded = @should_eager_load = nil
630
630
  @records = [].freeze
631
631
  @offsets = {}
632
+ @take = nil
632
633
  self
633
634
  end
634
635
 
@@ -376,7 +376,16 @@ module ActiveRecord
376
376
  )
377
377
  relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
378
378
 
379
- if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
379
+ if eager_loading && !(
380
+ using_limitable_reflections?(join_dependency.reflections) &&
381
+ using_limitable_reflections?(
382
+ construct_join_dependency(
383
+ select_association_list(joins_values).concat(
384
+ select_association_list(left_outer_joins_values)
385
+ ), nil
386
+ ).reflections
387
+ )
388
+ )
380
389
  if has_limit_or_offset?
381
390
  limited_ids = limited_ids_for(relation)
382
391
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
@@ -1083,15 +1083,23 @@ module ActiveRecord
1083
1083
  end
1084
1084
  end
1085
1085
 
1086
- def valid_association_list(associations)
1086
+ def select_association_list(associations)
1087
+ result = []
1087
1088
  associations.each do |association|
1088
1089
  case association
1089
1090
  when Hash, Symbol, Array
1090
- # valid
1091
+ result << association
1091
1092
  else
1092
- raise ArgumentError, "only Hash, Symbol and Array are allowed"
1093
+ yield if block_given?
1093
1094
  end
1094
1095
  end
1096
+ result
1097
+ end
1098
+
1099
+ def valid_association_list(associations)
1100
+ select_association_list(associations) do
1101
+ raise ArgumentError, "only Hash, Symbol and Array are allowed"
1102
+ end
1095
1103
  end
1096
1104
 
1097
1105
  def build_left_outer_joins(manager, outer_joins, aliases)
@@ -1108,6 +1116,10 @@ module ActiveRecord
1108
1116
  buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
1109
1117
  end
1110
1118
 
1119
+ if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
1120
+ buckets[:stashed_join] << joins.pop if joins.last.base_klass == klass
1121
+ end
1122
+
1111
1123
  joins.map! do |join|
1112
1124
  if join.is_a?(String)
1113
1125
  table.create_string_join(Arel.sql(join.strip)) unless join.blank?
@@ -333,6 +333,7 @@ module ActiveRecord
333
333
  # Ensure that it is not called if the object was never persisted (failed create),
334
334
  # but call it after the commit of a destroyed object.
335
335
  def committed!(should_run_callbacks: true) #:nodoc:
336
+ force_clear_transaction_record_state
336
337
  if should_run_callbacks
337
338
  @_committed_already_called = true
338
339
  _run_commit_without_transaction_enrollment_callbacks
@@ -340,7 +341,6 @@ module ActiveRecord
340
341
  end
341
342
  ensure
342
343
  @_committed_already_called = false
343
- force_clear_transaction_record_state
344
344
  end
345
345
 
346
346
  # Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
@@ -24,22 +24,29 @@ require "arel/update_manager"
24
24
  require "arel/delete_manager"
25
25
  require "arel/nodes"
26
26
 
27
- module Arel # :nodoc: all
27
+ module Arel
28
28
  VERSION = "10.0.0"
29
29
 
30
+ # Wrap a known-safe SQL string for passing to query methods, e.g.
31
+ #
32
+ # Post.order(Arel.sql("length(title)")).last
33
+ #
34
+ # Great caution should be taken to avoid SQL injection vulnerabilities.
35
+ # This method should not be used with unsafe values such as request
36
+ # parameters or model attributes.
30
37
  def self.sql(raw_sql)
31
38
  Arel::Nodes::SqlLiteral.new raw_sql
32
39
  end
33
40
 
34
- def self.star
41
+ def self.star # :nodoc:
35
42
  sql "*"
36
43
  end
37
44
 
38
- def self.arel_node?(value)
45
+ def self.arel_node?(value) # :nodoc:
39
46
  value.is_a?(Arel::Node) || value.is_a?(Arel::Attribute) || value.is_a?(Arel::Nodes::SqlLiteral)
40
47
  end
41
48
 
42
- def self.fetch_attribute(value)
49
+ def self.fetch_attribute(value) # :nodoc:
43
50
  case value
44
51
  when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
45
52
  yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
@@ -47,5 +54,5 @@ module Arel # :nodoc: all
47
54
  end
48
55
 
49
56
  ## Convenience Alias
50
- Node = Arel::Nodes::Node
57
+ Node = Arel::Nodes::Node # :nodoc:
51
58
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-16 00:00:00.000000000 Z
11
+ date: 2019-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0
19
+ version: 6.0.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 6.0.0
26
+ version: 6.0.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 6.0.0
33
+ version: 6.0.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 6.0.0
40
+ version: 6.0.1
41
41
  description: Databases on Rails. Build a persistent domain model by mapping database
42
42
  tables to Ruby classes. Strong conventions for associations, validations, aggregations,
43
43
  migrations, and testing come baked-in.
@@ -389,8 +389,11 @@ homepage: https://rubyonrails.org
389
389
  licenses:
390
390
  - MIT
391
391
  metadata:
392
- source_code_uri: https://github.com/rails/rails/tree/v6.0.0/activerecord
393
- changelog_uri: https://github.com/rails/rails/blob/v6.0.0/activerecord/CHANGELOG.md
392
+ bug_tracker_uri: https://github.com/rails/rails/issues
393
+ changelog_uri: https://github.com/rails/rails/blob/v6.0.1/activerecord/CHANGELOG.md
394
+ documentation_uri: https://api.rubyonrails.org/v6.0.1/
395
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rubyonrails-talk
396
+ source_code_uri: https://github.com/rails/rails/tree/v6.0.1/activerecord
394
397
  post_install_message:
395
398
  rdoc_options:
396
399
  - "--main"
@@ -408,7 +411,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
408
411
  - !ruby/object:Gem::Version
409
412
  version: '0'
410
413
  requirements: []
411
- rubygems_version: 3.0.1
414
+ rubygems_version: 3.0.3
412
415
  signing_key:
413
416
  specification_version: 4
414
417
  summary: Object-relational mapper framework (part of Rails).