activerecord 6.0.0.beta1 → 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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +529 -10
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +7 -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 +27 -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 +5 -6
  16. data/lib/active_record/associations/collection_proxy.rb +13 -42
  17. data/lib/active_record/associations/has_many_association.rb +1 -9
  18. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  19. data/lib/active_record/associations/join_dependency.rb +14 -9
  20. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  21. data/lib/active_record/associations/preloader.rb +12 -7
  22. data/lib/active_record/associations/preloader/association.rb +37 -34
  23. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  24. data/lib/active_record/attribute_methods.rb +3 -53
  25. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  26. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  27. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  28. data/lib/active_record/attribute_methods/query.rb +2 -3
  29. data/lib/active_record/attribute_methods/read.rb +3 -9
  30. data/lib/active_record/attribute_methods/write.rb +6 -12
  31. data/lib/active_record/attributes.rb +13 -0
  32. data/lib/active_record/autosave_association.rb +21 -7
  33. data/lib/active_record/base.rb +0 -1
  34. data/lib/active_record/callbacks.rb +3 -3
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
  36. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  37. data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
  38. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
  39. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  40. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  41. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  42. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  43. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  44. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  45. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
  46. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
  47. data/lib/active_record/connection_adapters/column.rb +17 -13
  48. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  49. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  50. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  51. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  52. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  53. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  54. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  55. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  56. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  59. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  60. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  61. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  62. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  63. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  66. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
  68. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  69. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  70. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  71. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  72. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  73. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
  74. data/lib/active_record/connection_handling.rb +40 -17
  75. data/lib/active_record/core.rb +35 -24
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  78. data/lib/active_record/database_configurations/url_config.rb +21 -16
  79. data/lib/active_record/dynamic_matchers.rb +1 -1
  80. data/lib/active_record/enum.rb +15 -0
  81. data/lib/active_record/errors.rb +18 -13
  82. data/lib/active_record/fixtures.rb +11 -6
  83. data/lib/active_record/gem_version.rb +2 -2
  84. data/lib/active_record/inheritance.rb +1 -1
  85. data/lib/active_record/insert_all.rb +179 -0
  86. data/lib/active_record/integration.rb +13 -1
  87. data/lib/active_record/internal_metadata.rb +5 -1
  88. data/lib/active_record/locking/optimistic.rb +3 -4
  89. data/lib/active_record/log_subscriber.rb +1 -1
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  92. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/migration/command_recorder.rb +28 -14
  95. data/lib/active_record/migration/compatibility.rb +72 -63
  96. data/lib/active_record/model_schema.rb +3 -0
  97. data/lib/active_record/persistence.rb +212 -19
  98. data/lib/active_record/querying.rb +18 -14
  99. data/lib/active_record/railtie.rb +9 -1
  100. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  101. data/lib/active_record/railties/databases.rake +124 -25
  102. data/lib/active_record/reflection.rb +18 -32
  103. data/lib/active_record/relation.rb +185 -35
  104. data/lib/active_record/relation/calculations.rb +40 -44
  105. data/lib/active_record/relation/delegation.rb +23 -31
  106. data/lib/active_record/relation/finder_methods.rb +23 -14
  107. data/lib/active_record/relation/merger.rb +11 -16
  108. data/lib/active_record/relation/query_attribute.rb +5 -3
  109. data/lib/active_record/relation/query_methods.rb +230 -69
  110. data/lib/active_record/relation/spawn_methods.rb +1 -1
  111. data/lib/active_record/relation/where_clause.rb +10 -10
  112. data/lib/active_record/sanitization.rb +33 -4
  113. data/lib/active_record/schema.rb +1 -1
  114. data/lib/active_record/schema_dumper.rb +10 -1
  115. data/lib/active_record/schema_migration.rb +1 -1
  116. data/lib/active_record/scoping.rb +6 -7
  117. data/lib/active_record/scoping/default.rb +7 -15
  118. data/lib/active_record/scoping/named.rb +10 -2
  119. data/lib/active_record/statement_cache.rb +2 -2
  120. data/lib/active_record/store.rb +48 -0
  121. data/lib/active_record/table_metadata.rb +9 -13
  122. data/lib/active_record/tasks/database_tasks.rb +109 -6
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  124. data/lib/active_record/test_databases.rb +1 -16
  125. data/lib/active_record/test_fixtures.rb +2 -2
  126. data/lib/active_record/timestamp.rb +35 -19
  127. data/lib/active_record/touch_later.rb +4 -2
  128. data/lib/active_record/transactions.rb +56 -46
  129. data/lib/active_record/type_caster/connection.rb +16 -10
  130. data/lib/active_record/validations.rb +1 -0
  131. data/lib/active_record/validations/uniqueness.rb +4 -4
  132. data/lib/arel.rb +18 -4
  133. data/lib/arel/insert_manager.rb +3 -3
  134. data/lib/arel/nodes.rb +2 -1
  135. data/lib/arel/nodes/and.rb +1 -1
  136. data/lib/arel/nodes/case.rb +1 -1
  137. data/lib/arel/nodes/comment.rb +29 -0
  138. data/lib/arel/nodes/select_core.rb +16 -12
  139. data/lib/arel/nodes/unary.rb +1 -0
  140. data/lib/arel/nodes/values_list.rb +2 -17
  141. data/lib/arel/select_manager.rb +10 -10
  142. data/lib/arel/visitors/depth_first.rb +7 -2
  143. data/lib/arel/visitors/dot.rb +7 -2
  144. data/lib/arel/visitors/ibm_db.rb +13 -0
  145. data/lib/arel/visitors/informix.rb +6 -0
  146. data/lib/arel/visitors/mssql.rb +15 -1
  147. data/lib/arel/visitors/oracle12.rb +4 -5
  148. data/lib/arel/visitors/postgresql.rb +4 -10
  149. data/lib/arel/visitors/to_sql.rb +107 -131
  150. data/lib/arel/visitors/visitor.rb +9 -5
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  152. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  154. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  155. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  156. metadata +19 -12
  157. data/lib/active_record/collection_cache_key.rb +0 -53
  158. 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)
