activerecord 7.2.2.1 → 8.1.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +564 -753
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -11
  7. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +10 -8
  14. data/lib/active_record/associations/collection_proxy.rb +22 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  19. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  20. data/lib/active_record/associations/join_dependency.rb +4 -2
  21. data/lib/active_record/associations/preloader/association.rb +2 -2
  22. data/lib/active_record/associations/preloader/batch.rb +7 -1
  23. data/lib/active_record/associations/preloader/branch.rb +1 -0
  24. data/lib/active_record/associations/singular_association.rb +8 -3
  25. data/lib/active_record/associations.rb +192 -24
  26. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  27. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  28. data/lib/active_record/attribute_methods/query.rb +34 -0
  29. data/lib/active_record/attribute_methods/serialization.rb +17 -4
  30. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  31. data/lib/active_record/attribute_methods.rb +24 -19
  32. data/lib/active_record/attributes.rb +40 -26
  33. data/lib/active_record/autosave_association.rb +91 -39
  34. data/lib/active_record/base.rb +3 -4
  35. data/lib/active_record/coders/json.rb +14 -5
  36. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  37. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  38. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
  39. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +458 -117
  40. data/lib/active_record/connection_adapters/abstract/database_statements.rb +136 -74
  41. data/lib/active_record/connection_adapters/abstract/query_cache.rb +44 -11
  42. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  43. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  45. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  46. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -29
  47. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  48. data/lib/active_record/connection_adapters/abstract_adapter.rb +175 -87
  49. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +77 -58
  50. data/lib/active_record/connection_adapters/column.rb +17 -4
  51. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  52. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
  53. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  54. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  55. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
  56. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  57. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -11
  58. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  59. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  60. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  62. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  65. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  66. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  67. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +28 -45
  68. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  69. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +140 -64
  70. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -105
  71. data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
  72. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  73. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  74. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  75. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  76. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -13
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +112 -42
  78. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  79. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
  80. data/lib/active_record/connection_adapters/trilogy_adapter.rb +2 -19
  81. data/lib/active_record/connection_adapters.rb +1 -56
  82. data/lib/active_record/connection_handling.rb +37 -10
  83. data/lib/active_record/core.rb +61 -25
  84. data/lib/active_record/counter_cache.rb +34 -9
  85. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  86. data/lib/active_record/database_configurations/database_config.rb +9 -1
  87. data/lib/active_record/database_configurations/hash_config.rb +67 -9
  88. data/lib/active_record/database_configurations/url_config.rb +13 -3
  89. data/lib/active_record/database_configurations.rb +7 -3
  90. data/lib/active_record/delegated_type.rb +19 -19
  91. data/lib/active_record/dynamic_matchers.rb +54 -69
  92. data/lib/active_record/encryption/config.rb +3 -1
  93. data/lib/active_record/encryption/encryptable_record.rb +9 -9
  94. data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
  95. data/lib/active_record/encryption/encryptor.rb +49 -28
  96. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  97. data/lib/active_record/encryption/scheme.rb +9 -2
  98. data/lib/active_record/enum.rb +46 -42
  99. data/lib/active_record/errors.rb +36 -12
  100. data/lib/active_record/explain.rb +1 -1
  101. data/lib/active_record/explain_registry.rb +51 -2
  102. data/lib/active_record/filter_attribute_handler.rb +73 -0
  103. data/lib/active_record/fixture_set/table_row.rb +19 -2
  104. data/lib/active_record/fixtures.rb +2 -4
  105. data/lib/active_record/future_result.rb +13 -9
  106. data/lib/active_record/gem_version.rb +3 -3
  107. data/lib/active_record/inheritance.rb +1 -1
  108. data/lib/active_record/insert_all.rb +12 -7
  109. data/lib/active_record/locking/optimistic.rb +8 -1
  110. data/lib/active_record/locking/pessimistic.rb +5 -0
  111. data/lib/active_record/log_subscriber.rb +3 -13
  112. data/lib/active_record/middleware/shard_selector.rb +34 -17
  113. data/lib/active_record/migration/command_recorder.rb +44 -11
  114. data/lib/active_record/migration/compatibility.rb +37 -24
  115. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  116. data/lib/active_record/migration.rb +50 -43
  117. data/lib/active_record/model_schema.rb +38 -13
  118. data/lib/active_record/nested_attributes.rb +6 -6
  119. data/lib/active_record/persistence.rb +162 -133
  120. data/lib/active_record/query_cache.rb +22 -15
  121. data/lib/active_record/query_logs.rb +104 -52
  122. data/lib/active_record/query_logs_formatter.rb +17 -28
  123. data/lib/active_record/querying.rb +12 -12
  124. data/lib/active_record/railtie.rb +37 -32
  125. data/lib/active_record/railties/controller_runtime.rb +11 -6
  126. data/lib/active_record/railties/databases.rake +26 -37
  127. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  128. data/lib/active_record/railties/job_runtime.rb +10 -11
  129. data/lib/active_record/reflection.rb +53 -21
  130. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  131. data/lib/active_record/relation/batches.rb +147 -73
  132. data/lib/active_record/relation/calculations.rb +80 -63
  133. data/lib/active_record/relation/delegation.rb +25 -15
  134. data/lib/active_record/relation/finder_methods.rb +54 -37
  135. data/lib/active_record/relation/merger.rb +8 -8
  136. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  137. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  138. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  139. data/lib/active_record/relation/predicate_builder.rb +22 -7
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +156 -95
  142. data/lib/active_record/relation/spawn_methods.rb +7 -7
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +122 -80
  145. data/lib/active_record/result.rb +109 -24
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +9 -6
  148. data/lib/active_record/schema_dumper.rb +47 -22
  149. data/lib/active_record/schema_migration.rb +2 -1
  150. data/lib/active_record/scoping/named.rb +5 -2
  151. data/lib/active_record/scoping.rb +0 -1
  152. data/lib/active_record/secure_token.rb +3 -3
  153. data/lib/active_record/signed_id.rb +47 -18
  154. data/lib/active_record/statement_cache.rb +24 -20
  155. data/lib/active_record/store.rb +51 -22
  156. data/lib/active_record/structured_event_subscriber.rb +85 -0
  157. data/lib/active_record/table_metadata.rb +6 -23
  158. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  159. data/lib/active_record/tasks/database_tasks.rb +85 -85
  160. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  161. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  162. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  163. data/lib/active_record/test_databases.rb +14 -4
  164. data/lib/active_record/test_fixtures.rb +39 -2
  165. data/lib/active_record/testing/query_assertions.rb +8 -2
  166. data/lib/active_record/timestamp.rb +4 -2
  167. data/lib/active_record/token_for.rb +1 -1
  168. data/lib/active_record/transaction.rb +2 -5
  169. data/lib/active_record/transactions.rb +39 -16
  170. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  171. data/lib/active_record/type/internal/timezone.rb +7 -0
  172. data/lib/active_record/type/json.rb +15 -2
  173. data/lib/active_record/type/serialized.rb +11 -4
  174. data/lib/active_record/type/type_map.rb +1 -1
  175. data/lib/active_record/type_caster/connection.rb +2 -1
  176. data/lib/active_record/validations/associated.rb +1 -1
  177. data/lib/active_record/validations/uniqueness.rb +8 -8
  178. data/lib/active_record.rb +85 -50
  179. data/lib/arel/alias_predication.rb +2 -0
  180. data/lib/arel/collectors/bind.rb +2 -2
  181. data/lib/arel/collectors/sql_string.rb +1 -1
  182. data/lib/arel/collectors/substitute_binds.rb +2 -2
  183. data/lib/arel/crud.rb +8 -11
  184. data/lib/arel/delete_manager.rb +5 -0
  185. data/lib/arel/nodes/binary.rb +1 -1
  186. data/lib/arel/nodes/count.rb +2 -2
  187. data/lib/arel/nodes/delete_statement.rb +4 -2
  188. data/lib/arel/nodes/function.rb +4 -10
  189. data/lib/arel/nodes/named_function.rb +2 -2
  190. data/lib/arel/nodes/node.rb +2 -2
  191. data/lib/arel/nodes/sql_literal.rb +1 -1
  192. data/lib/arel/nodes/update_statement.rb +4 -2
  193. data/lib/arel/nodes.rb +0 -2
  194. data/lib/arel/select_manager.rb +13 -4
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/update_manager.rb +5 -0
  197. data/lib/arel/visitors/dot.rb +2 -3
  198. data/lib/arel/visitors/postgresql.rb +55 -0
  199. data/lib/arel/visitors/sqlite.rb +55 -8
  200. data/lib/arel/visitors/to_sql.rb +6 -22
  201. data/lib/arel.rb +3 -1
  202. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  203. metadata +17 -17
  204. data/lib/active_record/explain_subscriber.rb +0 -34
  205. data/lib/active_record/normalization.rb +0 -163
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
  date: { name: "date" },
