activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,53 +1,241 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :enddoc:
4
+
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  class Preloader
6
- class Association #:nodoc:
7
- def initialize(klass, owners, reflection, preload_scope)
8
+ class Association # :nodoc:
9
+ class LoaderQuery
10
+ attr_reader :scope, :association_key_name
11
+
12
+ def initialize(scope, association_key_name)
13
+ @scope = scope
14
+ @association_key_name = association_key_name
15
+ end
16
+
17
+ def eql?(other)
18
+ association_key_name == other.association_key_name &&
19
+ scope.table_name == other.scope.table_name &&
20
+ scope.connection_specification_name == other.scope.connection_specification_name &&
21
+ scope.values_for_queries == other.scope.values_for_queries
22
+ end
23
+
24
+ def hash
25
+ [association_key_name, scope.table_name, scope.connection_specification_name, scope.values_for_queries].hash
26
+ end
27
+
28
+ def records_for(loaders)
29
+ LoaderRecords.new(loaders, self).records
30
+ end
31
+
32
+ def load_records_in_batch(loaders)
33
+ raw_records = records_for(loaders)
34
+
35
+ loaders.each do |loader|
36
+ loader.load_records(raw_records)
37
+ loader.run
38
+ end
39
+ end
40
+
41
+ def load_records_for_keys(keys, &block)
42
+ return [] if keys.empty?
43
+
44
+ if association_key_name.is_a?(Array)
45
+ query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new }
46
+
47
+ keys.each_with_object(query_constraints) do |values_set, constraints|
48
+ association_key_name.zip(values_set).each do |key_name, value|
49
+ constraints[key_name] << value
50
+ end
51
+ end
52
+
53
+ scope.where(query_constraints)
54
+ else
55
+ scope.where(association_key_name => keys)
56
+ end.load(&block)
57
+ end
58
+ end
59
+
60
+ class LoaderRecords
61
+ def initialize(loaders, loader_query)
62
+ @loader_query = loader_query
63
+ @loaders = loaders
64
+ @keys_to_load = Set.new
65
+ @already_loaded_records_by_key = {}
66
+
67
+ populate_keys_to_load_and_already_loaded_records
68
+ end
69
+
70
+ def records
71
+ load_records + already_loaded_records
72
+ end
73
+
74
+ private
75
+ attr_reader :loader_query, :loaders, :keys_to_load, :already_loaded_records_by_key
76
+
77
+ def populate_keys_to_load_and_already_loaded_records
78
+ loaders.each do |loader|
79
+ loader.owners_by_key.each do |key, owners|
80
+ if loaded_owner = owners.find { |owner| loader.loaded?(owner) }
81
+ already_loaded_records_by_key[key] = loader.target_for(loaded_owner)
82
+ else
83
+ keys_to_load << key
84
+ end
85
+ end
86
+ end
87
+
88
+ @keys_to_load.subtract(already_loaded_records_by_key.keys)
89
+ end
90
+
91
+ def load_records
92
+ loader_query.load_records_for_keys(keys_to_load) do |record|
93
+ loaders.each { |l| l.set_inverse(record) }
94
+ end
95
+ end
96
+
97
+ def already_loaded_records
98
+ already_loaded_records_by_key.values.flatten
99
+ end
100
+ end
101
+
102
+ attr_reader :klass
103
+
104
+ def initialize(klass, owners, reflection, preload_scope, reflection_scope, associate_by_default)
8
105
  @klass = klass
9
- @owners = owners
106
+ @owners = owners.uniq(&:__id__)
10
107
  @reflection = reflection
11
108
  @preload_scope = preload_scope
109
+ @reflection_scope = reflection_scope
110
+ @associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
12
111
  @model = owners.first && owners.first.class
112
+ @run = false
13
113
  end
14
114
 
15
- def run
16
- if !preload_scope || preload_scope.empty_scope?
17
- owners.each do |owner|
18
- associate_records_to_owner(owner, records_by_owner[owner] || [])
19
- end
115
+ def table_name
116
+ @klass.table_name
117
+ end
118
+
119
+ def future_classes
120
+ if run?
121
+ []
20
122
  else
21
- # Custom preload scope is used and
22
- # the association can not be marked as loaded
23
- # Loading into a Hash instead
24
- records_by_owner
123
+ [@klass]
25
124
  end
125
+ end
126
+
127
+ def runnable_loaders
128
+ [self]
129
+ end
130
+
131
+ def run?
132
+ @run
133
+ end
134
+
135
+ def run
136
+ return self if run?
137
+ @run = true
138
+
139
+ records = records_by_owner
140
+
141
+ owners.each do |owner|
142
+ associate_records_to_owner(owner, records[owner] || [])
143
+ end if @associate
144
+
26
145
  self
27
146
  end
28
147
 
29
148
  def records_by_owner
149
+ load_records unless defined?(@records_by_owner)
150
+
151
+ @records_by_owner
152
+ end
153
+
154
+ def preloaded_records
155
+ load_records unless defined?(@preloaded_records)
156
+
157
+ @preloaded_records
158
+ end
159
+
160
+ # The name of the key on the associated records
161
+ def association_key_name
162
+ reflection.join_primary_key(klass)
163
+ end
164
+
165
+ def loader_query
166
+ LoaderQuery.new(scope, association_key_name)
167
+ end
168
+
169
+ def owners_by_key
170
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
171
+ key = derive_key(owner, owner_key_name)
172
+ (result[key] ||= []) << owner if key
173
+ end
174
+ end
175
+
176
+ def loaded?(owner)
177
+ owner.association(reflection.name).loaded?
178
+ end
179
+
180
+ def target_for(owner)
181
+ Array.wrap(owner.association(reflection.name).target)
182
+ end
183
+
184
+ def scope
185
+ @scope ||= build_scope
186
+ end
187
+
188
+ def set_inverse(record)
189
+ if owners = owners_by_key[derive_key(record, association_key_name)]
190
+ # Processing only the first owner
191
+ # because the record is modified but not an owner
192
+ association = owners.first.association(reflection.name)
193
+ association.set_inverse_instance(record)
194
+ end
195
+ end
196
+
197
+ def load_records(raw_records = nil)
30
198
  # owners can be duplicated when a relation has a collection association join
31
199
  # #compare_by_identity makes such owners different hash keys
32
- @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
33
- owners_by_key[convert_key(record[association_key_name])].each do |owner|
34
- (result[owner] ||= []) << record
200
+ @records_by_owner = {}.compare_by_identity
201
+ raw_records ||= loader_query.records_for([self])
202
+ @preloaded_records = raw_records.select do |record|
203
+ assignments = false
204
+
205
+ owners_by_key[derive_key(record, association_key_name)]&.each do |owner|
206
+ entries = (@records_by_owner[owner] ||= [])
207
+
208
+ if reflection.collection? || entries.empty?
209
+ entries << record
210
+ assignments = true
211
+ end
35
212
  end
213
+
214
+ assignments
36
215
  end
37
216
  end
38
217
 
39
- def preloaded_records
40
- return @preloaded_records if defined?(@preloaded_records)
41
- @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
42
- end
218
+ def associate_records_from_unscoped(unscoped_records)
219
+ return if unscoped_records.nil? || unscoped_records.empty?
220
+ return if !reflection_scope.empty_scope?
221
+ return if preload_scope && !preload_scope.empty_scope?
222
+ return if reflection.collection?
43
223
 
44
- private
45
- attr_reader :owners, :reflection, :preload_scope, :model, :klass
224
+ unscoped_records.select { |r| r[association_key_name].present? }.each do |record|
225
+ owners = owners_by_key[derive_key(record, association_key_name)]
226
+ owners&.each_with_index do |owner, i|
227
+ association = owner.association(reflection.name)
228
+ association.target = record
46
229
 
47
- # The name of the key on the associated records
48
- def association_key_name
49
- reflection.join_primary_key(klass)
230
+ if i == 0 # Set inverse on first owner
231
+ association.set_inverse_instance(record)
232
+ end
233
+ end
50
234
  end
235
+ end
236
+
237
+ private
238
+ attr_reader :owners, :reflection, :preload_scope, :model
51
239
 
52
240
  # The name of the key on the model which declares the association
53
241
  def owner_key_name
@@ -55,25 +243,18 @@ module ActiveRecord
55
243
  end
56
244
 
57
245
  def associate_records_to_owner(owner, records)
246
+ return if loaded?(owner)
247
+
58
248
  association = owner.association(reflection.name)
249
+
59
250
  if reflection.collection?
60
- association.target = records
251
+ not_persisted_records = association.target.reject(&:persisted?)
252
+ association.target = records + not_persisted_records
61
253
  else
62
254
  association.target = records.first
63
255
  end
64
256
  end
65
257
 
