activerecord 6.0.0.beta3 → 6.0.2.rc2

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +466 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +10 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +6 -2
  16. data/lib/active_record/associations/collection_proxy.rb +2 -2
  17. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  18. data/lib/active_record/associations/join_dependency.rb +14 -9
  19. data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
  20. data/lib/active_record/associations/preloader.rb +13 -8
  21. data/lib/active_record/associations/preloader/association.rb +34 -30
  22. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  23. data/lib/active_record/attribute_methods.rb +3 -53
  24. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  25. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  26. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  27. data/lib/active_record/attribute_methods/query.rb +2 -3
  28. data/lib/active_record/attribute_methods/read.rb +3 -9
  29. data/lib/active_record/attribute_methods/write.rb +6 -12
  30. data/lib/active_record/attributes.rb +13 -0
  31. data/lib/active_record/autosave_association.rb +21 -7
  32. data/lib/active_record/base.rb +0 -1
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  39. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
  41. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
  42. data/lib/active_record/connection_adapters/abstract_adapter.rb +111 -33
  43. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
  44. data/lib/active_record/connection_adapters/column.rb +17 -13
  45. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  46. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
  47. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  48. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  49. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
  50. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  51. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  52. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  53. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  54. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  55. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  56. data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
  57. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  58. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  59. data/lib/active_record/connection_adapters/postgresql_adapter.rb +67 -26
  60. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  61. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  62. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  63. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  64. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  65. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
  66. data/lib/active_record/connection_handling.rb +31 -13
  67. data/lib/active_record/core.rb +23 -24
  68. data/lib/active_record/database_configurations.rb +73 -44
  69. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  70. data/lib/active_record/database_configurations/url_config.rb +12 -12
  71. data/lib/active_record/dynamic_matchers.rb +1 -1
  72. data/lib/active_record/enum.rb +15 -0
  73. data/lib/active_record/errors.rb +1 -1
  74. data/lib/active_record/fixtures.rb +11 -6
  75. data/lib/active_record/gem_version.rb +2 -2
  76. data/lib/active_record/insert_all.rb +179 -0
  77. data/lib/active_record/integration.rb +13 -1
  78. data/lib/active_record/internal_metadata.rb +5 -1
  79. data/lib/active_record/locking/optimistic.rb +3 -4
  80. data/lib/active_record/log_subscriber.rb +1 -1
  81. data/lib/active_record/middleware/database_selector.rb +3 -3
  82. data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
  83. data/lib/active_record/migration.rb +62 -44
  84. data/lib/active_record/migration/command_recorder.rb +28 -14
  85. data/lib/active_record/migration/compatibility.rb +10 -0
  86. data/lib/active_record/model_schema.rb +3 -0
  87. data/lib/active_record/persistence.rb +206 -13
  88. data/lib/active_record/querying.rb +17 -12
  89. data/lib/active_record/railtie.rb +0 -1
  90. data/lib/active_record/railties/databases.rake +127 -25
  91. data/lib/active_record/reflection.rb +3 -3
  92. data/lib/active_record/relation.rb +99 -20
  93. data/lib/active_record/relation/calculations.rb +38 -40
  94. data/lib/active_record/relation/delegation.rb +22 -30
  95. data/lib/active_record/relation/finder_methods.rb +17 -12
  96. data/lib/active_record/relation/merger.rb +11 -16
  97. data/lib/active_record/relation/query_methods.rb +228 -76
  98. data/lib/active_record/relation/where_clause.rb +9 -5
  99. data/lib/active_record/sanitization.rb +33 -4
  100. data/lib/active_record/schema.rb +1 -1
  101. data/lib/active_record/schema_dumper.rb +10 -1
  102. data/lib/active_record/schema_migration.rb +1 -1
  103. data/lib/active_record/scoping/default.rb +6 -7
  104. data/lib/active_record/scoping/named.rb +3 -2
  105. data/lib/active_record/statement_cache.rb +2 -2
  106. data/lib/active_record/store.rb +48 -0
  107. data/lib/active_record/table_metadata.rb +9 -13
  108. data/lib/active_record/tasks/database_tasks.rb +109 -6
  109. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  110. data/lib/active_record/test_databases.rb +1 -16
  111. data/lib/active_record/test_fixtures.rb +1 -0
  112. data/lib/active_record/timestamp.rb +26 -16
  113. data/lib/active_record/touch_later.rb +4 -2
  114. data/lib/active_record/transactions.rb +56 -46
  115. data/lib/active_record/type_caster/connection.rb +16 -10
  116. data/lib/active_record/validations.rb +1 -0
  117. data/lib/active_record/validations/uniqueness.rb +3 -5
  118. data/lib/arel.rb +12 -5
  119. data/lib/arel/insert_manager.rb +3 -3
  120. data/lib/arel/nodes.rb +2 -1
  121. data/lib/arel/nodes/comment.rb +29 -0
  122. data/lib/arel/nodes/select_core.rb +16 -12
  123. data/lib/arel/nodes/unary.rb +1 -0
  124. data/lib/arel/nodes/values_list.rb +2 -17
  125. data/lib/arel/select_manager.rb +10 -10
  126. data/lib/arel/visitors/depth_first.rb +7 -2
  127. data/lib/arel/visitors/dot.rb +7 -2
  128. data/lib/arel/visitors/ibm_db.rb +13 -0
  129. data/lib/arel/visitors/informix.rb +6 -0
  130. data/lib/arel/visitors/mssql.rb +15 -1
  131. data/lib/arel/visitors/oracle12.rb +4 -5
  132. data/lib/arel/visitors/postgresql.rb +4 -10
  133. data/lib/arel/visitors/to_sql.rb +107 -131
  134. data/lib/arel/visitors/visitor.rb +9 -5
  135. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  136. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  137. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  138. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  139. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  140. metadata +16 -12
  141. data/lib/active_record/collection_cache_key.rb +0 -53
  142. data/lib/arel/nodes/values.rb +0 -16
