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,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/filters"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Reflection
@@ -11,6 +10,8 @@ module ActiveRecord
11
10
  included do
12
11
  class_attribute :_reflections, instance_writer: false, default: {}
13
12
  class_attribute :aggregate_reflections, instance_writer: false, default: {}
13
+ class_attribute :automatic_scope_inversing, instance_writer: false, default: false
14
+ class_attribute :automatically_invert_plural_associations, instance_writer: false, default: false
14
15
  end
15
16
 
16
17
  class << self
@@ -21,12 +22,12 @@ module ActiveRecord
21
22
 
22
23
  def add_reflection(ar, name, reflection)
23
24
  ar.clear_reflections_cache
24
- name = -name.to_s
25
+ name = name.to_sym
25
26
  ar._reflections = ar._reflections.except(name).merge!(name => reflection)
26
27
  end
27
28
 
28
29
  def add_aggregate_reflection(ar, name, reflection)
29
- ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
30
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
30
31
  end
31
32
 
32
33
  private
@@ -46,6 +47,8 @@ module ActiveRecord
46
47
  end
47
48
  end
48
49
 
50
+ # = Active Record Reflection
51
+ #
49
52
  # \Reflection enables the ability to examine the associations and aggregations of
50
53
  # Active Record classes and objects. This information, for example,
51
54
  # can be used in a form builder that takes an Active Record object
@@ -65,7 +68,7 @@ module ActiveRecord
65
68
  # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
66
69
  #
67
70
  def reflect_on_aggregation(aggregation)
68
- aggregate_reflections[aggregation.to_s]
71
+ aggregate_reflections[aggregation.to_sym]
69
72
  end
70
73
 
71
74
  # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
@@ -73,6 +76,10 @@ module ActiveRecord
73
76
  # Account.reflections # => {"balance" => AggregateReflection}
74
77
  #
75
78
  def reflections
79
+ normalized_reflections.stringify_keys
80
+ end
81
+
82
+ def normalized_reflections # :nodoc:
76
83
  @__reflections ||= begin
77
84
  ref = {}
78
85
 
@@ -81,13 +88,13 @@ module ActiveRecord
81
88
 
82
89
  if parent_reflection
83
90
  parent_name = parent_reflection.name
84
- ref[parent_name.to_s] = parent_reflection
91
+ ref[parent_name] = parent_reflection
85
92
  else
86
93
  ref[name] = reflection
87
94
  end
88
95
  end
89
96
 
90
- ref
97
+ ref.freeze
91
98
  end
92
99
  end
93
100
 
@@ -102,7 +109,7 @@ module ActiveRecord
102
109
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
103
110
  #
104
111
  def reflect_on_all_associations(macro = nil)
105
- association_reflections = reflections.values
112
+ association_reflections = normalized_reflections.values
106
113
  association_reflections.select! { |reflection| reflection.macro == macro } if macro
107
114
  association_reflections
108
115
  end
@@ -113,21 +120,31 @@ module ActiveRecord
113
120
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
114
121
  #
115
122
  def reflect_on_association(association)
116
- reflections[association.to_s]
123
+ normalized_reflections[association.to_sym]
117
124
  end
118
125
 
119
- def _reflect_on_association(association) #:nodoc:
120
- _reflections[association.to_s]
126
+ def _reflect_on_association(association) # :nodoc:
127
+ _reflections[association.to_sym]
121
128
  end
122
129
 
123
130
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
124
131
  def reflect_on_all_autosave_associations
125
- reflections.values.select { |reflection| reflection.options[:autosave] }
132
+ reflections = normalized_reflections.values
133
+ reflections.select! { |reflection| reflection.options[:autosave] }
134
+ reflections
126
135
  end
127
136
 
128
137
  def clear_reflections_cache # :nodoc:
129
138
  @__reflections = nil
130
139
  end
140
+
141
+ private
142
+ def inherited(subclass)
143
+ super
144
+ subclass.class_eval do
145
+ @__reflections = nil
146
+ end
147
+ end
131
148
  end
132
149
 
133
150
  # Holds all the methods that are shared between MacroReflection and ThroughReflection.
@@ -144,6 +161,14 @@ module ActiveRecord
144
161
  # PolymorphicReflection
145
162
  # RuntimeReflection