43
43
  binary: { name: "blob" },
44
44
  blob: { name: "blob" },
45
- boolean: { name: "tinyint", limit: 1 },
45
+ boolean: { name: "boolean" },
46
46
  json: { name: "json" },
47
47
  }
48
48
 
@@ -79,7 +79,11 @@ 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
+ end
84
+
85
+ def native_database_types # :nodoc:
86
+ NATIVE_DATABASE_TYPES
83
87
  end
84
88
  end
85
89
 
@@ -98,7 +102,7 @@ module ActiveRecord
98
102
  end
99
103
 
100
104
  def supports_index_sort_order?
101
- !mariadb? && database_version >= "8.0.1"
105
+ mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
102
106
  end
103
107
 
104
108
  def supports_expression_index?
@@ -138,7 +142,7 @@ module ActiveRecord
138
142
  end
139
143
 
140
144
  def supports_datetime_with_precision?
141
- mariadb? || database_version >= "5.6.4"
145
+ true
142
146
  end
143
147
 
144
148
  def supports_virtual_columns?
@@ -174,6 +178,20 @@ module ActiveRecord
174
178
  mariadb? && database_version >= "10.5.0"
175
179
  end
176
180
 
181
+ def return_value_after_insert?(column) # :nodoc:
182
+ supports_insert_returning? ? column.auto_populated? : column.auto_increment?
183
+ end
184
+
185
+ # See https://dev.mysql.com/doc/refman/8.0/en/invisible-indexes.html for more details on MySQL feature.
186
+ # See https://mariadb.com/kb/en/ignored-indexes/ for more details on the MariaDB feature.
187
+ def supports_disabling_indexes?
188
+ if mariadb?
189
+ database_version >= "10.6.0"
190
+ else
191
+ database_version >= "8.0.0"
192
+ end
193
+ end
194
+
177
195
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
178
196
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
179
197
  end
@@ -182,10 +200,6 @@ module ActiveRecord
182
200
  query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
183
201
  end
184
202
 
185
- def native_database_types
186
- NATIVE_DATABASE_TYPES
187
- end
188
-
189
203
  def index_algorithms
190
204
  {
191
205
  default: "ALGORITHM = DEFAULT",
@@ -195,14 +209,6 @@ module ActiveRecord
195
209
  }
196
210
  end
197
211
 
198
- # HELPER METHODS ===========================================
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
212
  # Must return the MySQL error number from the exception, if the exception has an
207
213
  # error number.
208
214
  def error_number(exception) # :nodoc:
@@ -226,24 +232,19 @@ module ActiveRecord
226
232
  # DATABASE STATEMENTS ======================================
227
233
  #++
228
234
 
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
235
  def begin_db_transaction # :nodoc:
241
236
  internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
242
237
  end
243
238
 
244
239
  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
240
+ # From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
241
+ # So we don't need to implement #reset_isolation_level
242
+ execute_batch(
243
+ ["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
244
+ "TRANSACTION",
245
+ allow_retry: true,
246
+ materialize_transactions: false,
247
+ )
247
248
  end
248
249
 
249
250
  def commit_db_transaction # :nodoc:
@@ -343,7 +344,7 @@ module ActiveRecord
343
344
  rename_table_indexes(table_name, new_name, **options)
344
345
  end
345
346
 
346
- # Drops a table from the database.
347
+ # Drops a table or tables from the database.
347
348
  #
348
349
  # [<tt>:force</tt>]
349
350
  # Set to +:cascade+ to drop dependent objects as well.
@@ -357,10 +358,10 @@ module ActiveRecord
357
358
  #
358
359
  # Although this command ignores most +options+ and the block if one is given,
359
360
  # 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}"
361
+ # 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.
362
+ def drop_table(*table_names, **options)
363
+ table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
364
+ 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
365
  end
365
366
 
366
367
  def rename_index(table_name, old_name, new_name)
@@ -414,7 +415,11 @@ module ActiveRecord
414
415
  type ||= column.sql_type
415
416
 
416
417
  unless options.key?(:default)
417
- options[:default] = column.default
418
+ options[:default] = if column.default_function
419
+ -> { column.default_function }
420
+ else
421
+ column.default
422
+ end
418
423
  end
419
424
 
420
425
  unless options.key?(:null)
@@ -460,6 +465,24 @@ module ActiveRecord
460
465
  CreateIndexDefinition.new(index, algorithm)
461
466
  end
462
467
 
468
+ def enable_index(table_name, index_name) # :nodoc:
469
+ raise NotImplementedError unless supports_disabling_indexes?
470
+
471
+ query = <<~SQL
472
+ ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
473
+ SQL
474
+ execute(query)
475
+ end
476
+
477
+ def disable_index(table_name, index_name) # :nodoc:
478
+ raise NotImplementedError unless supports_disabling_indexes?
479
+
480
+ query = <<~SQL
481
+ ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "IGNORED" : "INVISIBLE"}
482
+ SQL
483
+ execute(query)
484
+ end
485
+
463
486
  def add_sql_comment!(sql, comment) # :nodoc:
464
487
  sql << " COMMENT #{quote(comment)}" if comment.present?
465
488
  sql
@@ -580,7 +603,7 @@ module ActiveRecord
580
603
 
581
604
  # SHOW VARIABLES LIKE 'name'
582
605
  def show_variable(name)
583
- query_value("SELECT @@#{name}", "SCHEMA")
606
+ query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
584
607
  rescue ActiveRecord::StatementInvalid
585
608
  nil
586
609
  end
@@ -639,24 +662,26 @@ module ActiveRecord
639
662
  end
640
663
 
641
664
  def build_insert_sql(insert) # :nodoc:
665
+ # Can use any column as it will be assigned to itself.
642
666
  no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
643
667
 
644
668
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
645
669
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
646
670
  if supports_insert_raw_alias_syntax?
671
+ quoted_table_name = insert.model.quoted_table_name
647
672
  values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
648
673
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
649
674
 
650
675
  if insert.skip_duplicates?
651
676
  if no_op_column
652
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
677
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
653
678
  end
654
679
  elsif insert.update_duplicates?
655
680
  if insert.raw_update_sql?
656
681
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
657
682
  else
658
683
  sql << " ON DUPLICATE KEY UPDATE "
659
- sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
684
+ sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
660
685
  sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
661
686
  end
662
687
  end
@@ -683,8 +708,8 @@ module ActiveRecord
683
708
  end
684
709
 
685
710
  def check_version # :nodoc:
686
- if database_version < "5.5.8"
687
- raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
711
+ if database_version < "5.6.4"
712
+ raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
688
713
  end
689
714
  end
690
715
 
@@ -733,12 +758,12 @@ module ActiveRecord
733
758
  m.alias_type %r(bit)i, "binary"
734
759
  end
735
760
 
736
- def register_integer_type(mapping, key, **options)
761
+ def register_integer_type(mapping, key, limit:)
737
762
  mapping.register_type(key) do |sql_type|
738
763
  if /\bunsigned\b/.match?(sql_type)
739
- Type::UnsignedInteger.new(**options)
764
+ Type::UnsignedInteger.new(limit: limit)
740
765
  else
741
- Type::Integer.new(**options)
766
+ Type::Integer.new(limit: limit)
742
767
  end
743
768
  end
744
769
  end
@@ -757,9 +782,7 @@ module ActiveRecord
757
782
 
758
783
  private
759
784
  def strip_whitespace_characters(expression)
760
- expression = expression.gsub(/\\n|\\\\/, "")
761
- expression = expression.gsub(/\s{2,}/, " ")
762
- expression
785
+ expression.gsub('\\\n', "").gsub("x0A", "").squish
763
786
  end
764
787
 
765
788
  def extended_type_map_key
@@ -770,14 +793,13 @@ module ActiveRecord
770
793
  end
771
794
  end
772
795
 
773
- def handle_warnings(sql)
796
+ def handle_warnings(_initial_result, sql)
774
797
  return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
775
798
 
776
- @affected_rows_before_warnings = @raw_connection.affected_rows
777
799
  warning_count = @raw_connection.warning_count
778
800
  result = @raw_connection.query("SHOW WARNINGS")
779
801
  result = [
780
- ["Warning", nil, "Query had warning_count=#{warning_count} but SHOW WARNINGS did not return the warnings. Check MySQL logs or database configuration."],
802
+ ["Warning", nil, "Query had warning_count=#{warning_count} but `SHOW WARNINGS` did not return the warnings. Check MySQL logs or database configuration."],
781
803
  ] if result.count == 0
782
804
  result.each do |level, code, message|
783
805
  warning = SQLWarning.new(message, code, level, sql, @pool)
@@ -791,11 +813,6 @@ module ActiveRecord
791
813
  warning.level == "Note" || super
792
814
  end
793
815
 
794
- # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
795
- # made since we established the connection
796
- def sync_timezone_changes(raw_connection)
797
- end
798
-
799
816
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
800
817
  ER_DB_CREATE_EXISTS = 1007
801
818
  ER_FILSORT_ABORT = 1028
@@ -819,6 +836,8 @@ module ActiveRecord
819
836
  CR_SERVER_LOST = 2013
820
837
  ER_QUERY_TIMEOUT = 3024
821
838
  ER_FK_INCOMPATIBLE_COLUMNS = 3780
839
+ ER_CHECK_CONSTRAINT_VIOLATED = 3819
840
+ ER_CONSTRAINT_FAILED = 4025
822
841
  ER_CLIENT_INTERACTION_TIMEOUT = 4031
823
842
 
824
843
  def translate_exception(exception, message:, sql:, binds:)
@@ -851,6 +870,8 @@ module ActiveRecord
851
870
  RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
852
871
  when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
853
872
  NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
873
+ when ER_CHECK_CONSTRAINT_VIOLATED, ER_CONSTRAINT_FAILED
874
+ CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
854
875
  when ER_LOCK_DEADLOCK
855
876
  Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
856
877
  when ER_LOCK_WAIT_TIMEOUT
@@ -965,13 +986,11 @@ module ActiveRecord
965
986
  end.join(", ")
966
987
 
967
988
  # ...and send them all in one query
968
- internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
989
+ raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
969
990
  end
970
991
 
971
992
  def column_definitions(table_name) # :nodoc:
972
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
973
- each_hash(result)
974
- end
993
+ internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA", allow_retry: true)
975
994
  end
976
995
 
977
996
  def create_table_info(table_name) # :nodoc:
@@ -17,16 +17,22 @@ module ActiveRecord
17
17
  # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
18
18
  # +sql_type_metadata+ is various information about the type of the column
19
19
  # +null+ determines if this column allows +NULL+ values.
20
- def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
20
+ def initialize(name, cast_type, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
21
21
  @name = name.freeze
22
+ @cast_type = cast_type
22
23
  @sql_type_metadata = sql_type_metadata
23
24
  @null = null
24
- @default = default
25
+ @default = default.nil? || cast_type.mutable? ? default : cast_type.deserialize(default)
25
26
  @default_function = default_function
26
27
  @collation = collation
27
28
  @comment = comment
28
29
  end
29
30
 
31
+ def fetch_cast_type(connection) # :nodoc:
32
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
33
+ @cast_type || connection.lookup_cast_type(sql_type)
34
+ end
35
+
30
36
  def has_default?
31
37
  !default.nil? || default_function
32
38
  end
@@ -45,6 +51,7 @@ module ActiveRecord
45
51
 
46
52
  def init_with(coder)
47
53
  @name = coder["name"]
54
+ @cast_type = coder["cast_type"]
48
55
  @sql_type_metadata = coder["sql_type_metadata"]
49
56
  @null = coder["null"]
50
57
  @default = coder["default"]
@@ -55,6 +62,7 @@ module ActiveRecord
55
62
 
56
63
  def encode_with(coder)
57
64
  coder["name"] = @name
65
+ coder["cast_type"] = @cast_type
58
66
  coder["sql_type_metadata"] = @sql_type_metadata
59
67
  coder["null"] = @null
60
68
  coder["default"] = @default
@@ -75,6 +83,7 @@ module ActiveRecord
75
83
  def ==(other)
76
84
  other.is_a?(Column) &&
77
85
  name == other.name &&
86
+ cast_type == other.cast_type &&
78
87
  default == other.default &&
79
88
  sql_type_metadata == other.sql_type_metadata &&
80
89
  null == other.null &&
@@ -88,6 +97,7 @@ module ActiveRecord
88
97
  Column.hash ^
89
98
  name.hash ^
90
99
  name.encoding.hash ^
100
+ cast_type.hash ^
91
101
  default.hash ^
92
102
  sql_type_metadata.hash ^
93
103
  null.hash ^
@@ -100,11 +110,14 @@ module ActiveRecord
100
110
  false
101
111
  end
102
112
 
113
+ protected
114
+ attr_reader :cast_type
115
+
103
116
  private
104
117
  def deduplicated
105
118
  @name = -name
106
119
  @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
107
- @default = -default if default
120
+ @default = -default if String === default
108
121
  @default_function = -default_function if default_function
109
122
  @collation = -collation if collation
110
123
  @comment = -comment if comment
@@ -114,7 +127,7 @@ module ActiveRecord
114
127
 
115
128
  class NullColumn < Column
116
129
  def initialize(name, **)
117
- super(name, nil)
130
+ super(name, nil, nil)
118
131
  end
119
132
  end
120
133
  end
@@ -45,16 +45,16 @@ module ActiveRecord
45
45
  end
46
46
  end
47
47
 
48
+ def default_insert_value(column) # :nodoc:
49
+ super unless column.auto_increment?
50
+ end
51
+
48
52
  private
49
53
  # https://mariadb.com/kb/en/analyze-statement/
50
54
  def analyze_without_explain?
51
55
  mariadb? && database_version >= "10.1.0"
52
56
  end
53
57
 
54
- def default_insert_value(column)
55
- super unless column.auto_increment?
56
- end
57
-
58
58
  def returning_column_values(result)
59
59
  if supports_insert_returning?
60
60
  result.rows.first
@@ -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
@@ -110,7 +102,13 @@ module ActiveRecord
110
102
  else
111
103
  value.getlocal
112
104
  end
113
- when Date, Time
105
+ when Time
106
+ if default_timezone == :utc
107
+ value.utc? ? value : value.getutc
108
+ else
109
+ value.utc? ? value.getlocal : value
110
+ end
111
+ when Date
114
112
  value
115
113
  else
116
114
  super
@@ -49,6 +49,8 @@ module ActiveRecord
49
49
  sql << "USING #{o.using}" if o.using
50
50
  sql << "ON #{quote_table_name(o.table)}" if create
51
51
  sql << "(#{quoted_columns(o)})"
52
+ sql << "INVISIBLE" if o.disabled? && !mariadb?
53
+ sql << "IGNORED" if o.disabled? && mariadb?
52
54
 
53
55
  add_sql_comment!(sql.join(" "), o.comment)
54
56
  end
@@ -5,6 +5,7 @@ module ActiveRecord
5
5
  module MySQL
6
6
  module ColumnMethods
7
7
  extend ActiveSupport::Concern
8
+ extend ConnectionAdapters::ColumnMethods::ClassMethods
8
9
 
9
10
  ##
10
11
  # :method: blob
@@ -42,18 +43,26 @@ module ActiveRecord
42
43
  # :method: unsigned_bigint
43
44
  # :call-seq: unsigned_bigint(*names, **options)
44
45
 
45
- ##
46
- # :method: unsigned_float
47
- # :call-seq: unsigned_float(*names, **options)
46
+ define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
47
+ :tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint
48
+ end
48
49
 
49
- ##
50
- # :method: unsigned_decimal
51
- # :call-seq: unsigned_decimal(*names, **options)
50
+ # = Active Record MySQL Adapter \Index Definition
51
+ class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition # :nodoc:
52
+ attr_accessor :enabled
53
+
54
+ def initialize(*args, **kwargs)
55
+ @enabled = kwargs.key?(:enabled) ? kwargs.delete(:enabled) : true
56
+ super
57
+ end
52
58
 
53
- included do
54
- define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
55
- :tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
56
- :unsigned_float, :unsigned_decimal
59
+ def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, enabled: nil, **options)
60
+ super(columns, name:, unique:, valid:, include:, nulls_not_distinct:, **options) &&
61
+ (enabled.nil? || self.enabled == enabled)
62
+ end
63
+
64
+ def disabled?
65
+ !@enabled
57
66
  end
58
67
  end
59
68
 
@@ -106,6 +115,28 @@ module ActiveRecord
106
115
  # = Active Record MySQL Adapter \Table
107
116
  class Table < ActiveRecord::ConnectionAdapters::Table
108
117
  include ColumnMethods
118
+
119
+ # Enables an index to be used by query optimizers.
120
+ #
121
+ # t.enable_index(:email)
122
+ #
123
+ # Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
124
+ #
125
+ # See {connection.enable_index}[rdoc-ref:SchemaStatements#enable_index]
126
+ def enable_index(index_name)
127
+ @base.enable_index(name, index_name)
128
+ end
129
+
130
+ # Disables an index not to be used by query optimizers.
131
+ #
132
+ # t.disable_index(:email)
133
+ #
134
+ # Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
135
+ #
136
+ # See {connection.disable_index}[rdoc-ref:SchemaStatements#disable_index]
137
+ def disable_index(index_name)
138
+ @base.disable_index(name, index_name)
139
+ end
109
140
  end
110
141
  end
111
142
  end