activerecord 7.2.3 → 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +391 -958
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/association.rb +34 -10
  6. data/lib/active_record/associations/builder/association.rb +7 -6
  7. data/lib/active_record/associations/collection_association.rb +1 -1
  8. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  9. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  10. data/lib/active_record/associations/preloader/association.rb +2 -2
  11. data/lib/active_record/associations/singular_association.rb +8 -3
  12. data/lib/active_record/associations.rb +34 -4
  13. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  14. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  15. data/lib/active_record/attribute_methods/query.rb +34 -0
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  17. data/lib/active_record/autosave_association.rb +69 -27
  18. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  19. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -15
  22. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  23. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -43
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
  31. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  32. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  33. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  34. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  36. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  37. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  38. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  39. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  40. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  41. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  42. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  43. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  44. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  45. data/lib/active_record/connection_adapters/postgresql_adapter.rb +46 -96
  46. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  47. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  48. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  49. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  50. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
  51. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  52. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  53. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  54. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  55. data/lib/active_record/connection_adapters.rb +0 -56
  56. data/lib/active_record/connection_handling.rb +23 -1
  57. data/lib/active_record/core.rb +29 -14
  58. data/lib/active_record/database_configurations/database_config.rb +4 -0
  59. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  60. data/lib/active_record/encryption/config.rb +3 -1
  61. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  62. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  63. data/lib/active_record/encryption/encryptor.rb +16 -8
  64. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  65. data/lib/active_record/encryption/scheme.rb +8 -1
  66. data/lib/active_record/enum.rb +9 -22
  67. data/lib/active_record/errors.rb +13 -5
  68. data/lib/active_record/fixtures.rb +0 -2
  69. data/lib/active_record/future_result.rb +13 -9
  70. data/lib/active_record/gem_version.rb +3 -3
  71. data/lib/active_record/insert_all.rb +1 -1
  72. data/lib/active_record/locking/optimistic.rb +1 -1
  73. data/lib/active_record/log_subscriber.rb +5 -11
  74. data/lib/active_record/migration/command_recorder.rb +31 -11
  75. data/lib/active_record/migration/compatibility.rb +5 -2
  76. data/lib/active_record/migration.rb +38 -42
  77. data/lib/active_record/model_schema.rb +3 -4
  78. data/lib/active_record/nested_attributes.rb +4 -6
  79. data/lib/active_record/persistence.rb +128 -130
  80. data/lib/active_record/query_logs.rb +102 -50
  81. data/lib/active_record/query_logs_formatter.rb +17 -28
  82. data/lib/active_record/querying.rb +8 -8
  83. data/lib/active_record/railtie.rb +2 -26
  84. data/lib/active_record/railties/databases.rake +11 -35
  85. data/lib/active_record/reflection.rb +18 -21
  86. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  87. data/lib/active_record/relation/batches.rb +132 -72
  88. data/lib/active_record/relation/calculations.rb +40 -39
  89. data/lib/active_record/relation/delegation.rb +25 -14
  90. data/lib/active_record/relation/finder_methods.rb +18 -18
  91. data/lib/active_record/relation/merger.rb +8 -8
  92. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  93. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  94. data/lib/active_record/relation/predicate_builder.rb +13 -0
  95. data/lib/active_record/relation/query_methods.rb +105 -61
  96. data/lib/active_record/relation/spawn_methods.rb +7 -7
  97. data/lib/active_record/relation.rb +79 -61
  98. data/lib/active_record/result.rb +66 -4
  99. data/lib/active_record/sanitization.rb +7 -6
  100. data/lib/active_record/schema_dumper.rb +5 -0
  101. data/lib/active_record/schema_migration.rb +2 -1
  102. data/lib/active_record/scoping/named.rb +5 -2
  103. data/lib/active_record/statement_cache.rb +14 -14
  104. data/lib/active_record/store.rb +7 -3
  105. data/lib/active_record/table_metadata.rb +1 -3
  106. data/lib/active_record/tasks/database_tasks.rb +69 -60
  107. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  108. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
  109. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  110. data/lib/active_record/test_databases.rb +1 -1
  111. data/lib/active_record/test_fixtures.rb +12 -0
  112. data/lib/active_record/token_for.rb +1 -1
  113. data/lib/active_record/transactions.rb +5 -6
  114. data/lib/active_record/validations/uniqueness.rb +8 -8
  115. data/lib/active_record.rb +21 -48
  116. data/lib/arel/collectors/bind.rb +2 -2
  117. data/lib/arel/collectors/sql_string.rb +1 -1
  118. data/lib/arel/collectors/substitute_binds.rb +2 -2
  119. data/lib/arel/nodes/binary.rb +1 -1
  120. data/lib/arel/nodes/node.rb +1 -1
  121. data/lib/arel/nodes/sql_literal.rb +1 -1
  122. data/lib/arel/table.rb +3 -7
  123. metadata +9 -10
  124. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -448,10 +448,14 @@ module ActiveRecord
