activerecord 6.1.7.6 → 7.0.8

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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1570 -1016
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +20 -22
  17. data/lib/active_record/associations/collection_proxy.rb +15 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +144 -82
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +115 -85
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -25
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -23
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +76 -73
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +33 -18
  93. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +19 -17
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +98 -36
  96. data/lib/active_record/connection_adapters.rb +6 -5
  97. data/lib/active_record/connection_handling.rb +49 -55
  98. data/lib/active_record/core.rb +123 -148
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  100. data/lib/active_record/database_configurations/database_config.rb +12 -9
  101. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  102. data/lib/active_record/database_configurations/url_config.rb +2 -2
  103. data/lib/active_record/database_configurations.rb +15 -32
  104. data/lib/active_record/delegated_type.rb +53 -12
  105. data/lib/active_record/destroy_association_async_job.rb +1 -1
  106. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  107. data/lib/active_record/dynamic_matchers.rb +1 -1
  108. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  109. data/lib/active_record/encryption/cipher.rb +53 -0
  110. data/lib/active_record/encryption/config.rb +44 -0
  111. data/lib/active_record/encryption/configurable.rb +67 -0
  112. data/lib/active_record/encryption/context.rb +35 -0
  113. data/lib/active_record/encryption/contexts.rb +72 -0
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  116. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  118. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  119. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  120. data/lib/active_record/encryption/encryptor.rb +155 -0
  121. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  122. data/lib/active_record/encryption/errors.rb +15 -0
  123. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  124. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  125. data/lib/active_record/encryption/key.rb +28 -0
  126. data/lib/active_record/encryption/key_generator.rb +42 -0
  127. data/lib/active_record/encryption/key_provider.rb +46 -0
  128. data/lib/active_record/encryption/message.rb +33 -0
  129. data/lib/active_record/encryption/message_serializer.rb +90 -0
  130. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  131. data/lib/active_record/encryption/properties.rb +76 -0
  132. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  133. data/lib/active_record/encryption/scheme.rb +99 -0
  134. data/lib/active_record/encryption.rb +55 -0
  135. data/lib/active_record/enum.rb +50 -43
  136. data/lib/active_record/errors.rb +67 -4
  137. data/lib/active_record/explain_registry.rb +11 -6
  138. data/lib/active_record/explain_subscriber.rb +1 -1
  139. data/lib/active_record/fixture_set/file.rb +15 -1
  140. data/lib/active_record/fixture_set/table_row.rb +41 -6
  141. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  142. data/lib/active_record/fixtures.rb +20 -23
  143. data/lib/active_record/future_result.rb +139 -0
  144. data/lib/active_record/gem_version.rb +5 -5
  145. data/lib/active_record/inheritance.rb +55 -17
  146. data/lib/active_record/insert_all.rb +80 -14
  147. data/lib/active_record/integration.rb +4 -3
  148. data/lib/active_record/internal_metadata.rb +1 -5
  149. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  150. data/lib/active_record/locking/optimistic.rb +36 -21
  151. data/lib/active_record/locking/pessimistic.rb +10 -4
  152. data/lib/active_record/log_subscriber.rb +23 -7
  153. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  154. data/lib/active_record/middleware/database_selector.rb +18 -6
  155. data/lib/active_record/middleware/shard_selector.rb +60 -0
  156. data/lib/active_record/migration/command_recorder.rb +8 -9
  157. data/lib/active_record/migration/compatibility.rb +93 -46
  158. data/lib/active_record/migration/join_table.rb +1 -1
  159. data/lib/active_record/migration.rb +167 -87
  160. data/lib/active_record/model_schema.rb +58 -59
  161. data/lib/active_record/nested_attributes.rb +13 -12
  162. data/lib/active_record/no_touching.rb +3 -3
  163. data/lib/active_record/null_relation.rb +2 -6
  164. data/lib/active_record/persistence.rb +231 -61
  165. data/lib/active_record/query_cache.rb +2 -2
  166. data/lib/active_record/query_logs.rb +149 -0
  167. data/lib/active_record/querying.rb +16 -6
  168. data/lib/active_record/railtie.rb +136 -22
  169. data/lib/active_record/railties/controller_runtime.rb +4 -5
  170. data/lib/active_record/railties/databases.rake +78 -136
  171. data/lib/active_record/readonly_attributes.rb +11 -0
  172. data/lib/active_record/reflection.rb +80 -49
  173. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  174. data/lib/active_record/relation/batches.rb +6 -6
  175. data/lib/active_record/relation/calculations.rb +92 -60
  176. data/lib/active_record/relation/delegation.rb +7 -7
  177. data/lib/active_record/relation/finder_methods.rb +31 -35
  178. data/lib/active_record/relation/merger.rb +20 -13
  179. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  180. data/lib/active_record/relation/predicate_builder.rb +1 -6
  181. data/lib/active_record/relation/query_attribute.rb +28 -11
  182. data/lib/active_record/relation/query_methods.rb +304 -68
  183. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  184. data/lib/active_record/relation/spawn_methods.rb +2 -2
  185. data/lib/active_record/relation/where_clause.rb +10 -19
  186. data/lib/active_record/relation.rb +189 -88
  187. data/lib/active_record/result.rb +23 -11
  188. data/lib/active_record/runtime_registry.rb +9 -13
  189. data/lib/active_record/sanitization.rb +17 -12
  190. data/lib/active_record/schema.rb +38 -23
  191. data/lib/active_record/schema_dumper.rb +29 -19
  192. data/lib/active_record/schema_migration.rb +4 -4
  193. data/lib/active_record/scoping/default.rb +60 -13
  194. data/lib/active_record/scoping/named.rb +3 -11
  195. data/lib/active_record/scoping.rb +64 -34
  196. data/lib/active_record/serialization.rb +6 -1
  197. data/lib/active_record/signed_id.rb +3 -3
  198. data/lib/active_record/store.rb +2 -2
  199. data/lib/active_record/suppressor.rb +11 -15
  200. data/lib/active_record/table_metadata.rb +6 -2
  201. data/lib/active_record/tasks/database_tasks.rb +127 -60
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  204. data/lib/active_record/test_databases.rb +1 -1
  205. data/lib/active_record/test_fixtures.rb +9 -6
  206. data/lib/active_record/timestamp.rb +3 -4
  207. data/lib/active_record/transactions.rb +12 -17
  208. data/lib/active_record/translation.rb +3 -3
  209. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  210. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  211. data/lib/active_record/type/internal/timezone.rb +2 -2
  212. data/lib/active_record/type/serialized.rb +9 -5
  213. data/lib/active_record/type/type_map.rb +17 -20
  214. data/lib/active_record/type.rb +1 -2
  215. data/lib/active_record/validations/associated.rb +4 -4
  216. data/lib/active_record/validations/presence.rb +2 -2
  217. data/lib/active_record/validations/uniqueness.rb +4 -4
  218. data/lib/active_record/version.rb +1 -1
  219. data/lib/active_record.rb +225 -27
  220. data/lib/arel/attributes/attribute.rb +0 -8
  221. data/lib/arel/crud.rb +28 -22
  222. data/lib/arel/delete_manager.rb +18 -4
  223. data/lib/arel/filter_predications.rb +9 -0
  224. data/lib/arel/insert_manager.rb +2 -3
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/casted.rb +1 -1
  227. data/lib/arel/nodes/delete_statement.rb +12 -13
  228. data/lib/arel/nodes/filter.rb +10 -0
  229. data/lib/arel/nodes/function.rb +1 -0
  230. data/lib/arel/nodes/insert_statement.rb +2 -2
  231. data/lib/arel/nodes/select_core.rb +2 -2
  232. data/lib/arel/nodes/select_statement.rb +2 -2
  233. data/lib/arel/nodes/update_statement.rb +8 -3
  234. data/lib/arel/nodes.rb +1 -0
  235. data/lib/arel/predications.rb +11 -3
  236. data/lib/arel/select_manager.rb +10 -4
  237. data/lib/arel/table.rb +0 -1
  238. data/lib/arel/tree_manager.rb +0 -12
  239. data/lib/arel/update_manager.rb +18 -4
  240. data/lib/arel/visitors/dot.rb +80 -90
  241. data/lib/arel/visitors/mysql.rb +8 -2
  242. data/lib/arel/visitors/postgresql.rb +0 -10
  243. data/lib/arel/visitors/to_sql.rb +58 -2
  244. data/lib/arel.rb +2 -1
  245. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  247. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  248. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  249. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  250. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  251. metadata +55 -11
@@ -59,17 +59,13 @@ module ActiveRecord
59
59
  end
60
60
 
61
61
  # Returns an ActiveRecord::Result instance.
62
- def select_all(arel, name = nil, binds = [], preparable: nil)
62
+ def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
63
63
  arel = arel_from_relation(arel)
64
64
  sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
65
65
 
66
- if prepared_statements && preparable
67
- select_prepared(sql, name, binds)
68
- else
69
- select(sql, name, binds)
70
- end
66
+ select(sql, name, binds, prepare: prepared_statements && preparable, async: async && FutureResult::SelectAll)
71
67
  rescue ::RangeError
72
- ActiveRecord::Result.new([], [])
68
+ ActiveRecord::Result.empty
73
69
  end
74
70
 
75
71
  # Returns a record hash with the column names as keys and column values
@@ -310,20 +306,20 @@ module ActiveRecord
310
306
  #
311
307
  # The mysql2 and postgresql adapters support setting the transaction
312
308
  # isolation level.
313
- def transaction(requires_new: nil, isolation: nil, joinable: true)
309
+ def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
314
310
  if !requires_new && current_transaction.joinable?
315
311
  if isolation
316
312
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
317
313
  end
318
314
  yield
319
315
  else
320
- transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable) { yield }
316
+ transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
321
317
  end
322
318
  rescue ActiveRecord::Rollback
323
319
  # rollbacks are silently swallowed
324
320
  end
325
321
 
326
- attr_reader :transaction_manager #:nodoc:
322
+ attr_reader :transaction_manager # :nodoc:
327
323
 
328
324
  delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
329
325
  :commit_transaction, :rollback_transaction, :materialize_transactions,
@@ -340,7 +336,7 @@ module ActiveRecord
340
336
  current_transaction.open?
341
337
  end
342
338
 
343
- def reset_transaction #:nodoc:
339
+ def reset_transaction # :nodoc:
344
340
  @transaction_manager = ConnectionAdapters::TransactionManager.new(self)
345
341
  end
346
342
 
@@ -378,7 +374,7 @@ module ActiveRecord
378
374
  exec_rollback_db_transaction
379
375
  end
380
376
 
381
- def exec_rollback_db_transaction() end #:nodoc:
377
+ def exec_rollback_db_transaction() end # :nodoc:
382
378
 
383
379
  def rollback_to_savepoint(name = nil)
384
380
  exec_rollback_to_savepoint(name)
@@ -445,6 +441,19 @@ module ActiveRecord
445
441
  end
446
442
  end
447
443
 
444
+ # This is a safe default, even if not high precision on all databases
445
+ HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP").freeze # :nodoc:
446
+ private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
447
+
448
+ # Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
449
+ # arbitrary precision date/time columns.
450
+ #
451
+ # Adapters supporting datetime with precision should override this to
452
+ # provide as much precision as is available.
453
+ def high_precision_current_timestamp
454
+ HIGH_PRECISION_CURRENT_TIMESTAMP
455
+ end
456
+
448
457
  private
449
458
  def execute_batch(statements, name = nil)
450
459
  statements.each do |statement|
@@ -460,7 +469,7 @@ module ActiveRecord
460
469
  end
461
470
 
462
471
  def build_fixture_sql(fixtures, table_name)
463
- columns = schema_cache.columns_hash(table_name)
472
+ columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
464
473
 
465
474
  values_list = fixtures.map do |fixture|
466
475
  fixture = fixture.stringify_keys
@@ -481,8 +490,7 @@ module ActiveRecord
481
490
  end
482
491
 
483
492
  table = Arel::Table.new(table_name)
484
- manager = Arel::InsertManager.new
485
- manager.into(table)
493
+ manager = Arel::InsertManager.new(table)
486
494
 
487
495
  if values_list.size == 1
488
496
  values = values_list.shift
@@ -503,10 +511,10 @@ module ActiveRecord
503
511
  end
504
512
 
505
513
  def build_fixture_statements(fixture_set)
506
- fixture_set.map do |table_name, fixtures|
514
+ fixture_set.filter_map do |table_name, fixtures|
507
515
  next if fixtures.empty?
508
516
  build_fixture_sql(fixtures, table_name)
509
- end.compact
517
+ end
510
518
  end
511
519
 
512
520
  def build_truncate_statement(table_name)
@@ -528,12 +536,28 @@ module ActiveRecord
528
536
  end
529
537
 
530
538
  # Returns an ActiveRecord::Result instance.
531
- def select(sql, name = nil, binds = [])
532
- exec_query(sql, name, binds, prepare: false)
533
- end
539
+ def select(sql, name = nil, binds = [], prepare: false, async: false)
540
+ if async && async_enabled?
541
+ if current_transaction.joinable?
542
+ raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
543
+ end
544
+
545
+ future_result = async.new(
546
+ pool,
547
+ sql,
548
+ name,
549
+ binds,
550
+ prepare: prepare,
551
+ )
552
+ if supports_concurrent_connections? && current_transaction.closed?
553
+ future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
554
+ else
555
+ future_result.execute!(self)
556
+ end
557
+ return future_result
558
+ end
534
559
 
535
- def select_prepared(sql, name = nil, binds = [])
536
- exec_query(sql, name, binds, prepare: true)
560
+ exec_query(sql, name, binds, prepare: prepare)
537
561
  end
538
562
 
539
563
  def sql_for_insert(sql, pk, binds)
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module ConnectionAdapters # :nodoc:
7
7
  module QueryCache
8
8
  class << self
9
- def included(base) #:nodoc:
9
+ def included(base) # :nodoc:
10
10
  dirties_query_cache base, :create, :insert, :update, :delete, :truncate, :truncate_tables,
11
11
  :rollback_to_savepoint, :rollback_db_transaction, :exec_insert_all
12
12
 
@@ -93,18 +93,37 @@ module ActiveRecord
93
93
  end
94
94
  end
95
95
 
96
- def select_all(arel, name = nil, binds = [], preparable: nil)
97
- if @query_cache_enabled && !locked?(arel)
98
- arel = arel_from_relation(arel)
96
+ def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
97
+ arel = arel_from_relation(arel)
98
+
99
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
100
+ # Such queries should not be cached.
101
+ if @query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
99
102
  sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)
100
103
 
101
- cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
104
+ if async
105
+ lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
106
+ else
107
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
108
+ end
102
109
  else
103
110
  super
104
111
  end
105
112
  end
106
113
 
107
114
  private
115
+ def lookup_sql_cache(sql, name, binds)
116
+ @lock.synchronize do
117
+ if @query_cache[sql].key?(binds)
118
+ ActiveSupport::Notifications.instrument(
119
+ "sql.active_record",
120
+ cache_notification_info(sql, name, binds)
121
+ )
122
+ @query_cache[sql][binds]
123
+ end
124
+ end
125
+ end
126
+
108
127
  def cache_sql(sql, name, binds)
109
128
  @lock.synchronize do
110
129
  result =
@@ -134,13 +153,6 @@ module ActiveRecord
134
153
  }
135
154
  end
136
155
 
137
- # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
138
- # queries should not be cached.
139
- def locked?(arel)
140
- arel = arel.arel if arel.is_a?(Relation)
141
- arel.respond_to?(:locked) && arel.locked
142
- end
143
-
144
156
  def configure_query_cache!
145
157
  enable_query_cache! if pool.query_cache_enabled
146
158
  end
@@ -9,38 +9,46 @@ module ActiveRecord
9
9
  # Quotes the column value to help prevent
10
10
  # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
11
11
  def quote(value)
12
- if value.is_a?(Base)
13
- ActiveSupport::Deprecation.warn(<<~MSG)
14
- Passing an Active Record object to `quote` directly is deprecated
15
- and will be no longer quoted as id value in Rails 7.0.
16
- MSG
17
- value = value.id_for_database
12
+ case value
13
+ when String, Symbol, ActiveSupport::Multibyte::Chars
14
+ "'#{quote_string(value.to_s)}'"
15
+ when true then quoted_true
16
+ when false then quoted_false
17
+ when nil then "NULL"
18
+ # BigDecimals need to be put in a non-normalized form and quoted.
19
+ when BigDecimal then value.to_s("F")
20
+ when Numeric, ActiveSupport::Duration then value.to_s
21
+ when Type::Binary::Data then quoted_binary(value)
22
+ when Type::Time::Value then "'#{quoted_time(value)}'"
23
+ when Date, Time then "'#{quoted_date(value)}'"
24
+ when Class then "'#{value}'"
25
+ else raise TypeError, "can't quote #{value.class.name}"
18
26
  end
19
-
20
- _quote(value)
21
27
  end
22
28
 
23
29
  # Cast a +value+ to a type that the database understands. For example,
24
30
  # SQLite does not understand dates, so this method will convert a Date
25
31
  # to a String.
26
- def type_cast(value, column = nil)
27
- if value.is_a?(Base)
28
- ActiveSupport::Deprecation.warn(<<~MSG)
29
- Passing an Active Record object to `type_cast` directly is deprecated
30
- and will be no longer type casted as id value in Rails 7.0.
31
- MSG
32
- value = value.id_for_database
33
- end
34
-
35
- if column
36
- ActiveSupport::Deprecation.warn(<<~MSG)
37
- Passing a column to `type_cast` is deprecated and will be removed in Rails 7.0.
38
- MSG
39
- type = lookup_cast_type_from_column(column)
40
- value = type.serialize(value)
32
+ def type_cast(value)
33
+ case value
34
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
35
+ value.to_s
36
+ when true then unquoted_true
37
+ when false then unquoted_false
38
+ # BigDecimals need to be put in a non-normalized form and quoted.
39
+ when BigDecimal then value.to_s("F")
40
+ when nil, Numeric, String then value
41
+ when Type::Time::Value then quoted_time(value)
42
+ when Date, Time then quoted_date(value)
43
+ else raise TypeError, "can't cast #{value.class.name}"
41
44
  end
45
+ end
42
46
 
43
- _type_cast(value)
47
+ # Quote a value to be used as a bound parameter of unknown type. For example,
48
+ # MySQL might perform dangerous castings when comparing a string to a number,
49
+ # so this method will cast numbers to string.
50
+ def quote_bound_value(value)
51
+ quote(value)
44
52
  end
45
53
 
46
54
  # If you are having to call this function, you are likely doing something
@@ -59,7 +67,7 @@ module ActiveRecord
59
67
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
60
68
  # characters.
61
69
  def quote_string(s)
62
- s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
70
+ s.gsub("\\", '\&\&').gsub("'", "''") # ' (for ruby-mode)
63
71
  end
64
72
 
65
73
  # Quotes the column name. Defaults to no quoting.
@@ -113,14 +121,14 @@ module ActiveRecord
113
121
  # if the value is a Time responding to usec.
114
122
  def quoted_date(value)
115
123
  if value.acts_like?(:time)
116
- if ActiveRecord::Base.default_timezone == :utc
117
- value = value.getutc if value.respond_to?(:getutc) && !value.utc?
124
+ if ActiveRecord.default_timezone == :utc
125
+ value = value.getutc if !value.utc?
118
126
  else
119
- value = value.getlocal if value.respond_to?(:getlocal)
127
+ value = value.getlocal
120
128
  end
121
129
  end
122
130
 
123
- result = value.to_s(:db)
131
+ result = value.to_fs(:db)
124
132
  if value.respond_to?(:usec) && value.usec > 0
125
133
  result << "." << sprintf("%06d", value.usec)
126
134
  else
@@ -205,16 +213,11 @@ module ActiveRecord
205
213
 
206
214
  private
207
215
  def type_casted_binds(binds)
208
- case binds.first
209
- when Array
210
- binds.map { |column, value| type_cast(value, column) }
211
- else
212
- binds.map do |value|
213
- if ActiveModel::Attribute === value
214
- type_cast(value.value_for_database)
215
- else
216
- type_cast(value)
217
- end
216
+ binds.map do |value|
217
+ if ActiveModel::Attribute === value
218
+ type_cast(value.value_for_database)
219
+ else
220
+ type_cast(value)
218
221
  end
219
222
  end
220
223
  end
@@ -222,39 +225,6 @@ module ActiveRecord
222
225
  def lookup_cast_type(sql_type)
223
226
  type_map.lookup(sql_type)
224
227
  end
225
-
226
- def _quote(value)
227
- case value
228
- when String, Symbol, ActiveSupport::Multibyte::Chars
229
- "'#{quote_string(value.to_s)}'"
230
- when true then quoted_true
231
- when false then quoted_false
232
- when nil then "NULL"
233
- # BigDecimals need to be put in a non-normalized form and quoted.
234
- when BigDecimal then value.to_s("F")
235
- when Numeric, ActiveSupport::Duration then value.to_s
236
- when Type::Binary::Data then quoted_binary(value)
237
- when Type::Time::Value then "'#{quoted_time(value)}'"
238
- when Date, Time then "'#{quoted_date(value)}'"
239
- when Class then "'#{value}'"
240
- else raise TypeError, "can't quote #{value.class.name}"
241
- end
242
- end
243
-
244
- def _type_cast(value)
245
- case value
246
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
247
- value.to_s
248
- when true then unquoted_true
249
- when false then unquoted_false
250
- # BigDecimals need to be put in a non-normalized form and quoted.
251
- when BigDecimal then value.to_s("F")
252
- when nil, Numeric, String then value
253
- when Type::Time::Value then quoted_time(value)
254
- when Date, Time then quoted_date(value)
255
- else raise TypeError, "can't cast #{value.class.name}"
256
- end
257
- end
258
228
  end
259
229
  end
260
230
  end
@@ -14,8 +14,8 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
17
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
18
- :quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?, :check_constraint_options,
17
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?,
18
+ :quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?,
19
19
  to: :@conn, private: true
20
20
 
21
21
  private
@@ -52,11 +52,11 @@ module ActiveRecord
52
52
  end
53
53
 
54
54
  if supports_foreign_keys?
55
- statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
55
+ statements.concat(o.foreign_keys.map { |fk| accept fk })
56
56
  end
57
57
 
58
58
  if supports_check_constraints?
59
- statements.concat(o.check_constraints.map { |expression, options| check_constraint_in_create(o.name, expression, options) })
59
+ statements.concat(o.check_constraints.map { |chk| accept chk })
60
60
  end
61
61
 
62
62
  create_sql << "(#{statements.join(', ')})" if statements.present?
@@ -159,19 +159,6 @@ module ActiveRecord
159
159
  " TEMPORARY" if o.temporary
160
160
  end
161
161
 
162
- def foreign_key_in_create(from_table, to_table, options)
163
- prefix = ActiveRecord::Base.table_name_prefix
164
- suffix = ActiveRecord::Base.table_name_suffix
165
- to_table = "#{prefix}#{to_table}#{suffix}"
166
- options = foreign_key_options(from_table, to_table, options)
167
- accept ForeignKeyDefinition.new(from_table, to_table, options)
168
- end
169
-
170
- def check_constraint_in_create(table_name, expression, options)
171
- options = check_constraint_options(table_name, expression, options)
172
- accept CheckConstraintDefinition.new(table_name, expression, options)
173
- end
174
-
175
162
  def action_sql(action, dependency)
176
163
  case dependency
177
164
  when :nullify then "ON #{action} SET NULL"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- module ConnectionAdapters #:nodoc:
4
+ module ConnectionAdapters # :nodoc:
5
5
  # Abstract representation of an index definition on a table. Instances of
6
6
  # this type are typically created and returned by methods in database
7
7
  # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes
@@ -79,13 +79,13 @@ module ActiveRecord
79
79
 
80
80
  AddColumnDefinition = Struct.new(:column) # :nodoc:
81
81
 
82
- ChangeColumnDefinition = Struct.new(:column, :name) #:nodoc:
82
+ ChangeColumnDefinition = Struct.new(:column, :name) # :nodoc:
83
83
 
84
84
  CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists) # :nodoc:
85
85
 
86
86
  PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
87
87
 
88
- ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do #:nodoc:
88
+ ForeignKeyDefinition = Struct.new(:from_table, :to_table, :options) do # :nodoc:
89
89
  def name
90
90
  options[:name]
91
91
  end
@@ -106,6 +106,10 @@ module ActiveRecord
106
106
  options[:on_update]
107
107
  end
108
108
 
109
+ def deferrable
110
+ options[:deferrable]
111
+ end
112
+
109
113
  def custom_primary_key?
110
114
  options[:primary_key] != default_primary_key
111
115
  end
@@ -257,6 +261,7 @@ module ActiveRecord
257
261
  define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
258
262
  :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
259
263
 
264
+ alias :blob :binary
260
265
  alias :numeric :decimal
261
266
  end
262
267
 
@@ -281,7 +286,7 @@ module ActiveRecord
281
286
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
282
287
  # is actually of this type:
283
288
  #
284
- # class SomeMigration < ActiveRecord::Migration[6.0]
289
+ # class SomeMigration < ActiveRecord::Migration[7.0]
285
290
  # def up
286
291
  # create_table :foo do |t|
287
292
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -406,14 +411,7 @@ module ActiveRecord
406
411
  name = name.to_s
407
412
  type = type.to_sym if type
408
413
 
409
- if @columns_hash[name]
410
- if @columns_hash[name].primary_key?
411
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
412
- else
413
- raise ArgumentError, "you can't define an already defined column '#{name}'."
414
- end
415
- end
416
-
414
+ raise_on_duplicate_column(name)
417
415
  @columns_hash[name] = new_column_definition(name, type, **options)
418
416
 
419
417
  if index
@@ -438,12 +436,12 @@ module ActiveRecord
438
436
  indexes << [column_name, options]
439
437
  end
440
438
 
441
- def foreign_key(table_name, **options) # :nodoc:
442
- foreign_keys << [table_name, options]
439
+ def foreign_key(to_table, **options)
440
+ foreign_keys << new_foreign_key_definition(to_table, options)
443
441
  end
444
442
 
445
443
  def check_constraint(expression, **options)
446
- check_constraints << [expression, options]
444
+ check_constraints << new_check_constraint_definition(expression, options)
447
445
  end
448
446
 
449
447
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -480,11 +478,31 @@ module ActiveRecord
480
478
  type = integer_like_primary_key_type(type, options)
481
479
  end
482
480
  type = aliased_types(type.to_s, type)
481
+
482
+ if @conn.supports_datetime_with_precision?
483
+ if type == :datetime && !options.key?(:precision)
484
+ options[:precision] = 6
485
+ end
486
+ end
487
+
483
488
  options[:primary_key] ||= type == :primary_key
484
489
  options[:null] = false if options[:primary_key]
485
490
  create_column_definition(name, type, options)
486
491
  end
487
492
 
493
+ def new_foreign_key_definition(to_table, options) # :nodoc:
494
+ prefix = ActiveRecord::Base.table_name_prefix
495
+ suffix = ActiveRecord::Base.table_name_suffix
496
+ to_table = "#{prefix}#{to_table}#{suffix}"
497
+ options = @conn.foreign_key_options(name, to_table, options)
498
+ ForeignKeyDefinition.new(name, to_table, options)
499
+ end
500
+
501
+ def new_check_constraint_definition(expression, options) # :nodoc:
502
+ options = @conn.check_constraint_options(name, expression, options)
503
+ CheckConstraintDefinition.new(name, expression, options)
504
+ end
505
+
488
506
  private
489
507
  def create_column_definition(name, type, options)
490
508
  ColumnDefinition.new(name, type, options)
@@ -501,6 +519,16 @@ module ActiveRecord
501
519
  def integer_like_primary_key_type(type, options)
502
520
  type
503
521
  end
522
+
523
+ def raise_on_duplicate_column(name)
524
+ if @columns_hash[name]
525
+ if @columns_hash[name].primary_key?
526
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
527
+ else
528
+ raise ArgumentError, "you can't define an already defined column '#{name}'."
529
+ end
530
+ end
531
+ end
504
532
  end
505
533
 
506
534
  class AlterTable # :nodoc:
@@ -520,7 +548,7 @@ module ActiveRecord
520
548
  def name; @td.name; end
521
549
 
522
550
  def add_foreign_key(to_table, options)
523
- @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
551
+ @foreign_key_adds << @td.new_foreign_key_definition(to_table, options)
524
552
  end
525
553
 
526
554
  def drop_foreign_key(name)
@@ -528,7 +556,7 @@ module ActiveRecord
528
556
  end
529
557
 
530
558
  def add_check_constraint(expression, options)
531
- @check_constraint_adds << CheckConstraintDefinition.new(name, expression, options)
559
+ @check_constraint_adds << @td.new_check_constraint_definition(expression, options)
532
560
  end
533
561
 
534
562
  def drop_check_constraint(constraint_name)
@@ -572,6 +600,7 @@ module ActiveRecord
572
600
  # t.time
573
601
  # t.date
574
602
  # t.binary
603
+ # t.blob
575
604
  # t.boolean
576
605
  # t.foreign_key
577
606
  # t.json
@@ -636,8 +665,8 @@ module ActiveRecord
636
665
  # end
637
666
  #
638
667
  # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
639
- def index_exists?(column_name, options = {})
640
- @base.index_exists?(name, column_name, options)
668
+ def index_exists?(column_name, **options)
669
+ @base.index_exists?(name, column_name, **options)
641
670
  end
642
671
 
643
672
  # Renames the given index on the table.
@@ -789,8 +818,8 @@ module ActiveRecord
789
818
  # t.check_constraint("price > 0", name: "price_check")
790
819
  #
791
820
  # See {connection.add_check_constraint}[rdoc-ref:SchemaStatements#add_check_constraint]
792
- def check_constraint(*args)
793
- @base.add_check_constraint(name, *args)
821
+ def check_constraint(*args, **options)
822
+ @base.add_check_constraint(name, *args, **options)
794
823
  end
795
824
 
796
825
  # Removes the given check constraint from the table.
@@ -798,8 +827,8 @@ module ActiveRecord
798
827
  # t.remove_check_constraint(name: "price_check")
799
828
  #
800
829
  # See {connection.remove_check_constraint}[rdoc-ref:SchemaStatements#remove_check_constraint]
801
- def remove_check_constraint(*args)
802
- @base.remove_check_constraint(name, *args)
830
+ def remove_check_constraint(*args, **options)
831
+ @base.remove_check_constraint(name, *args, **options)
803
832
  end
804
833
  end
805
834
  end
@@ -3,6 +3,8 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters # :nodoc:
5
5
  class SchemaDumper < SchemaDumper # :nodoc:
6
+ DEFAULT_DATETIME_PRECISION = 6 # :nodoc:
7
+
6
8
  def self.create(connection, options)
7
9
  new(connection, options)
8
10
  end
@@ -63,7 +65,18 @@ module ActiveRecord
63
65
  end
64
66
 
65
67
  def schema_precision(column)
66
- column.precision.inspect if column.precision
68
+ if column.type == :datetime
69
+ case column.precision
70
+ when nil
71
+ "nil"
72
+ when DEFAULT_DATETIME_PRECISION
73
+ nil
74
+ else
75
+ column.precision.inspect
76
+ end
77
+ elsif column.precision
78
+ column.precision.inspect
79
+ end
67
80
  end
68
81
 
69
82
  def schema_scale(column)