@@ -98,9 +98,13 @@ module ActiveRecord
98
98
  end
99
99
 
100
100
  def rollback_records
101
- ite = records.uniq
101
+ ite = records.uniq(&:object_id)
102
+ already_run_callbacks = {}
102
103
  while record = ite.shift
103
- record.rolledback!(force_restore_state: full_rollback?)
104
+ trigger_callbacks = record.trigger_transactional_callbacks?
105
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
106
+ already_run_callbacks[record] ||= trigger_callbacks
107
+ record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
104
108
  end
105
109
  ensure
106
110
  ite.each do |i|
@@ -113,10 +117,14 @@ module ActiveRecord
113
117
  end
114
118
 
115
119
  def commit_records
116
- ite = records.uniq
120
+ ite = records.uniq(&:object_id)
121
+ already_run_callbacks = {}
117
122
  while record = ite.shift
118
123
  if @run_commit_callbacks
119
- record.committed!
124
+ trigger_callbacks = record.trigger_transactional_callbacks?
125
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
126
+ already_run_callbacks[record] ||= trigger_callbacks
127
+ record.committed!(should_run_callbacks: should_run_callbacks)
120
128
  else
121
129
  # if not running callbacks, only adds the record to the parent transaction
122
130
  connection.add_transaction_record(record)
@@ -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 :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
82
+ attr_reader :visitor, :owner, :logger, :lock
82
83
  alias :in_use? :owner
83
84
 
84
85
  set_callback :checkin, :after, :enable_lazy_transactions!
@@ -106,6 +107,14 @@ module ActiveRecord
106
107
  Regexp.union(*parts)
107
108
  end
108
109
 
110
+ def self.quoted_column_names # :nodoc:
111
+ @quoted_column_names ||= {}
112
+ end
113
+
114
+ def self.quoted_table_names # :nodoc:
115
+ @quoted_table_names ||= {}
116
+ end
117
+
109
118
  def initialize(connection, logger = nil, config = {}) # :nodoc:
110
119
  super()
111
120
 
@@ -114,12 +123,10 @@ module ActiveRecord
114
123
  @instrumenter = ActiveSupport::Notifications.instrumenter
115
124
  @logger = logger