66
- def owner_keys
67
- @owner_keys ||= owners_by_key.keys
68
- end
69
-
70
- def owners_by_key
71
- @owners_by_key ||= owners.each_with_object({}) do |owner, result|
72
- key = convert_key(owner[owner_key_name])
73
- (result[key] ||= []) << owner if key
74
- end
75
- end
76
-
77
258
  def key_conversion_required?
78
259
  unless defined?(@key_conversion_required)
79
260
  @key_conversion_required = (association_key_type != owner_key_type)
@@ -82,6 +263,14 @@ module ActiveRecord
82
263
  @key_conversion_required
83
264
  end
84
265
 
266
+ def derive_key(owner, key)
267
+ if key.is_a?(Array)
268
+ key.map { |k| convert_key(owner._read_attribute(k)) }
269
+ else
270
+ convert_key(owner._read_attribute(key))
271
+ end
272
+ end
273
+
85
274
  def convert_key(key)
86
275
  if key_conversion_required?
87
276
  key.to_s
@@ -98,22 +287,8 @@ module ActiveRecord
98
287
  @model.type_for_attribute(owner_key_name).type
99
288
  end
100
289
 
101
- def records_for(ids)
102
- scope.where(association_key_name => ids).load do |record|
103
- # Processing only the first owner
104
- # because the record is modified but not an owner
105
- owner = owners_by_key[convert_key(record[association_key_name])].first
106
- association = owner.association(reflection.name)
107
- association.set_inverse_instance(record)
108
- end
109
- end
110
-
111
- def scope
112
- @scope ||= build_scope
113
- end
114
-
115
290
  def reflection_scope
116
- @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
291
+ @reflection_scope ||= reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(klass.unscoped, &:merge!)
117
292
  end
118
293
 
119
294
  def build_scope
@@ -123,9 +298,17 @@ module ActiveRecord
123
298
  scope.where!(reflection.type => model.polymorphic_name)
124
299
  end
125
300
 
126
- scope.merge!(reflection_scope) if reflection.scope
127
- scope.merge!(preload_scope) if preload_scope
128
- scope
301
+ scope.merge!(reflection_scope) unless reflection_scope.empty_scope?
302
+
303
+ if preload_scope && !preload_scope.empty_scope?
304
+ scope.merge!(preload_scope)
305
+ end
306
+
307
+ cascade_strict_loading(scope)
308
+ end
309
+
310
+ def cascade_strict_loading(scope)
311
+ preload_scope&.strict_loading_value ? scope.strict_loading : scope
129
312
  end
130
313
  end
131
314
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class Preloader
6
+ class Batch # :nodoc:
7
+ def initialize(preloaders, available_records:)
8
+ @preloaders = preloaders.reject(&:empty?)
9
+ @available_records = available_records.flatten.group_by { |r| r.class.base_class }
10
+ end
11
+
12
+ def call
13
+ branches = @preloaders.flat_map(&:branches)
14
+ until branches.empty?
15
+ loaders = branches.flat_map(&:runnable_loaders)
16
+
17
+ loaders.each { |loader| loader.associate_records_from_unscoped(@available_records[loader.klass.base_class]) }
18
+
19
+ if loaders.any?
20
+ future_tables = branches.flat_map do |branch|
21
+ branch.future_classes - branch.runnable_loaders.map(&:klass)
22
+ end.map(&:table_name).uniq
23
+
24
+ target_loaders = loaders.reject { |l| future_tables.include?(l.table_name) }
25
+ target_loaders = loaders if target_loaders.empty?
26
+
27
+ group_and_load_similar(target_loaders)
28
+ target_loaders.each(&:run)
29
+ end
30
+
31
+ finished, in_progress = branches.partition(&:done?)
32
+
33
+ branches = in_progress + finished.flat_map(&:children)
34
+ end
35
+ end
36
+
37
+ private
38
+ attr_reader :loaders
39
+
40
+ def group_and_load_similar(loaders)
41
+ loaders.grep_v(ThroughAssociation).group_by(&:loader_query).each_pair do |query, similar_loaders|
42
+ query.load_records_in_batch(similar_loaders)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class Preloader
6
+ class Branch # :nodoc:
7
+ attr_reader :association, :children, :parent
8
+ attr_reader :scope, :associate_by_default
9
+ attr_writer :preloaded_records
10
+
11
+ def initialize(association:, children:, parent:, associate_by_default:, scope:)
12
+ @association = if association
13
+ begin
14
+ @association = association.to_sym
15
+ rescue NoMethodError
16
+ raise ArgumentError, "Association names must be Symbol or String, got: #{association.class.name}"
17
+ end
18
+ end
19
+ @parent = parent
20
+ @scope = scope
21
+ @associate_by_default = associate_by_default
22
+
23
+ @children = build_children(children)
24
+ @loaders = nil
25
+ end
26
+
27
+ def future_classes
28
+ (immediate_future_classes + children.flat_map(&:future_classes)).uniq
29
+ end
30
+
31
+ def immediate_future_classes
32
+ if parent.done?
33
+ loaders.flat_map(&:future_classes).uniq
34
+ else
35
+ likely_reflections.reject(&:polymorphic?).flat_map do |reflection|
36
+ reflection.
37
+ chain.
38
+ map(&:klass)
39
+ end.uniq
40
+ end
41
+ end
42
+
43
+ def target_classes
44
+ if done?
45
+ preloaded_records.map(&:klass).uniq
46
+ elsif parent.done?
47
+ loaders.map(&:klass).uniq
48
+ else
49
+ likely_reflections.reject(&:polymorphic?).map(&:klass).uniq
50
+ end
51
+ end
52
+
53
+ def likely_reflections
54
+ parent_classes = parent.target_classes
55
+ parent_classes.filter_map do |parent_klass|
56
+ parent_klass._reflect_on_association(@association)
57
+ end
58
+ end
59
+
60
+ def root?
61
+ parent.nil?
62
+ end
63
+
64
+ def source_records
65
+ @parent.preloaded_records
66
+ end
67
+
68
+ def preloaded_records
69
+ @preloaded_records ||= loaders.flat_map(&:preloaded_records)
70
+ end
71
+
72
+ def done?
73
+ root? || (@loaders && @loaders.all?(&:run?))
74
+ end
75
+
76
+ def runnable_loaders
77
+ loaders.flat_map(&:runnable_loaders).reject(&:run?)
78
+ end
79
+
80
+ def grouped_records
81
+ h = {}
82
+ polymorphic_parent = !root? && parent.polymorphic?
83
+ source_records.each do |record|
84
+ reflection = record.class._reflect_on_association(association)
85
+ next if polymorphic_parent && !reflection || !record.association(association).klass
86
+ (h[reflection] ||= []) << record
87
+ end
88
+ h
89
+ end
90
+
91
+ def preloaders_for_reflection(reflection, reflection_records)
92
+ reflection_records.group_by do |record|
93
+ klass = record.association(association).klass
94
+
95
+ if reflection.scope && reflection.scope.arity != 0
96
+ # For instance dependent scopes, the scope is potentially
97
+ # different for each record. To allow this we'll group each
98
+ # object separately into its own preloader
99
+ reflection_scope = reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass, record).inject(&:merge!)
100
+ end
101
+
102
+ [klass, reflection_scope]
103
+ end.map do |(rhs_klass, reflection_scope), rs|
104
+ preloader_for(reflection).new(rhs_klass, rs, reflection, scope, reflection_scope, associate_by_default)
105
+ end
106
+ end
107
+
108
+ def polymorphic?
109
+ return false if root?
110
+ return @polymorphic if defined?(@polymorphic)
111
+
112
+ @polymorphic = source_records.any? do |record|
113
+ reflection = record.class._reflect_on_association(association)
114
+ reflection && reflection.options[:polymorphic]
115
+ end
116
+ end
117
+
118
+ def loaders
119
+ @loaders ||=
120
+ grouped_records.flat_map do |reflection, reflection_records|
121
+ preloaders_for_reflection(reflection, reflection_records)
122
+ end
123
+ end
124
+
125
+ private
126
+ def build_children(children)
127
+ Array.wrap(children).flat_map { |association|
128
+ Array(association).flat_map { |parent, child|
129
+ Branch.new(
130
+ parent: self,
131
+ association: parent,
132
+ children: child,
133
+ associate_by_default: associate_by_default,
134
+ scope: scope
135
+ )
136
+ }
137
+ }
138
+ end
139
+
140
+ # Returns a class containing the logic needed to load preload the data
141
+ # and attach it to a relation. The class returned implements a `run` method
142
+ # that accepts a preloader.
143
+ def preloader_for(reflection)
144
+ if reflection.options[:through]
145
+ ThroughAssociation
146
+ else
147
+ Association
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -4,26 +4,20 @@ module ActiveRecord
4
4
  module Associations
5
5
  class Preloader
6
6
  class ThroughAssociation < Association # :nodoc:
7
- PRELOADER = ActiveRecord::Associations::Preloader.new
8
-
9
- def initialize(*)
10
- super
11
- @already_loaded = owners.first.association(through_reflection.name).loaded?
12
- end
13
-
14
7
  def preloaded_records
15
8
  @preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
16
9
  end
17
10
 
18
11
  def records_by_owner
19
- return @records_by_owner if defined?(@records_by_owner)
20
- source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
21
- through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
12
+ @records_by_owner ||= owners.each_with_object({}) do |owner, result|
13
+ if loaded?(owner)
14
+ result[owner] = target_for(owner)
15
+ next
16
+ end
22
17
 
23
- @records_by_owner = owners.each_with_object({}) do |owner, result|
24
18
  through_records = through_records_by_owner[owner] || []
25
19
 
26
- if @already_loaded
20
+ if owners.first.association(through_reflection.name).loaded?
27
21
  if source_type = reflection.options[:source_type]
28
22
  through_records = through_records.select do |record|
29
23
  record[reflection.foreign_type] == source_type
@@ -42,17 +36,47 @@ module ActiveRecord
42
36
  end
43
37
  end
44
38
 
39
+ def runnable_loaders
40
+ if data_available?
41
+ [self]
42
+ elsif through_preloaders.all?(&:run?)
43
+ source_preloaders.flat_map(&:runnable_loaders)
44
+ else
45
+ through_preloaders.flat_map(&:runnable_loaders)
46
+ end
47
+ end
48
+
49
+ def future_classes
50
+ if run?
51
+ []
52
+ elsif through_preloaders.all?(&:run?)
53
+ source_preloaders.flat_map(&:future_classes).uniq
54
+ else
55
+ through_classes = through_preloaders.flat_map(&:future_classes)
56
+ source_classes = source_reflection.
57
+ chain.
58
+ reject { |reflection| reflection.respond_to?(:polymorphic?) && reflection.polymorphic? }.
59
+ map(&:klass)
60
+ (through_classes + source_classes).uniq
61
+ end
62
+ end
63
+
45
64
  private
65
+ def data_available?
66
+ owners.all? { |owner| loaded?(owner) } ||
67
+ through_preloaders.all?(&:run?) && source_preloaders.all?(&:run?)
68
+ end
69
+
46
70
  def source_preloaders
47
- @source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
71
+ @source_preloaders ||= ActiveRecord::Associations::Preloader.new(records: middle_records, associations: source_reflection.name, scope: scope, associate_by_default: false).loaders
48
72
  end
49
73
 
50
74
  def middle_records
51
- through_preloaders.flat_map(&:preloaded_records)
75
+ through_records_by_owner.values.flatten
52
76
  end
53
77
 
54
78
  def through_preloaders
55
- @through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
79
+ @through_preloaders ||= ActiveRecord::Associations::Preloader.new(records: owners, associations: through_reflection.name, scope: through_scope, associate_by_default: false).loaders
56
80
  end
57
81
 
58
82
  def through_reflection
@@ -63,6 +87,14 @@ module ActiveRecord
63
87
  reflection.source_reflection
64
88
  end
65
89
 
90
+ def source_records_by_owner
91
+ @source_records_by_owner ||= source_preloaders.map(&:records_by_owner).reduce(:merge)
92
+ end
93
+
94
+ def through_records_by_owner
95
+ @through_records_by_owner ||= through_preloaders.map(&:records_by_owner).reduce(:merge)
96
+ end
97
+
66
98
  def preload_index
67
99
  @preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
68
100
  result[record] = index
@@ -73,6 +105,8 @@ module ActiveRecord
73
105
  scope = through_reflection.klass.unscoped
74
106
  options = reflection.options
75
107
 
108
+ return scope if options[:disable_joins]
109
+
76
110
  values = reflection_scope.values
77
111
  if annotations = values[:annotate]
78
112
  scope.annotate!(*annotations)
@@ -90,7 +124,7 @@ module ActiveRecord
90
124
  end
91
125
 
92
126
  if values[:references] && !values[:references].empty?
93
- scope.references!(values[:references])
127
+ scope.references_values |= values[:references]
94
128
  else
95
129
  scope.references!(source_reflection.table_name)
96
130
  end
@@ -108,7 +142,7 @@ module ActiveRecord
108
142
  end
109
143
  end
110
144
 
111
- scope
145
+ cascade_strict_loading(scope)
112
146
  end
113
147
  end
114
148
  end