activerecord 6.1.7.7 → 7.0.0.alpha1

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 (220) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +726 -1389
  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 +31 -9
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  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 +14 -23
  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 -47
  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 +2 -14
  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 +12 -14
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  63. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  65. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
  67. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  70. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  71. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -32
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  78. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  79. data/lib/active_record/connection_adapters/postgresql_adapter.rb +159 -102
  80. data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
  82. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  84. data/lib/active_record/connection_adapters.rb +6 -5
  85. data/lib/active_record/connection_handling.rb +20 -38
  86. data/lib/active_record/core.rb +111 -125
  87. data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
  88. data/lib/active_record/database_configurations/database_config.rb +12 -0
  89. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  90. data/lib/active_record/database_configurations/url_config.rb +2 -2
  91. data/lib/active_record/database_configurations.rb +17 -9
  92. data/lib/active_record/delegated_type.rb +33 -11
  93. data/lib/active_record/destroy_association_async_job.rb +1 -1
  94. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  95. data/lib/active_record/dynamic_matchers.rb +1 -1
  96. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  97. data/lib/active_record/encryption/cipher.rb +53 -0
  98. data/lib/active_record/encryption/config.rb +44 -0
  99. data/lib/active_record/encryption/configurable.rb +61 -0
  100. data/lib/active_record/encryption/context.rb +35 -0
  101. data/lib/active_record/encryption/contexts.rb +72 -0
  102. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  103. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  104. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  105. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  106. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  107. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  108. data/lib/active_record/encryption/encryptor.rb +155 -0
  109. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  110. data/lib/active_record/encryption/errors.rb +15 -0
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  113. data/lib/active_record/encryption/key.rb +28 -0
  114. data/lib/active_record/encryption/key_generator.rb +42 -0
  115. data/lib/active_record/encryption/key_provider.rb +46 -0
  116. data/lib/active_record/encryption/message.rb +33 -0
  117. data/lib/active_record/encryption/message_serializer.rb +80 -0
  118. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  119. data/lib/active_record/encryption/properties.rb +76 -0
  120. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  121. data/lib/active_record/encryption/scheme.rb +99 -0
  122. data/lib/active_record/encryption.rb +55 -0
  123. data/lib/active_record/enum.rb +41 -41
  124. data/lib/active_record/errors.rb +66 -3
  125. data/lib/active_record/fixture_set/file.rb +15 -1
  126. data/lib/active_record/fixture_set/table_row.rb +40 -5
  127. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  128. data/lib/active_record/fixtures.rb +16 -11
  129. data/lib/active_record/future_result.rb +139 -0
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +55 -17
  132. data/lib/active_record/insert_all.rb +34 -5
  133. data/lib/active_record/integration.rb +1 -1
  134. data/lib/active_record/internal_metadata.rb +1 -5
  135. data/lib/active_record/locking/optimistic.rb +10 -9
  136. data/lib/active_record/log_subscriber.rb +6 -2
  137. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  138. data/lib/active_record/middleware/database_selector.rb +8 -3
  139. data/lib/active_record/migration/command_recorder.rb +4 -4
  140. data/lib/active_record/migration/compatibility.rb +89 -10
  141. data/lib/active_record/migration/join_table.rb +1 -1
  142. data/lib/active_record/migration.rb +109 -79
  143. data/lib/active_record/model_schema.rb +45 -31
  144. data/lib/active_record/nested_attributes.rb +3 -3
  145. data/lib/active_record/no_touching.rb +2 -2
  146. data/lib/active_record/null_relation.rb +2 -6
  147. data/lib/active_record/persistence.rb +134 -45
  148. data/lib/active_record/query_cache.rb +2 -2
  149. data/lib/active_record/query_logs.rb +203 -0
  150. data/lib/active_record/querying.rb +15 -5
  151. data/lib/active_record/railtie.rb +117 -17
  152. data/lib/active_record/railties/controller_runtime.rb +1 -1
  153. data/lib/active_record/railties/databases.rake +72 -48
  154. data/lib/active_record/readonly_attributes.rb +11 -0
  155. data/lib/active_record/reflection.rb +45 -44
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +3 -3
  158. data/lib/active_record/relation/calculations.rb +39 -26
  159. data/lib/active_record/relation/delegation.rb +6 -6
  160. data/lib/active_record/relation/finder_methods.rb +31 -22
  161. data/lib/active_record/relation/merger.rb +20 -13
  162. data/lib/active_record/relation/predicate_builder.rb +1 -6
  163. data/lib/active_record/relation/query_attribute.rb +5 -11
  164. data/lib/active_record/relation/query_methods.rb +230 -49
  165. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  166. data/lib/active_record/relation/spawn_methods.rb +2 -2
  167. data/lib/active_record/relation/where_clause.rb +8 -4
  168. data/lib/active_record/relation.rb +166 -77
  169. data/lib/active_record/result.rb +17 -2
  170. data/lib/active_record/runtime_registry.rb +2 -4
  171. data/lib/active_record/sanitization.rb +11 -7
  172. data/lib/active_record/schema_dumper.rb +3 -3
  173. data/lib/active_record/schema_migration.rb +0 -4
  174. data/lib/active_record/scoping/default.rb +61 -12
  175. data/lib/active_record/scoping/named.rb +3 -11
  176. data/lib/active_record/scoping.rb +40 -22
  177. data/lib/active_record/serialization.rb +1 -1
  178. data/lib/active_record/signed_id.rb +1 -1
  179. data/lib/active_record/store.rb +1 -6
  180. data/lib/active_record/tasks/database_tasks.rb +106 -22
  181. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  182. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  183. data/lib/active_record/test_databases.rb +1 -1
  184. data/lib/active_record/test_fixtures.rb +9 -13
  185. data/lib/active_record/timestamp.rb +3 -4
  186. data/lib/active_record/transactions.rb +9 -14
  187. data/lib/active_record/translation.rb +2 -2
  188. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  189. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  190. data/lib/active_record/type/internal/timezone.rb +2 -2
  191. data/lib/active_record/type/serialized.rb +1 -1
  192. data/lib/active_record/type/type_map.rb +17 -20
  193. data/lib/active_record/type.rb +1 -2
  194. data/lib/active_record/validations/associated.rb +1 -1
  195. data/lib/active_record.rb +170 -2
  196. data/lib/arel/attributes/attribute.rb +0 -8
  197. data/lib/arel/crud.rb +18 -22
  198. data/lib/arel/delete_manager.rb +2 -4
  199. data/lib/arel/insert_manager.rb +2 -3
  200. data/lib/arel/nodes/casted.rb +1 -1
  201. data/lib/arel/nodes/delete_statement.rb +8 -13
  202. data/lib/arel/nodes/insert_statement.rb +2 -2
  203. data/lib/arel/nodes/select_core.rb +2 -2
  204. data/lib/arel/nodes/select_statement.rb +2 -2
  205. data/lib/arel/nodes/update_statement.rb +3 -2
  206. data/lib/arel/predications.rb +1 -1
  207. data/lib/arel/select_manager.rb +10 -4
  208. data/lib/arel/table.rb +0 -1
  209. data/lib/arel/tree_manager.rb +0 -12
  210. data/lib/arel/update_manager.rb +2 -4
  211. data/lib/arel/visitors/dot.rb +80 -90
  212. data/lib/arel/visitors/mysql.rb +6 -1
  213. data/lib/arel/visitors/postgresql.rb +0 -10
  214. data/lib/arel/visitors/to_sql.rb +43 -2
  215. data/lib/arel.rb +1 -1
  216. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  217. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  218. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  219. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  220. metadata +55 -17
@@ -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
@@ -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
 
@@ -138,16 +145,7 @@ module ActiveRecord
138
145
  end
139
146
 
140
147
  def sanitize_as_sql_comment(value) # :nodoc:
141
- # Sanitize a string to appear within a SQL comment
142
- # For compatibility, this also surrounding "/*+", "/*", and "*/"
143
- # charcacters, possibly with single surrounding space.
144
- # Then follows that by replacing any internal "*/" or "/ *" with
145
- # "* /" or "/ *"
146
- comment = value.to_s.dup
147
- comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
148
- comment.gsub!("*/", "* /")
149
- comment.gsub!("/*", "/ *")
150
- comment
148
+ value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
151
149
  end
152
150
 
153
151
  def column_name_matcher # :nodoc:
@@ -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
@@ -198,10 +198,6 @@ module ActiveRecord
198
198
 
199
199
  def index_options(table_name)
200
200
  index_options = as_options(index)
201
-
202
- # legacy reference index names are used on versions 6.0 and earlier
203
- return index_options if options[:_uses_legacy_reference_index_name]
204
-
205
201
  index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
206
202
  index_options
207
203
  end
@@ -257,6 +253,7 @@ module ActiveRecord
257
253
  define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
258
254
  :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
259
255
 
256
+ alias :blob :binary
260
257
  alias :numeric :decimal
261
258
  end
262
259
 
@@ -281,7 +278,7 @@ module ActiveRecord
281
278
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
282
279
  # is actually of this type:
283
280
  #
284
- # class SomeMigration < ActiveRecord::Migration[6.0]
281
+ # class SomeMigration < ActiveRecord::Migration[7.0]
285
282
  # def up
286
283
  # create_table :foo do |t|
287
284
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -414,6 +411,12 @@ module ActiveRecord
414
411
  end
415
412
  end
416
413
 
414
+ if @conn.supports_datetime_with_precision?
415
+ if type == :datetime && !options.key?(:precision)
416
+ options[:precision] = 6
417
+ end
418
+ end
419
+
417
420
  @columns_hash[name] = new_column_definition(name, type, **options)
418
421
 
419
422
  if index
@@ -438,12 +441,12 @@ module ActiveRecord
438
441
  indexes << [column_name, options]
439
442
  end
440
443
 
441
- def foreign_key(table_name, **options) # :nodoc:
442
- foreign_keys << [table_name, options]
444
+ def foreign_key(to_table, **options)
445
+ foreign_keys << new_foreign_key_definition(to_table, options)
443
446
  end
444
447
 
445
448
  def check_constraint(expression, **options)
446
- check_constraints << [expression, options]
449
+ check_constraints << new_check_constraint_definition(expression, options)
447
450
  end
448
451
 
449
452
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -485,6 +488,19 @@ module ActiveRecord
485
488
  create_column_definition(name, type, options)
486
489
  end
487
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
+
488
504
  private
489
505
  def create_column_definition(name, type, options)
490
506
  ColumnDefinition.new(name, type, options)
@@ -520,7 +536,7 @@ module ActiveRecord
520
536
  def name; @td.name; end
521
537
 
522
538
  def add_foreign_key(to_table, options)
523
- @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
539
+ @foreign_key_adds << @td.new_foreign_key_definition(to_table, options)
524
540
  end
525
541
 
526
542
  def drop_foreign_key(name)
@@ -528,7 +544,7 @@ module ActiveRecord
528
544
  end
529
545
 
530
546
  def add_check_constraint(expression, options)
531
- @check_constraint_adds << CheckConstraintDefinition.new(name, expression, options)
547
+ @check_constraint_adds << @td.new_check_constraint_definition(expression, options)
532
548
  end
533
549
 
534
550
  def drop_check_constraint(constraint_name)
@@ -572,6 +588,7 @@ module ActiveRecord
572
588
  # t.time
573
589
  # t.date
574
590
  # t.binary
591
+ # t.blob
575
592
  # t.boolean
576
593
  # t.foreign_key
577
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
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  end
74
74
  end
75
75
 
76
- class NullTransaction #:nodoc:
76
+ class NullTransaction # :nodoc:
77
77
  def initialize; end
78
78
  def state; end
79
79
  def closed?; true; end
@@ -82,7 +82,7 @@ module ActiveRecord
82
82
  def add_record(record, _ = true); end
83
83
  end
84
84
 
85
- class Transaction #:nodoc:
85
+ class Transaction # :nodoc:
86
86
  attr_reader :connection, :state, :savepoint_name, :isolation_level
87
87
  attr_accessor :written
88
88
 
@@ -221,7 +221,7 @@ module ActiveRecord
221
221
  end
222
222
  end
223
223
 
224
- class TransactionManager #:nodoc:
224
+ class TransactionManager # :nodoc:
225
225
  def initialize(connection)
226
226
  @stack = []
227
227
  @connection = connection