116
125
  @config = config
117
- @pool = nil
126
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
118
127
  @idle_since = Concurrent.monotonic_time
119
- @schema_cache = SchemaCache.new self
120
- @quoted_column_names, @quoted_table_names = {}, {}
121
- @prevent_writes = false
122
128
  @visitor = arel_visitor
129
+ @statements = build_statement_pool
123
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
124
131
 
125
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -132,8 +139,6 @@ module ActiveRecord
132
139
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
133
140
  config.fetch(:advisory_locks, true)
134
141
  )
135
-
136
- check_version
137
142
  end
138
143
 
139
144
  def replica?
@@ -145,19 +150,7 @@ module ActiveRecord
145
150
  # Returns true if the connection is a replica, or if +prevent_writes+
146
151
  # is set to true.
147
152
  def preventing_writes?
148
- replica? || prevent_writes
149
- end
150
-
151
- # Prevent writing to the database regardless of role.
152
- #
153
- # In some cases you may want to prevent writes to the database
154
- # even if you are on a database that can write. `while_preventing_writes`
155
- # will prevent writes to the database for the duration of the block.
156
- def while_preventing_writes
157
- original, @prevent_writes = @prevent_writes, true
158
- yield
159
- ensure
160
- @prevent_writes = original
153
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
161
154
  end
162
155
 
163
156
  def migrations_paths # :nodoc:
@@ -165,14 +158,40 @@ module ActiveRecord
165
158
  end
166
159
 
167
160
  def migration_context # :nodoc:
168
- MigrationContext.new(migrations_paths)
161
+ MigrationContext.new(migrations_paths, schema_migration)
162
+ end
163
+
164
+ def schema_migration # :nodoc:
165
+ @schema_migration ||= begin
166
+ conn = self
167
+ spec_name = conn.pool.spec.name
168
+ name = "#{spec_name}::SchemaMigration"
169
+
170
+ Class.new(ActiveRecord::SchemaMigration) do
171
+ define_singleton_method(:name) { name }
172
+ define_singleton_method(:to_s) { name }
173
+
174
+ self.connection_specification_name = spec_name
175
+ end
176
+ end
177
+ end
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
169
185
  end
170
186
 
171
187
  class Version
172
188
  include Comparable
173
189
 
174
- def initialize(version_string)
190
+ attr_reader :full_version_string
191
+
192
+ def initialize(version_string, full_version_string = nil)
175
193
  @version = version_string.split(".").map(&:to_i)
194
+ @full_version_string = full_version_string
176
195
  end
177
196
 
178
197
  def <=>(version_string)
@@ -204,9 +223,13 @@ module ActiveRecord
204
223
  @owner = Thread.current
205
224
  end
206
225
 
226
+ def schema_cache
227
+ @pool.get_schema_cache(self)
228
+ end
229
+
207
230
  def schema_cache=(cache)
208
231
  cache.connection = self
209
- @schema_cache = cache
232
+ @pool.set_schema_cache(cache)
210
233
  end
211
234
 
212
235
  # this method must only be called while holding connection pool's mutex
@@ -245,10 +268,10 @@ module ActiveRecord
245
268
  end
246
269
 
247
270
  def unprepared_statement
248
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
249
272
  yield
250
273
  ensure
251
- @prepared_statements = old_prepared_statements
274
+ cache&.delete(object_id)
252
275
  end
253
276
 
254
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -257,6 +280,11 @@ module ActiveRecord
257
280
  self.class::ADAPTER_NAME
258
281
  end
259
282
 
283
+ # Does the database for this adapter exist?
284
+ def self.database_exists?(config)
285
+ raise NotImplementedError
286
+ end
287
+
260
288
  # Does this adapter support DDL rollbacks in transactions? That is, would
261
289
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
262
290
  def supports_ddl_transactions?
@@ -383,10 +411,35 @@ module ActiveRecord
383
411
  false
384
412
  end
385
413
 
414
+ # Does this adapter support optimizer hints?
415
+ def supports_optimizer_hints?
416
+ false
417
+ end
418
+
419
+ def supports_common_table_expressions?
420
+ false
421
+ end
422
+
386
423
  def supports_lazy_transactions?
387
424
  false
388
425
  end
389
426
 
427
+ def supports_insert_returning?
428
+ false
429
+ end
430
+
431
+ def supports_insert_on_duplicate_skip?
432
+ false
433
+ end
434
+
435
+ def supports_insert_on_duplicate_update?
436
+ false
437
+ end
438
+
439
+ def supports_insert_conflict_target?
440
+ false
441
+ end
442
+
390
443
  # This is meant to be implemented by the adapters that support extensions
391
444
  def disable_extension(name)
392
445
  end
@@ -464,6 +517,9 @@ module ActiveRecord
464
517
  #
465
518
  # Prevent @connection's finalizer from touching the socket, or
466
519
  # otherwise communicating with its server, when it is collected.
520
+ if schema_cache.connection == self
521
+ schema_cache.connection = nil
522
+ end
467
523
  end
468
524
 
469
525
  # Reset the state of this connection, directing the DBMS to clear
@@ -476,11 +532,9 @@ module ActiveRecord
476
532
  # this should be overridden by concrete adapters
477
533
  end
478
534
 
479
- ###
480
- # Clear any caching the database adapter may be doing, for example
481
- # clearing the prepared statement cache. This is database specific.
535
+ # Clear any caching the database adapter may be doing.
482
536
  def clear_cache!
483
- # this should be overridden by concrete adapters
537
+ @lock.synchronize { @statements.clear } if @statements
484
538
  end
485
539
 
486
540
  # Returns true if its required to reload the connection between requests for development mode.
@@ -506,8 +560,8 @@ module ActiveRecord
506
560
  @connection
507
561
  end
508
562
 
509
- def default_uniqueness_comparison(attribute, value) # :nodoc:
510
- case_sensitive_comparison(attribute, value)
563
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
564
+ attribute.eq(value)
511
565
  end
512
566
 
513
567
  def case_sensitive_comparison(attribute, value) # :nodoc:
@@ -542,10 +596,31 @@ module ActiveRecord
542
596
  index.using.nil?
543
597
  end
544
598
 
545
- private
546
- def check_version
599
+ # Called by ActiveRecord::InsertAll,
600
+ # Passed an instance of ActiveRecord::InsertAll::Builder,
601
+ # This method implements standard bulk inserts for all databases, but
602
+ # should be overridden by adapters to implement common features with
603
+ # non-standard syntax like handling duplicates or returning values.
604
+ def build_insert_sql(insert) # :nodoc:
605
+ if insert.skip_duplicates? || insert.update_duplicates?
606
+ raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
547
607
  end
548
608
 
609
+ "INSERT #{insert.into} #{insert.values_list}"
610
+ end
611
+
612
+ def get_database_version # :nodoc:
613
+ end
614
+
615
+ def database_version # :nodoc:
616
+ schema_cache.database_version
617
+ end
618
+
619
+ def check_version # :nodoc:
620
+ end
621
+
622
+ private
623
+
549
624
  def type_map
550
625
  @type_map ||= Type::TypeMap.new.tap do |mapping|
551
626
  initialize_type_map(mapping)
@@ -689,6 +764,9 @@ module ActiveRecord
689
764
  def arel_visitor
690
765
  Arel::Visitors::ToSql.new(self)
691
766
  end
767
+
768
+ def build_statement_pool
769
+ end
692
770
  end
693
771
  end
694
772
  end
@@ -53,28 +53,28 @@ module ActiveRecord
53
53
 
54
54
  def initialize(connection, logger, connection_options, config)
55
55
  super(connection, logger, config)
56
-
57
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
58
56
  end
59
57
 
60
- def version #:nodoc:
61
- @version ||= Version.new(version_string)
58
+ def get_database_version #:nodoc:
59
+ full_version_string = get_full_version
60
+ version_string = version_string(full_version_string)
61
+ Version.new(version_string, full_version_string)
62
62
  end
63
63
 
64
64
  def mariadb? # :nodoc:
65
65
  /mariadb/i.match?(full_version)
66
66
  end
67
67
 
68
- def supports_bulk_alter? #:nodoc:
68
+ def supports_bulk_alter?
69
69
  true
70
70
  end
71
71
 
72
72
  def supports_index_sort_order?
73
- !mariadb? && version >= "8.0.1"
73
+ !mariadb? && database_version >= "8.0.1"
74
74
  end
75
75
 
76
76
  def supports_expression_index?
77
- !mariadb? && version >= "8.0.13"
77
+ !mariadb? && database_version >= "8.0.13"
78
78
  end
79
79
 
80
80
  def supports_transaction_isolation?
@@ -98,17 +98,38 @@ module ActiveRecord
98
98
  end
99
99
 
100
100
  def supports_datetime_with_precision?
101
- mariadb? || version >= "5.6.4"
101
+ mariadb? || database_version >= "5.6.4"
102
102
  end
103
103
 
104
104
  def supports_virtual_columns?
105
- mariadb? || version >= "5.7.5"
105
+ mariadb? || database_version >= "5.7.5"
106
+ end
107
+
108
+ # See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
109
+ def supports_optimizer_hints?
110
+ !mariadb? && database_version >= "5.7.7"
111
+ end
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
106
119
  end
107
120
 
108
121
  def supports_advisory_locks?
109
122
  true
110
123
  end
111
124
 
125
+ def supports_insert_on_duplicate_skip?
126
+ true
127
+ end
128
+
129
+ def supports_insert_on_duplicate_update?
130
+ true
131
+ end
132
+
112
133
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
113
134
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
114
135
  end
@@ -154,10 +175,9 @@ module ActiveRecord
154
175
 
155
176
  # CONNECTION MANAGEMENT ====================================
156
177
 
157
- # Clears the prepared statements cache.
158
- def clear_cache!
178
+ def clear_cache! # :nodoc:
159
179
  reload_type_map
160
- @statements.clear
180
+ super
161
181
  end
162
182
 
163
183
  #--
@@ -264,10 +284,6 @@ module ActiveRecord
264
284
  show_variable "collation_database"
265
285
  end
266
286
 
267
- def truncate(table_name, name = nil)
268
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
269
- end
270
-
271
287
  def table_comment(table_name) # :nodoc:
272
288
  scope = quoted_scope(table_name)
273
289
 
@@ -279,22 +295,8 @@ module ActiveRecord
279
295
  SQL
280
296
  end
281
297
 
282
- def bulk_change_table(table_name, operations) #:nodoc:
283
- sqls = operations.flat_map do |command, args|
284
- table, arguments = args.shift, args
285
- method = :"#{command}_for_alter"
286
-
287
- if respond_to?(method, true)
288
- send(method, table, *arguments)
289
- else
290
- raise "Unknown method called : #{method}(#{arguments.inspect})"
291
- end
292
- end.join(", ")
293
-
294
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
295
- end
296
-
297
- def change_table_comment(table_name, comment) #:nodoc:
298
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
299
+ comment = extract_new_comment_value(comment_or_changes)
298
300
  comment = "" if comment.nil?
299
301
  execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
300
302
  end
@@ -350,7 +352,8 @@ module ActiveRecord
350
352
  change_column table_name, column_name, nil, null: null
351
353
  end
352
354
 
353
- def change_column_comment(table_name, column_name, comment) #:nodoc:
355
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
356
+ comment = extract_new_comment_value(comment_or_changes)
354
357
  change_column table_name, column_name, nil, comment: comment
355
358
  end
356
359
 
@@ -445,14 +448,29 @@ module ActiveRecord
445
448
 
446
449
  query_values(<<~SQL, "SCHEMA")
447
450
  SELECT column_name
448
- FROM information_schema.key_column_usage
449
- WHERE constraint_name = 'PRIMARY'
451
+ FROM information_schema.statistics
452
+ WHERE index_name = 'PRIMARY'
450
453
  AND table_schema = #{scope[:schema]}
451
454
  AND table_name = #{scope[:name]}
452
- ORDER BY ordinal_position
455
+ ORDER BY seq_in_index
453
456
  SQL
454
457
  end
