activerecord 7.2.1.1 → 8.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +220 -756
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/association.rb +25 -5
  5. data/lib/active_record/associations/builder/association.rb +7 -6
  6. data/lib/active_record/associations/collection_association.rb +10 -8
  7. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  8. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  9. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +4 -4
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/singular_association.rb +8 -3
  13. data/lib/active_record/associations.rb +34 -4
  14. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  15. data/lib/active_record/attribute_assignment.rb +9 -1
  16. data/lib/active_record/attribute_methods/primary_key.rb +2 -7
  17. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -12
  18. data/lib/active_record/attributes.rb +1 -2
  19. data/lib/active_record/autosave_association.rb +69 -27
  20. data/lib/active_record/callbacks.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  22. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  24. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -9
  25. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  26. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
  27. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  28. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  29. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  30. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
  31. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  32. data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
  33. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -42
  34. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  35. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  36. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
  37. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  38. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  39. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  40. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  41. data/lib/active_record/connection_adapters/postgresql/oid/cidr.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 +0 -11
  45. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
  46. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  47. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +54 -14
  48. data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -97
  49. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  50. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  51. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  52. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  53. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
  54. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  55. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  56. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  57. data/lib/active_record/connection_adapters.rb +0 -56
  58. data/lib/active_record/connection_handling.rb +22 -0
  59. data/lib/active_record/core.rb +28 -18
  60. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  61. data/lib/active_record/encryption/config.rb +3 -1
  62. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  63. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  64. data/lib/active_record/encryption/encryptor.rb +15 -8
  65. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  66. data/lib/active_record/encryption/key_provider.rb +1 -1
  67. data/lib/active_record/encryption/scheme.rb +8 -1
  68. data/lib/active_record/encryption.rb +2 -0
  69. data/lib/active_record/enum.rb +54 -75
  70. data/lib/active_record/errors.rb +13 -5
  71. data/lib/active_record/fixtures.rb +0 -2
  72. data/lib/active_record/future_result.rb +14 -10
  73. data/lib/active_record/gem_version.rb +4 -4
  74. data/lib/active_record/insert_all.rb +1 -1
  75. data/lib/active_record/locking/optimistic.rb +1 -1
  76. data/lib/active_record/log_subscriber.rb +5 -11
  77. data/lib/active_record/marshalling.rb +4 -1
  78. data/lib/active_record/migration/command_recorder.rb +22 -5
  79. data/lib/active_record/migration/compatibility.rb +5 -2
  80. data/lib/active_record/migration.rb +35 -38
  81. data/lib/active_record/model_schema.rb +4 -6
  82. data/lib/active_record/nested_attributes.rb +11 -2
  83. data/lib/active_record/persistence.rb +128 -130
  84. data/lib/active_record/query_cache.rb +0 -4
  85. data/lib/active_record/query_logs.rb +102 -50
  86. data/lib/active_record/query_logs_formatter.rb +17 -28
  87. data/lib/active_record/querying.rb +8 -8
  88. data/lib/active_record/railtie.rb +9 -38
  89. data/lib/active_record/railties/databases.rake +1 -1
  90. data/lib/active_record/reflection.rb +23 -23
  91. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  92. data/lib/active_record/relation/batches.rb +132 -72
  93. data/lib/active_record/relation/calculations.rb +41 -40
  94. data/lib/active_record/relation/delegation.rb +25 -14
  95. data/lib/active_record/relation/finder_methods.rb +18 -18
  96. data/lib/active_record/relation/merger.rb +8 -8
  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 +14 -1
  100. data/lib/active_record/relation/query_methods.rb +122 -71
  101. data/lib/active_record/relation/spawn_methods.rb +1 -1
  102. data/lib/active_record/relation.rb +79 -61
  103. data/lib/active_record/result.rb +66 -4
  104. data/lib/active_record/sanitization.rb +7 -6
  105. data/lib/active_record/schema_dumper.rb +5 -0
  106. data/lib/active_record/schema_migration.rb +2 -1
  107. data/lib/active_record/scoping/named.rb +5 -2
  108. data/lib/active_record/statement_cache.rb +12 -12
  109. data/lib/active_record/store.rb +7 -3
  110. data/lib/active_record/table_metadata.rb +1 -3
  111. data/lib/active_record/tasks/database_tasks.rb +40 -47
  112. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  113. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  114. data/lib/active_record/test_fixtures.rb +12 -0
  115. data/lib/active_record/testing/query_assertions.rb +2 -2
  116. data/lib/active_record/token_for.rb +1 -1
  117. data/lib/active_record/validations/uniqueness.rb +9 -8
  118. data/lib/active_record.rb +15 -45
  119. data/lib/arel/collectors/bind.rb +1 -1
  120. data/lib/arel/table.rb +3 -7
  121. data/lib/arel/visitors/sqlite.rb +25 -0
  122. metadata +10 -11
  123. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -345,6 +345,15 @@ module ActiveRecord
345
345
  # # Creates a table called 'assemblies_parts' with no id.
346
346
  # create_join_table(:assemblies, :parts)
347
347
  #
348
+ # # Creates a table called 'paper_boxes_papers' with no id.
349
+ # create_join_table('papers', 'paper_boxes')
350
+ #
351
+ # A duplicate prefix is combined into a single prefix. This is useful for
352
+ # namespaced models like Music::Artist and Music::Record:
353
+ #
354
+ # # Creates a table called 'music_artists_records' with no id.
355
+ # create_join_table('music_artists', 'music_records')
356
+ #
348
357
  # You can pass an +options+ hash which can include the following keys:
349
358
  # [<tt>:table_name</tt>]
350
359
  # Sets the table name, overriding the default.
@@ -516,7 +525,7 @@ module ActiveRecord
516
525
  raise NotImplementedError, "rename_table is not implemented"
517
526
  end
518
527
 
519
- # Drops a table from the database.
528
+ # Drops a table or tables from the database.
520
529
  #
521
530
  # [<tt>:force</tt>]
522
531
  # Set to +:cascade+ to drop dependent objects as well.
@@ -527,10 +536,12 @@ module ActiveRecord
527
536
  #
528
537
  # Although this command ignores most +options+ and the block if one is given,
529
538
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
530
- # In that case, +options+ and the block will be used by #create_table.
531
- def drop_table(table_name, **options)
532
- schema_cache.clear_data_source_cache!(table_name.to_s)
533
- execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
539
+ # 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.
540
+ def drop_table(*table_names, **options)
541
+ table_names.each do |table_name|
542
+ schema_cache.clear_data_source_cache!(table_name.to_s)
543
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
544
+ end
534
545
  end
535
546
 
536
547
  # Add a new +type+ column named +column_name+ to +table_name+.
@@ -682,7 +693,7 @@ module ActiveRecord
682
693
  #
683
694
  # If the options provided include an +if_exists+ key, it will be used to check if the
684
695
  # column does not exist. This will silently ignore the migration rather than raising
685
- # if the column was already used.
696
+ # if the column was already removed.
686
697
  #
687
698
  # remove_column(:suppliers, :qualification, if_exists: true)
688
699
  def remove_column(table_name, column_name, type = nil, **options)
@@ -844,6 +855,16 @@ module ActiveRecord
844
855
  #
845
856
  # Note: only supported by PostgreSQL.
846
857
  #
858
+ # ====== Creating an index where NULLs are treated equally
859
+ #
860
+ # add_index(:people, :last_name, nulls_not_distinct: true)
861
+ #
862
+ # generates:
863
+ #
864
+ # CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
865
+ #
866
+ # Note: only supported by PostgreSQL version 15.0.0 and greater.
867
+ #
847
868
  # ====== Creating an index with a specific method
848
869
  #
849
870
  # add_index(:developers, :name, using: 'btree')
@@ -1313,7 +1334,6 @@ module ActiveRecord
1313
1334
  execute schema_creation.accept(at)
1314
1335
  end
1315
1336
 
1316
-
1317
1337
  # Checks to see if a check constraint exists on a table for a given check constraint definition.
1318
1338
  #
1319
1339
  # check_constraint_exists?(:products, name: "price_check")
@@ -1325,6 +1345,13 @@ module ActiveRecord
1325
1345
  check_constraint_for(table_name, **options).present?
1326
1346
  end
1327
1347
 
1348
+ def remove_constraint(table_name, constraint_name) # :nodoc:
1349
+ at = create_alter_table(table_name)
1350
+ at.drop_constraint(constraint_name)
1351
+
1352
+ execute schema_creation.accept(at)
1353
+ end
1354
+
1328
1355
  def dump_schema_information # :nodoc:
1329
1356
  versions = pool.schema_migration.versions
1330
1357
  insert_versions_sql(versions) if versions.any?
@@ -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"
@@ -576,23 +575,31 @@ module ActiveRecord
576
575
  end
577
576
 
578
577
  # This is meant to be implemented by the adapters that support custom enum types
579
- def create_enum(*) # :nodoc:
578
+ def create_enum(...) # :nodoc:
580
579
  end
581
580
 
582
581
  # This is meant to be implemented by the adapters that support custom enum types
583
- def drop_enum(*) # :nodoc:
582
+ def drop_enum(...) # :nodoc:
584
583
  end