@@ -205,9 +213,12 @@ module ActiveRecord
205
213
  run_commit_callbacks: run_commit_callbacks)
206
214
  end
207
215
 
208
- transaction.materialize! unless @connection.supports_lazy_transactions? && lazy_transactions_enabled?
216
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
217
+ @has_unmaterialized_transactions = true
218
+ else
219
+ transaction.materialize!
220
+ end
209
221
  @stack.push(transaction)
210
- @has_unmaterialized_transactions = true if @connection.supports_lazy_transactions?
211
222
  transaction
212
223
  end
213
224
  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"
@@ -77,11 +78,8 @@ module ActiveRecord
77
78
 
78
79
  SIMPLE_INT = /\A\d+\z/
79
80
 
80
- attr_writer :visitor
81
- deprecate :visitor=
82
-
83
81
  attr_accessor :pool
84
- attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes
82
+ attr_reader :visitor, :owner, :logger, :lock
85
83
  alias :in_use? :owner
86
84
 
87
85
  set_callback :checkin, :after, :enable_lazy_transactions!
@@ -105,10 +103,18 @@ module ActiveRecord
105
103
  end
106
104
 
107
105
  def self.build_read_query_regexp(*parts) # :nodoc:
108
- parts = parts.map { |part| /\A\s*#{part}/i }
106
+ parts = parts.map { |part| /\A[\(\s]*#{part}/i }
109
107
  Regexp.union(*parts)
110
108
  end
111
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
+
112
118
  def initialize(connection, logger = nil, config = {}) # :nodoc:
113
119
  super()
114
120
 
@@ -117,12 +123,10 @@ module ActiveRecord
117
123
  @instrumenter = ActiveSupport::Notifications.instrumenter
118
124
  @logger = logger
119
125
  @config = config
120
- @pool = nil
126
+ @pool = ActiveRecord::ConnectionAdapters::NullPool.new
121
127
  @idle_since = Concurrent.monotonic_time
122
- @schema_cache = SchemaCache.new self
123
- @quoted_column_names, @quoted_table_names = {}, {}
124
- @prevent_writes = false
125
128
  @visitor = arel_visitor
129
+ @statements = build_statement_pool
126
130
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
127
131
 
128
132
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -135,8 +139,6 @@ module ActiveRecord
135
139
  @advisory_locks_enabled = self.class.type_cast_config_to_boolean(
136
140
  config.fetch(:advisory_locks, true)
137
141
  )
138
-
139
- check_version
140
142
  end
141
143
 
142
144
  def replica?
@@ -148,19 +150,7 @@ module ActiveRecord
148
150
  # Returns true if the connection is a replica, or if +prevent_writes+
149
151
  # is set to true.
150
152
  def preventing_writes?
151
- replica? || prevent_writes
152
- end
153
-
154
- # Prevent writing to the database regardless of role.
155
- #
156
- # In some cases you may want to prevent writes to the database
157
- # even if you are on a database that can write. `while_preventing_writes`
158
- # will prevent writes to the database for the duration of the block.
159
- def while_preventing_writes
160
- original, @prevent_writes = @prevent_writes, true
161
- yield
162
- ensure
163
- @prevent_writes = original
153
+ replica? || ActiveRecord::Base.connection_handler.prevent_writes
164
154
  end
165
155
 
166
156
  def migrations_paths # :nodoc:
@@ -168,14 +158,40 @@ module ActiveRecord
168
158
  end
169
159
 
170
160
  def migration_context # :nodoc:
171
- 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
172
185
  end
173
186
 
174
187
  class Version
175
188
  include Comparable
176
189
 
177
- def initialize(version_string)
190
+ attr_reader :full_version_string
191
+
192
+ def initialize(version_string, full_version_string = nil)
178
193
  @version = version_string.split(".").map(&:to_i)
194
+ @full_version_string = full_version_string
179
195
  end
180
196
 
181
197
  def <=>(version_string)
@@ -207,9 +223,13 @@ module ActiveRecord
207
223
  @owner = Thread.current
208
224
  end
209
225
 
226
+ def schema_cache
227
+ @pool.get_schema_cache(self)
228
+ end
229
+
210
230
  def schema_cache=(cache)
211
231
  cache.connection = self
212
- @schema_cache = cache
232
+ @pool.set_schema_cache(cache)
213
233
  end
214
234
 
215
235
  # this method must only be called while holding connection pool's mutex
@@ -248,10 +268,10 @@ module ActiveRecord
248
268
  end
249
269
 
250
270
  def unprepared_statement
251
- old_prepared_statements, @prepared_statements = @prepared_statements, false
271
+ cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
252
272
  yield
253
273
  ensure
254
- @prepared_statements = old_prepared_statements
274
+ cache&.delete(object_id)
255
275
  end
256
276
 
257
277
  # Returns the human-readable name of the adapter. Use mixed case - one
@@ -260,6 +280,11 @@ module ActiveRecord
260
280
  self.class::ADAPTER_NAME
261
281
  end
262
282
 
283
+ # Does the database for this adapter exist?
284
+ def self.database_exists?(config)
285
+ raise NotImplementedError
286
+ end
287
+
263
288
  # Does this adapter support DDL rollbacks in transactions? That is, would
264
289
  # CREATE TABLE or ALTER TABLE get rolled back by a transaction?
265
290
  def supports_ddl_transactions?
@@ -338,6 +363,7 @@ module ActiveRecord
338
363
  def supports_foreign_keys_in_create?
339
364
  supports_foreign_keys?
340
365
  end
366
+ deprecate :supports_foreign_keys_in_create?
341
367
 
342
368
  # Does this adapter support views?
343
369
  def supports_views?
@@ -385,10 +411,35 @@ module ActiveRecord
385
411
  false
386
412
  end
387
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
+
388
423
  def supports_lazy_transactions?
389
424
  false
390
425
  end
391
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
+
392
443
  # This is meant to be implemented by the adapters that support extensions
393
444
  def disable_extension(name)
394
445
  end
@@ -466,6 +517,9 @@ module ActiveRecord
466
517
  #
467
518
  # Prevent @connection's finalizer from touching the socket, or
468
519
  # otherwise communicating with its server, when it is collected.
520
+ if schema_cache.connection == self
521
+ schema_cache.connection = nil
522
+ end
469
523
  end
470
524
 
471
525
  # Reset the state of this connection, directing the DBMS to clear
@@ -478,11 +532,9 @@ module ActiveRecord
478
532
  # this should be overridden by concrete adapters
479
533
  end
480
534
 
481
- ###
482
- # Clear any caching the database adapter may be doing, for example
483
- # clearing the prepared statement cache. This is database specific.
535
+ # Clear any caching the database adapter may be doing.
484
536
  def clear_cache!
485
- # this should be overridden by concrete adapters
537
+ @lock.synchronize { @statements.clear } if @statements
486
538
  end
487
539
 
488
540
  # Returns true if its required to reload the connection between requests for development mode.
@@ -508,6 +560,10 @@ module ActiveRecord
508
560
  @connection
509
561
  end
510
562
 
563
+ def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
564
+ attribute.eq(value)
565
+ end
566
+
511
567
  def case_sensitive_comparison(attribute, value) # :nodoc:
512
568
  attribute.eq(value)
513
569
  end
@@ -540,10 +596,31 @@ module ActiveRecord
540
596
  index.using.nil?
541
597
  end
542
598
 
543
- private
544
- 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"
545
607
  end
546
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
+
547
624
  def type_map
548
625
  @type_map ||= Type::TypeMap.new.tap do |mapping|
549
626
  initialize_type_map(mapping)
@@ -687,6 +764,9 @@ module ActiveRecord
687
764
  def arel_visitor
688
765
  Arel::Visitors::ToSql.new(self)
689
766
  end
767
+
768
+ def build_statement_pool
769
+ end
690
770
  end
691
771
  end
692
772
  end
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  NATIVE_DATABASE_TYPES = {
30
30
  primary_key: "bigint auto_increment PRIMARY KEY",
31
31
  string: { name: "varchar", limit: 255 },
32
- text: { name: "text", limit: 65535 },
32
+ text: { name: "text" },
33
33
  integer: { name: "int", limit: 4 },
34
34
  float: { name: "float", limit: 24 },
35
35
  decimal: { name: "decimal" },
@@ -37,7 +37,8 @@ module ActiveRecord
37
37
  timestamp: { name: "timestamp" },
38
38
  time: { name: "time" },
39
39
  date: { name: "date" },
40
- binary: { name: "blob", limit: 65535 },
40
+ binary: { name: "blob" },
41
+ blob: { name: "blob" },
41
42
  boolean: { name: "tinyint", limit: 1 },
42
43
  json: { name: "json" },
43
44
  }
@@ -52,28 +53,28 @@ module ActiveRecord
52
53
 
53
54
  def initialize(connection, logger, connection_options, config)
54
55
  super(connection, logger, config)
55
-
56
- @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
57
56
  end
58
57
 
59
- def version #:nodoc:
60
- @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)
61
62
  end
62
63
 
63
64
  def mariadb? # :nodoc:
64
65
  /mariadb/i.match?(full_version)
65
66
  end
66
67
 
67
- def supports_bulk_alter? #:nodoc:
68
+ def supports_bulk_alter?
68
69
  true
69
70
  end
70
71
 
71
72
  def supports_index_sort_order?
72
- !mariadb? && version >= "8.0.1"
73
+ !mariadb? && database_version >= "8.0.1"
73
74
  end
74
75
 
75
76
  def supports_expression_index?
76
- !mariadb? && version >= "8.0.13"
77
+ !mariadb? && database_version >= "8.0.13"
77
78
  end
78
79
 
79
80
  def supports_transaction_isolation?
@@ -97,17 +98,38 @@ module ActiveRecord
97
98
  end
98
99
 
99
100
  def supports_datetime_with_precision?
100
- mariadb? || version >= "5.6.4"
101
+ mariadb? || database_version >= "5.6.4"
101
102
  end
102
103
 
103
104
  def supports_virtual_columns?
104
- 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
105
119
  end
106
120
 
107
121
  def supports_advisory_locks?
108
122
  true
109
123
  end
110
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
+
111
133
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
112
134
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
113
135
  end
@@ -153,10 +175,9 @@ module ActiveRecord
153
175
 
154
176
  # CONNECTION MANAGEMENT ====================================
155
177
 
156
- # Clears the prepared statements cache.
157
- def clear_cache!
178
+ def clear_cache! # :nodoc:
158
179
  reload_type_map
159
- @statements.clear
180
+ super
160
181
  end
161
182
 
162
183
  #--
@@ -165,9 +186,9 @@ module ActiveRecord
165
186
 
166
187
  def explain(arel, binds = [])
167
188
  sql = "EXPLAIN #{to_sql(arel, binds)}"
168
- start = Time.now
189
+ start = Concurrent.monotonic_time
169
190
  result = exec_query(sql, "EXPLAIN", binds)
170
- elapsed = Time.now - start
191
+ elapsed = Concurrent.monotonic_time - start
171
192
 
172
193
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
173
194
  end
@@ -263,10 +284,6 @@ module ActiveRecord
263
284
  show_variable "collation_database"
264
285
  end
265
286
 
266
- def truncate(table_name, name = nil)
267
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
268
- end
269
-
270
287
  def table_comment(table_name) # :nodoc:
271
288
  scope = quoted_scope(table_name)
272
289
 
@@ -278,22 +295,8 @@ module ActiveRecord
278
295
  SQL
279
296
  end
280
297
 
281
- def bulk_change_table(table_name, operations) #:nodoc:
282
- sqls = operations.flat_map do |command, args|
283
- table, arguments = args.shift, args
284
- method = :"#{command}_for_alter"
285
-
286
- if respond_to?(method, true)
287
- send(method, table, *arguments)
288
- else
289
- raise "Unknown method called : #{method}(#{arguments.inspect})"
290
- end
291
- end.join(", ")
292
-
293
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
294
- end
295
-
296
- 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)
297
300
  comment = "" if comment.nil?
298
301
  execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
299
302
  end
@@ -349,7 +352,8 @@ module ActiveRecord
349
352
  change_column table_name, column_name, nil, null: null
350
353
  end
351
354
 
352
- 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)
353
357
  change_column table_name, column_name, nil, comment: comment