455
458
 
459
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
460
+ column = column_for_attribute(attribute)
461
+
462
+ if column.collation && !column.case_sensitive? && !value.nil?
463
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
464
+ Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
465
+ To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
466
+ pass `case_sensitive: true` option explicitly to the uniqueness validator.
467
+ MSG
468
+ attribute.eq(Arel::Nodes::Bin.new(value))
469
+ else
470
+ super
471
+ end
472
+ end
473
+
456
474
  def case_sensitive_comparison(attribute, value) # :nodoc:
457
475
  column = column_for_attribute(attribute)
458
476
 
@@ -491,45 +509,27 @@ module ActiveRecord
491
509
  index.using == :btree || super
492
510
  end
493
511
 
494
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
495
- with_multi_statements do
496
- super { discard_remaining_results }
497
- end
498
- end
512
+ def build_insert_sql(insert) # :nodoc:
513
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
499
514
 
500
- private
501
- def check_version
502
- if version < "5.5.8"
503
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
504
- end
515
+ if insert.skip_duplicates?
516
+ no_op_column = quote_column_name(insert.keys.first)
517
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
518
+ elsif insert.update_duplicates?
519
+ sql << " ON DUPLICATE KEY UPDATE "
520
+ sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
505
521
  end
506
522
 
507
- def combine_multi_statements(total_sql)
508
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
509
- previous_packet = total_sql_chunks.last
510
- sql << ";\n"
511
- if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
512
- total_sql_chunks << sql
513
- else
514
- previous_packet << sql
515
- end
516
- end
517
- end
523
+ sql
524
+ end
518
525
 
519
- def max_allowed_packet_reached?(current_packet, previous_packet)
520
- if current_packet.bytesize > max_allowed_packet
521
- raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
522
- elsif previous_packet.nil?
523
- false
524
- else
525
- (current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
526
- end
526
+ def check_version # :nodoc:
527
+ if database_version < "5.5.8"
528
+ raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
527
529
  end
530
+ end
528
531
 
529
- def max_allowed_packet
530
- bytes_margin = 2
531
- @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
532
- end
532
+ private
533
533
 
534
534
  def initialize_type_map(m = type_map)
535
535
  super
@@ -589,6 +589,7 @@ module ActiveRecord
589
589
  end
590
590
 
591
591
  # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
592
+ ER_FILSORT_ABORT = 1028
592
593
  ER_DUP_ENTRY = 1062
593
594
  ER_NOT_NULL_VIOLATION = 1048
594
595
  ER_NO_REFERENCED_ROW = 1216
@@ -630,7 +631,7 @@ module ActiveRecord
630
631
  Deadlocked.new(message, sql: sql, binds: binds)
631
632
  when ER_LOCK_WAIT_TIMEOUT
632
633
  LockWaitTimeout.new(message, sql: sql, binds: binds)
633
- when ER_QUERY_TIMEOUT
634
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
634
635
  StatementTimeout.new(message, sql: sql, binds: binds)
635
636
  when ER_QUERY_INTERRUPTED
636
637
  QueryCanceled.new(message, sql: sql, binds: binds)
@@ -700,7 +701,7 @@ module ActiveRecord
700
701
  end
701
702
 
702
703
  def supports_rename_index?
703
- mariadb? ? false : version >= "5.7.6"
704
+ mariadb? ? false : database_version >= "5.7.6"
704
705
  end
705
706
 
706
707
  def configure_connection
@@ -770,6 +771,10 @@ module ActiveRecord
770
771
  Arel::Visitors::MySQL.new(self)
771
772
  end
772
773
 
774
+ def build_statement_pool
775
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
776
+ end
777
+
773
778
  def mismatched_foreign_key(message, sql:, binds:)
774
779
  match = %r/
775
780
  (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
@@ -794,8 +799,8 @@ module ActiveRecord
794
799
  MismatchedForeignKey.new(options)
795
800
  end
796
801
 
797
- def version_string
798
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
802
+ def version_string(full_version_string)
803
+ full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
799
804
  end
800
805
 
801
806
  class MysqlString < Type::String # :nodoc: