activerecord 7.2.3.1 → 8.0.5.1

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +475 -946
  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/join_dependency.rb +2 -2
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/preloader/batch.rb +7 -1
  13. data/lib/active_record/associations/singular_association.rb +8 -3
  14. data/lib/active_record/associations.rb +34 -4
  15. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  16. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  17. data/lib/active_record/attribute_methods/query.rb +34 -0
  18. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  19. data/lib/active_record/autosave_association.rb +69 -27
  20. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -17
  24. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  25. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  26. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  28. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  29. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -10
  30. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  31. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -74
  32. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
  33. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  34. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  35. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  36. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  37. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  38. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  39. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  40. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  41. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  42. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  43. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  44. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  45. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  46. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  47. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  48. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -96
  49. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  50. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  51. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  52. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  53. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  54. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  56. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  57. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  58. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  59. data/lib/active_record/connection_adapters.rb +0 -56
  60. data/lib/active_record/connection_handling.rb +23 -1
  61. data/lib/active_record/core.rb +29 -14
  62. data/lib/active_record/database_configurations/database_config.rb +4 -0
  63. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  64. data/lib/active_record/encryption/config.rb +3 -1
  65. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  66. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  67. data/lib/active_record/encryption/encryptor.rb +16 -8
  68. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  69. data/lib/active_record/encryption/scheme.rb +8 -1
  70. data/lib/active_record/enum.rb +9 -22
  71. data/lib/active_record/errors.rb +13 -5
  72. data/lib/active_record/fixtures.rb +0 -2
  73. data/lib/active_record/future_result.rb +15 -9
  74. data/lib/active_record/gem_version.rb +3 -3
  75. data/lib/active_record/insert_all.rb +3 -3
  76. data/lib/active_record/locking/optimistic.rb +1 -1
  77. data/lib/active_record/log_subscriber.rb +5 -11
  78. data/lib/active_record/migration/command_recorder.rb +32 -12
  79. data/lib/active_record/migration/compatibility.rb +5 -2
  80. data/lib/active_record/migration.rb +38 -42
  81. data/lib/active_record/model_schema.rb +3 -4
  82. data/lib/active_record/nested_attributes.rb +4 -6
  83. data/lib/active_record/persistence.rb +128 -130
  84. data/lib/active_record/query_logs.rb +102 -50
  85. data/lib/active_record/query_logs_formatter.rb +17 -28
  86. data/lib/active_record/querying.rb +8 -8
  87. data/lib/active_record/railtie.rb +2 -26
  88. data/lib/active_record/railties/databases.rake +11 -35
  89. data/lib/active_record/reflection.rb +18 -21
  90. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  91. data/lib/active_record/relation/batches.rb +132 -72
  92. data/lib/active_record/relation/calculations.rb +40 -39
  93. data/lib/active_record/relation/delegation.rb +25 -14
  94. data/lib/active_record/relation/finder_methods.rb +18 -18
  95. data/lib/active_record/relation/merger.rb +8 -8
  96. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  97. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  98. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  99. data/lib/active_record/relation/predicate_builder.rb +13 -0
  100. data/lib/active_record/relation/query_methods.rb +105 -61
  101. data/lib/active_record/relation/spawn_methods.rb +7 -7
  102. data/lib/active_record/relation/where_clause.rb +1 -1
  103. data/lib/active_record/relation.rb +80 -62
  104. data/lib/active_record/result.rb +66 -4
  105. data/lib/active_record/sanitization.rb +7 -6
  106. data/lib/active_record/schema_dumper.rb +5 -0
  107. data/lib/active_record/schema_migration.rb +2 -1
  108. data/lib/active_record/scoping/named.rb +5 -2
  109. data/lib/active_record/statement_cache.rb +14 -14
  110. data/lib/active_record/store.rb +7 -3
  111. data/lib/active_record/table_metadata.rb +1 -3
  112. data/lib/active_record/tasks/database_tasks.rb +69 -60
  113. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  114. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
  115. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  116. data/lib/active_record/test_databases.rb +1 -1
  117. data/lib/active_record/test_fixtures.rb +12 -0
  118. data/lib/active_record/token_for.rb +1 -1
  119. data/lib/active_record/transactions.rb +5 -6
  120. data/lib/active_record/type/serialized.rb +5 -0
  121. data/lib/active_record/validations/uniqueness.rb +8 -8
  122. data/lib/active_record.rb +21 -48
  123. data/lib/arel/collectors/bind.rb +2 -2
  124. data/lib/arel/collectors/sql_string.rb +1 -1
  125. data/lib/arel/collectors/substitute_binds.rb +2 -2
  126. data/lib/arel/nodes/binary.rb +1 -1
  127. data/lib/arel/nodes/node.rb +1 -1
  128. data/lib/arel/nodes/sql_literal.rb +1 -1
  129. data/lib/arel/predications.rb +1 -3
  130. data/lib/arel/table.rb +3 -7
  131. metadata +10 -11
  132. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -28,6 +28,7 @@ module ActiveRecord
28
28
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
29
29
  sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
30
30
  sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
31
+ sql << o.constraint_drops.map { |con| visit_DropConstraint con }.join(" ")
31
32
  end
32
33
 
33
34
  def visit_ColumnDefinition(o)
@@ -96,9 +97,11 @@ module ActiveRecord
96
97
  "ADD #{accept(o)}"
97
98
  end
98
99
 
99
- def visit_DropForeignKey(name)
100
+ def visit_DropConstraint(name)
100
101
  "DROP CONSTRAINT #{quote_column_name(name)}"
101
102
  end
103
+ alias :visit_DropForeignKey :visit_DropConstraint
104
+ alias :visit_DropCheckConstraint :visit_DropConstraint
102
105
 
103
106
  def visit_CreateIndexDefinition(o)
104
107
  index = o.index
@@ -127,10 +130,6 @@ module ActiveRecord
127
130
  "ADD #{accept(o)}"
128
131
  end
129
132
 
130
- def visit_DropCheckConstraint(name)
131
- "DROP CONSTRAINT #{quote_column_name(name)}"
132
- end
133
-
134
133
  def quoted_columns(o)
135
134
  String === o.columns ? o.columns : quoted_columns_for_index(o.columns, o.column_options)
136
135
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module ActiveRecord
5
4
  module ConnectionAdapters # :nodoc:
6
5
  # Abstract representation of an index definition on a table. Instances of
@@ -352,7 +351,7 @@ module ActiveRecord
352
351
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
353
352
  # is actually of this type:
354
353
  #
355
- # class SomeMigration < ActiveRecord::Migration[7.2]
354
+ # class SomeMigration < ActiveRecord::Migration[8.0]
356
355
  # def up
357
356
  # create_table :foo do |t|
358
357
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -626,6 +625,7 @@ module ActiveRecord
626
625
  attr_reader :adds
627
626
  attr_reader :foreign_key_adds, :foreign_key_drops
628
627
  attr_reader :check_constraint_adds, :check_constraint_drops
628
+ attr_reader :constraint_drops
629
629
 
630
630
  def initialize(td)
631
631
  @td = td
@@ -634,6 +634,7 @@ module ActiveRecord
634
634
  @foreign_key_drops = []
635
635
  @check_constraint_adds = []
636
636
  @check_constraint_drops = []
637
+ @constraint_drops = []
637
638
  end
638
639
 
639
640
  def name; @td.name; end
@@ -654,6 +655,10 @@ module ActiveRecord
654
655
  @check_constraint_drops << constraint_name
655
656
  end
656
657
 
658
+ def drop_constraint(constraint_name)
659
+ @constraint_drops << constraint_name
660
+ end
661
+
657
662
  def add_column(name, type, **options)
658
663
  name = name.to_s
659
664
  type = type.to_sym
@@ -348,6 +348,15 @@ module ActiveRecord
348
348
  # # Creates a table called 'assemblies_parts' with no id.
349
349
  # create_join_table(:assemblies, :parts)
350
350
  #
351
+ # # Creates a table called 'paper_boxes_papers' with no id.
352
+ # create_join_table('papers', 'paper_boxes')
353
+ #
354
+ # A duplicate prefix is combined into a single prefix. This is useful for
355
+ # namespaced models like Music::Artist and Music::Record:
356
+ #
357
+ # # Creates a table called 'music_artists_records' with no id.
358
+ # create_join_table('music_artists', 'music_records')
359
+ #
351
360
  # You can pass an +options+ hash which can include the following keys:
352
361
  # [<tt>:table_name</tt>]
353
362
  # Sets the table name, overriding the default.
@@ -519,7 +528,7 @@ module ActiveRecord
519
528
  raise NotImplementedError, "rename_table is not implemented"
520
529
  end
521
530
 
522
- # Drops a table from the database.
531
+ # Drops a table or tables from the database.
523
532
  #
524
533
  # [<tt>:force</tt>]
525
534
  # Set to +:cascade+ to drop dependent objects as well.
@@ -530,17 +539,19 @@ module ActiveRecord
530
539
  #
531
540
  # Although this command ignores most +options+ and the block if one is given,
532
541
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
533
- # In that case, +options+ and the block will be used by #create_table.
534
- def drop_table(table_name, **options)
535
- schema_cache.clear_data_source_cache!(table_name.to_s)
536
- execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
542
+ # 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.
543
+ def drop_table(*table_names, **options)
544
+ table_names.each do |table_name|
545
+ schema_cache.clear_data_source_cache!(table_name.to_s)
546
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
547
+ end
537
548
  end
538
549
 
539
550
  # Add a new +type+ column named +column_name+ to +table_name+.
540
551
  #
541
552
  # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
542
553
  #
543
- # The +type+ parameter is normally one of the migrations native types,
554
+ # The +type+ parameter is normally one of the migration's native types,
544
555
  # which is one of the following:
545
556
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
546
557
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
@@ -847,6 +858,16 @@ module ActiveRecord
847
858
  #
848
859
  # Note: only supported by PostgreSQL.
849
860
  #
861
+ # ====== Creating an index where NULLs are treated equally
862
+ #
863
+ # add_index(:people, :last_name, nulls_not_distinct: true)
864
+ #
865
+ # generates:
866
+ #
867
+ # CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
868
+ #
869
+ # Note: only supported by PostgreSQL version 15.0.0 and greater.
870
+ #
850
871
  # ====== Creating an index with a specific method
851
872
  #
852
873
  # add_index(:developers, :name, using: 'btree')
@@ -1316,7 +1337,6 @@ module ActiveRecord
1316
1337
  execute schema_creation.accept(at)
1317
1338
  end
1318
1339
 
1319
-
1320
1340
  # Checks to see if a check constraint exists on a table for a given check constraint definition.
1321
1341
  #
1322
1342
  # check_constraint_exists?(:products, name: "price_check")
@@ -1328,6 +1348,13 @@ module ActiveRecord
1328
1348
  check_constraint_for(table_name, **options).present?
1329
1349
  end
1330
1350
 
1351
+ def remove_constraint(table_name, constraint_name) # :nodoc:
1352
+ at = create_alter_table(table_name)
1353
+ at.drop_constraint(constraint_name)
1354
+
1355
+ execute schema_creation.accept(at)
1356
+ end
1357
+
1331
1358
  def dump_schema_information # :nodoc:
1332
1359
  versions = pool.schema_migration.versions
1333
1360
  insert_versions_sql(versions) if versions.any?
@@ -1533,11 +1560,11 @@ module ActiveRecord
1533
1560
  non_combinable_operations = []
1534
1561
 
1535
1562
  operations.each do |command, args|
1536
- table, arguments = args.shift, args
1563
+ args.shift # remove table_name
1537
1564
  method = :"#{command}_for_alter"
1538
1565
 
1539
1566
  if respond_to?(method, true)
1540
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1567
+ sqls, procs = Array(send(method, table_name, *args)).partition { |v| v.is_a?(String) }
1541
1568
  sql_fragments.concat(sqls)
1542
1569
  non_combinable_operations.concat(procs)
1543
1570
  else
@@ -1545,7 +1572,7 @@ module ActiveRecord
1545
1572
  non_combinable_operations.each(&:call)
1546
1573
  sql_fragments = []
1547
1574
  non_combinable_operations = []
1548
- send(command, table, *arguments)
1575
+ send(command, table_name, *args)
1549
1576
  end
1550
1577
  end
1551
1578
 
@@ -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:
@@ -673,34 +666,33 @@ module ActiveRecord
673
666
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
674
667
 
675
668
  @lock.synchronize do
676
- reconnect
669
+ attempt_configure_connection do
670
+ reconnect
677
671
 
678
- enable_lazy_transactions!
679
- @raw_connection_dirty = false
680
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
681
- @verified = true
672
+ enable_lazy_transactions!
673
+ @raw_connection_dirty = false
674
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
675
+ @verified = true
682
676
 
683
- reset_transaction(restore: restore_transactions) do
684
- clear_cache!(new_connection: true)
685
- attempt_configure_connection
686
- end
687
- rescue => original_exception
688
- translated_exception = translate_exception_class(original_exception, nil, nil)
689
- retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
677
+ reset_transaction(restore: restore_transactions) do
678
+ clear_cache!(new_connection: true)
679
+ configure_connection
680
+ end
681
+ rescue => original_exception
682
+ translated_exception = translate_exception_class(original_exception, nil, nil)
683
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
690
684
 
691
- if !retry_deadline_exceeded && retries_available > 0
692
- retries_available -= 1
685
+ if !retry_deadline_exceeded && retries_available > 0
686
+ retries_available -= 1
693
687
 
694
- if retryable_connection_error?(translated_exception)
695
- backoff(connection_retries - retries_available)
696
- retry
688
+ if retryable_connection_error?(translated_exception)
689
+ backoff(connection_retries - retries_available)
690
+ retry
691
+ end
697
692
  end
698
- end
699
-
700
- @last_activity = nil
701
- @verified = false
702
693
 
703
- raise translated_exception
694
+ raise translated_exception
695
+ end
704
696
  end
705
697
  end
706
698
 
@@ -708,6 +700,8 @@ module ActiveRecord
708
700
  # method does nothing.
709
701
  def disconnect!
710
702
  @lock.synchronize do
703
+ @last_activity = nil
704
+ @verified = false
711
705
  clear_cache!(new_connection: true)
712
706
  reset_transaction
713
707
  @raw_connection_dirty = false
@@ -733,9 +727,11 @@ module ActiveRecord
733
727
  # should call super immediately after resetting the connection (and while
734
728
  # still holding @lock).
735
729
  def reset!
736
- clear_cache!(new_connection: true)
737
- reset_transaction
738
- attempt_configure_connection
730
+ attempt_configure_connection do
731
+ clear_cache!(new_connection: true)
732
+ reset_transaction
733
+ configure_connection
734
+ end
739
735
  end
740
736
 
741
737
  # Removes the connection from the pool and disconnect it.
@@ -769,11 +765,13 @@ module ActiveRecord
769
765
  unless active?
770
766
  @lock.synchronize do
771
767
  if @unconfigured_connection
772
- @raw_connection = @unconfigured_connection
773
- @unconfigured_connection = nil
774
- attempt_configure_connection
775
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
776
- @verified = true
768
+ attempt_configure_connection do
769
+ @raw_connection = @unconfigured_connection
770
+ @unconfigured_connection = nil
771
+ configure_connection
772
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
773
+ @verified = true
774
+ end
777
775
  return
778
776
  end
779
777
 
@@ -1066,7 +1064,8 @@ module ActiveRecord
1066
1064
  end
1067
1065
 
1068
1066
  def retryable_connection_error?(exception)
1069
- exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1067
+ (exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
1068
+ exception.is_a?(ConnectionFailed)
1070
1069
  end
1071
1070
 
1072
1071
  def invalidate_transaction(exception)
@@ -1127,24 +1126,25 @@ module ActiveRecord
1127
1126
  end
1128
1127
  end
1129
1128
 
1130
- def translate_exception_class(e, sql, binds)
1131
- message = "#{e.class.name}: #{e.message}"
1129
+ def translate_exception_class(native_error, sql, binds)
1130
+ return native_error if native_error.is_a?(ActiveRecordError)
1132
1131
 
1133
- exception = translate_exception(
1134
- e, message: message, sql: sql, binds: binds
1132
+ message = "#{native_error.class.name}: #{native_error.message}"
1133
+
1134
+ active_record_error = translate_exception(
1135
+ native_error, message: message, sql: sql, binds: binds
1135
1136
  )
1136
- exception.set_backtrace e.backtrace
1137
- exception
1137
+ active_record_error.set_backtrace(native_error.backtrace)
1138
+ active_record_error
1138
1139
  end
1139
1140
 
1140
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
1141
- @instrumenter.instrument(
1141
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1142
+ instrumenter.instrument(
1142
1143
  "sql.active_record",
1143
1144
  sql: sql,
1144
1145
  name: name,
1145
1146
  binds: binds,
1146
1147
  type_casted_binds: type_casted_binds,
1147
- statement_name: statement_name,
1148
1148
  async: async,
1149
1149
  connection: self,
1150
1150
  transaction: current_transaction.user_transaction.presence,
@@ -1155,11 +1155,8 @@ module ActiveRecord
1155
1155
  raise ex.set_query(sql, binds)
1156
1156
  end
1157
1157
 
1158
- def transform_query(sql)
1159
- ActiveRecord.query_transformers.each do |transformer|
1160
- sql = transformer.call(sql, self)
1161
- end
1162
- sql
1158
+ def instrumenter # :nodoc:
1159
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
1163
1160
  end
1164
1161
 
1165
1162
  def translate_exception(exception, message:, sql:, binds:)
@@ -1172,10 +1169,6 @@ module ActiveRecord
1172
1169
  end
1173
1170
  end
1174
1171
 
1175
- def without_prepared_statement?(binds)
1176
- !prepared_statements || binds.empty?
1177
- end
1178
-
1179
1172
  def column_for(table_name, column_name)
1180
1173
  column_name = column_name.to_s
1181
1174
  columns(table_name).detect { |c| c.name == column_name } ||
@@ -1228,7 +1221,7 @@ module ActiveRecord
1228
1221
  end
1229
1222
 
1230
1223
  def attempt_configure_connection
1231
- configure_connection
1224
+ yield
1232
1225
  rescue Exception # Need to handle things such as Timeout::ExitException
1233
1226
  disconnect!
1234
1227
  raise
@@ -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