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,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  module Associations
7
+ # = Active Record \Preloader
8
+ #
5
9
  # Implements the details of eager loading of Active Record associations.
6
10
  #
7
11
  # Suppose that you have the following two Active Record models:
@@ -20,8 +24,8 @@ module ActiveRecord
20
24
  #
21
25
  # Author.includes(:books).where(name: ['bell hooks', 'Homer']).to_a
22
26
  #
23
- # => SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
24
- # => SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
27
+ # # SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
28
+ # # SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
25
29
  #
26
30
  # Active Record saves the ids of the records from the first query to use in
27
31
  # the second. Depending on the number of associations involved there can be
@@ -31,23 +35,26 @@ module ActiveRecord
31
35
  # Record will fall back to a slightly more resource-intensive single query:
32
36
  #
33
37
  # Author.includes(:books).where(books: {title: 'Illiad'}).to_a
34
- # => SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
35
- # `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
36
- # FROM `authors`
37
- # LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
38
- # WHERE `books`.`title` = 'Illiad'
38
+ # # SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
39
+ # # `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
40
+ # # FROM `authors`
41
+ # # LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
42
+ # # WHERE `books`.`title` = 'Illiad'
39
43
  #
40
44
  # This could result in many rows that contain redundant data and it performs poorly at scale
41
45
  # and is therefore only used when necessary.
42
- #
43
- class Preloader #:nodoc:
46
+ class Preloader # :nodoc:
44
47
  extend ActiveSupport::Autoload
45
48
 
46
49
  eager_autoload do
47
50
  autoload :Association, "active_record/associations/preloader/association"
51
+ autoload :Batch, "active_record/associations/preloader/batch"
52
+ autoload :Branch, "active_record/associations/preloader/branch"
48
53
  autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
49
54
  end
50
55
 
56
+ attr_reader :records, :associations, :scope, :associate_by_default
57
+
51
58
  # Eager loads the named associations for the given Active Record record(s).
52
59
  #
53
60
  # In this description, 'association name' shall refer to the name passed
@@ -58,7 +65,7 @@ module ActiveRecord
58
65
  # == Parameters
59
66
  # +records+ is an array of ActiveRecord::Base. This array needs not be flat,
60
67
  # i.e. +records+ itself may also contain arrays of records. In any case,
61
- # +preload_associations+ will preload the all associations records by
68
+ # +preload_associations+ will preload all associations records by
62
69
  # flattening +records+.
63
70
  #
64
71
  # +associations+ specifies one or more associations that you want to
@@ -68,134 +75,61 @@ module ActiveRecord
68
75
  # for an Author.
69
76
  # - an Array which specifies multiple association names. This array
70
77
  # is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
71
- # allows this method to preload an author's avatar as well as all of his
78
+ # allows this method to preload an author's avatar as well as all of their
72
79
  # books.
73
80
  # - a Hash which specifies multiple association names, as well as
74
81
  # association names for the to-be-preloaded association objects. For
75
82
  # example, specifying <tt>{ author: :avatar }</tt> will preload a
76
83
  # book's author, as well as that author's avatar.
77
84
  #
78
- # +:associations+ has the same format as the +:include+ option for
79
- # <tt>ActiveRecord::Base.find</tt>. So +associations+ could look like this:
85
+ # +:associations+ has the same format as the arguments to
86
+ # ActiveRecord::QueryMethods#includes. So +associations+ could look like
87
+ # this:
80
88
  #
81
89
  # :books
82
90
  # [ :books, :author ]
83
91
  # { author: :avatar }
84
92
  # [ :books, { author: :avatar } ]
85
- def preload(records, associations, preload_scope = nil)
86
- records = Array.wrap(records).compact
87
-
88
- if records.empty?
89
- []
90
- else
91
- Array.wrap(associations).flat_map { |association|
92
- preloaders_on association, records, preload_scope
93
- }
94
- end
93
+ #
94
+ # +available_records+ is an array of ActiveRecord::Base. The Preloader
95
+ # will try to use the objects in this array to preload the requested
96
+ # associations before querying the database. This can save database
97
+ # queries by reusing in-memory objects. The optimization is only applied
98
+ # to single associations (i.e. :belongs_to, :has_one) with no scopes.
99
+ def initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true)
100
+ @records = records
101
+ @associations = associations
102
+ @scope = scope
103
+ @available_records = available_records || []
104
+ @associate_by_default = associate_by_default
105
+
106
+ @tree = Branch.new(
107
+ parent: nil,
108
+ association: nil,
109
+ children: @associations,
110
+ associate_by_default: @associate_by_default,
111
+ scope: @scope
112
+ )
113
+ @tree.preloaded_records = @records
95
114
  end
96
115
 
97
- private
98
-
99
- # Loads all the given data into +records+ for the +association+.
100
- def preloaders_on(association, records, scope, polymorphic_parent = false)
101
- case association
102
- when Hash
103
- preloaders_for_hash(association, records, scope, polymorphic_parent)
104
- when Symbol, String
105
- preloaders_for_one(association, records, scope, polymorphic_parent)
106
- else
107
- raise ArgumentError, "#{association.inspect} was not recognized for preload"
108
- end
109
- end
110
-
111
- def preloaders_for_hash(association, records, scope, polymorphic_parent)
112
- association.flat_map { |parent, child|
113
- grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
- loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
- recs = loaders.flat_map(&:preloaded_records)
116
- child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
- loaders.concat Array.wrap(child).flat_map { |assoc|
118
- preloaders_on assoc, recs, scope, child_polymorphic_parent
119
- }
120
- loaders
121
- end
122
- }
123
- end
124
-
125
- # Loads all the given data into +records+ for a singular +association+.
126
- #
127
- # Functions by instantiating a preloader class such as Preloader::Association and
128
- # call the +run+ method for each passed in class in the +records+ argument.
129
- #
130
- # Not all records have the same class, so group then preload group on the reflection
131
- # itself so that if various subclass share the same association then we do not split
132
- # them unnecessarily
133
- #
134
- # Additionally, polymorphic belongs_to associations can have multiple associated
135
- # classes, depending on the polymorphic_type field. So we group by the classes as
136
- # well.
137
- def preloaders_for_one(association, records, scope, polymorphic_parent)
138
- grouped_records(association, records, polymorphic_parent)
139
- .flat_map do |reflection, reflection_records|
140
- preloaders_for_reflection reflection, reflection_records, scope
141
- end
142
- end
143
-
144
- def preloaders_for_reflection(reflection, records, scope)
145
- records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
146
- preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
147
- end
148
- end
149
-
150
- def grouped_records(association, records, polymorphic_parent)
151
- h = {}
152
- records.each do |record|
153
- reflection = record.class._reflect_on_association(association)
154
- next if polymorphic_parent && !reflection || !record.association(association).klass
155
- (h[reflection] ||= []) << record
156
- end
157
- h
158
- end
159
-
160
- class AlreadyLoaded # :nodoc:
161
- def initialize(klass, owners, reflection, preload_scope)
162
- @owners = owners
163
- @reflection = reflection
164
- end
165
-
166
- def run
167
- self
168
- end
169
-
170
- def preloaded_records
171
- @preloaded_records ||= records_by_owner.flat_map(&:last)
172
- end
116
+ def empty?
117
+ associations.nil? || records.length == 0
118
+ end
173
119
 
174
- def records_by_owner
175
- @records_by_owner ||= owners.each_with_object({}) do |owner, result|
176
- result[owner] = Array(owner.association(reflection.name).target)
177
- end
178
- end
120
+ def call
121
+ Batch.new([self], available_records: @available_records).call
179
122
 
180
- private
181
- attr_reader :owners, :reflection
182
- end
123
+ loaders
124
+ end
183
125
 
184
- # Returns a class containing the logic needed to load preload the data
185
- # and attach it to a relation. The class returned implements a `run` method
186
- # that accepts a preloader.
187
- def preloader_for(reflection, owners)
188
- if owners.first.association(reflection.name).loaded?
189
- return AlreadyLoaded
190
- end
191
- reflection.check_preloadable!
126
+ def branches
127
+ @tree.children
128
+ end
192
129
 
193
- if reflection.options[:through]
194
- ThroughAssociation
195
- else
196
- Association
197
- end
198
- end
130
+ def loaders
131
+ branches.flat_map(&:loaders)
132
+ end
199
133
  end
200
134
  end
201
135
  end
@@ -2,9 +2,11 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- class SingularAssociation < Association #:nodoc:
5
+ class SingularAssociation < Association # :nodoc:
6
6
  # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
7
7
  def reader
8
+ ensure_klass_exists!
9
+
8
10
  if !loaded? || stale_target?
9
11
  reload
10
12
  end
@@ -12,12 +14,18 @@ module ActiveRecord
12
14
  target
13
15
  end
14
16
 
17
+ # Resets the \loaded flag to +false+ and sets the \target to +nil+.
18
+ def reset
19
+ super
20
+ @target = nil
21
+ end
22
+
15
23
  # Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
16
24
  def writer(record)
17
25
  replace(record)
18
26
  end
19
27
 
20
- def build(attributes = {}, &block)
28
+ def build(attributes = nil, &block)
21
29
  record = build_record(attributes, &block)
22
30
  set_new_record(record)
23
31
  record
@@ -32,11 +40,15 @@ module ActiveRecord
32
40
 
33
41
  private
34
42
  def scope_for_create
35
- super.except!(klass.primary_key)
43
+ super.except!(*Array(klass.primary_key))
36
44
  end
37
45
 
38
46
  def find_target
39
- super.first
47
+ if disable_joins
48
+ scope.first
49
+ else
50
+ super.first
51
+ end
40
52
  end
41
53
 
42
54
  def replace(record)
@@ -3,10 +3,14 @@
3
3
  module ActiveRecord
4
4
  module Associations
5
5
  # = Active Record Through Association
6
- module ThroughAssociation #:nodoc:
6
+ module ThroughAssociation # :nodoc:
7
7
  delegate :source_reflection, to: :reflection
8
8
 
9
9
  private
10
+ def transaction(&block)
11
+ through_reflection.klass.transaction(&block)
12
+ end
13
+
10
14
  def through_reflection
11
15
  @through_reflection ||= begin
12
16
  refl = reflection.through_reflection
@@ -32,7 +36,7 @@ module ActiveRecord
32
36
  reflection.chain.drop(1).each do |reflection|
33
37
  relation = reflection.klass.scope_for_association
34
38
  scope.merge!(
35
- relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
39
+ relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
36
40
  )
37
41
  end
38
42
  scope
@@ -55,12 +59,11 @@ module ActiveRecord
55
59
 
56
60
  association_primary_key = source_reflection.association_primary_key(reflection.klass)
57
61
 
58
- if association_primary_key == reflection.klass.primary_key && !options[:source_type]
62
+ if Array(association_primary_key) == reflection.klass.composite_query_constraints_list && !options[:source_type]
59
63
  join_attributes = { source_reflection.name => records }
60
64
  else
61
- join_attributes = {
62
- source_reflection.foreign_key => records.map(&association_primary_key.to_sym)
63
- }
65
+ assoc_pk_values = records.map { |record| record._read_attribute(association_primary_key) }
66
+ join_attributes = { source_reflection.foreign_key => assoc_pk_values }
64
67
  end
65
68
 
66
69
  if options[:source_type]
@@ -74,16 +77,20 @@ module ActiveRecord
74
77
  end
75
78
  end
76
79
 
77
- # Note: this does not capture all cases, for example it would be crazy to try to
78
- # properly support stale-checking for nested associations.
80
+ # Note: this does not capture all cases, for example it would be impractical
81
+ # to try to properly support stale-checking for nested associations.
79
82
  def stale_state
80
83
  if through_reflection.belongs_to?
81
- owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
84
+ Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
85
+ owner[foreign_key_column]
86
+ end.presence
82
87
  end
83
88
  end
84
89
 
85
90
  def foreign_key_present?
86
- through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
91
+ through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? do |foreign_key_column|
92
+ !owner[foreign_key_column].nil?
93
+ end
87
94
  end
88
95
 
89
96
  def ensure_mutable
@@ -107,11 +114,15 @@ module ActiveRecord
107
114
  end
108
115
 
109
116
  def build_record(attributes)
110
- inverse = source_reflection.inverse_of
111
- target = through_association.target
112
-
113
- if inverse && target && !target.is_a?(Array)
114
- attributes[inverse.foreign_key] = target.id
117
+ if source_reflection.collection?
118
+ inverse = source_reflection.inverse_of
119
+ target = through_association.target
120
+
121
+ if inverse && target && !target.is_a?(Array)
122
+ Array(target.id).zip(Array(inverse.foreign_key)).map do |primary_key_value, foreign_key_column|
123
+ attributes[foreign_key_column] = primary_key_value
124
+ end
125
+ end
115
126
  end
116
127
 
117
128
  super