146
163
  class AbstractReflection # :nodoc:
164
+ def initialize
165
+ @class_name = nil
166
+ @counter_cache_column = nil
167
+ @inverse_of = nil
168
+ @inverse_which_updates_counter_cache_defined = false
169
+ @inverse_which_updates_counter_cache = nil
170
+ end
171
+
147
172
  def through_reflection?
148
173
  false
149
174
  end
@@ -163,13 +188,7 @@ module ActiveRecord
163
188
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
164
189
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
165
190
  def class_name
166
- @class_name ||= (options[:class_name] || derive_class_name).to_s
167
- end
168
-
169
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
170
-
171
- def join_keys
172
- @join_keys ||= get_join_keys(klass)
191
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
173
192
  end
174
193
 
175
194
  # Returns a list of scopes that should be applied for this Reflection
@@ -183,25 +202,31 @@ module ActiveRecord
183
202
  scope_chain_items = join_scopes(table, predicate_builder)
184
203
  klass_scope = klass_join_scope(table, predicate_builder)
185
204
 
186
- key = join_keys.key
187
- foreign_key = join_keys.foreign_key
188
-
189
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
190
-
191
205
  if type
192
206
  klass_scope.where!(type => foreign_klass.polymorphic_name)
193
207
  end
194
208
 
209
+ scope_chain_items.inject(klass_scope, &:merge!)
210
+
211
+ primary_key_column_names = Array(join_primary_key)
212
+ foreign_key_column_names = Array(join_foreign_key)
213
+
214
+ primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
215
+
216
+ primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
217
+ klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
218
+ end
219
+
195
220
  if klass.finder_needs_type_condition?
196
221
  klass_scope.where!(klass.send(:type_condition, table))
197
222
  end
198
223
 
199
- scope_chain_items.inject(klass_scope, &:merge!)
224
+ klass_scope
200
225
  end
201
226
 
202
- def join_scopes(table, predicate_builder) # :nodoc:
227
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
203
228
  if scope
204
- [scope_for(build_scope(table, predicate_builder))]
229
+ [scope_for(build_scope(table, predicate_builder, klass), record)]
205
230
  else
206
231
  []
207
232
  end
@@ -217,14 +242,16 @@ module ActiveRecord
217
242
  end
218
243
 
219
244
  def counter_cache_column
220
- if belongs_to?
221
- if options[:counter_cache] == true
222
- "#{active_record.name.demodulize.underscore.pluralize}_count"
223
- elsif options[:counter_cache]
224
- options[:counter_cache].to_s
245
+ @counter_cache_column ||= begin
246
+ counter_cache = options[:counter_cache]
247
+
248
+ if belongs_to?
249
+ if counter_cache
250
+ counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
251
+ end
252
+ else
253
+ -((counter_cache && -counter_cache[:column]) || "#{name}_count")
225
254
  end
226
- else
227
- options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
228
255
  end
229
256
  end
230
257
 
@@ -235,14 +262,17 @@ module ActiveRecord
235
262
  end
236
263
 
237
264
  def check_validity_of_inverse!
238
- unless polymorphic?
239
- if has_inverse? && inverse_of.nil?
265
+ if !polymorphic? && has_inverse?
266
+ if inverse_of.nil?
240
267
  raise InverseOfAssociationNotFoundError.new(self)
241
268
  end
269
+ if inverse_of == self
270
+ raise InverseOfAssociationRecursiveError.new(self)
271
+ end
242
272
  end
243
273
  end
244
274
 
245
- # This shit is nasty. We need to avoid the following situation:
275
+ # We need to avoid the following situation:
246
276
  #
247
277
  # * An associated record is deleted via record.destroy
248
278
  # * Hence the callbacks run, and they find a belongs_to on the record with a
@@ -253,10 +283,16 @@ module ActiveRecord
253
283
  #
254
284
  # Hence this method.
255
285
  def inverse_which_updates_counter_cache
256
- return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
257
- @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
258
- inverse.counter_cache_column == counter_cache_column
286
+ unless @inverse_which_updates_counter_cache_defined
287
+ if counter_cache_column
288
+ inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
289
+ @inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
290
+ inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
291
+ end
292
+ end
293
+ @inverse_which_updates_counter_cache_defined = true
259
294
  end
295
+ @inverse_which_updates_counter_cache
260
296
  end
261
297
  alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
262
298
 
@@ -264,14 +300,25 @@ module ActiveRecord
264
300
  inverse_of && inverse_which_updates_counter_cache == inverse_of
265
301
  end
266
302
 
267
- # Returns whether a counter cache should be used for this association.
303
+ # Returns whether this association has a counter cache.
268
304
  #
269
305
  # The counter_cache option must be given on either the owner or inverse
270
306
  # association, and the column must be present on the owner.
271
307
  def has_cached_counter?
272
308
  options[:counter_cache] ||
273
309
  inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
274
- !!active_record.columns_hash[counter_cache_column]
310
+ active_record.has_attribute?(counter_cache_column)
311
+ end
312
+
313
+ # Returns whether this association has a counter cache and its column values were backfilled
314
+ # (and so it is used internally by methods like +size+/+any?+/etc).
315
+ def has_active_cached_counter?
316
+ return false unless has_cached_counter?
317
+
318
+ counter_cache = options[:counter_cache] ||
319
+ (inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache])
320
+
321
+ counter_cache[:active] != false
275
322
  end
276
323
 
277
324
  def counter_must_be_updated_by_has_many?
@@ -286,11 +333,7 @@ module ActiveRecord
286
333
  collect_join_chain
287
334
  end
288
335
 
289
- def get_join_keys(association_klass)
290
- JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
291
- end
292
-
293
- def build_scope(table, predicate_builder = predicate_builder(table))
336
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
294
337
  Relation.create(
295
338
  klass,
296
339
  table: table,
@@ -298,12 +341,14 @@ module ActiveRecord
298
341
  )
299
342
  end
300
343
 
301
- def join_primary_key(*)
302
- foreign_key
344
+ def strict_loading?
345
+ options[:strict_loading]
303
346
  end
304
347
 
305
- def join_foreign_key
306
- active_record_primary_key
348
+ def strict_loading_violation_message(owner)
349
+ message = +"`#{owner}` is marked for strict_loading."
350
+ message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
351
+ message << " named `:#{name}` cannot be lazily loaded."
307
352
  end
308
353
 
309
354
  protected
@@ -319,6 +364,12 @@ module ActiveRecord
319
364
  def primary_key(klass)
320
365
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
321
366
  end
367
+
368
+ def ensure_option_not_given_as_class!(option_name)
369
+ if options[option_name] && options[option_name].class == Class
370
+ raise ArgumentError, "A class was passed to `:#{option_name}` but we are expecting a string."
371
+ end
372
+ end
322
373
  end
323
374
 
324
375
  # Base class for AggregateReflection and AssociationReflection. Objects of
@@ -343,9 +394,10 @@ module ActiveRecord
343
394
  attr_reader :plural_name # :nodoc:
344
395
 
345
396
  def initialize(name, scope, options, active_record)
397
+ super()
346
398
  @name = name
347
399
  @scope = scope
348
- @options = options
400
+ @options = normalize_options(options)
349
401
  @active_record = active_record
350
402
  @klass = options[:anonymous_class]
351
403
  @plural_name = active_record.pluralize_table_names ?
@@ -376,7 +428,15 @@ module ActiveRecord
376
428
  # a new association object. Use +build_association+ or +create_association+
377
429
  # instead. This allows plugins to hook into association object creation.
378
430
  def klass
379
- @klass ||= compute_class(class_name)
431
+ @klass ||= _klass(class_name)
432
+ end
433
+
434
+ def _klass(class_name) # :nodoc:
435
+ if active_record.name.demodulize == class_name
436
+ return compute_class("::#{class_name}") rescue NameError
437
+ end
438
+
439
+ compute_class(class_name)
380
440
  end
381
441
 
382
442
  def compute_class(name)
@@ -401,11 +461,31 @@ module ActiveRecord
401
461
  def derive_class_name
402
462
  name.to_s.camelize
403
463
  end
464
+
465
+ def normalize_options(options)
466
+ counter_cache = options.delete(:counter_cache)
467
+
468
+ if counter_cache
469
+ active = true
470
+
471
+ case counter_cache
472
+ when String, Symbol
473
+ column = -counter_cache.to_s
474
+ when Hash
475
+ active = counter_cache.fetch(:active, true)
476
+ column = counter_cache[:column]&.to_s
477
+ end
478
+
479
+ options[:counter_cache] = { active: active, column: column }
480
+ end
481
+
482
+ options
483
+ end
404
484
  end