448
448
  # = Active Record Real \Transaction
449
449
  class RealTransaction < Transaction
450
450
  def materialize!
451
- if isolation_level
452
- connection.begin_isolated_db_transaction(isolation_level)
451
+ if joinable?
452
+ if isolation_level
453
+ connection.begin_isolated_db_transaction(isolation_level)
454
+ else
455
+ connection.begin_db_transaction
456
+ end
453
457
  else
454
- connection.begin_db_transaction
458
+ connection.begin_deferred_transaction(isolation_level)
455
459
  end
456
460
 
457
461
  super
@@ -472,13 +476,19 @@ module ActiveRecord
472
476
  end
473
477
 
474
478
  def rollback
475
- connection.rollback_db_transaction if materialized?
479
+ if materialized?
480
+ connection.rollback_db_transaction
481
+ connection.reset_isolation_level if isolation_level
482
+ end
476
483
  @state.full_rollback!
477
484
  @instrumenter.finish(:rollback) if materialized?
478
485
  end
479
486
 
480
487
  def commit
481
- connection.commit_db_transaction if materialized?
488
+ if materialized?
489
+ connection.commit_db_transaction
490
+ connection.reset_isolation_level if isolation_level
491
+ end
482
492
  @state.full_commit!
483
493
  @instrumenter.finish(:commit) if materialized?
484
494
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "active_record/connection_adapters/sql_type_metadata"
5
4
  require "active_record/connection_adapters/abstract/schema_dumper"
6
5
  require "active_record/connection_adapters/abstract/schema_creation"
@@ -153,7 +152,6 @@ module ActiveRecord
153
152
 
154
153
  @owner = nil
155
154
  @pinned = false
156
- @instrumenter = ActiveSupport::Notifications.instrumenter
157
155
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
158
156
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
159
157
  @visitor = arel_visitor
@@ -194,19 +192,6 @@ module ActiveRecord
194
192
  end
195
193
  end
196
194
 
197
- EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
198
- EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
199
- private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
200
- def with_instrumenter(instrumenter, &block) # :nodoc:
201
- Thread.handle_interrupt(EXCEPTION_NEVER) do
202
- previous_instrumenter = @instrumenter
203
- @instrumenter = instrumenter
204
- Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
205
- ensure
206
- @instrumenter = previous_instrumenter
207
- end
208
- end
209
-
210
195
  def check_if_write_query(sql) # :nodoc:
211
196
  if preventing_writes? && write_query?(sql)
212
197
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
@@ -243,9 +228,9 @@ module ActiveRecord
243
228
  # the value of +current_preventing_writes+.
244
229
  def preventing_writes?
245
230
  return true if replica?
246
- return false if connection_class.nil?
231
+ return false if connection_descriptor.nil?
247
232
 
248
- connection_class.current_preventing_writes
233
+ connection_descriptor.current_preventing_writes
249
234
  end
250
235
 
251
236
  def prepared_statements?
@@ -296,8 +281,8 @@ module ActiveRecord
296
281
  @owner = ActiveSupport::IsolatedExecutionState.context
297
282
  end
298
283
 
299
- def connection_class # :nodoc:
300
- @pool.connection_class
284
+ def connection_descriptor # :nodoc:
285
+ @pool.connection_descriptor
301
286
  end
302
287
 
303
288
  # The role (e.g. +:writing+) for the current connection. In a
@@ -590,23 +575,31 @@ module ActiveRecord
590
575
  end
591
576
 
592
577
  # This is meant to be implemented by the adapters that support custom enum types
593
- def create_enum(*) # :nodoc:
578
+ def create_enum(...) # :nodoc:
594
579
  end
595
580
 
596
581
  # This is meant to be implemented by the adapters that support custom enum types
597
- def drop_enum(*) # :nodoc:
582
+ def drop_enum(...) # :nodoc:
598
583
  end
599
584
 
600
585
  # This is meant to be implemented by the adapters that support custom enum types
601
- def rename_enum(*) # :nodoc:
586
+ def rename_enum(...) # :nodoc:
602
587
  end
603
588
 
604
589
  # This is meant to be implemented by the adapters that support custom enum types
605
- def add_enum_value(*) # :nodoc:
590
+ def add_enum_value(...) # :nodoc:
606
591
  end
607
592
 
608
593
  # This is meant to be implemented by the adapters that support custom enum types
609
- def rename_enum_value(*) # :nodoc:
594
+ def rename_enum_value(...) # :nodoc:
595
+ end
596
+
597
+ # This is meant to be implemented by the adapters that support virtual tables
598
+ def create_virtual_table(*) # :nodoc:
599
+ end
600
+
601
+ # This is meant to be implemented by the adapters that support virtual tables
602
+ def drop_virtual_table(*) # :nodoc:
610
603
  end
611
604
 
612
605
  def advisory_locks_enabled? # :nodoc:
@@ -1066,7 +1059,8 @@ module ActiveRecord
1066
1059
  end
1067
1060
 
1068
1061
  def retryable_connection_error?(exception)
1069
- exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1062
+ (exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
1063
+ exception.is_a?(ConnectionFailed)
1070
1064
  end
1071
1065
 
1072
1066
  def invalidate_transaction(exception)
@@ -1127,24 +1121,25 @@ module ActiveRecord
1127
1121
  end
1128
1122
  end
1129
1123
 
1130
- def translate_exception_class(e, sql, binds)
1131
- message = "#{e.class.name}: #{e.message}"
1124
+ def translate_exception_class(native_error, sql, binds)
1125
+ return native_error if native_error.is_a?(ActiveRecordError)
1126
+
1127
+ message = "#{native_error.class.name}: #{native_error.message}"
1132
1128
 
1133
- exception = translate_exception(
1134
- e, message: message, sql: sql, binds: binds
1129
+ active_record_error = translate_exception(
1130
+ native_error, message: message, sql: sql, binds: binds
1135
1131
  )
1136
- exception.set_backtrace e.backtrace
1137
- exception
1132
+ active_record_error.set_backtrace(native_error.backtrace)
1133
+ active_record_error
1138
1134
  end
1139
1135
 
1140
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
1141
- @instrumenter.instrument(
1136
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1137
+ instrumenter.instrument(
1142
1138
  "sql.active_record",
1143
1139
  sql: sql,
1144
1140
  name: name,
1145
1141
  binds: binds,
1146
1142
  type_casted_binds: type_casted_binds,
1147
- statement_name: statement_name,
1148
1143
  async: async,
1149
1144
  connection: self,
1150
1145
  transaction: current_transaction.user_transaction.presence,
@@ -1155,11 +1150,8 @@ module ActiveRecord
1155
1150
  raise ex.set_query(sql, binds)
1156
1151
  end
1157
1152
 
1158
- def transform_query(sql)
1159
- ActiveRecord.query_transformers.each do |transformer|
1160
- sql = transformer.call(sql, self)
1161
- end
1162
- sql
1153
+ def instrumenter # :nodoc:
1154
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
1163
1155
  end
1164
1156
 
1165
1157
  def translate_exception(exception, message:, sql:, binds:)
@@ -1172,10 +1164,6 @@ module ActiveRecord
1172
1164
  end
1173
1165
  end
1174
1166
 
1175
- def without_prepared_statement?(binds)
1176
- !prepared_statements || binds.empty?
1177
- end
1178
-
1179
1167
  def column_for(table_name, column_name)
1180
1168
  column_name = column_name.to_s
1181
1169
  columns(table_name).detect { |c| c.name == column_name } ||
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
 
80
80
  args << config.database
81
81
 
82
- find_cmd_and_exec(["mysql", "mysql5"], *args)
82
+ find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
83
83
  end
84
84
  end
85
85
 
@@ -98,7 +98,7 @@ module ActiveRecord
98
98
  end
99
99
 
100
100
  def supports_index_sort_order?
101
- !mariadb? && database_version >= "8.0.1"
101
+ mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
102
102
  end
103
103
 
104
104
  def supports_expression_index?
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  end
139
139
 
140
140
  def supports_datetime_with_precision?
141
- mariadb? || database_version >= "5.6.4"
141
+ true
142
142
  end
143
143
 
144
144
  def supports_virtual_columns?
@@ -201,12 +201,6 @@ module ActiveRecord
201
201
 
202
202
  # HELPER METHODS ===========================================
203
203
 
204
- # The two drivers have slightly different ways of yielding hashes of results, so
205
- # this method must be implemented to provide a uniform interface.
206
- def each_hash(result) # :nodoc:
207
- raise NotImplementedError
208
- end
209
-
210
204
  # Must return the MySQL error number from the exception, if the exception has an
211
205
  # error number.
212
206
  def error_number(exception) # :nodoc:
@@ -230,24 +224,19 @@ module ActiveRecord
230
224
  # DATABASE STATEMENTS ======================================
231
225
  #++
232
226
 
233
- # Mysql2Adapter doesn't have to free a result after using it, but we use this method
234
- # to write stuff in an abstract way without concerning ourselves about whether it
235
- # needs to be explicitly freed or not.
236
- def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
237
- sql = transform_query(sql)
238
- check_if_write_query(sql)
239
-
240
- mark_transaction_written_if_write(sql)
241
- yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
242
- end
243
-
244
227
  def begin_db_transaction # :nodoc:
245
228
  internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
246
229
  end
247
230
 
248
231
  def begin_isolated_db_transaction(isolation) # :nodoc:
249
- internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
250
- begin_db_transaction
232
+ # From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
233
+ # So we don't need to implement #reset_isolation_level
234
+ execute_batch(
235
+ ["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
236
+ "TRANSACTION",
237
+ allow_retry: true,
238
+ materialize_transactions: false,
239
+ )
251
240
  end
252
241
 
253
242
  def commit_db_transaction # :nodoc:
@@ -347,7 +336,7 @@ module ActiveRecord
347
336
  rename_table_indexes(table_name, new_name, **options)
348
337
  end
349
338
 
350
- # Drops a table from the database.
339
+ # Drops a table or tables from the database.
351
340
  #
352
341
  # [<tt>:force</tt>]
353
342
  # Set to +:cascade+ to drop dependent objects as well.
@@ -361,10 +350,10 @@ module ActiveRecord
361
350
  #
362
351
  # Although this command ignores most +options+ and the block if one is given,
363
352
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
364
- # In that case, +options+ and the block will be used by create_table.
365
- def drop_table(table_name, **options)
366
- schema_cache.clear_data_source_cache!(table_name.to_s)
367
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
353
+ # In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
354
+ def drop_table(*table_names, **options)
355
+ table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
356
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
368
357
  end
369
358
 
370
359
  def rename_index(table_name, old_name, new_name)
@@ -588,7 +577,7 @@ module ActiveRecord
588
577
 
589
578
  # SHOW VARIABLES LIKE 'name'
590
579
  def show_variable(name)
591
- query_value("SELECT @@#{name}", "SCHEMA")
580
+ query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
592
581
  rescue ActiveRecord::StatementInvalid
593
582
  nil
594
583
  end
@@ -693,8 +682,8 @@ module ActiveRecord
693
682
  end
694
683
 
695
684
  def check_version # :nodoc:
696
- if database_version < "5.5.8"
697
- raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
685
+ if database_version < "5.6.4"
686
+ raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
698
687
  end
699
688
  end
700
689
 
@@ -781,7 +770,6 @@ module ActiveRecord
781
770
  def handle_warnings(sql)
782
771
  return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
783
772
 
784
- @affected_rows_before_warnings = @raw_connection.affected_rows
785
773
  warning_count = @raw_connection.warning_count
786
774
  result = @raw_connection.query("SHOW WARNINGS")
787
775
  result = [
@@ -799,11 +787,6 @@ module ActiveRecord
799
787
  warning.level == "Note" || super
800
788
  end
801
789
 
802
- # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
803
- # made since we established the connection
804
- def sync_timezone_changes(raw_connection)
805
- end
806
-
807
790
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
808
791
  ER_DB_CREATE_EXISTS = 1007
809
792
  ER_FILSORT_ABORT = 1028
@@ -973,13 +956,11 @@ module ActiveRecord
973
956
  end.join(", ")
974
957
 
975
958
  # ...and send them all in one query
976
- internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
959
+ raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
977
960
  end
978
961
 
979
962
  def column_definitions(table_name) # :nodoc:
980
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
981
- each_hash(result)
982
- end
963
+ internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
983
964
  end
984
965
 
985
966
  def create_table_info(table_name) # :nodoc:
@@ -77,14 +77,6 @@ module ActiveRecord
77
77
  0
78
78
  end
79
79
 
80
- def quoted_date(value)
81
- if supports_datetime_with_precision?
82
- super
83
- else
84
- super.sub(/\.\d{6}\z/, "")
85
- end
86
- end
87
-
88
80
  def quoted_binary(value)
89
81
  "x'#{value.hex}'"
90
82
  end
@@ -42,18 +42,12 @@ module ActiveRecord
42
42
  # :method: unsigned_bigint
43
43
  # :call-seq: unsigned_bigint(*names, **options)
44
44
 
45
- ##
46
- # :method: unsigned_float
47
- # :call-seq: unsigned_float(*names, **options)
48
-
49
- ##
50
- # :method: unsigned_decimal
51
- # :call-seq: unsigned_decimal(*names, **options)
52
-
53
45
  included do
54
46
  define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
55
47
  :tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
56
48
  :unsigned_float, :unsigned_decimal
49
+
50
+ deprecate :unsigned_float, :unsigned_decimal, deprecator: ActiveRecord.deprecator
57
51
  end
58
52
  end
59
53
 
@@ -8,45 +8,43 @@ module ActiveRecord
8
8
  def indexes(table_name)
9
9
  indexes = []
10
10
  current_index = nil
11
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
12
- each_hash(result) do |row|
13
- if current_index != row[:Key_name]
14
- next if row[:Key_name] == "PRIMARY" # skip the primary key
15
- current_index = row[:Key_name]
16
-
17
- mysql_index_type = row[:Index_type].downcase.to_sym
18
- case mysql_index_type
19
- when :fulltext, :spatial
20
- index_type = mysql_index_type
21
- when :btree, :hash
22
- index_using = mysql_index_type
23
- end
24
-
25
- indexes << [
26
- row[:Table],
27
- row[:Key_name],
28
- row[:Non_unique].to_i == 0,
29
- [],
30
- lengths: {},
31
- orders: {},
32
- type: index_type,
33
- using: index_using,
34
- comment: row[:Index_comment].presence
35
- ]
11
+ internal_exec_query("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA").each do |row|
12
+ if current_index != row["Key_name"]
13
+ next if row["Key_name"] == "PRIMARY" # skip the primary key
14
+ current_index = row["Key_name"]
15
+
16
+ mysql_index_type = row["Index_type"].downcase.to_sym
17
+ case mysql_index_type
18
+ when :fulltext, :spatial
19
+ index_type = mysql_index_type
20
+ when :btree, :hash
21
+ index_using = mysql_index_type
36
22
  end
37
23
 
38
- if row[:Expression]
39
- expression = row[:Expression].gsub("\\'", "'")
40
- expression = +"(#{expression})" unless expression.start_with?("(")
41
- indexes.last[-2] << expression
42
- indexes.last[-1][:expressions] ||= {}
43
- indexes.last[-1][:expressions][expression] = expression
44
- indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
45
- else
46
- indexes.last[-2] << row[:Column_name]
47
- indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
48
- indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
49
- end
24
+ indexes << [
25
+ row["Table"],
26
+ row["Key_name"],
27
+ row["Non_unique"].to_i == 0,
28
+ [],
29
+ lengths: {},
30
+ orders: {},
31
+ type: index_type,
32
+ using: index_using,
33
+ comment: row["Index_comment"].presence
34
+ ]
35
+ end
36
+
37
+ if expression = row["Expression"]
38
+ expression = expression.gsub("\\'", "'")
39
+ expression = +"(#{expression})" unless expression.start_with?("(")
40
+ indexes.last[-2] << expression
41
+ indexes.last[-1][:expressions] ||= {}
42
+ indexes.last[-1][:expressions][expression] = expression
43
+ indexes.last[-1][:orders][expression] = :desc if row["Collation"] == "D"
44
+ else
45
+ indexes.last[-2] << row["Column_name"]
46
+ indexes.last[-1][:lengths][row["Column_name"]] = row["Sub_part"].to_i if row["Sub_part"]
47
+ indexes.last[-1][:orders][row["Column_name"]] = :desc if row["Collation"] == "D"
50
48
  end
51
49
  end
52
50
 
@@ -87,6 +85,13 @@ module ActiveRecord
87
85
  super
88
86
  end
89
87
 
88
+ def remove_foreign_key(from_table, to_table = nil, **options)
89
+ # RESTRICT is by default in MySQL.
90
+ options.delete(:on_update) if options[:on_update] == :restrict
91
+ options.delete(:on_delete) if options[:on_delete] == :restrict
92
+ super
93
+ end
94
+
90
95
  def internal_string_options_for_primary_key
91
96
  super.tap do |options|
92
97
  if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
@@ -182,12 +187,12 @@ module ActiveRecord
182
187
  end
183
188
 
184
189
  def new_column_from_field(table_name, field, _definitions)
185
- field_name = field.fetch(:Field)
186
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
187
- default, default_function = field[:Default], nil
190
+ field_name = field.fetch("Field")
191
+ type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
192
+ default, default_function = field["Default"], nil
188
193
 
189
194
  if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
190
- default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
195
+ default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
191
196
  default, default_function = nil, default
192
197
  elsif type_metadata.extra == "DEFAULT_GENERATED"
193
198
  default = +"(#{default})" unless default.start_with?("(")
@@ -203,13 +208,13 @@ module ActiveRecord
203
208
  end
204
209
 
205
210
  MySQL::Column.new(
206
- field[:Field],
211
+ field["Field"],
207
212
  default,
208
213
  type_metadata,
209
- field[:Null] == "YES",
214
+ field["Null"] == "YES",
210
215
  default_function,
211
- collation: field[:Collation],
212
- comment: field[:Comment].presence
216
+ collation: field["Collation"],
217
+ comment: field["Comment"].presence
213
218
  )
214
219
  end
215
220