activerecord 7.1.5.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +369 -2484
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +2 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +21 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +7 -6
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  13. data/lib/active_record/associations/builder/has_many.rb +3 -4
  14. data/lib/active_record/associations/builder/has_one.rb +3 -4
  15. data/lib/active_record/associations/collection_association.rb +17 -9
  16. data/lib/active_record/associations/collection_proxy.rb +14 -1
  17. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  18. data/lib/active_record/associations/errors.rb +265 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  21. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +4 -3
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +14 -3
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +92 -295
  29. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  30. data/lib/active_record/attribute_assignment.rb +0 -2
  31. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  32. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  33. data/lib/active_record/attribute_methods/read.rb +1 -13
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
  36. data/lib/active_record/attribute_methods.rb +71 -75
  37. data/lib/active_record/attributes.rb +63 -49
  38. data/lib/active_record/autosave_association.rb +92 -57
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/callbacks.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
  42. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
  48. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  49. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  50. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
  51. data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
  52. data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
  53. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
  60. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
  73. data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
  74. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  75. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  77. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  78. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  79. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
  80. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
  81. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
  82. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  83. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
  84. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  85. data/lib/active_record/connection_adapters.rb +65 -0
  86. data/lib/active_record/connection_handling.rb +74 -37
  87. data/lib/active_record/core.rb +132 -51
  88. data/lib/active_record/counter_cache.rb +19 -10
  89. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  90. data/lib/active_record/database_configurations/database_config.rb +23 -4
  91. data/lib/active_record/database_configurations/hash_config.rb +46 -34
  92. data/lib/active_record/database_configurations/url_config.rb +20 -1
  93. data/lib/active_record/database_configurations.rb +1 -1
  94. data/lib/active_record/delegated_type.rb +41 -17
  95. data/lib/active_record/dynamic_matchers.rb +2 -2
  96. data/lib/active_record/encryption/config.rb +3 -1
  97. data/lib/active_record/encryption/encryptable_record.rb +7 -7
  98. data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
  99. data/lib/active_record/encryption/encryptor.rb +28 -6
  100. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  101. data/lib/active_record/encryption/key_provider.rb +1 -1
  102. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  103. data/lib/active_record/encryption/message_serializer.rb +4 -0
  104. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  105. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  106. data/lib/active_record/encryption/scheme.rb +8 -1
  107. data/lib/active_record/enum.rb +20 -16
  108. data/lib/active_record/errors.rb +54 -20
  109. data/lib/active_record/explain.rb +13 -24
  110. data/lib/active_record/fixtures.rb +37 -33
  111. data/lib/active_record/future_result.rb +21 -13
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +4 -2
  114. data/lib/active_record/insert_all.rb +19 -16
  115. data/lib/active_record/integration.rb +4 -1
  116. data/lib/active_record/internal_metadata.rb +48 -34
  117. data/lib/active_record/locking/optimistic.rb +8 -7
  118. data/lib/active_record/log_subscriber.rb +5 -32
  119. data/lib/active_record/message_pack.rb +1 -1
  120. data/lib/active_record/migration/command_recorder.rb +33 -14
  121. data/lib/active_record/migration/compatibility.rb +8 -3
  122. data/lib/active_record/migration/default_strategy.rb +4 -5
  123. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  124. data/lib/active_record/migration.rb +104 -98
  125. data/lib/active_record/model_schema.rb +32 -70
  126. data/lib/active_record/nested_attributes.rb +15 -9
  127. data/lib/active_record/normalization.rb +3 -7
  128. data/lib/active_record/persistence.rb +127 -451
  129. data/lib/active_record/query_cache.rb +19 -8
  130. data/lib/active_record/query_logs.rb +104 -37
  131. data/lib/active_record/query_logs_formatter.rb +17 -28
  132. data/lib/active_record/querying.rb +24 -12
  133. data/lib/active_record/railtie.rb +26 -68
  134. data/lib/active_record/railties/controller_runtime.rb +13 -4
  135. data/lib/active_record/railties/databases.rake +43 -61
  136. data/lib/active_record/reflection.rb +112 -53
  137. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  138. data/lib/active_record/relation/batches.rb +138 -72
  139. data/lib/active_record/relation/calculations.rb +122 -82
  140. data/lib/active_record/relation/delegation.rb +30 -22
  141. data/lib/active_record/relation/finder_methods.rb +32 -18
  142. data/lib/active_record/relation/merger.rb +12 -14
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  146. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  147. data/lib/active_record/relation/predicate_builder.rb +16 -3
  148. data/lib/active_record/relation/query_attribute.rb +1 -1
  149. data/lib/active_record/relation/query_methods.rb +317 -101
  150. data/lib/active_record/relation/spawn_methods.rb +3 -19
  151. data/lib/active_record/relation/where_clause.rb +7 -19
  152. data/lib/active_record/relation.rb +561 -119
  153. data/lib/active_record/result.rb +95 -46
  154. data/lib/active_record/runtime_registry.rb +39 -0
  155. data/lib/active_record/sanitization.rb +31 -25
  156. data/lib/active_record/schema.rb +8 -6
  157. data/lib/active_record/schema_dumper.rb +53 -20
  158. data/lib/active_record/schema_migration.rb +31 -14
  159. data/lib/active_record/scoping/named.rb +6 -2
  160. data/lib/active_record/signed_id.rb +24 -4
  161. data/lib/active_record/statement_cache.rb +19 -19
  162. data/lib/active_record/store.rb +7 -3
  163. data/lib/active_record/table_metadata.rb +2 -13
  164. data/lib/active_record/tasks/database_tasks.rb +87 -58
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
  168. data/lib/active_record/test_fixtures.rb +98 -89
  169. data/lib/active_record/testing/query_assertions.rb +121 -0
  170. data/lib/active_record/timestamp.rb +2 -2
  171. data/lib/active_record/token_for.rb +22 -12
  172. data/lib/active_record/touch_later.rb +1 -1
  173. data/lib/active_record/transaction.rb +132 -0
  174. data/lib/active_record/transactions.rb +72 -17
  175. data/lib/active_record/translation.rb +0 -2
  176. data/lib/active_record/type/serialized.rb +1 -3
  177. data/lib/active_record/type_caster/connection.rb +4 -4
  178. data/lib/active_record/validations/associated.rb +9 -3
  179. data/lib/active_record/validations/uniqueness.rb +23 -18
  180. data/lib/active_record/validations.rb +4 -1
  181. data/lib/active_record.rb +138 -57
  182. data/lib/arel/alias_predication.rb +1 -1
  183. data/lib/arel/collectors/bind.rb +4 -2
  184. data/lib/arel/collectors/composite.rb +7 -0
  185. data/lib/arel/collectors/sql_string.rb +2 -2
  186. data/lib/arel/collectors/substitute_binds.rb +3 -3
  187. data/lib/arel/nodes/binary.rb +1 -7
  188. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  189. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  190. data/lib/arel/nodes/node.rb +5 -4
  191. data/lib/arel/nodes/sql_literal.rb +8 -1
  192. data/lib/arel/nodes.rb +2 -2
  193. data/lib/arel/predications.rb +1 -1
  194. data/lib/arel/select_manager.rb +1 -1
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/tree_manager.rb +3 -2
  197. data/lib/arel/update_manager.rb +2 -1
  198. data/lib/arel/visitors/dot.rb +1 -0
  199. data/lib/arel/visitors/mysql.rb +9 -4
  200. data/lib/arel/visitors/postgresql.rb +1 -12
  201. data/lib/arel/visitors/sqlite.rb +25 -0
  202. data/lib/arel/visitors/to_sql.rb +29 -16
  203. data/lib/arel.rb +7 -3
  204. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  205. metadata +18 -16
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  end
74
74
  self.configurations = {}
75
75
 
76
- # Returns fully resolved ActiveRecord::DatabaseConfigurations object
76
+ # Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
77
77
  def self.configurations
78
78
  @@configurations
79
79
  end
@@ -89,6 +89,7 @@ module ActiveRecord
89
89
  class_attribute :belongs_to_required_by_default, instance_accessor: false
90
90
 
91
91
  class_attribute :strict_loading_by_default, instance_accessor: false, default: false
92
+ class_attribute :strict_loading_mode, instance_accessor: false, default: :all
92
93
 
93
94
  class_attribute :has_many_inversing, instance_accessor: false, default: false
94
95
 
@@ -102,6 +103,21 @@ module ActiveRecord
102
103
 
103
104
  class_attribute :shard_selector, instance_accessor: false, default: nil
104
105
 
106
+ ##
107
+ # :singleton-method:
108
+ #
109
+ # Specifies the attributes that will be included in the output of the
110
+ # #inspect method:
111
+ #
112
+ # Post.attributes_for_inspect = [:id, :title]
113
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
114
+ #
115
+ # When set to `:all` inspect will list all the record's attributes:
116
+ #
117
+ # Post.attributes_for_inspect = :all
118
+ # Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
119
+ class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
120
+
105
121
  def self.application_record_class? # :nodoc:
106
122
  if ActiveRecord.application_record_class
107
123
  self == ActiveRecord.application_record_class
@@ -186,6 +202,17 @@ module ActiveRecord
186
202
  false
187
203
  end
188
204
 
205
+ # Intended to behave like `.current_preventing_writes` given the class name as input.
206
+ # See PoolConfig and ConnectionHandler::ConnectionDescriptor.
207
+ def self.preventing_writes?(class_name) # :nodoc:
208
+ connected_to_stack.reverse_each do |hash|
209
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
210
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
211
+ end
212
+
213
+ false
214
+ end
215
+
189
216
  def self.connected_to_stack # :nodoc:
190
217
  if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
191
218
  connected_to_stack
@@ -350,8 +377,8 @@ module ActiveRecord
350
377
  super
351
378
  elsif abstract_class?
352
379
  "#{super}(abstract)"
353
- elsif !connected?
354
- "#{super} (call '#{super}.connection' to establish a connection)"
380
+ elsif !schema_loaded? && !connected?
381
+ "#{super} (call '#{super}.load_schema' to load schema informations)"
355
382
  elsif table_exists?
356
383
  attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
357
384
  "#{super}(#{attr_list})"
@@ -366,14 +393,14 @@ module ActiveRecord
366
393
  end
367
394
 
368
395
  def predicate_builder # :nodoc:
369
- @predicate_builder ||= PredicateBuilder.new(table_metadata)
396
+ @predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
370
397
  end
371
398
 
372
399
  def type_caster # :nodoc:
373
400
  TypeCaster::Map.new(self)
374
401
  end
375
402
 
376
- def cached_find_by_statement(key, &block) # :nodoc:
403
+ def cached_find_by_statement(connection, key, &block) # :nodoc:
377
404
  cache = @find_by_statement_cache[connection.prepared_statements]
378
405
  cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
379
406
  end
@@ -411,26 +438,24 @@ module ActiveRecord
411
438
  end
412
439
  end
413
440
 
414
- def table_metadata
415
- TableMetadata.new(self, arel_table)
416
- end
417
-
418
441
  def cached_find_by(keys, values)
419
- statement = cached_find_by_statement(keys) { |params|
420
- wheres = keys.index_with do |key|
421
- if key.is_a?(Array)
422
- [key.map { params.bind }]
423
- else
424
- params.bind
442
+ with_connection do |connection|
443
+ statement = cached_find_by_statement(connection, keys) { |params|
444
+ wheres = keys.index_with do |key|
445
+ if key.is_a?(Array)
446
+ [key.map { params.bind }]
447
+ else
448
+ params.bind
449
+ end
425
450
  end
426
- end
427
- where(wheres).limit(1)
428
- }
451
+ where(wheres).limit(1)
452
+ }
429
453
 
430
- begin
431
- statement.execute(values.flatten, connection).first
432
- rescue TypeError
433
- raise ActiveRecord::StatementInvalid
454
+ statement.execute(values.flatten, connection, allow_retry: true).then do |r|
455
+ r.first
456
+ rescue TypeError
457
+ raise ActiveRecord::StatementInvalid
458
+ end
434
459
  end
435
460
  end
436
461
  end
@@ -450,7 +475,7 @@ module ActiveRecord
450
475
  init_internals
451
476
  initialize_internals_callback
452
477
 
453
- assign_attributes(attributes) if attributes
478
+ super
454
479
 
455
480
  yield self if block_given?
456
481
  _run_initialize_callbacks
@@ -523,12 +548,7 @@ module ActiveRecord
523
548
 
524
549
  ##
525
550
  def initialize_dup(other) # :nodoc:
526
- @attributes = @attributes.deep_dup
527
- if self.class.composite_primary_key?
528
- @primary_key.each { |key| @attributes.reset(key) }
529
- else
530
- @attributes.reset(@primary_key)
531
- end
551
+ @attributes = init_attributes(other)
532
552
 
533
553
  _run_initialize_callbacks
534
554
 
@@ -540,6 +560,18 @@ module ActiveRecord
540
560
  super
541
561
  end
542
562
 
563
+ def init_attributes(_) # :nodoc:
564
+ attrs = @attributes.deep_dup
565
+
566
+ if self.class.composite_primary_key?
567
+ @primary_key.each { |key| attrs.reset(key) }
568
+ else
569
+ attrs.reset(@primary_key)
570
+ end
571
+
572
+ attrs
573
+ end
574
+
543
575
  # Populate +coder+ with attributes about this record that should be
544
576
  # serialized. The structure of +coder+ defined in this method is
545
577
  # guaranteed to match the structure of +coder+ passed to the #init_with
@@ -706,11 +738,29 @@ module ActiveRecord
706
738
  @strict_loading_mode == :all
707
739
  end
708
740
 
709
- # Marks this record as read only.
741
+ # Prevents records from being written to the database:
742
+ #
743
+ # customer = Customer.new
744
+ # customer.readonly!
745
+ # customer.save # raises ActiveRecord::ReadOnlyRecord
746
+ #
747
+ # customer = Customer.first
748
+ # customer.readonly!
749
+ # customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
750
+ #
751
+ # Read-only records cannot be deleted from the database either:
710
752
  #
711
753
  # customer = Customer.first
712
754
  # customer.readonly!
713
- # customer.save # Raises an ActiveRecord::ReadOnlyRecord
755
+ # customer.destroy # raises ActiveRecord::ReadOnlyRecord
756
+ #
757
+ # Please, note that the objects themselves are still mutable in memory:
758
+ #
759
+ # customer = Customer.new
760
+ # customer.readonly!
761
+ # customer.name = 'New Name' # OK
762
+ #
763
+ # but you won't be able to persist the changes.
714
764
  def readonly!
715
765
  @readonly = true
716
766
  end
@@ -719,21 +769,28 @@ module ActiveRecord
719
769
  self.class.connection_handler
720
770
  end
721
771
 
722
- # Returns the contents of the record as a nicely formatted string.
772
+ # Returns the attributes of the record as a nicely formatted string.
773
+ #
774
+ # Post.first.inspect
775
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
776
+ #
777
+ # The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
778
+ #
779
+ # Post.attributes_for_inspect = [:id, :title]
780
+ # Post.first.inspect
781
+ # #=> "#<Post id: 1, title: "Hello, World!">"
723
782
  def inspect
724
- # We check defined?(@attributes) not to issue warnings if the object is
725
- # allocated but not initialized.
726
- inspection = if defined?(@attributes) && @attributes
727
- attribute_names.filter_map do |name|
728
- if _has_attribute?(name)
729
- "#{name}: #{attribute_for_inspect(name)}"
730
- end
731
- end.join(", ")
732
- else
733
- "not initialized"
734
- end
783
+ inspect_with_attributes(attributes_for_inspect)
784
+ end
735
785
 
736
- "#<#{self.class} #{inspection}>"
786
+ # Returns all attributes of the record as a nicely formatted string,
787
+ # ignoring <tt>.attributes_for_inspect</tt>.
788
+ #
789
+ # Post.first.full_inspect
790
+ # #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
791
+ #
792
+ def full_inspect
793
+ inspect_with_attributes(all_attributes_for_inspect)
737
794
  end
738
795
 
739
796
  # Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
@@ -741,17 +798,17 @@ module ActiveRecord
741
798
  def pretty_print(pp)
742
799
  return super if custom_inspect_method_defined?
743
800
  pp.object_address_group(self) do
744
- if defined?(@attributes) && @attributes
745
- attr_names = self.class.attribute_names.select { |name| _has_attribute?(name) }
801
+ if @attributes
802
+ attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
746
803
  pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
804
+ attr_name = attr_name.to_s
747
805
  pp.breakable " "
748
806
  pp.group(1) do
749
807
  pp.text attr_name
750
808
  pp.text ":"
751
809
  pp.breakable
752
- value = _read_attribute(attr_name)
753
- value = inspection_filter.filter_param(attr_name, value) unless value.nil?
754
- pp.pp value
810
+ value = attribute_for_inspect(attr_name)
811
+ pp.text value
755
812
  end
756
813
  end
757
814
  else
@@ -786,10 +843,9 @@ module ActiveRecord
786
843
 
787
844
  @primary_key = klass.primary_key
788
845
  @strict_loading = klass.strict_loading_by_default
789
- @strict_loading_mode = :all
846
+ @strict_loading_mode = klass.strict_loading_mode
790
847
 
791
848
  klass.define_attribute_methods
792
- klass.generate_alias_attributes
793
849
  end
794
850
 
795
851
  def initialize_internals_callback
@@ -809,5 +865,30 @@ module ActiveRecord
809
865
  def inspection_filter
810
866
  self.class.inspection_filter
811
867
  end
868
+
869
+ def inspect_with_attributes(attributes_to_list)
870
+ inspection = if @attributes
871
+ attributes_to_list.filter_map do |name|
872
+ name = name.to_s
873
+ if _has_attribute?(name)
874
+ "#{name}: #{attribute_for_inspect(name)}"
875
+ end
876
+ end.join(", ")
877
+ else
878
+ "not initialized"
879
+ end
880
+
881
+ "#<#{self.class} #{inspection}>"
882
+ end
883
+
884
+ def attributes_for_inspect
885
+ self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
886
+ end
887
+
888
+ def all_attributes_for_inspect
889
+ return [] unless @attributes
890
+
891
+ attribute_names
892
+ end
812
893
  end
813
894
  end
@@ -7,6 +7,7 @@ module ActiveRecord
7
7
 
8
8
  included do
9
9
  class_attribute :_counter_cache_columns, instance_accessor: false, default: []
10
+ class_attribute :counter_cached_association_names, instance_writer: false, default: []
10
11
  end
11
12
 
12
13
  module ClassMethods
@@ -27,7 +28,7 @@ module ActiveRecord
27
28
  # # For the Post with id #1, reset the comments_count
28
29
  # Post.reset_counters(1, :comments)
29
30
  #
30
- # # Like above, but also touch the +updated_at+ and/or +updated_on+
31
+ # # Like above, but also touch the updated_at and/or updated_on
31
32
  # # attributes.
32
33
  # Post.reset_counters(1, :comments, touch: true)
33
34
  def reset_counters(id, *counters, touch: nil)
@@ -181,14 +182,26 @@ module ActiveRecord
181
182
  def counter_cache_column?(name) # :nodoc:
182
183
  _counter_cache_columns.include?(name)
183
184
  end
185
+
186
+ def load_schema! # :nodoc:
187
+ super
188
+
189
+ association_names = _reflections.filter_map do |name, reflection|
190
+ next unless reflection.belongs_to? && reflection.counter_cache_column
191
+
192
+ name.to_sym
193
+ end
194
+
195
+ self.counter_cached_association_names |= association_names
196
+ end
184
197
  end
185
198
 
186
199
  private
187
200
  def _create_record(attribute_names = self.attribute_names)
188
201
  id = super
189
202
 
190
- each_counter_cached_associations do |association|
191
- association.increment_counters
203
+ counter_cached_association_names.each do |association_name|
204
+ association(association_name).increment_counters
192
205
  end
193
206
 
194
207
  id
@@ -198,7 +211,9 @@ module ActiveRecord
198
211
  affected_rows = super
199
212
 
200
213
  if affected_rows > 0
201
- each_counter_cached_associations do |association|
214
+ counter_cached_association_names.each do |association_name|
215
+ association = association(association_name)
216
+
202
217
  unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
203
218
  association.decrement_counters
204
219
  end
@@ -208,12 +223,6 @@ module ActiveRecord
208
223
  affected_rows
209
224
  end
210
225
 
211
- def each_counter_cached_associations
212
- _reflections.each do |name, reflection|
213
- yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
214
- end
215
- end
216
-
217
226
  def _foreign_keys_equal?(fkey1, fkey2)
218
227
  fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
219
228
  end
@@ -25,8 +25,7 @@ module ActiveRecord
25
25
  def initialize(url)
26
26
  raise "Database URL cannot be empty" if url.blank?
27
27
  @uri = uri_parser.parse(url)
28
- @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
29
- @adapter = "postgresql" if @adapter == "postgres"
28
+ @adapter = resolved_adapter
30
29
 
31
30
  if @uri.opaque
32
31
  @uri.opaque, @query = @uri.opaque.split("?", 2)
@@ -80,6 +79,14 @@ module ActiveRecord
80
79
  end
81
80
  end
82
81
 
82
+ def resolved_adapter
83
+ adapter = uri.scheme && @uri.scheme.tr("-", "_")
84
+ if adapter && ActiveRecord.protocol_adapters[adapter]
85
+ adapter = ActiveRecord.protocol_adapters[adapter]
86
+ end
87
+ adapter
88
+ end
89
+
83
90
  # Returns name of the database.
84
91
  def database_from_path
85
92
  if @adapter == "sqlite3"
@@ -11,14 +11,25 @@ module ActiveRecord
11
11
  def initialize(env_name, name)
12
12
  @env_name = env_name
13
13
  @name = name
14
+ @adapter_class = nil
14
15
  end
15
16
 
16
- def adapter_method
17
- "#{adapter}_connection"
17
+ def adapter_class
18
+ @adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
18
19
  end
19
20
 
20
- def adapter_class_method
21
- "#{adapter}_adapter_class"
21
+ def inspect # :nodoc:
22
+ "#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
23
+ end
24
+
25
+ def new_connection
26
+ adapter_class.new(configuration_hash)
27
+ end
28
+
29
+ def validate!
30
+ adapter_class if adapter
31
+
32
+ true
22
33
  end
23
34
 
24
35
  def host
@@ -84,6 +95,14 @@ module ActiveRecord
84
95
  def schema_cache_path
85
96
  raise NotImplementedError
86
97
  end
98
+
99
+ def use_metadata_table?
100
+ raise NotImplementedError
101
+ end
102
+
103
+ def seeds?
104
+ raise NotImplementedError
105
+ end
87
106
  end
88
107
  end
89
108
  end
@@ -1,53 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActiveRecord
4
6
  class DatabaseConfigurations
5
- # = Active Record Database Hash Config
7
+ # # Active Record Database Hash Config
6
8
  #
7
- # A +HashConfig+ object is created for each database configuration entry that
8
- # is created from a hash.
9
+ # A `HashConfig` object is created for each database configuration entry that is
10
+ # created from a hash.
9
11
  #
10
12
  # A hash config:
11
13
  #
12
- # { "development" => { "database" => "db_name" } }
14
+ # { "development" => { "database" => "db_name" } }
13
15
  #
14
16
  # Becomes:
15
17
  #
16
- # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
17
- # @env_name="development", @name="primary", @config={database: "db_name"}>
18
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
19
+ # @env_name="development", @name="primary", @config={database: "db_name"}>
18
20
  #
19
21
  # See ActiveRecord::DatabaseConfigurations for more info.
20
22
  class HashConfig < DatabaseConfig
21
23
  attr_reader :configuration_hash
22
24
 
23
-
24
- # Initialize a new +HashConfig+ object
25
+ # Initialize a new `HashConfig` object
25
26
  #
26
- # ==== Options
27
+ # #### Parameters
28
+ #
29
+ # * `env_name` - The Rails environment, i.e. "development".
30
+ # * `name` - The db config name. In a standard two-tier database configuration
31
+ # this will default to "primary". In a multiple database three-tier database
32
+ # configuration this corresponds to the name used in the second tier, for
33
+ # example "primary_readonly".
34
+ # * `configuration_hash` - The config hash. This is the hash that contains the
35
+ # database adapter, name, and other important information for database
36
+ # connections.
27
37
  #
28
- # * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
29
- # * <tt>:name</tt> - The db config name. In a standard two-tier
30
- # database configuration this will default to "primary". In a multiple
31
- # database three-tier database configuration this corresponds to the name
32
- # used in the second tier, for example "primary_readonly".
33
- # * <tt>:config</tt> - The config hash. This is the hash that contains the
34
- # database adapter, name, and other important information for database
35
- # connections.
36
38
  def initialize(env_name, name, configuration_hash)
37
39
  super(env_name, name)
38
40
  @configuration_hash = configuration_hash.symbolize_keys.freeze
39
41
  end
40
42
 
41
43
  # Determines whether a database configuration is for a replica / readonly
42
- # connection. If the +replica+ key is present in the config, +replica?+ will
43
- # return +true+.
44
+ # connection. If the `replica` key is present in the config, `replica?` will
45
+ # return `true`.
44
46
  def replica?
45
47
  configuration_hash[:replica]
46
48
  end
47
49
 
48
- # The migrations paths for a database configuration. If the
49
- # +migrations_paths+ key is present in the config, +migrations_paths+
50
- # will return its value.
50
+ # The migrations paths for a database configuration. If the `migrations_paths`
51
+ # key is present in the config, `migrations_paths` will return its value.
51
52
  def migrations_paths