405
485
 
406
486
  # Holds all the metadata about an aggregation as it was specified in the
407
487
  # Active Record class.
408
- class AggregateReflection < MacroReflection #:nodoc:
488
+ class AggregateReflection < MacroReflection # :nodoc:
409
489
  def mapping
410
490
  mapping = options[:mapping] || [name, name]
411
491
  mapping.first.is_a?(Array) ? mapping : [mapping]
@@ -414,12 +494,29 @@ module ActiveRecord
414
494
 
415
495
  # Holds all the metadata about an association as it was specified in the
416
496
  # Active Record class.
417
- class AssociationReflection < MacroReflection #:nodoc:
497
+ class AssociationReflection < MacroReflection # :nodoc:
418
498
  def compute_class(name)
419
499
  if polymorphic?
420
500
  raise ArgumentError, "Polymorphic associations do not support computing the class."
421
501
  end
422
- active_record.send(:compute_type, name)
502
+
503
+ begin
504
+ klass = active_record.send(:compute_type, name)
505
+ rescue NameError => error
506
+ if error.name.match?(/(?:\A|::)#{name}\z/)
507
+ message = "Missing model class #{name} for the #{active_record}##{self.name} association."
508
+ message += " You can specify a different model class with the :class_name option." unless options[:class_name]
509
+ raise NameError.new(message, name)
510
+ else
511
+ raise
512
+ end
513
+ end
514
+
515
+ unless klass < ActiveRecord::Base
516
+ raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
517
+ end
518
+
519
+ klass
423
520
  end
424
521
 
425
522
  attr_reader :type, :foreign_type
@@ -427,68 +524,126 @@ module ActiveRecord
427
524
 
428
525
  def initialize(name, scope, options, active_record)
429
526
  super
430
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
431
- @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
432
- @constructable = calculate_constructable(macro, options)
433
- @association_scope_cache = Concurrent::Map.new
527
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
528
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
529
+ @join_table = nil
530
+ @foreign_key = nil
531
+ @association_foreign_key = nil
532
+ @association_primary_key = nil
533
+ if options[:query_constraints]
534
+ ActiveRecord.deprecator.warn <<~MSG.squish
535
+ Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
536
+ To maintain current behavior, use the `foreign_key` option instead.
537
+ MSG
538
+ end
434
539
 
435
- if options[:class_name] && options[:class_name].class == Class
436
- raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
540
+ # If the foreign key is an array, set query constraints options and don't use the foreign key
541
+ if options[:foreign_key].is_a?(Array)
542
+ options[:query_constraints] = options.delete(:foreign_key)
437
543
  end
544
+
545
+ ensure_option_not_given_as_class!(:class_name)
438
546
  end
439
547
 
440
- def association_scope_cache(conn, owner, &block)
441
- key = conn.prepared_statements
548
+ def association_scope_cache(klass, owner, &block)
549
+ key = self
442
550
  if polymorphic?
443
551
  key = [key, owner._read_attribute(@foreign_type)]
444
552
  end
445
- @association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
446
- end
447
-
448
- def constructable? # :nodoc:
449
- @constructable
553
+ klass.with_connection do |connection|
554
+ klass.cached_find_by_statement(connection, key, &block)
555
+ end
450
556
  end
451
557
 
452
558
  def join_table
453
- @join_table ||= options[:join_table] || derive_join_table
559
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
454
560
  end
455
561
 
456
- def foreign_key
457
- @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
562
+ def foreign_key(infer_from_inverse_of: true)
563
+ @foreign_key ||= if options[:foreign_key]
564
+ if options[:foreign_key].is_a?(Array)
565
+ options[:foreign_key].map { |fk| fk.to_s.freeze }.freeze
566
+ else
567
+ options[:foreign_key].to_s.freeze
568
+ end
569
+ elsif options[:query_constraints]
570
+ options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
571
+ else
572
+ derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
573
+
574
+ if active_record.has_query_constraints?
575
+ derived_fk = derive_fk_query_constraints(derived_fk)
576
+ end
577
+
578
+ derived_fk
579
+ end
458
580
  end
459
581
 
460
582
  def association_foreign_key
461
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
583
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
462
584
  end
463
585
 
464
- # klass option is necessary to support loading polymorphic associations
465
586
  def association_primary_key(klass = nil)
466
- options[:primary_key] || primary_key(klass || self.klass)
587
+ primary_key(klass || self.klass)
467
588
  end
468
589
 
469
590
  def active_record_primary_key
470
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
591
+ custom_primary_key = options[:primary_key]
592
+ @active_record_primary_key ||= if custom_primary_key
593
+ if custom_primary_key.is_a?(Array)
594
+ custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
595
+ else
596
+ custom_primary_key.to_s.freeze
597
+ end
598
+ elsif active_record.has_query_constraints? || options[:query_constraints]
599
+ active_record.query_constraints_list
600
+ elsif active_record.composite_primary_key?
601
+ # If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
602
+ primary_key = primary_key(active_record)
603
+ primary_key.include?("id") ? "id" : primary_key.freeze
604
+ else
605
+ primary_key(active_record).freeze
606
+ end
607
+ end
608
+
609
+ def join_primary_key(klass = nil)
610
+ foreign_key
611
+ end
612
+
613
+ def join_primary_type
614
+ type
615
+ end
616
+
617
+ def join_foreign_key
618
+ active_record_primary_key
471
619
  end
472
620
 
473
621
  def check_validity!
474
622
  check_validity_of_inverse!
623
+
624
+ if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
625
+ if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
626
+ raise CompositePrimaryKeyMismatchError.new(self)
627
+ elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
628
+ raise CompositePrimaryKeyMismatchError.new(self)
629
+ end
630
+ end
475
631
  end
476
632
 
477
- def check_preloadable!
633
+ def check_eager_loadable!
478
634
  return unless scope
479
635
 
480
636
  unless scope.arity == 0
481
637
  raise ArgumentError, <<-MSG.squish
482
638
  The association scope '#{name}' is instance dependent (the scope
483
- block takes an argument). Preloading instance dependent scopes is
484
- not supported.
639
+ block takes an argument). Eager loading instance dependent scopes
640
+ is not supported.
485
641
  MSG
486
642
  end
487
643
  end
488
- alias :check_eager_loadable! :check_preloadable!
489
644
 
490
645
  def join_id_for(owner) # :nodoc:
491
- owner[join_foreign_key]
646
+ Array(join_foreign_key).map { |key| owner._read_attribute(key) }
492
647
  end
493
648
 
494
649
  def through_reflection
@@ -508,7 +663,7 @@ module ActiveRecord
508
663
  # This is for clearing cache on the reflection. Useful for tests that need to compare
509
664
  # SQL queries on associations.
510
665
  def clear_association_scope_cache # :nodoc:
511
- @association_scope_cache.clear
666
+ klass.initialize_find_by_cache
512
667
  end
513
668
 
514
669
  def nested?
@@ -570,8 +725,9 @@ module ActiveRecord
570
725
  options[:polymorphic]
571
726
  end
572
727
 
573
- VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
574
- INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
728
+ def polymorphic_name
729
+ active_record.polymorphic_name
730
+ end
575
731
 
576
732
  def add_as_source(seed)
577
733
  seed
@@ -590,11 +746,6 @@ module ActiveRecord
590
746
  end
591
747
 
592
748
  private
593
-
594
- def calculate_constructable(macro, options)
595
- true
596
- end
597
-
598
749
  # Attempts to find the inverse association name automatically.
599
750
  # If it cannot find a suitable inverse association name, it returns
600
751
  # +nil+.
@@ -613,14 +764,20 @@ module ActiveRecord
613
764
 
614
765
  begin
615
766
  reflection = klass._reflect_on_association(inverse_name)
616
- rescue NameError
767
+ if !reflection && active_record.automatically_invert_plural_associations
768
+ plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
769
+ reflection = klass._reflect_on_association(plural_inverse_name)
770
+ end
771
+ rescue NameError => error
772
+ raise unless error.name.to_s == class_name
773
+
617
774
  # Give up: we couldn't compute the klass type so we won't be able
618
775
  # to find any associations either.
619
776
  reflection = false
620
777
  end
621
778
 
622
779
  if valid_inverse_reflection?(reflection)
623
- return inverse_name
780
+ reflection.name
624
781
  end
625
782
  end
626
783
  end
@@ -631,8 +788,10 @@ module ActiveRecord
631
788
  # with the current reflection's klass name.
632
789
  def valid_inverse_reflection?(reflection)
633
790
  reflection &&
791
+ reflection != self &&
792
+ foreign_key == reflection.foreign_key &&
634
793
  klass <= reflection.active_record &&
635
- can_find_inverse_of_automatically?(reflection)
794
+ can_find_inverse_of_automatically?(reflection, true)
636
795
  end
637
796
 
638
797
  # Checks to see if the reflection doesn't have any options that prevent
@@ -641,14 +800,25 @@ module ActiveRecord
641
800
  # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
642
801
  # Third, we must not have options such as <tt>:foreign_key</tt>
643
802
  # which prevent us from correctly guessing the inverse association.
644
- #
645
- # Anything with a scope can additionally ruin our attempt at finding an
646
- # inverse, so we exclude reflections with scopes.
647
- def can_find_inverse_of_automatically?(reflection)
803
+ def can_find_inverse_of_automatically?(reflection, inverse_reflection = false)
648
804
  reflection.options[:inverse_of] != false &&
649
- VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
650
- !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
805
+ !reflection.options[:through] &&
806
+ !reflection.options[:foreign_key] &&
807
+ scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
808
+ end
809
+
810
+ # Scopes on the potential inverse reflection prevent automatic
811
+ # <tt>inverse_of</tt>, since the scope could exclude the owner record
812
+ # we would inverse from. Scopes on the reflection itself allow for
813
+ # automatic <tt>inverse_of</tt> as long as
814
+ # <tt>config.active_record.automatic_scope_inversing<tt> is set to
815
+ # +true+ (the default for new applications).
816
+ def scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
817
+ if inverse_reflection
651
818
  !reflection.scope
819
+ else
820
+ !reflection.scope || reflection.klass.automatic_scope_inversing
821
+ end
652
822
  end
653
823
 
654
824
  def derive_class_name
@@ -657,13 +827,55 @@ module ActiveRecord
657
827
  class_name.camelize
658
828
  end
659
829
 
660
- def derive_foreign_key
830
+ def derive_foreign_key(infer_from_inverse_of: true)
661
831
  if belongs_to?
662
832
  "#{name}_id"
663
833
  elsif options[:as]
664
834
  "#{options[:as]}_id"
835
+ elsif options[:inverse_of] && infer_from_inverse_of
836
+ inverse_of.foreign_key(infer_from_inverse_of: false)
665
837
  else