354
358
  end
355
359
 
@@ -430,30 +434,6 @@ module ActiveRecord
430
434
  table_options
431
435
  end
432
436
 
433
- # Maps logical Rails types to MySQL-specific data types.
434
- def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
435
- sql = \
436
- case type.to_s
437
- when "integer"
438
- integer_to_sql(limit)
439
- when "text"
440
- text_to_sql(limit)
441
- when "blob"
442
- binary_to_sql(limit)
443
- when "binary"
444
- if (0..0xfff) === limit
445
- "varbinary(#{limit})"
446
- else
447
- binary_to_sql(limit)
448
- end
449
- else
450
- super
451
- end
452
-
453
- sql = "#{sql} unsigned" if unsigned && type != :primary_key
454
- sql
455
- end
456
-
457
437
  # SHOW VARIABLES LIKE 'name'
458
438
  def show_variable(name)
459
439
  query_value("SELECT @@#{name}", "SCHEMA")
@@ -468,14 +448,29 @@ module ActiveRecord
468
448
 
469
449
  query_values(<<~SQL, "SCHEMA")
470
450
  SELECT column_name
471
- FROM information_schema.key_column_usage
472
- WHERE constraint_name = 'PRIMARY'
451
+ FROM information_schema.statistics
452
+ WHERE index_name = 'PRIMARY'
473
453
  AND table_schema = #{scope[:schema]}
474
454
  AND table_name = #{scope[:name]}
475
- ORDER BY ordinal_position
455
+ ORDER BY seq_in_index
476
456
  SQL
477
457
  end
478
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
+
479
474
  def case_sensitive_comparison(attribute, value) # :nodoc:
480
475
  column = column_for_attribute(attribute)
481
476
 
@@ -514,45 +509,27 @@ module ActiveRecord
514
509
  index.using == :btree || super
515
510
  end
516
511
 
517
- def insert_fixtures_set(fixture_set, tables_to_delete = [])
518
- with_multi_statements do
519
- super { discard_remaining_results }
520
- end
521
- end
512
+ def build_insert_sql(insert) # :nodoc:
513
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
522
514
 
523
- private
524
- def check_version
525
- if version < "5.5.8"
526
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
527
- 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(",")
528
521
  end
529
522
 
530
- def combine_multi_statements(total_sql)
531
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
532
- previous_packet = total_sql_chunks.last
533
- sql << ";\n"
534
- if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
535
- total_sql_chunks << sql
536
- else
537
- previous_packet << sql
538
- end
539
- end
540
- end
523
+ sql
524
+ end
541
525
 
542
- def max_allowed_packet_reached?(current_packet, previous_packet)
543
- if current_packet.bytesize > max_allowed_packet
544
- raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
545
- elsif previous_packet.nil?
546
- false
547
- else
548
- (current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
549
- 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."
550
529
  end
530
+ end
551
531
 
552
- def max_allowed_packet
553
- bytes_margin = 2
554
- @max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
555
- end
532
+ private
556
533
 
557
534
  def initialize_type_map(m = type_map)
558
535
  super
@@ -612,6 +589,7 @@ module ActiveRecord
612
589
  end
613
590
 
614
591
  # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
592
+ ER_FILSORT_ABORT = 1028
615
593
  ER_DUP_ENTRY = 1062
616
594
  ER_NOT_NULL_VIOLATION = 1048
617
595
  ER_NO_REFERENCED_ROW = 1216
@@ -627,6 +605,7 @@ module ActiveRecord
627
605
  ER_LOCK_WAIT_TIMEOUT = 1205
628
606
  ER_QUERY_INTERRUPTED = 1317
629
607
  ER_QUERY_TIMEOUT = 3024
608
+ ER_FK_INCOMPATIBLE_COLUMNS = 3780
630
609
 
631
610
  def translate_exception(exception, message:, sql:, binds:)
632
611
  case error_number(exception)
@@ -634,7 +613,7 @@ module ActiveRecord
634
613
  RecordNotUnique.new(message, sql: sql, binds: binds)
635
614
  when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
636
615
  InvalidForeignKey.new(message, sql: sql, binds: binds)
637
- when ER_CANNOT_ADD_FOREIGN
616
+ when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
638
617
  mismatched_foreign_key(message, sql: sql, binds: binds)
639
618
  when ER_CANNOT_CREATE_TABLE
640
619
  if message.include?("errno: 150")
@@ -652,7 +631,7 @@ module ActiveRecord
652
631
  Deadlocked.new(message, sql: sql, binds: binds)
653
632
  when ER_LOCK_WAIT_TIMEOUT
654
633
  LockWaitTimeout.new(message, sql: sql, binds: binds)
655
- when ER_QUERY_TIMEOUT
634
+ when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
656
635
  StatementTimeout.new(message, sql: sql, binds: binds)
657
636
  when ER_QUERY_INTERRUPTED
658
637
  QueryCanceled.new(message, sql: sql, binds: binds)
@@ -708,6 +687,12 @@ module ActiveRecord
708
687
  end
709
688
 
710
689
  def add_timestamps_for_alter(table_name, options = {})
690
+ options[:null] = false if options[:null].nil?
691
+
692
+ if !options.key?(:precision) && supports_datetime_with_precision?
693
+ options[:precision] = 6
694
+ end
695
+
711
696
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
712
697
  end
713
698
 
@@ -716,7 +701,7 @@ module ActiveRecord
716
701
  end
717
702
 
718
703
  def supports_rename_index?
719
- mariadb? ? false : version >= "5.7.6"
704
+ mariadb? ? false : database_version >= "5.7.6"
720
705
  end
721
706
 
722
707
  def configure_connection
@@ -786,53 +771,36 @@ module ActiveRecord
786
771
  Arel::Visitors::MySQL.new(self)
787
772
  end
788
773
 
774
+ def build_statement_pool
775
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
776
+ end
777
+
789
778
  def mismatched_foreign_key(message, sql:, binds:)
790
- parts = sql.scan(/`(\w+)`[ $)]/).flatten
791
- MismatchedForeignKey.new(
792
- self,
779
+ match = %r/
780
+ (?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
781
+ FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
782
+ REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
783
+ /xmi.match(sql)
784
+
785
+ options = {
793
786
  message: message,
794
787
  sql: sql,
795
788
  binds: binds,
796
- table: parts[0],
797
- foreign_key: parts[1],
798
- target_table: parts[2],
799
- primary_key: parts[3],
800
- )
801
- end
802
-
803
- def integer_to_sql(limit) # :nodoc:
804
- case limit
805
- when 1; "tinyint"
806
- when 2; "smallint"
807
- when 3; "mediumint"
808
- when nil, 4; "int"
809
- when 5..8; "bigint"
810
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
811
- end
812
- end
789
+ }
813
790
 
814
- def text_to_sql(limit) # :nodoc:
815
- case limit
816
- when 0..0xff; "tinytext"
817
- when nil, 0x100..0xffff; "text"
818
- when 0x10000..0xffffff; "mediumtext"
819
- when 0x1000000..0xffffffff; "longtext"
820
- else raise(ActiveRecordError, "No text type has byte length #{limit}")
791
+ if match
792
+ options[:table] = match[:table]
793
+ options[:foreign_key] = match[:foreign_key]
794
+ options[:target_table] = match[:target_table]
795
+ options[:primary_key] = match[:primary_key]
796
+ options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
821
797
  end
822
- end
823
798
 
824
- def binary_to_sql(limit) # :nodoc:
825
- case limit
826
- when 0..0xff; "tinyblob"
827
- when nil, 0x100..0xffff; "blob"
828
- when 0x10000..0xffffff; "mediumblob"
829
- when 0x1000000..0xffffffff; "longblob"
830
- else raise(ActiveRecordError, "No binary type has byte length #{limit}")
831
- end
799
+ MismatchedForeignKey.new(options)
832
800
  end
833
801
 
834
- def version_string
835
- 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]
836
804
  end
837
805
 
838
806
  class MysqlString < Type::String # :nodoc: