omg-activerecord 8.0.0.alpha1

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 (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,301 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class JoinDependency # :nodoc:
6
+ extend ActiveSupport::Autoload
7
+
8
+ eager_autoload do
9
+ autoload :JoinBase
10
+ autoload :JoinAssociation
11
+ end
12
+
13
+ class Aliases # :nodoc:
14
+ def initialize(tables)
15
+ @tables = tables
16
+ @alias_cache = tables.each_with_object({}) { |table, h|
17
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
18
+ i[column.name] = column.alias
19
+ }
20
+ }
21
+ @columns_cache = tables.each_with_object({}) { |table, h|
22
+ h[table.node] = table.columns
23
+ }
24
+ end
25
+
26
+ def columns
27
+ @tables.flat_map(&:column_aliases)
28
+ end
29
+
30
+ def column_aliases(node)
31
+ @columns_cache[node]
32
+ end
33
+
34
+ def column_alias(node, column)
35
+ @alias_cache[node][column]
36
+ end
37
+
38
+ Table = Struct.new(:node, :columns) do # :nodoc:
39
+ def column_aliases
40
+ t = node.table
41
+ columns.map { |column| t[column.name].as(column.alias) }
42
+ end
43
+ end
44
+ Column = Struct.new(:name, :alias)
45
+ end
46
+
47
+ def self.make_tree(associations)
48
+ hash = {}
49
+ walk_tree associations, hash
50
+ hash
51
+ end
52
+
53
+ def self.walk_tree(associations, hash)
54
+ case associations
55
+ when Symbol, String
56
+ hash[associations.to_sym] ||= {}
57
+ when Array
58
+ associations.each do |assoc|
59
+ walk_tree assoc, hash
60
+ end
61
+ when Hash
62
+ associations.each do |k, v|
63
+ cache = hash[k] ||= {}
64
+ walk_tree v, cache if v
65
+ end
66
+ else
67
+ raise ConfigurationError, associations.inspect
68
+ end
69
+ end
70
+
71
+ def initialize(base, table, associations, join_type)
72
+ tree = self.class.make_tree associations
73
+ @join_root = JoinBase.new(base, table, build(tree, base))
74
+ @join_type = join_type
75
+ end
76
+
77
+ def base_klass
78
+ join_root.base_klass
79
+ end
80
+
81
+ def reflections
82
+ join_root.drop(1).map!(&:reflection)
83
+ end
84
+
85
+ def join_constraints(joins_to_add, alias_tracker, references)
86
+ @alias_tracker = alias_tracker
87
+ @joined_tables = {}
88
+ @references = {}
89
+
90
+ references.each do |table_name|
91
+ @references[table_name.to_sym] = table_name if table_name.is_a?(Arel::Nodes::SqlLiteral)
92
+ end unless references.empty?
93
+
94
+ joins = make_join_constraints(join_root, join_type)
95
+
96
+ joins.concat joins_to_add.flat_map { |oj|
97
+ if join_root.match? oj.join_root
98
+ walk(join_root, oj.join_root, oj.join_type)
99
+ else
100
+ make_join_constraints(oj.join_root, oj.join_type)
101
+ end
102
+ }
103
+ end
104
+
105
+ def instantiate(result_set, strict_loading_value, &block)
106
+ primary_key = aliases.column_alias(join_root, join_root.primary_key)
107
+
108
+ seen = Hash.new { |i, parent|
109
+ i[parent] = Hash.new { |j, child_class|
110
+ j[child_class] = {}
111
+ }
112
+ }.compare_by_identity
113
+
114
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
115
+ parents = model_cache[join_root]
116
+
117
+ column_aliases = aliases.column_aliases(join_root)
118
+ column_names = []
119
+
120
+ result_set.columns.each do |name|
121
+ column_names << name unless /\At\d+_r\d+\z/.match?(name)
122
+ end
123
+
124
+ if column_names.empty?
125
+ column_types = {}
126
+ else
127
+ column_types = result_set.column_types
128
+ unless column_types.empty?
129
+ attribute_types = join_root.attribute_types
130
+ column_types = column_types.slice(*column_names).delete_if { |k, _| attribute_types.key?(k) }
131
+ end
132
+ column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
133
+ end
134
+
135
+ message_bus = ActiveSupport::Notifications.instrumenter
136
+
137
+ payload = {
138
+ record_count: result_set.length,
139
+ class_name: join_root.base_klass.name
140
+ }
141
+
142
+ message_bus.instrument("instantiation.active_record", payload) do
143
+ result_set.each { |row_hash|
144
+ parent_key = primary_key ? row_hash[primary_key] : row_hash
145
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
146
+ construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
147
+ }
148
+ end
149
+
150
+ parents.values
151
+ end
152
+
153
+ def apply_column_aliases(relation)
154
+ @join_root_alias = relation.select_values.empty?
155
+ relation._select!(-> { aliases.columns })
156
+ end
157
+
158
+ def each(&block)
159
+ join_root.each(&block)
160
+ end
161
+
162
+ protected
163
+ attr_reader :join_root, :join_type
164
+
165
+ private
166
+ attr_reader :alias_tracker, :join_root_alias
167
+
168
+ def aliases
169
+ @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
170
+ column_names = if join_part == join_root && !join_root_alias
171
+ primary_key = join_root.primary_key
172
+ primary_key ? [primary_key] : []
173
+ else
174
+ join_part.column_names
175
+ end
176
+
177
+ columns = column_names.each_with_index.map { |column_name, j|
178
+ Aliases::Column.new column_name, "t#{i}_r#{j}"
179
+ }
180
+ Aliases::Table.new(join_part, columns)
181
+ }
182
+ end
183
+
184
+ def make_join_constraints(join_root, join_type)
185
+ join_root.children.flat_map do |child|
186
+ make_constraints(join_root, child, join_type)
187
+ end
188
+ end
189
+
190
+ def make_constraints(parent, child, join_type)
191
+ foreign_table = parent.table
192
+ foreign_klass = parent.base_klass
193
+ child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection, remaining_reflection_chain|
194
+ table, terminated = @joined_tables[remaining_reflection_chain]
195
+ root = reflection == child.reflection
196
+
197
+ if table && (!root || !terminated)
198
+ @joined_tables[remaining_reflection_chain] = [table, root] if root
199
+ next table, true
200
+ end
201
+
202
+ table_name = @references[reflection.name.to_sym]&.to_s
203
+
204
+ table = alias_tracker.aliased_table_for(reflection.klass.arel_table, table_name) do
205
+ name = reflection.alias_candidate(parent.table_name)
206
+ root ? name : "#{name}_join"
207
+ end
208
+
209
+ @joined_tables[remaining_reflection_chain] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
210
+ table
211
+ end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
212
+ end
213
+
214
+ def walk(left, right, join_type)
215
+ intersection, missing = right.children.map { |node1|
216
+ [left.children.find { |node2| node1.match? node2 }, node1]
217
+ }.partition(&:first)
218
+
219
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
220
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
221
+ end
222
+
223
+ def find_reflection(klass, name)
224
+ klass._reflect_on_association(name) ||
225
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
226
+ end
227
+
228
+ def build(associations, base_klass)
229
+ associations.map do |name, right|
230
+ reflection = find_reflection base_klass, name
231
+ reflection.check_validity!
232
+ reflection.check_eager_loadable!
233
+
234
+ if reflection.polymorphic?
235
+ raise EagerLoadPolymorphicError.new(reflection)
236
+ end
237
+
238
+ JoinAssociation.new(reflection, build(right, reflection.klass))
239
+ end
240
+ end
241
+
242
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
243
+ return if ar_parent.nil?
244
+
245
+ parent.children.each do |node|
246
+ if node.reflection.collection?
247
+ other = ar_parent.association(node.reflection.name)
248
+ other.loaded!
249
+ elsif ar_parent.association_cached?(node.reflection.name)
250
+ model = ar_parent.association(node.reflection.name).target
251
+ construct(model, node, row, seen, model_cache, strict_loading_value)
252
+ next
253
+ end
254
+
255
+ if node.primary_key
256
+ keys = Array(node.primary_key).map { |column| aliases.column_alias(node, column) }
257
+ id = keys.map { |key| row[key] }
258
+ else
259
+ keys = Array(node.reflection.join_primary_key).map { |column| aliases.column_alias(node, column.to_s) }
260
+ id = keys.map { nil } # Avoid id-based model caching.
261
+ end
262
+
263
+ if keys.any? { |key| row[key].nil? }
264
+ nil_association = ar_parent.association(node.reflection.name)
265
+ nil_association.loaded!
266
+ next
267
+ end
268
+
269
+ unless model = seen[ar_parent][node][id]
270
+ model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
271
+ seen[ar_parent][node][id] = model if id
272
+ end
273
+
274
+ construct(model, node, row, seen, model_cache, strict_loading_value)
275
+ end
276
+ end
277
+
278
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
279
+ other = record.association(node.reflection.name)
280
+
281
+ unless model = model_cache[node][id]
282
+ model = node.instantiate(row, aliases.column_aliases(node)) do |m|
283
+ m.strict_loading! if strict_loading_value
284
+ other.set_inverse_instance(m)
285
+ end
286
+ model_cache[node][id] = model if id
287
+ end
288
+
289
+ if node.reflection.collection?
290
+ other.target.push(model)
291
+ else
292
+ other.target = model
293
+ end
294
+
295
+ model.readonly! if node.readonly?
296
+ model.strict_loading! if node.strict_loading?
297
+ model
298
+ end
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Validation error class to wrap association records' errors,
4
+ # with index_errors support.
5
+ module ActiveRecord
6
+ module Associations
7
+ class NestedError < ::ActiveModel::NestedError
8
+ def initialize(association, inner_error)
9
+ @base = association.owner
10
+ @association = association
11
+ @inner_error = inner_error
12
+ super(@base, inner_error, { attribute: compute_attribute(inner_error) })
13
+ end
14
+
15
+ private
16
+ attr_reader :association
17
+
18
+ def compute_attribute(inner_error)
19
+ association_name = association.reflection.name
20
+
21
+ if association.collection? && index_errors_setting && index
22
+ "#{association_name}[#{index}].#{inner_error.attribute}".to_sym
23
+ else
24
+ "#{association_name}.#{inner_error.attribute}".to_sym
25
+ end
26
+ end
27
+
28
+ def index_errors_setting
29
+ @index_errors_setting ||=
30
+ association.options.fetch(:index_errors, ActiveRecord.index_nested_attribute_errors)
31
+ end
32
+
33
+ def index
34
+ @index ||= ordered_records&.find_index(inner_error.base)
35
+ end
36
+
37
+ def ordered_records
38
+ case index_errors_setting
39
+ when true # default is association order
40
+ association.target
41
+ when :nested_attributes_order
42
+ association.nested_attributes_target
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :enddoc:
4
+
5
+ module ActiveRecord
6
+ module Associations
7
+ class Preloader
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.model.connection_specification_name == other.scope.model.connection_specification_name &&
21
+ scope.values_for_queries == other.scope.values_for_queries
22
+ end
23
+
24
+ def hash
25
+ [association_key_name, scope.model.table_name, scope.model.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)
105
+ @klass = klass
106
+ @owners = owners.uniq(&:__id__)
107
+ @reflection = reflection
108
+ @preload_scope = preload_scope
109
+ @reflection_scope = reflection_scope
110
+ @associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
111
+ @model = owners.first && owners.first.class
112
+ @run = false
113
+ end
114
+
115
+ def table_name
116
+ @klass.table_name
117
+ end
118
+
119
+ def future_classes
120
+ if run?
121
+ []
122
+ else
123
+ [@klass]
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
+
145
+ self
146
+ end
147
+
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)
198
+ # owners can be duplicated when a relation has a collection association join
199
+ # #compare_by_identity makes such owners different hash keys
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
212
+ end
213
+
214
+ assignments
215
+ end
216
+ end
217
+
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?
223
+
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
229
+
230
+ if i == 0 # Set inverse on first owner
231
+ association.set_inverse_instance(record)
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ private
238
+ attr_reader :owners, :reflection, :preload_scope, :model
239
+
240
+ # The name of the key on the model which declares the association
241
+ def owner_key_name
242
+ reflection.join_foreign_key
243
+ end
244
+
245
+ def associate_records_to_owner(owner, records)
246
+ return if loaded?(owner)
247
+
248
+ association = owner.association(reflection.name)
249
+
250
+ if reflection.collection?
251
+ not_persisted_records = association.target.reject(&:persisted?)
252
+ association.target = records + not_persisted_records
253
+ else
254
+ association.target = records.first
255
+ end
256
+ end
257
+
258
+ def key_conversion_required?
259
+ unless defined?(@key_conversion_required)
260
+ @key_conversion_required = (association_key_type != owner_key_type)
261
+ end
262
+
263
+ @key_conversion_required
264
+ end
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
+
274
+ def convert_key(key)
275
+ if key_conversion_required?
276
+ key.to_s
277
+ else
278
+ key
279
+ end
280
+ end
281
+
282
+ def association_key_type
283
+ @klass.type_for_attribute(association_key_name).type
284
+ end
285
+
286
+ def owner_key_type
287
+ @model.type_for_attribute(owner_key_name).type
288
+ end
289
+
290
+ def reflection_scope
291
+ @reflection_scope ||= reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(klass.unscoped, &:merge!)
292
+ end
293
+
294
+ def build_scope
295
+ scope = klass.scope_for_association
296
+
297
+ if reflection.type && !reflection.through_reflection?
298
+ scope.where!(reflection.type => model.polymorphic_name)
299
+ end
300
+
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
312
+ end
313
+ end
314
+ end
315
+ end
316
+ 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