activerecord 6.1.3.2 → 7.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +24 -25
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -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 +41 -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 +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  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 +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -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 +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -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|
@@ -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
@@ -12,7 +12,7 @@ module ActiveRecord
12
12
  if value.is_a?(Base)
13
13
  ActiveSupport::Deprecation.warn(<<~MSG)
14
14
  Passing an Active Record object to `quote` directly is deprecated
15
- and will be no longer quoted as id value in Rails 6.2.
15
+ and will be no longer quoted as id value in Rails 7.0.
16
16
  MSG
17
17
  value = value.id_for_database
18
18
  end
@@ -27,14 +27,14 @@ module ActiveRecord
27
27
  if value.is_a?(Base)
28
28
  ActiveSupport::Deprecation.warn(<<~MSG)
29
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 6.2.
30
+ and will be no longer type casted as id value in Rails 7.0.
31
31
  MSG
32
32
  value = value.id_for_database
33
33
  end
34
34
 
35
35
  if column
36
36
  ActiveSupport::Deprecation.warn(<<~MSG)
37
- Passing a column to `type_cast` is deprecated and will be removed in Rails 6.2.
37
+ Passing a column to `type_cast` is deprecated and will be removed in Rails 7.0.
38
38
  MSG
39
39
  type = lookup_cast_type_from_column(column)
40
40
  value = type.serialize(value)
@@ -43,6 +43,13 @@ module ActiveRecord
43
43
  _type_cast(value)
44
44
  end
45
45
 
46
+ # Quote a value to be used as a bound parameter of unknown type. For example,
47
+ # MySQL might perform dangerous castings when comparing a string to a number,
48
+ # so this method will cast numbers to string.
49
+ def quote_bound_value(value)
50
+ _quote(value)
51
+ end
52
+
46
53
  # If you are having to call this function, you are likely doing something
47
54
  # wrong. The column does not have sufficient type information if the user
48
55
  # provided a custom type on the class level either explicitly (via
@@ -59,7 +66,7 @@ module ActiveRecord
59
66
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
60
67
  # characters.
61
68
  def quote_string(s)
62
- s.gsub('\\', '\&\&').gsub("'", "''") # ' (for ruby-mode)
69
+ s.gsub("\\", '\&\&').gsub("'", "''") # ' (for ruby-mode)
63
70
  end
64
71
 
65
72
  # Quotes the column name. Defaults to no quoting.
@@ -113,10 +120,10 @@ module ActiveRecord
113
120
  # if the value is a Time responding to usec.
114
121
  def quoted_date(value)
115
122
  if value.acts_like?(:time)
116
- if ActiveRecord::Base.default_timezone == :utc
117
- value = value.getutc if value.respond_to?(:getutc) && !value.utc?
123
+ if ActiveRecord.default_timezone == :utc
124
+ value = value.getutc if !value.utc?
118
125
  else
119
- value = value.getlocal if value.respond_to?(:getlocal)
126
+ value = value.getlocal
120
127
  end
121
128
  end
122
129
 
@@ -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?
@@ -94,8 +94,8 @@ module ActiveRecord
94
94
  sql = ["CREATE"]
95
95
  sql << "UNIQUE" if index.unique
96
96
  sql << "INDEX"
97
- sql << "IF NOT EXISTS" if o.if_not_exists
98
97
  sql << o.algorithm if o.algorithm
98
+ sql << "IF NOT EXISTS" if o.if_not_exists
99
99
  sql << index.type if index.type
100
100
  sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}"
101
101
  sql << "USING #{index.using}" if supports_index_using? && index.using
@@ -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
@@ -253,6 +253,7 @@ module ActiveRecord
253
253
  define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
254
254
  :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
255
255
 
256
+ alias :blob :binary
256
257
  alias :numeric :decimal
257
258
  end
258
259
 
@@ -277,7 +278,7 @@ module ActiveRecord
277
278
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
278
279
  # is actually of this type:
279
280
  #
280
- # class SomeMigration < ActiveRecord::Migration[6.0]
281
+ # class SomeMigration < ActiveRecord::Migration[7.0]
281
282
  # def up
282
283
  # create_table :foo do |t|
283
284
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -410,6 +411,12 @@ module ActiveRecord
410
411
  end
411
412
  end
412
413
 
414
+ if @conn.supports_datetime_with_precision?
415
+ if type == :datetime && !options.key?(:precision)
416
+ options[:precision] = 6
417
+ end
418
+ end
419
+
413
420
  @columns_hash[name] = new_column_definition(name, type, **options)
414
421
 
415
422
  if index
@@ -434,12 +441,12 @@ module ActiveRecord
434
441
  indexes << [column_name, options]
435
442
  end
436
443
 
437
- def foreign_key(table_name, **options) # :nodoc:
438
- foreign_keys << [table_name, options]
444
+ def foreign_key(to_table, **options)
445
+ foreign_keys << new_foreign_key_definition(to_table, options)
439
446
  end
440
447
 
441
448
  def check_constraint(expression, **options)
442
- check_constraints << [expression, options]
449
+ check_constraints << new_check_constraint_definition(expression, options)
443
450
  end
444
451
 
445
452
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -481,6 +488,19 @@ module ActiveRecord
481
488
  create_column_definition(name, type, options)
482
489
  end
483
490
 
491
+ def new_foreign_key_definition(to_table, options) # :nodoc:
492
+ prefix = ActiveRecord::Base.table_name_prefix
493
+ suffix = ActiveRecord::Base.table_name_suffix
494
+ to_table = "#{prefix}#{to_table}#{suffix}"
495
+ options = @conn.foreign_key_options(name, to_table, options)
496
+ ForeignKeyDefinition.new(name, to_table, options)
497
+ end
498
+
499
+ def new_check_constraint_definition(expression, options) # :nodoc:
500
+ options = @conn.check_constraint_options(name, expression, options)
501
+ CheckConstraintDefinition.new(name, expression, options)
502
+ end
503
+
484
504
  private
485
505
  def create_column_definition(name, type, options)
486
506
  ColumnDefinition.new(name, type, options)
@@ -516,7 +536,7 @@ module ActiveRecord
516
536
  def name; @td.name; end
517
537
 
518
538
  def add_foreign_key(to_table, options)
519
- @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
539
+ @foreign_key_adds << @td.new_foreign_key_definition(to_table, options)
520
540
  end
521
541
 
522
542
  def drop_foreign_key(name)
@@ -524,7 +544,7 @@ module ActiveRecord
524
544
  end
525
545
 
526
546
  def add_check_constraint(expression, options)
527
- @check_constraint_adds << CheckConstraintDefinition.new(name, expression, options)
547
+ @check_constraint_adds << @td.new_check_constraint_definition(expression, options)
528
548
  end
529
549
 
530
550
  def drop_check_constraint(constraint_name)
@@ -568,6 +588,7 @@ module ActiveRecord
568
588
  # t.time
569
589
  # t.date
570
590
  # t.binary
591
+ # t.blob
571
592
  # t.boolean
572
593
  # t.foreign_key
573
594
  # t.json
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/access"
4
- require "digest/sha2"
4
+ require "openssl"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters # :nodoc:
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  table_name[0...table_alias_length].tr(".", "_")
30
30
  end
31
31
 
32
- # Returns the relation names useable to back Active Record models.
32
+ # Returns the relation names usable to back Active Record models.
33
33
  # For most adapters this means all #tables and #views.
34
34
  def data_sources
35
35
  query_values(data_source_sql, "SCHEMA")
@@ -518,24 +518,31 @@ module ActiveRecord
518
518
 
519
519
  # Add a new +type+ column named +column_name+ to +table_name+.
520
520
  #
521
+ # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
522
+ #
521
523
  # The +type+ parameter is normally one of the migrations native types,
522
524
  # which is one of the following:
523
525
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
524
526
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
525
527
  # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
526
- # <tt>:binary</tt>, <tt>:boolean</tt>.
528
+ # <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
527
529
  #
528
530
  # You may use a type not in this list as long as it is supported by your
529
531
  # database (for example, "polygon" in MySQL), but this will not be database
530
532
  # agnostic and should usually be avoided.
531
533
  #
532
534
  # Available options are (none of these exists by default):
535
+ # * <tt>:comment</tt> -
536
+ # Specifies the comment for the column. This option is ignored by some backends.
537
+ # * <tt>:collation</tt> -
538
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
539
+ # If not specified, the column will have the same collation as the table.
540
+ # * <tt>:default</tt> -
541
+ # The column's default value. Use +nil+ for +NULL+.
533
542
  # * <tt>:limit</tt> -
534
543
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
535
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
544
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
536
545
  # This option is ignored by some backends.
537
- # * <tt>:default</tt> -
538
- # The column's default value. Use +nil+ for +NULL+.
539
546
  # * <tt>:null</tt> -
540
547
  # Allows or disallows +NULL+ values in the column.
541
548
  # * <tt>:precision</tt> -
@@ -604,7 +611,13 @@ module ActiveRecord
604
611
  # # Ignores the method call if the column exists
605
612
  # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
606
613
  def add_column(table_name, column_name, type, **options)
607
- return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
614
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
615
+
616
+ if supports_datetime_with_precision?
617
+ if type == :datetime && !options.key?(:precision)
618
+ options[:precision] = 6
619
+ end
620
+ end
608
621
 
609
622
  at = create_alter_table table_name
610
623
  at.add_column(column_name, type, **options)