52
53
  configuration_hash[:migrations_paths]
53
54
  end
@@ -92,8 +93,8 @@ module ActiveRecord
92
93
  (configuration_hash[:checkout_timeout] || 5).to_f
93
94
  end
94
95
 
95
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
96
- # also be useful if someone wants a very low +idle_timeout+.
96
+ # `reaping_frequency` is configurable mostly for historical reasons, but it
97
+ # could also be useful if someone wants a very low `idle_timeout`.
97
98
  def reaping_frequency
98
99
  configuration_hash.fetch(:reaping_frequency, 60)&.to_f
99
100
  end
@@ -104,12 +105,11 @@ module ActiveRecord
104
105
  end
105
106
 
106
107
  def adapter
107
- configuration_hash[:adapter]
108
+ configuration_hash[:adapter]&.to_s
108
109
  end
109
110
 
110
- # The path to the schema cache dump file for a database.
111
- # If omitted, the filename will be read from ENV or a
112
- # default will be derived.
111
+ # The path to the schema cache dump file for a database. If omitted, the
112
+ # filename will be read from ENV or a default will be derived.
113
113
  def schema_cache_path
114
114
  configuration_hash[:schema_cache_path]
115
115
  end
@@ -130,14 +130,22 @@ module ActiveRecord
130
130
  Base.configurations.primary?(name)
131
131
  end
132
132
 
133
- # Determines whether to dump the schema/structure files and the
134
- # filename that should be used.
133
+ # Determines whether the db:prepare task should seed the database from db/seeds.rb.
134
+ #
135
+ # If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
136
+ # will return `true` for the primary database and `false` for all other configs.
137
+ def seeds?
138
+ configuration_hash.fetch(:seeds, primary?)
139
+ end
140
+
141
+ # Determines whether to dump the schema/structure files and the filename that
142
+ # should be used.
135
143
  #
136
- # If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
137
- # the schema will not be dumped.
144
+ # If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
145
+ # will not be dumped.
138
146
  #
139
- # If the config option is set that will be used. Otherwise \Rails
140
- # will generate the filename from the database config name.
147
+ # If the config option is set that will be used. Otherwise Rails will generate
148
+ # the filename from the database config name.
141
149
  def schema_dump(format = ActiveRecord.schema_format)
142
150
  if configuration_hash.key?(:schema_dump)
143
151
  if config = configuration_hash[:schema_dump]
@@ -154,6 +162,10 @@ module ActiveRecord
154
162
  !replica? && !!configuration_hash.fetch(:database_tasks, true)
155
163
  end
156
164
 
165
+ def use_metadata_table? # :nodoc:
166
+ configuration_hash.fetch(:use_metadata_table, true)
167
+ end
168
+
157
169
  private
158
170
  def schema_file_type(format)
159
171
  case format
@@ -41,10 +41,29 @@ module ActiveRecord
41
41
  super(env_name, name, configuration_hash)
42
42
 
43
43
  @url = url
44
- @configuration_hash = @configuration_hash.merge(build_url_hash).freeze
44
+ @configuration_hash = @configuration_hash.merge(build_url_hash)
45
+
46
+ if @configuration_hash[:schema_dump] == "false"
47
+ @configuration_hash[:schema_dump] = false
48
+ end
49
+
50
+ if @configuration_hash[:query_cache] == "false"
51
+ @configuration_hash[:query_cache] = false
52
+ end
53
+
54
+ to_boolean!(@configuration_hash, :replica)
55
+ to_boolean!(@configuration_hash, :database_tasks)
56
+
57
+ @configuration_hash.freeze
45
58
  end
46
59
 
47
60
  private
61
+ def to_boolean!(configuration_hash, key)
62
+ if configuration_hash[key].is_a?(String)
63
+ configuration_hash[key] = configuration_hash[key] != "false"
64
+ end
65
+ end
66
+
48
67
  # Return a Hash that can be merged into the main config that represents
49
68
  # the passed in url
50
69
  def build_url_hash
@@ -113,7 +113,7 @@ module ActiveRecord
113
113
 
114
114
  if name
115
115
  configs.find do |db_config|
116
- db_config.name == name
116
+ db_config.name == name.to_s
117
117
  end
118
118
  else
119
119
  configs