585
584
 
586
585
  # This is meant to be implemented by the adapters that support custom enum types
587
- def rename_enum(*) # :nodoc:
586
+ def rename_enum(...) # :nodoc:
588
587
  end
589
588
 
590
589
  # This is meant to be implemented by the adapters that support custom enum types
591
- def add_enum_value(*) # :nodoc:
590
+ def add_enum_value(...) # :nodoc:
592
591
  end
593
592
 
594
593
  # This is meant to be implemented by the adapters that support custom enum types
595
- 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:
596
603
  end
597
604
 
598
605
  def advisory_locks_enabled? # :nodoc:
@@ -1044,7 +1051,8 @@ module ActiveRecord
1044
1051
  end
1045
1052
 
1046
1053
  def retryable_connection_error?(exception)
1047
- exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1054
+ (exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
1055
+ exception.is_a?(ConnectionFailed)
1048
1056
  end
1049
1057
 
1050
1058
  def invalidate_transaction(exception)
@@ -1105,24 +1113,25 @@ module ActiveRecord
1105
1113
  end
1106
1114
  end
1107
1115
 
1108
- def translate_exception_class(e, sql, binds)
1109
- message = "#{e.class.name}: #{e.message}"
1116
+ def translate_exception_class(native_error, sql, binds)
1117
+ return native_error if native_error.is_a?(ActiveRecordError)
1118
+
1119
+ message = "#{native_error.class.name}: #{native_error.message}"
1110
1120
 
1111
- exception = translate_exception(
1112
- e, message: message, sql: sql, binds: binds
1121
+ active_record_error = translate_exception(
1122
+ native_error, message: message, sql: sql, binds: binds
1113
1123
  )
1114
- exception.set_backtrace e.backtrace
1115
- exception
1124
+ active_record_error.set_backtrace(native_error.backtrace)
1125
+ active_record_error
1116
1126
  end
1117
1127
 
1118
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
1128
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1119
1129
  @instrumenter.instrument(
1120
1130
  "sql.active_record",
1121
1131
  sql: sql,
1122
1132
  name: name,
1123
1133
  binds: binds,
1124
1134
  type_casted_binds: type_casted_binds,
1125
- statement_name: statement_name,
1126
1135
  async: async,
1127
1136
  connection: self,
1128
1137
  transaction: current_transaction.user_transaction.presence,
@@ -1133,13 +1142,6 @@ module ActiveRecord
1133
1142
  raise ex.set_query(sql, binds)
1134
1143
  end
1135
1144
 
1136
- def transform_query(sql)
1137
- ActiveRecord.query_transformers.each do |transformer|
1138
- sql = transformer.call(sql, self)
1139
- end
1140
- sql
1141
- end
1142
-
1143
1145
  def translate_exception(exception, message:, sql:, binds:)
1144
1146
  # override in derived class
1145
1147
  case exception
@@ -1150,10 +1152,6 @@ module ActiveRecord
1150
1152
  end
1151
1153
  end
1152
1154
 
1153
- def without_prepared_statement?(binds)
1154
- !prepared_statements || binds.empty?
1155
- end
1156
-
1157
1155
  def column_for(table_name, column_name)
1158
1156
  column_name = column_name.to_s
1159
1157
  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?
@@ -197,12 +197,6 @@ module ActiveRecord
197
197
 
198
198
  # HELPER METHODS ===========================================
199
199
 
200
- # The two drivers have slightly different ways of yielding hashes of results, so
201
- # this method must be implemented to provide a uniform interface.
202
- def each_hash(result) # :nodoc:
203
- raise NotImplementedError
204
- end
205
-
206
200
  # Must return the MySQL error number from the exception, if the exception has an
207
201
  # error number.
208
202
  def error_number(exception) # :nodoc:
@@ -226,24 +220,19 @@ module ActiveRecord
226
220
  # DATABASE STATEMENTS ======================================
227
221
  #++
228
222
 
229
- # Mysql2Adapter doesn't have to free a result after using it, but we use this method
230
- # to write stuff in an abstract way without concerning ourselves about whether it
231
- # needs to be explicitly freed or not.
232
- def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
233
- sql = transform_query(sql)
234
- check_if_write_query(sql)
235
-
236
- mark_transaction_written_if_write(sql)
237
- yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
238
- end
239
-
240
223
  def begin_db_transaction # :nodoc:
241
224
  internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
242
225
  end
243
226
 
244
227
  def begin_isolated_db_transaction(isolation) # :nodoc:
245
- internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
246
- begin_db_transaction
228
+ # From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
229
+ # So we don't need to implement #reset_isolation_level
230
+ execute_batch(
231
+ ["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
232
+ "TRANSACTION",
233
+ allow_retry: true,
234
+ materialize_transactions: false,
235
+ )
247
236
  end
248
237
 
249
238
  def commit_db_transaction # :nodoc:
@@ -343,7 +332,7 @@ module ActiveRecord
343
332
  rename_table_indexes(table_name, new_name, **options)
344
333
  end
345
334
 
346
- # Drops a table from the database.
335
+ # Drops a table or tables from the database.
347
336
  #
348
337
  # [<tt>:force</tt>]
349
338
  # Set to +:cascade+ to drop dependent objects as well.
@@ -357,10 +346,10 @@ module ActiveRecord
357
346
  #
358
347
  # Although this command ignores most +options+ and the block if one is given,
359
348
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
360
- # In that case, +options+ and the block will be used by create_table.
361
- def drop_table(table_name, **options)
362
- schema_cache.clear_data_source_cache!(table_name.to_s)
363
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
349
+ # 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.
350
+ def drop_table(*table_names, **options)
351
+ table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
352
+ 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}"
364
353
  end
365
354
 
366
355
  def rename_index(table_name, old_name, new_name)
@@ -580,7 +569,7 @@ module ActiveRecord
580
569
 
581
570
  # SHOW VARIABLES LIKE 'name'
582
571
  def show_variable(name)
583
- query_value("SELECT @@#{name}", "SCHEMA")
572
+ query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
584
573
  rescue ActiveRecord::StatementInvalid
585
574
  nil
586
575
  end
@@ -639,7 +628,7 @@ module ActiveRecord
639
628
  end
640
629
 
641
630
  def build_insert_sql(insert) # :nodoc:
642
- no_op_column = quote_column_name(insert.keys.first)
631
+ no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
643
632
 
644
633
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
645
634
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
@@ -648,7 +637,9 @@ module ActiveRecord
648
637
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
649
638
 
650
639
  if insert.skip_duplicates?
651
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
640
+ if no_op_column
641
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
642
+ end
652
643
  elsif insert.update_duplicates?
653
644
  if insert.raw_update_sql?
654
645
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
@@ -662,7 +653,9 @@ module ActiveRecord
662
653
  sql = +"INSERT #{insert.into} #{insert.values_list}"
663
654
 
664
655
  if insert.skip_duplicates?
665
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
656
+ if no_op_column
657
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
658
+ end
666
659
  elsif insert.update_duplicates?
667
660
  sql << " ON DUPLICATE KEY UPDATE "
668
661
  if insert.raw_update_sql?
@@ -679,8 +672,8 @@ module ActiveRecord
679
672
  end
680
673
 
681
674
  def check_version # :nodoc:
682
- if database_version < "5.5.8"
683
- raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
675
+ if database_version < "5.6.4"
676
+ raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
684
677
  end
685
678
  end
686
679
 
@@ -787,11 +780,6 @@ module ActiveRecord
787
780
  warning.level == "Note" || super
788
781
  end
789
782
 
790
- # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
791
- # made since we established the connection
792
- def sync_timezone_changes(raw_connection)
793
- end
794
-
795
783
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
796
784
  ER_DB_CREATE_EXISTS = 1007
797
785
  ER_FILSORT_ABORT = 1028
@@ -961,13 +949,11 @@ module ActiveRecord
961
949
  end.join(", ")
962
950
 
963
951
  # ...and send them all in one query
964
- internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
952
+ raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
965
953
  end
966
954
 
967
955
  def column_definitions(table_name) # :nodoc:
968
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
969
- each_hash(result)
970
- end
956
+ internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
971
957
  end
972
958
 
973
959
  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
 
@@ -182,12 +180,12 @@ module ActiveRecord
182
180
  end
183
181
 
184
182
  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
183
+ field_name = field.fetch("Field")
184
+ type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
185
+ default, default_function = field["Default"], nil
188
186
 
189
187
  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])
188
+ default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
191
189
  default, default_function = nil, default
192
190
  elsif type_metadata.extra == "DEFAULT_GENERATED"
193
191
  default = +"(#{default})" unless default.start_with?("(")
@@ -203,13 +201,13 @@ module ActiveRecord
203
201
  end
204
202
 
205
203
  MySQL::Column.new(
206
- field[:Field],
204
+ field["Field"],
207
205
  default,
208
206
  type_metadata,
209
- field[:Null] == "YES",
207
+ field["Null"] == "YES",
210
208
  default_function,
211
- collation: field[:Collation],
212
- comment: field[:Comment].presence
209
+ collation: field["Collation"],
210
+ comment: field["Comment"].presence
213
211
  )
214
212
  end
215
213