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
data/README.rdoc CHANGED
@@ -34,7 +34,7 @@ A short rundown of some of the major features:
34
34
  This would also define the following accessors: <tt>Product#name</tt> and
35
35
  <tt>Product#name=(new_name)</tt>.
36
36
 
37
- {Learn more}[link:classes/ActiveRecord/Base.html]
37
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
38
38
 
39
39
  * Associations between objects defined by simple class methods.
40
40
 
@@ -44,7 +44,7 @@ A short rundown of some of the major features:
44
44
  belongs_to :conglomerate
45
45
  end
46
46
 
47
- {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
47
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html]
48
48
 
49
49
 
50
50
  * Aggregations of value objects.
@@ -56,7 +56,7 @@ A short rundown of some of the major features:
56
56
  mapping: [%w(address_street street), %w(address_city city)]
57
57
  end
58
58
 
59
- {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
59
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html]
60
60
 
61
61
 
62
62
  * Validation rules that can differ for new or existing objects.
@@ -68,7 +68,7 @@ A short rundown of some of the major features:
68
68
  validates :password, :email_address, confirmation: true, on: :create
69
69
  end
70
70
 
71
- {Learn more}[link:classes/ActiveRecord/Validations.html]
71
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Validations.html]
72
72
 
73
73
 
74
74
  * Callbacks available for the entire life cycle (instantiation, saving, destroying, validating, etc.).
@@ -78,7 +78,7 @@ A short rundown of some of the major features:
78
78
  # the `invalidate_payment_plan` method gets called just before Person#destroy
79
79
  end
80
80
 
81
- {Learn more}[link:classes/ActiveRecord/Callbacks.html]
81
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html]
82
82
 
83
83
 
84
84
  * Inheritance hierarchies.
@@ -88,7 +88,7 @@ A short rundown of some of the major features:
88
88
  class Client < Company; end
89
89
  class PriorityClient < Client; end
90
90
 
91
- {Learn more}[link:classes/ActiveRecord/Base.html]
91
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
92
92
 
93
93
 
94
94
  * Transactions.
@@ -99,7 +99,7 @@ A short rundown of some of the major features:
99
99
  mary.deposit(100)
100
100
  end
101
101
 
102
- {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
102
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html]
103
103
 
104
104
 
105
105
  * Reflections on columns, associations, and aggregations.
@@ -108,7 +108,7 @@ A short rundown of some of the major features:
108
108
  reflection.klass # => Client (class)
109
109
  Firm.columns # Returns an array of column descriptors for the firms table
110
110
 
111
- {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
111
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html]
112
112
 
113
113
 
114
114
  * Database abstraction through simple adapters.
@@ -125,13 +125,13 @@ A short rundown of some of the major features:
125
125
  database: 'activerecord'
126
126
  )
127
127
 
128
- {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
129
- MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
130
- PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
131
- SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
128
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html] and read about the built-in support for
129
+ MySQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
130
+ PostgreSQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
131
+ SQLite3[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
132
132
 
133
133
 
134
- * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-doc.org/stdlib/libdoc/logger/rdoc/].
134
+ * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://docs.ruby-lang.org/en/master/Logger.html].
135
135
 
136
136
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
137
137
  ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
@@ -139,7 +139,7 @@ A short rundown of some of the major features:
139
139
 
140
140
  * Database agnostic schema management with Migrations.
141
141
 
142
- class AddSystemSettings < ActiveRecord::Migration[7.1]
142
+ class AddSystemSettings < ActiveRecord::Migration[8.0]
143
143
  def up
144
144
  create_table :system_settings do |t|
145
145
  t.string :name
@@ -157,7 +157,7 @@ A short rundown of some of the major features:
157
157
  end
158
158
  end
159
159
 
160
- {Learn more}[link:classes/ActiveRecord/Migration.html]
160
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Migration.html]
161
161
 
162
162
 
163
163
  == Philosophy
@@ -176,10 +176,10 @@ Benchmark.ips(TIME) do |x|
176
176
  end
177
177
 
178
178
  x.report "Model.log" do
179
- Exhibit.connection.send(:log, "hello", "world") { }
179
+ Exhibit.lease_connection.send(:log, "hello", "world") { }
180
180
  end
181
181
 