@@ -629,9 +642,8 @@ module ActiveRecord
629
642
  raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
630
643
  end
631
644
 
632
- column_names.each do |column_name|
633
- remove_column(table_name, column_name, type, **options)
634
- end
645
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
646
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
635
647
  end
636
648
 
637
649
  # Removes the column from the table definition.
@@ -901,7 +913,7 @@ module ActiveRecord
901
913
  remove_index(table_name, name: old_name)
902
914
  end
903
915
 
904
- def index_name(table_name, options) #:nodoc:
916
+ def index_name(table_name, options) # :nodoc:
905
917
  if Hash === options
906
918
  if options[:column]
907
919
  "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
@@ -1027,6 +1039,10 @@ module ActiveRecord
1027
1039
  #
1028
1040
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
1029
1041
  #
1042
+ # ====== Creating a foreign key, ignoring method call if the foreign key exists
1043
+ #
1044
+ # add_foreign_key(:articles, :authors, if_not_exists: true)
1045
+ #
1030
1046
  # ====== Creating a foreign key on a specific column
1031
1047
  #
1032
1048
  # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
@@ -1054,10 +1070,14 @@ module ActiveRecord
1054
1070
  # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1055
1071
  # [<tt>:on_update</tt>]
1056
1072
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1073
+ # [<tt>:if_not_exists</tt>]
1074
+ # Specifies if the foreign key already exists to not try to re-add it. This will avoid
1075
+ # duplicate column errors.
1057
1076
  # [<tt>:validate</tt>]
1058
1077
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1059
1078
  def add_foreign_key(from_table, to_table, **options)
1060
1079
  return unless supports_foreign_keys?
1080
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table)
1061
1081
 
1062
1082
  options = foreign_key_options(from_table, to_table, options)
1063
1083
  at = create_alter_table from_table
@@ -1087,12 +1107,18 @@ module ActiveRecord
1087
1107
  #
1088
1108
  # remove_foreign_key :accounts, name: :special_fk_name
1089
1109
  #
1110
+ # Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
1111
+ # don't exist.
1112
+ #
1113
+ # remove_foreign_key :accounts, :branches, if_exists: true
1114
+ #
1090
1115
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1091
1116
  # with an addition of
1092
1117
  # [<tt>:to_table</tt>]
1093
1118
  # The name of the table that contains the referenced primary key.
1094
1119
  def remove_foreign_key(from_table, to_table = nil, **options)
1095
1120
  return unless supports_foreign_keys?
1121
+ return if options[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
1096
1122
 
1097
1123
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1098
1124
 
@@ -1256,6 +1282,25 @@ module ActiveRecord
1256
1282
  columns
1257
1283
  end
1258
1284
 
1285
+ def distinct_relation_for_primary_key(relation) # :nodoc:
1286
+ values = columns_for_distinct(
1287
+ visitor.compile(relation.table[relation.primary_key]),
1288
+ relation.order_values
1289
+ )
1290
+
1291
+ limited = relation.reselect(values).distinct!
1292
+ limited_ids = select_rows(limited.arel, "SQL").map(&:last)
1293
+
1294
+ if limited_ids.empty?
1295
+ relation.none!
1296
+ else
1297
+ relation.where!(relation.primary_key => limited_ids)
1298
+ end
1299
+
1300
+ relation.limit_value = relation.offset_value = nil
1301
+ relation
1302
+ end
1303
+
1259
1304
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1260
1305
  # Additional options (like +:null+) are forwarded to #add_column.
1261
1306
  #
@@ -1277,11 +1322,10 @@ module ActiveRecord
1277
1322
  # remove_timestamps(:suppliers)
1278
1323
  #
1279
1324
  def remove_timestamps(table_name, **options)
1280
- remove_column table_name, :updated_at
1281
- remove_column table_name, :created_at
1325
+ remove_columns table_name, :updated_at, :created_at
1282
1326
  end
1283
1327
 
1284
- def update_table_definition(table_name, base) #:nodoc:
1328
+ def update_table_definition(table_name, base) # :nodoc:
1285
1329
  Table.new(table_name, base)
1286
1330
  end
1287
1331
 
@@ -1488,7 +1532,7 @@ module ActiveRecord
1488
1532
  def foreign_key_name(table_name, options)
1489
1533
  options.fetch(:name) do
1490
1534
  identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1491
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1535
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1492
1536
 
1493
1537
  "fk_rails_#{hashed_identifier}"
1494
1538
  end
@@ -1516,7 +1560,7 @@ module ActiveRecord
1516
1560
  options.fetch(:name) do
1517
1561
  expression = options.fetch(:expression)
1518
1562
  identifier = "#{table_name}_#{expression}_chk"
1519
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1563
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1520
1564
 
1521
1565
  "chk_rails_#{hashed_identifier}"
1522
1566
  end