666
- active_record.name.foreign_key
838
+ active_record.model_name.to_s.foreign_key
839
+ end
840
+ end
841
+
842
+ def derive_fk_query_constraints(foreign_key)
843
+ primary_query_constraints = active_record.query_constraints_list
844
+ owner_pk = active_record.primary_key
845
+
846
+ if primary_query_constraints.size > 2
847
+ raise ArgumentError, <<~MSG.squish
848
+ The query constraints list on the `#{active_record}` model has more than 2
849
+ attributes. Active Record is unable to derive the query constraints
850
+ for the association. You need to explicitly define the query constraints
851
+ for this association.
852
+ MSG
853
+ end
854
+
855
+ if !primary_query_constraints.include?(owner_pk)
856
+ raise ArgumentError, <<~MSG.squish
857
+ The query constraints on the `#{active_record}` model does not include the primary
858
+ key so Active Record is unable to derive the foreign key constraints for
859
+ the association. You need to explicitly define the query constraints for this
860
+ association.
861
+ MSG
862
+ end
863
+
864
+ return foreign_key if primary_query_constraints.include?(foreign_key)
865
+
866
+ first_key, last_key = primary_query_constraints
867
+
868
+ if first_key == owner_pk
869
+ [foreign_key, last_key.to_s]
870
+ elsif last_key == owner_pk
871
+ [first_key.to_s, foreign_key]
872
+ else
873
+ raise ArgumentError, <<~MSG.squish
874
+ Active Record couldn't correctly interpret the query constraints
875
+ for the `#{active_record}` model. The query constraints on `#{active_record}` are
876
+ `#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
877
+ You need to explicitly set the query constraints for this association.
878
+ MSG
667
879
  end
668
880
  end
669
881
 
@@ -684,10 +896,6 @@ module ActiveRecord
684
896
  Associations::HasManyAssociation
685
897
  end
686
898
  end
687
-
688
- def association_primary_key(klass = nil)
689
- primary_key(klass || self.klass)
690
- end
691
899
  end
692
900
 
693
901
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -702,12 +910,6 @@ module ActiveRecord
702
910
  Associations::HasOneAssociation
703
911
  end
704
912
  end
705
-
706
- private
707
-
708
- def calculate_constructable(macro, options)
709
- !options[:through]
710
- end
711
913
  end
712
914
 
713
915
  class BelongsToReflection < AssociationReflection # :nodoc:
@@ -723,6 +925,25 @@ module ActiveRecord
723
925
  end
724
926
  end
725
927
 
928
+ # klass option is necessary to support loading polymorphic associations
929
+ def association_primary_key(klass = nil)
930
+ if primary_key = options[:primary_key]
931
+ @association_primary_key ||= if primary_key.is_a?(Array)
932
+ primary_key.map { |pk| pk.to_s.freeze }.freeze
933
+ else
934
+ -primary_key.to_s
935
+ end
936
+ elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
937
+ (klass || self.klass).composite_query_constraints_list
938
+ elsif (klass || self.klass).composite_primary_key?
939
+ # If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
940
+ primary_key = (klass || self.klass).primary_key
941
+ primary_key.include?("id") ? "id" : primary_key
942
+ else
943
+ primary_key(klass || self.klass)
944
+ end
945
+ end
946
+
726
947
  def join_primary_key(klass = nil)
727
948
  polymorphic? ? association_primary_key(klass) : association_primary_key
728
949
  end
@@ -731,14 +952,14 @@ module ActiveRecord
731
952
  foreign_key
732
953
  end
733
954
 
955
+ def join_foreign_type
956
+ foreign_type
957
+ end
958
+
734
959
  private
735
- def can_find_inverse_of_automatically?(_)
960
+ def can_find_inverse_of_automatically?(*)
736
961
  !polymorphic? && super
737
962
  end
738
-
739
- def calculate_constructable(macro, options)
740
- !polymorphic?
741
- end
742
963
  end
743
964
 
744
965
  class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
@@ -751,14 +972,17 @@ module ActiveRecord
751
972
 
752
973
  # Holds all the metadata about a :through association as it was specified
753
974
  # in the Active Record class.
754
- class ThroughReflection < AbstractReflection #:nodoc:
755
- delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
756
- :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
975
+ class ThroughReflection < AbstractReflection # :nodoc:
976
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
977
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
757
978
 
758
979
  def initialize(delegate_reflection)
980
+ super()
759
981
  @delegate_reflection = delegate_reflection
760
982
  @klass = delegate_reflection.options[:anonymous_class]
761
983
  @source_reflection_name = delegate_reflection.options[:source]
984
+
985
+ ensure_option_not_given_as_class!(:source_type)
762
986
  end
763
987
 
764
988
  def through_reflection?
@@ -766,7 +990,7 @@ module ActiveRecord
766
990
  end
767
991
 
768
992
  def klass
769
- @klass ||= delegate_reflection.compute_class(class_name)
993
+ @klass ||= delegate_reflection._klass(class_name)
770
994
  end
771
995
 
772
996
  # Returns the source of the through reflection. It checks both a singularized
@@ -787,6 +1011,8 @@ module ActiveRecord
787
1011
  # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
788
1012
  #
789
1013
  def source_reflection
1014
+ return unless source_reflection_name
1015
+
790
1016
  through_reflection.klass._reflect_on_association(source_reflection_name)
791
1017
  end
792
1018
 
@@ -839,8 +1065,8 @@ module ActiveRecord
839
1065
  source_reflection.scopes + super
840
1066
  end
841
1067
 
842
- def join_scopes(table, predicate_builder) # :nodoc:
843
- source_reflection.join_scopes(table, predicate_builder) + super
1068
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1069
+ source_reflection.join_scopes(table, predicate_builder, klass, record) + super
844
1070
  end
845
1071
 
846
1072
  def has_scope?
@@ -860,7 +1086,15 @@ module ActiveRecord
860
1086
  def association_primary_key(klass = nil)
861
1087
  # Get the "actual" source reflection if the immediate source reflection has a
862
1088
  # source reflection itself
863
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
1089
+ if primary_key = actual_source_reflection.options[:primary_key]
1090
+ @association_primary_key ||= -primary_key.to_s
1091
+ else
1092
+ primary_key(klass || self.klass)
1093
+ end
1094
+ end
1095
+
1096
+ def join_primary_key(klass = self.klass)
1097
+ source_reflection.join_primary_key(klass)
864
1098
  end
865
1099
 
866
1100
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -879,24 +1113,23 @@ module ActiveRecord
879
1113
  end
880
1114
 
881
1115
  def source_reflection_name # :nodoc:
882
- return @source_reflection_name if @source_reflection_name
883
-
884
- names = [name.to_s.singularize, name].collect(&:to_sym).uniq
885
- names = names.find_all { |n|
886
- through_reflection.klass._reflect_on_association(n)
887
- }
888
-
889
- if names.length > 1
890
- raise AmbiguousSourceReflectionForThroughAssociation.new(
891
- active_record.name,
892
- macro,
893
- name,
894
- options,
895
- source_reflection_names
896
- )
1116
+ @source_reflection_name ||= begin
1117
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
1118
+ names = names.find_all { |n|
1119
+ through_reflection.klass._reflect_on_association(n)
1120
+ }
1121
+
1122
+ if names.length > 1
1123
+ raise AmbiguousSourceReflectionForThroughAssociation.new(
1124
+ active_record.name,
1125
+ macro,
1126
+ name,
1127
+ options,
1128
+ source_reflection_names
1129
+ )
1130
+ end
1131
+ names.first
897
1132
  end
898
-
899
- @source_reflection_name = names.first
900
1133
  end
901
1134
 
902
1135
  def source_options
@@ -909,7 +1142,7 @@ module ActiveRecord
909
1142
 
910
1143
  def check_validity!
911
1144
  if through_reflection.nil?
912
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
1145
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
913
1146
  end
914
1147
 
915
1148
  if through_reflection.polymorphic?
@@ -937,7 +1170,7 @@ module ActiveRecord
937
1170
  end
938
1171
 
939
1172
  if parent_reflection.nil?
940
- reflections = active_record.reflections.keys.map(&:to_sym)
1173
+ reflections = active_record.normalized_reflections.keys
941
1174
 
942
1175
  if reflections.index(through_reflection.name) > reflections.index(name)
943
1176
  raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
@@ -996,16 +1229,21 @@ module ActiveRecord
996
1229
  end
997
1230
 
998
1231
  class PolymorphicReflection < AbstractReflection # :nodoc:
999
- delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1232
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1233
+ :name, :scope_for, to: :@reflection
1000
1234
 
1001
1235
  def initialize(reflection, previous_reflection)
1236
+ super()
1002
1237
  @reflection = reflection
1003
1238
  @previous_reflection = previous_reflection
1004
1239
  end
1005
1240
 
1006
- def join_scopes(table, predicate_builder) # :nodoc:
1007
- scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1008
- scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
1241
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1242
+ scopes = super
1243
+ unless @previous_reflection.through_reflection?
1244
+ scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1245
+ end
1246
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1009
1247
  end
1010
1248
 
1011
1249
  def constraints
@@ -1021,9 +1259,10 @@ module ActiveRecord
1021
1259
  end
1022
1260
 
1023
1261
  class RuntimeReflection < AbstractReflection # :nodoc:
1024
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1262
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1025
1263
 
1026
1264
  def initialize(reflection, association)
1265
+ super()
1027
1266
  @reflection = reflection
1028
1267
  @association = association
1029
1268
  end
@@ -1033,7 +1272,11 @@ module ActiveRecord
1033
1272
  end
1034
1273
 
1035
1274
  def aliased_table
1036
- @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1275
+ klass.arel_table
1276
+ end
1277
+
1278
+ def join_primary_key(klass = self.klass)
1279
+ @reflection.join_primary_key(klass)
1037
1280
  end
1038
1281
 
1039
1282
  def all_includes; yield; end