182
182
  x.report "AR.execute(query)" do
183
- ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
183
+ ActiveRecord::Base.lease_connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
184
184
  end
185
185
  end
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
23
23
  end
24
24
 
25
- scoping { klass.#{method}(attributes, **kwargs) }
25
+ super
26
26
  end
27
27
  RUBY
28
28
  end
@@ -43,6 +43,7 @@ module ActiveRecord
43
43
  def exec_queries
44
44
  super do |record|
45
45
  @association.set_inverse_instance_from_queries(record)
46
+ @association.set_strict_loading(record)
46
47
  yield record if block_given?
47
48
  end
48
49
  end
@@ -6,34 +6,38 @@ module ActiveRecord
6
6
  module Associations
7
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
8
  class AliasTracker # :nodoc:
9
- def self.create(connection, initial_table, joins, aliases = nil)
10
- if joins.empty?
11
- aliases ||= Hash.new(0)
12
- elsif aliases
13
- default_proc = aliases.default_proc || proc { 0 }
14
- aliases.default_proc = proc { |h, k|
15
- h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
16
- }
17
- else
18
- aliases = Hash.new { |h, k|
19
- h[k] = initial_count_for(connection, k, joins)
20
- }
9
+ def self.create(pool, initial_table, joins, aliases = nil)
10
+ pool.with_connection do |connection|
11
+ if joins.empty?
12
+ aliases ||= Hash.new(0)
13
+ elsif aliases
14
+ default_proc = aliases.default_proc || proc { 0 }
15
+ aliases.default_proc = proc { |h, k|
16
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
17
+ }
18
+ else
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
21
+ }
22
+ end
23
+ aliases[initial_table] = 1
24
+ new(connection.table_alias_length, aliases)
21
25
  end
22
- aliases[initial_table] = 1
23
- new(connection, aliases)
24
26
  end
25
27
 
26
28
  def self.initial_count_for(connection, name, table_joins)
27
- quoted_name = nil
29
+ quoted_name_escaped = nil
30
+ name_escaped = nil
28
31
 
29
32
  counts = table_joins.map do |join|
30
33
  if join.is_a?(Arel::Nodes::StringJoin)
31
- # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
32
- quoted_name ||= connection.quote_table_name(name)
34
+ # quoted_name_escaped should be case ignored as some database adapters (Oracle) return quoted name in uppercase
35
+ quoted_name_escaped ||= Regexp.escape(connection.quote_table_name(name))
36
+ name_escaped ||= Regexp.escape(name)
33
37
 
34
38
  # Table names + table aliases
35
39
  join.left.scan(
36
- /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
40
+ /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name_escaped}|#{name_escaped})\sON/i
37
41
  ).size
38
42
  elsif join.is_a?(Arel::Nodes::Join)
39
43
  join.left.name == name ? 1 : 0
@@ -46,9 +50,9 @@ module ActiveRecord
46
50
  end
47
51
 
48
52
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
49
- def initialize(connection, aliases)
50
- @aliases = aliases
51
- @connection = connection
53
+ def initialize(table_alias_length, aliases)
54
+ @aliases = aliases
55
+ @table_alias_length = table_alias_length
52
56
  end
53
57
 
54
58
  def aliased_table_for(arel_table, table_name = nil)
@@ -60,7 +64,7 @@ module ActiveRecord
60
64
  arel_table = arel_table.alias(table_name) if arel_table.name != table_name
61
65
  else
62
66
  # Otherwise, we need to use an alias
63
- aliased_name = @connection.table_alias_for(yield)
67
+ aliased_name = table_alias_for(yield)
64
68
 
65
69
  # Update the count
66
70
  count = aliases[aliased_name] += 1
@@ -76,8 +80,12 @@ module ActiveRecord
76
80
  attr_reader :aliases
77
81
 
78
82
  private
83
+ def table_alias_for(table_name)
84
+ table_name[0...@table_alias_length].tr(".", "_")
85
+ end
86
+
79
87
  def truncate(name)
80
- name.slice(0, @connection.table_alias_length - 2)
88
+ name.slice(0, @table_alias_length - 2)
81
89
  end
82
90
  end
83
91
  end
@@ -34,7 +34,7 @@ module ActiveRecord
34
34
  # the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
35
35
  class Association # :nodoc:
36
36
  attr_accessor :owner
37
- attr_reader :target, :reflection, :disable_joins
37
+ attr_reader :reflection, :disable_joins
38
38
 
39
39
  delegate :options, to: :reflection
40
40
 
@@ -50,10 +50,16 @@ module ActiveRecord
50
50
  @skip_strict_loading = nil
51
51
  end
52
52
 
53
+ def target
54
+ if @target.is_a?(Promise)
55
+ @target = @target.value
56
+ end
57
+ @target
58
+ end
59
+
53
60
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
54
61
  def reset
55
62
  @loaded = false
56
- @target = nil
57
63
  @stale_state = nil
58
64
  end
59
65
 
@@ -64,7 +70,7 @@ module ActiveRecord
64
70
  # Reloads the \target and returns +self+ on success.
65
71
  # The QueryCache is cleared if +force+ is true.
66
72
  def reload(force = false)
67
- klass.connection.clear_query_cache if force && klass
73
+ klass.connection_pool.clear_query_cache if force && klass
68
74
  reset
69
75
  reset_scope
70
76
  load_target
@@ -114,6 +120,14 @@ module ActiveRecord
114
120
  @association_scope = nil
115
121
  end
116
122
 
123
+ def set_strict_loading(record)
124
+ if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
125
+ record.strict_loading!
126
+ else
127
+ record.strict_loading!(false, mode: owner.strict_loading_mode)
128
+ end
129
+ end
130
+
117
131
  # Set the inverse association, if possible
118
132
  def set_inverse_instance(record)
119
133
  if inverse = inverse_association_for(record)
@@ -173,7 +187,7 @@ module ActiveRecord
173
187
  # ActiveRecord::RecordNotFound is rescued within the method, and it is
174
188
  # not reraised. The proxy is \reset and +nil+ is the return value.
175
189
  def load_target
176
- @target = find_target if (@stale_state && stale_target?) || find_target?
190
+ @target = find_target(async: false) if (@stale_state && stale_target?) || find_target?
177
191
 
178
192
  loaded! unless loaded?
179
193
  target
@@ -181,6 +195,13 @@ module ActiveRecord
181
195
  reset
182
196
  end
183
197
 
198
+ def async_load_target # :nodoc:
199
+ @target = find_target(async: true) if (@stale_state && stale_target?) || find_target?
200
+
201
+ loaded! unless loaded?
202
+ nil
203
+ end
204
+
184
205
  # We can't dump @reflection and @through_reflection since it contains the scope proc
185
206
  def marshal_dump
186
207
  ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
@@ -211,6 +232,12 @@ module ActiveRecord
211
232
  _create_record(attributes, true, &block)
212
233
  end
213
234
 
235
+ # Whether the association represent a single record
236
+ # or a collection of records.
237
+ def collection?
238
+ false
239
+ end
240
+
214
241
  private
215
242
  # Reader and writer methods call this so that consistent errors are presented
216
243
  # when the association target class does not exist.
@@ -218,13 +245,19 @@ module ActiveRecord
218
245
  klass
219
246
  end
220
247
 
221
- def find_target
248
+ def find_target(async: false)
222
249
  if violates_strict_loading?
223
250
  Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
224
251
  end
225
252
 
226
253
  scope = self.scope
227
- return scope.to_a if skip_statement_cache?(scope)
254
+ if skip_statement_cache?(scope)
255
+ if async
256
+ return scope.load_async.then(&:to_a)
257
+ else
258
+ return scope.to_a
259
+ end
260
+ end
228
261
 
229
262
  sc = reflection.association_scope_cache(klass, owner) do |params|
230
263
  as = AssociationScope.create { params.bind }
@@ -232,12 +265,10 @@ module ActiveRecord
232
265
  end
233
266
 
234
267
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
235
- sc.execute(binds, klass.connection) do |record|
236
- set_inverse_instance(record)
237
- if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
238
- record.strict_loading!
239
- else
240
- record.strict_loading!(false, mode: owner.strict_loading_mode)
268
+ klass.with_connection do |c|
269
+ sc.execute(binds, c, async: async) do |record|
270
+ set_inverse_instance(record)
271
+ set_strict_loading(record)
241
272
  end
242
273
  end
243
274
  end
@@ -19,10 +19,16 @@ module ActiveRecord
19
19
  id = owner.public_send(reflection.foreign_key)
20
20
  end
21
21
 
22
+ association_class = if reflection.polymorphic?
23
+ owner.public_send(reflection.foreign_type)
24
+ else
25
+ reflection.klass
26
+ end
27
+
22
28
  enqueue_destroy_association(
23
29
  owner_model_name: owner.class.to_s,
24
30
  owner_id: owner.id,
25
- association_class: reflection.klass.to_s,
31
+ association_class: association_class.to_s,
26
32
  association_ids: [id],
27
33
  association_primary_key_column: primary_key_column,
28
34
  ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
@@ -124,12 +130,20 @@ module ActiveRecord
124
130
  end
125
131
 
126
132
  def replace_keys(record, force: false)
127
- target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
128
- reflection_fk = Array(reflection.foreign_key)
133
+ reflection_fk = reflection.foreign_key
134
+ if reflection_fk.is_a?(Array)
135
+ target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
136
+
137
+ if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
138
+ reflection_fk.each_with_index do |key, index|
139
+ owner[key] = target_key_values[index]
140
+ end
141
+ end
142
+ else
143
+ target_key_value = record ? record._read_attribute(primary_key(record.class)) : nil
129
144
 
130
- if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
131
- reflection_fk.zip(target_key_values).each do |key, value|
132
- owner[key] = value
145
+ if force || owner._read_attribute(reflection_fk) != target_key_value
146
+ owner[reflection_fk] = target_key_value
133
147
  end
134
148
  end
135
149
  end
@@ -148,8 +162,7 @@ module ActiveRecord
148
162
  end
149
163
 
150
164
  def stale_state
151
- result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
152
- result && result.to_s
165
+ owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
153
166
  end
154
167
  end
155
168
  end
@@ -41,8 +41,9 @@ module ActiveRecord
41
41
  end
42
42
 
43
43
  def stale_state
44
- foreign_key = super
45
- foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
44
+ if foreign_key = super
45
+ [foreign_key, owner[reflection.foreign_type]]
46
+ end
46
47
  end
47
48
  end
48
49
  end
@@ -30,10 +30,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
30
30
  end
31
31
 
32
32
  reflection = create_reflection(model, name, scope, options, &block)
33
- define_accessors model, reflection
34
- define_callbacks model, reflection
35
- define_validations model, reflection
36
- define_change_tracking_methods model, reflection
33
+ define_accessors(model, reflection)
34
+ define_callbacks(model, reflection)
35
+ define_validations(model, reflection)
36
+ define_change_tracking_methods(model, reflection)
37
37
  reflection
38
38
  end
39
39
 
@@ -71,6 +71,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
71
71
  end
72
72
 
73
73
  def self.define_extensions(model, name)
74
+ # noop
74
75
  end
75
76
 
76
77
  def self.define_callbacks(model, reflection)
@@ -81,7 +82,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
81
82
  end
82
83
 
83
84
  Association.extensions.each do |extension|
84
- extension.build model, reflection
85
+ extension.build(model, reflection)
85
86
  end
86
87
  end
87
88
 
@@ -131,7 +132,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
131
132
  err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
132
133
  raise ActiveRecord::ConfigurationError, err_message
133
134
  end
134
- unless valid_dependent_options.include? dependent
135
+ unless valid_dependent_options.include?(dependent)
135
136
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
136
137
  end
137
138
  end
@@ -38,6 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
38
38
 
39
39
  klass = reflection.class_name.safe_constantize
40
40
  klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
41
+ model.counter_cached_association_names |= [reflection.name]
41
42
  end
42
43
 
43
44
  def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
@@ -42,8 +42,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
42
42
  self.right_reflection = _reflect_on_association(rhs_name)
43
43
  end
44
44
 
45
- def self.retrieve_connection
46
- left_model.retrieve_connection
45
+ def self.connection_pool
46
+ left_model.connection_pool
47
47
  end
48
48
  }
49
49
 
@@ -7,11 +7,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- valid = super + [:counter_cache, :join_table, :index_errors]
11
- valid += [:as, :foreign_type] if options[:as]
12
- valid += [:through, :source, :source_type] if options[:through]
10
+ valid = super + [:counter_cache, :join_table, :index_errors, :as, :through]
11
+ valid += [:foreign_type] if options[:as]
12
+ valid += [:source, :source_type, :disable_joins] if options[:through]
13
13
  valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
14
- valid += [:disable_joins] if options[:disable_joins] && options[:through]
15
14
  valid
16
15
  end
17
16
 
@@ -7,11 +7,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- valid = super
11
- valid += [:as, :foreign_type] if options[:as]
10
+ valid = super + [:as, :through]
11
+ valid += [:foreign_type] if options[:as]
12
12
  valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
13
- valid += [:through, :source, :source_type] if options[:through]
14
- valid += [:disable_joins] if options[:disable_joins] && options[:through]
13
+ valid += [:source, :source_type, :disable_joins] if options[:through]
15
14
  valid
16
15
  end
17
16
 
@@ -28,6 +28,8 @@ module ActiveRecord
28
28
  # If you need to work on all current children, new and existing records,
29
29
  # +load_target+ and the +loaded+ flag are your friends.
30
30
  class CollectionAssociation < Association # :nodoc:
31
+ attr_accessor :nested_attributes_target
32
+
31
33
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
32
34
  def reader
33
35
  ensure_klass_exists!
@@ -92,7 +94,7 @@ module ActiveRecord
92
94
  def find(*args)
93
95
  if options[:inverse_of] && loaded?
94
96
  args_flatten = args.flatten
95
- model = scope.klass
97
+ model = scope.model
96
98
 
97
99
  if args_flatten.blank?
98
100
  error_message = "Couldn't find #{model.name} without an ID"
@@ -228,7 +230,7 @@ module ActiveRecord
228
230
  # loaded and you are going to fetch the records anyway it is better to
229
231
  # check <tt>collection.length.zero?</tt>.
230
232
  def empty?
231
- if loaded? || @association_ids || reflection.has_cached_counter?
233
+ if loaded? || @association_ids || reflection.has_active_cached_counter?
232
234
  size.zero?
233
235
  else
234
236
  target.empty? && !scope.exists?
@@ -254,14 +256,16 @@ module ActiveRecord
254
256
  end
255
257
 
256
258
  def include?(record)
257
- if record.is_a?(reflection.klass)
258
- if record.new_record?
259
- include_in_memory?(record)
260
- else
261
- loaded? ? target.include?(record) : scope.exists?(record.id)
262
- end
259
+ klass = reflection.klass
260
+ return false unless record.is_a?(klass)
261
+
262
+ if record.new_record?
263
+ include_in_memory?(record)
264
+ elsif loaded?
265
+ target.include?(record)
263
266
  else
264
- false
267
+ record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
268
+ scope.exists?(record_id)
265
269
  end
266
270
  end
267
271
 
@@ -309,6 +313,10 @@ module ActiveRecord
309
313
  target.any? { |record| record.new_record? || record.changed? }
310
314
  end
311
315
 
316
+ def collection?
317
+ true
318
+ end
319
+
312
320
  private
313
321
  def transaction(&block)
314
322
  reflection.klass.transaction(&block)
@@ -928,7 +928,20 @@ module ActiveRecord
928
928
  !!@association.include?(record)
929
929
  end
930
930
 
931
- def proxy_association # :nodoc:
931
+ # Returns the association object for the collection.
932
+ #
933
+ # class Person < ActiveRecord::Base
934
+ # has_many :pets
935
+ # end
936
+ #
937
+ # person.pets.proxy_association
938
+ # # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
939
+ #
940
+ # Returns the same object as <tt>person.association(:pets)</tt>,
941
+ # allowing you to make calls like <tt>person.pets.proxy_association.owner</tt>.
942
+ #
943
+ # See Associations::ClassMethods@Association+extensions for more.
944
+ def proxy_association
932
945
  @association
933
946
  end
934
947
 
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  end
48
48
 
49
49
  if scope.order_values.empty? && ordered
50
- split_scope = DisableJoinsAssociationRelation.create(scope.klass, key, join_ids)
50
+ split_scope = DisableJoinsAssociationRelation.create(scope.model, key, join_ids)
51
51
  split_scope.where_clause += scope.where_clause
52
52
  split_scope
53
53
  else