activerecord 4.2.9 → 6.1.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,13 +1,14 @@
1
- require 'active_support/deprecation'
2
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
3
4
 
4
5
  module ActiveRecord
5
6
  module FinderMethods
6
- ONE_AS_ONE = '1 AS one'
7
+ ONE_AS_ONE = "1 AS one"
7
8
 
8
9
  # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
9
- # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
10
- # is an integer, find by id coerces its arguments using +to_i+.
10
+ # If one or more records cannot be found for the requested ids, then ActiveRecord::RecordNotFound will be raised.
11
+ # If the primary key is an integer, find by id coerces its arguments by using +to_i+.
11
12
  #
12
13
  # Person.find(1) # returns the object for ID = 1
13
14
  # Person.find("1") # returns the object for ID = 1
@@ -17,11 +18,10 @@ module ActiveRecord
17
18
  # Person.find([1]) # returns an array for the object with ID = 1
18
19
  # Person.where("administrator = 1").order("created_on DESC").find(1)
19
20
  #
20
- # <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found.
21
- #
22
- # NOTE: The returned records may not be in the same order as the ids you
23
- # provide since database rows are unordered. You'd need to provide an explicit <tt>order</tt>
24
- # option if you want the results are sorted.
21
+ # NOTE: The returned records are in the same order as the ids you provide.
22
+ # If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
23
+ # method and provide an explicit ActiveRecord::QueryMethods#order option.
24
+ # But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
25
25
  #
26
26
  # ==== Find with lock
27
27
  #
@@ -37,7 +37,7 @@ module ActiveRecord
37
37
  # person.save!
38
38
  # end
39
39
  #
40
- # ==== Variations of +find+
40
+ # ==== Variations of #find
41
41
  #
42
42
  # Person.where(name: 'Spartacus', rating: 4)
43
43
  # # returns a chainable list (which can be empty).
@@ -45,13 +45,13 @@ module ActiveRecord
45
45
  # Person.find_by(name: 'Spartacus', rating: 4)
46
46
  # # returns the first item or nil.
47
47
  #
48
- # Person.where(name: 'Spartacus', rating: 4).first_or_initialize
48
+ # Person.find_or_initialize_by(name: 'Spartacus', rating: 4)
49
49
  # # returns the first item or returns a new instance (requires you call .save to persist against the database).
50
50
  #
51
- # Person.where(name: 'Spartacus', rating: 4).first_or_create
52
- # # returns the first item or creates it and returns it, available since Rails 3.2.1.
51
+ # Person.find_or_create_by(name: 'Spartacus', rating: 4)
52
+ # # returns the first item or creates it and returns it.
53
53
  #
54
- # ==== Alternatives for +find+
54
+ # ==== Alternatives for #find
55
55
  #
56
56
  # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
57
57
  # # returns a boolean indicating if any record with the given conditions exist.
@@ -60,16 +60,13 @@ module ActiveRecord
60
60
  # # returns a chainable list of instances with only the mentioned fields.
61
61
  #
62
62
  # Person.where(name: 'Spartacus', rating: 4).ids
63
- # # returns an Array of ids, available since Rails 3.2.1.
63
+ # # returns an Array of ids.
64
64
  #
65
65
  # Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
66
- # # returns an Array of the required fields, available since Rails 3.1.
66
+ # # returns an Array of the required fields.
67
67
  def find(*args)
68
- if block_given?
69
- to_a.find(*args) { |*block_args| yield(*block_args) }
70
- else
71
- find_with_ids(*args)
72
- end
68
+ return super if block_given?
69
+ find_with_ids(*args)
73
70
  end
74
71
 
75
72
  # Finds the first record matching the specified conditions. There
@@ -80,18 +77,14 @@ module ActiveRecord
80
77
  #
81
78
  # Post.find_by name: 'Spartacus', rating: 4
82
79
  # Post.find_by "published_at < ?", 2.weeks.ago
83
- def find_by(*args)
84
- where(*args).take
85
- rescue RangeError
86
- nil
80
+ def find_by(arg, *args)
81
+ where(arg, *args).take
87
82
  end
88
83
 
89
- # Like <tt>find_by</tt>, except that if no record is found, raises
90
- # an <tt>ActiveRecord::RecordNotFound</tt> error.
91
- def find_by!(*args)
92
- where(*args).take!
93
- rescue RangeError
94
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
84
+ # Like #find_by, except that if no record is found, raises
85
+ # an ActiveRecord::RecordNotFound error.
86
+ def find_by!(arg, *args)
87
+ where(arg, *args).take!
95
88
  end
96
89
 
97
90
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -102,13 +95,13 @@ module ActiveRecord
102
95
  # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
103
96
  # Person.where(["name LIKE '%?'", name]).take
104
97
  def take(limit = nil)
105
- limit ? limit(limit).to_a : find_take
98
+ limit ? find_take_with_limit(limit) : find_take
106
99
  end
107
100
 
108
- # Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
109
- # is found. Note that <tt>take!</tt> accepts no arguments.
101
+ # Same as #take but raises ActiveRecord::RecordNotFound if no record
102
+ # is found. Note that #take! accepts no arguments.
110
103
  def take!
111
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
104
+ take || raise_record_not_found_exception!
112
105
  end
113
106
 
114
107
  # Find the first record (or first N records if a parameter is supplied).
@@ -121,17 +114,19 @@ module ActiveRecord
121
114
  # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
122
115
  #
123
116
  def first(limit = nil)
117
+ check_reorder_deprecation unless loaded?
118
+
124
119
  if limit
125
- find_nth_with_limit(offset_index, limit)
120
+ find_nth_with_limit(0, limit)
126
121
  else
127
- find_nth(0, offset_index)
122
+ find_nth 0
128
123
  end
129
124
  end
130
125
 
131
- # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
132
- # is found. Note that <tt>first!</tt> accepts no arguments.
126
+ # Same as #first but raises ActiveRecord::RecordNotFound if no record
127
+ # is found. Note that #first! accepts no arguments.
133
128
  def first!
134
- find_nth! 0
129
+ first || raise_record_not_found_exception!
135
130
  end
136
131
 
137
132
  # Find the last record (or last N records if a parameter is supplied).
@@ -150,21 +145,18 @@ module ActiveRecord
150
145
  #
151
146
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
152
147
  def last(limit = nil)
153
- if limit
154
- if order_values.empty? && primary_key
155
- order(arel_table[primary_key].desc).limit(limit).reverse
156
- else
157
- to_a.last(limit)
158
- end
159
- else
160
- find_last
161
- end
148
+ return find_last(limit) if loaded? || has_limit_or_offset?
149
+
150
+ result = ordered_relation.limit(limit)
151
+ result = result.reverse_order!
152
+
153
+ limit ? result.reverse : result.first
162
154
  end
163
155
 
164
- # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
165
- # is found. Note that <tt>last!</tt> accepts no arguments.
156
+ # Same as #last but raises ActiveRecord::RecordNotFound if no record
157
+ # is found. Note that #last! accepts no arguments.
166
158
  def last!
167
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
159
+ last || raise_record_not_found_exception!
168
160
  end
169
161
 
170
162
  # Find the second record.
@@ -174,13 +166,13 @@ module ActiveRecord
174
166
  # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
175
167
  # Person.where(["user_name = :u", { u: user_name }]).second
176
168
  def second
177
- find_nth(1, offset_index)
169
+ find_nth 1
178
170
  end
179
171
 
180
- # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
172
+ # Same as #second but raises ActiveRecord::RecordNotFound if no record
181
173
  # is found.
182
174
  def second!
183
- find_nth! 1
175
+ second || raise_record_not_found_exception!
184
176
  end
185
177
 
186
178
  # Find the third record.
@@ -190,13 +182,13 @@ module ActiveRecord
190
182
  # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
191
183
  # Person.where(["user_name = :u", { u: user_name }]).third
192
184
  def third
193
- find_nth(2, offset_index)
185
+ find_nth 2
194
186
  end
195
187
 
196
- # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
188
+ # Same as #third but raises ActiveRecord::RecordNotFound if no record
197
189
  # is found.
198
190
  def third!
199
- find_nth! 2
191
+ third || raise_record_not_found_exception!
200
192
  end
201
193
 
202
194
  # Find the fourth record.
@@ -206,13 +198,13 @@ module ActiveRecord
206
198
  # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
207
199
  # Person.where(["user_name = :u", { u: user_name }]).fourth
208
200
  def fourth
209
- find_nth(3, offset_index)
201
+ find_nth 3
210
202
  end
211
203
 
212
- # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
204
+ # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
213
205
  # is found.
214
206
  def fourth!
215
- find_nth! 3
207
+ fourth || raise_record_not_found_exception!
216
208
  end
217
209
 
218
210
  # Find the fifth record.
@@ -222,13 +214,13 @@ module ActiveRecord
222
214
  # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
223
215
  # Person.where(["user_name = :u", { u: user_name }]).fifth
224
216
  def fifth
225
- find_nth(4, offset_index)
217
+ find_nth 4
226
218
  end
227
219
 
228
- # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
220
+ # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
229
221
  # is found.
230
222
  def fifth!
231
- find_nth! 4
223
+ fifth || raise_record_not_found_exception!
232
224
  end
233
225
 
234
226
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -238,30 +230,62 @@ module ActiveRecord
238
230
  # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
239
231
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
240
232
  def forty_two
241
- find_nth(41, offset_index)
233
+ find_nth 41
242
234
  end
243
235
 
244
- # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
236
+ # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
245
237
  # is found.
246
238
  def forty_two!
247
- find_nth! 41
239
+ forty_two || raise_record_not_found_exception!
240
+ end
241
+
242
+ # Find the third-to-last record.
243
+ # If no order is defined it will order by primary key.
244
+ #
245
+ # Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
246
+ # Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
247
+ # Person.where(["user_name = :u", { u: user_name }]).third_to_last
248
+ def third_to_last
249
+ find_nth_from_last 3
250
+ end
251
+
252
+ # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
253
+ # is found.
254
+ def third_to_last!
255
+ third_to_last || raise_record_not_found_exception!
256
+ end
257
+
258
+ # Find the second-to-last record.
259
+ # If no order is defined it will order by primary key.
260
+ #
261
+ # Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
262
+ # Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
263
+ # Person.where(["user_name = :u", { u: user_name }]).second_to_last
264
+ def second_to_last
265
+ find_nth_from_last 2
248
266
  end
249
267
 
250
- # Returns +true+ if a record exists in the table that matches the +id+ or
251
- # conditions given, or +false+ otherwise. The argument can take six forms:
268
+ # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
269
+ # is found.
270
+ def second_to_last!
271
+ second_to_last || raise_record_not_found_exception!
272
+ end
273
+
274
+ # Returns true if a record exists in the table that matches the +id+ or
275
+ # conditions given, or false otherwise. The argument can take six forms:
252
276
  #
253
277
  # * Integer - Finds the record with this primary key.
254
278
  # * String - Finds the record with a primary key corresponding to this
255
279
  # string (such as <tt>'5'</tt>).
256
- # * Array - Finds the record that matches these +find+-style conditions
280
+ # * Array - Finds the record that matches these +where+-style conditions
257
281
  # (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
258
- # * Hash - Finds the record that matches these +find+-style conditions
282
+ # * Hash - Finds the record that matches these +where+-style conditions
259
283
  # (such as <tt>{name: 'David'}</tt>).
260
284
  # * +false+ - Returns always +false+.
261
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
285
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
262
286
  #
263
287
  # For more information about specifying conditions as a hash or array,
264
- # see the Conditions section in the introduction to <tt>ActiveRecord::Base</tt>.
288
+ # see the Conditions section in the introduction to ActiveRecord::Base.
265
289
  #
266
290
  # Note: You can't pass in a condition as a string (like <tt>name =
267
291
  # 'Jamie'</tt>), since it would be sanitized and then queried against
@@ -274,243 +298,293 @@ module ActiveRecord
274
298
  # Person.exists?(name: 'David')
275
299
  # Person.exists?(false)
276
300
  # Person.exists?
301
+ # Person.where(name: 'Spartacus', rating: 4).exists?
277
302
  def exists?(conditions = :none)
278
303
  if Base === conditions
279
- conditions = conditions.id
280
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
304
+ raise ArgumentError, <<-MSG.squish
281
305
  You are passing an instance of ActiveRecord::Base to `exists?`.
282
- Please pass the id of the object by calling `.id`
306
+ Please pass the id of the object by calling `.id`.
283
307
  MSG
284
308
  end
285
309
 
286
- return false if !conditions
310
+ return false if !conditions || limit_value == 0
287
311
 
288
- relation = apply_join_dependency(self, construct_join_dependency)
289
- return false if ActiveRecord::NullRelation === relation
312
+ if eager_loading?
313
+ relation = apply_join_dependency(eager_loading: false)
314
+ return relation.exists?(conditions)
315
+ end
290
316
 
291
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
317
+ relation = construct_relation_for_exists(conditions)
318
+ return false if relation.where_clause.contradiction?
292
319
 
293
- case conditions
294
- when Array, Hash
295
- relation = relation.where(conditions)
320
+ skip_query_cache_if_necessary { connection.select_rows(relation.arel, "#{name} Exists?").size == 1 }
321
+ end
322
+
323
+ # Returns true if the relation contains the given record or false otherwise.
324
+ #
325
+ # No query is performed if the relation is loaded; the given record is
326
+ # compared to the records in memory. If the relation is unloaded, an
327
+ # efficient existence query is performed, as in #exists?.
328
+ def include?(record)
329
+ if loaded? || offset_value || limit_value || having_clause.any?
330
+ records.include?(record)
296
331
  else
297
- unless conditions == :none
298
- relation = relation.where(primary_key => conditions)
299
- end
332
+ record.is_a?(klass) && exists?(record.id)
300
333
  end
301
-
302
- connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
303
334
  end
304
335
 
336
+ alias :member? :include?
337
+
305
338
  # This method is called whenever no records are found with either a single
306
- # id or multiple ids and raises a +ActiveRecord::RecordNotFound+ exception.
339
+ # id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
307
340
  #
308
341
  # The error message is different depending on whether a single id or
309
342
  # multiple ids are provided. If multiple ids are provided, then the number
310
343
  # of results obtained should be provided in the +result_size+ argument and
311
344
  # the expected number of results should be provided in the +expected_size+
312
345
  # argument.
313
- def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
314
- conditions = arel.where_sql
315
- conditions = " [#{conditions}]" if conditions
316
-
317
- if Array(ids).size == 1
318
- error = "Couldn't find #{@klass.name} with '#{primary_key}'=#{ids}#{conditions}"
346
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
347
+ conditions = " [#{arel.where_sql(klass)}]" unless where_clause.empty?
348
+
349
+ name = @klass.name
350
+
351
+ if ids.nil?
352
+ error = +"Couldn't find #{name}"
353
+ error << " with#{conditions}" if conditions
354
+ raise RecordNotFound.new(error, name, key)
355
+ elsif Array.wrap(ids).size == 1
356
+ error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
357
+ raise RecordNotFound.new(error, name, key, ids)
319
358
  else
320
- error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
321
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
359
+ error = +"Couldn't find all #{name.pluralize} with '#{key}': "
360
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
361
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
362
+ raise RecordNotFound.new(error, name, key, ids)
322
363
  end
323
-
324
- raise RecordNotFound, error
325
364
  end
326
365
 
327
366
  private
367
+ def check_reorder_deprecation
368
+ if !order_values.empty? && order_values.all?(&:blank?)
369
+ blank_value = order_values.first
370
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
371
+ `.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
372
+ takes non-deterministic result in Rails 6.2.
373
+ To continue taking non-deterministic result, use `.take` / `.take!` instead.
374
+ MSG
375
+ end
376
+ end
328
377
 
329
- def offset_index
330
- offset_value || 0
331
- end
378
+ def construct_relation_for_exists(conditions)
379
+ conditions = sanitize_forbidden_attributes(conditions)
332
380
 
333
- def find_with_associations
334
- # NOTE: the JoinDependency constructed here needs to know about
335
- # any joins already present in `self`, so pass them in
336
- #
337
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
338
- # incorrect SQL is generated. In that case, the join dependency for
339
- # SpecialCategorizations is constructed without knowledge of the
340
- # preexisting join in joins_values to categorizations (by way of
341
- # the `has_many :through` for categories).
342
- #
343
- join_dependency = construct_join_dependency(joins_values)
344
-
345
- aliases = join_dependency.aliases
346
- relation = select aliases.columns
347
- relation = apply_join_dependency(relation, join_dependency)
348
-
349
- if block_given?
350
- yield relation
351
- else
352
- if ActiveRecord::NullRelation === relation
353
- []
381
+ if distinct_value && offset_value
382
+ relation = except(:order).limit!(1)
354
383
  else
355
- arel = relation.arel
356
- rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
357
- join_dependency.instantiate(rows, aliases)
384
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
358
385
  end
359
- end
360
- end
361
386
 
362
- def construct_join_dependency(joins = [])
363
- including = eager_load_values + includes_values
364
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
365
- end
387
+ case conditions
388
+ when Array, Hash
389
+ relation.where!(conditions) unless conditions.empty?
390
+ else
391
+ relation.where!(primary_key => conditions) unless conditions == :none
392
+ end
366
393
 
367
- def construct_relation_for_association_calculations
368
- from = arel.froms.first
369
- if Arel::Table === from
370
- apply_join_dependency(self, construct_join_dependency(joins_values))
371
- else
372
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
373
- # There are no tests that test this branch, but presumably it's
374
- # possible for `from` to be a list?
375
- apply_join_dependency(self, construct_join_dependency(from))
394
+ relation
376
395
  end
377
- end
378
396
 
379
- def apply_join_dependency(relation, join_dependency)
380
- relation = relation.except(:includes, :eager_load, :preload)
381
- relation = relation.joins join_dependency
397
+ def apply_join_dependency(eager_loading: group_values.empty?)
398
+ join_dependency = construct_join_dependency(
399
+ eager_load_values | includes_values, Arel::Nodes::OuterJoin
400
+ )
401
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
402
+
403
+ if eager_loading && !(
404
+ using_limitable_reflections?(join_dependency.reflections) &&
405
+ using_limitable_reflections?(
406
+ construct_join_dependency(
407
+ select_association_list(joins_values).concat(
408
+ select_association_list(left_outer_joins_values)
409
+ ), nil
410
+ ).reflections
411
+ )
412
+ )
413
+ if has_limit_or_offset?
414
+ limited_ids = limited_ids_for(relation)
415
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
416
+ end
417
+ relation.limit_value = relation.offset_value = nil
418
+ end
382
419
 
383
- if using_limitable_reflections?(join_dependency.reflections)
384
- relation
385
- else
386
- if relation.limit_value
387
- limited_ids = limited_ids_for(relation)
388
- limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
420
+ if block_given?
421
+ yield relation, join_dependency
422
+ else
423
+ relation
389
424
  end
390
- relation.except(:limit, :offset)
391
425
  end
392
- end
393
426
 
394
- def limited_ids_for(relation)
395
- values = @klass.connection.columns_for_distinct(
396
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
427
+ def limited_ids_for(relation)
428
+ values = @klass.connection.columns_for_distinct(
429
+ connection.visitor.compile(table[primary_key]),
430
+ relation.order_values
431
+ )
397
432
 
398
- relation = relation.except(:select).select(values).distinct!
399
- arel = relation.arel
433
+ relation = relation.except(:select).select(values).distinct!
400
434
 
401
- id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
402
- id_rows.map {|row| row[primary_key]}
403
- end
435
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
436
+ id_rows.map(&:last)
437
+ end
404
438
 
405
- def using_limitable_reflections?(reflections)
406
- reflections.none? { |r| r.collection? }
407
- end
439
+ def using_limitable_reflections?(reflections)
440
+ reflections.none?(&:collection?)
441
+ end
408
442
 
409
- protected
443
+ def find_with_ids(*ids)
444
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
410
445
 
411
- def find_with_ids(*ids)
412
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
446
+ expects_array = ids.first.kind_of?(Array)
447
+ return [] if expects_array && ids.first.empty?
413
448
 
414
- expects_array = ids.first.kind_of?(Array)
415
- return ids.first if expects_array && ids.first.empty?
449
+ ids = ids.flatten.compact.uniq
416
450
 
417
- ids = ids.flatten.compact.uniq
451
+ model_name = @klass.name
418
452
 
419
- case ids.size
420
- when 0
421
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
422
- when 1
423
- result = find_one(ids.first)
424
- expects_array ? [ result ] : result
425
- else
426
- find_some(ids)
453
+ case ids.size
454
+ when 0
455
+ error_message = "Couldn't find #{model_name} without an ID"
456
+ raise RecordNotFound.new(error_message, model_name, primary_key)
457
+ when 1
458
+ result = find_one(ids.first)
459
+ expects_array ? [ result ] : result
460
+ else
461
+ find_some(ids)
462
+ end
427
463
  end
428
- rescue RangeError
429
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
430
- end
431
464
 
432
- def find_one(id)
433
- if ActiveRecord::Base === id
434
- id = id.id
435
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
436
- You are passing an instance of ActiveRecord::Base to `find`.
437
- Please pass the id of the object by calling `.id`
438
- MSG
465
+ def find_one(id)
466
+ if ActiveRecord::Base === id
467
+ raise ArgumentError, <<-MSG.squish
468
+ You are passing an instance of ActiveRecord::Base to `find`.
469
+ Please pass the id of the object by calling `.id`.
470
+ MSG
471
+ end
472
+
473
+ relation = where(primary_key => id)
474
+ record = relation.take
475
+
476
+ raise_record_not_found_exception!(id, 0, 1) unless record
477
+
478
+ record
439
479
  end
440
480
 
441
- relation = where(primary_key => id)
442
- record = relation.take
481
+ def find_some(ids)
482
+ return find_some_ordered(ids) unless order_values.present?
443
483
 
444
- raise_record_not_found_exception!(id, 0, 1) unless record
484
+ result = where(primary_key => ids).to_a
445
485
 
446
- record
447
- end
486
+ expected_size =
487
+ if limit_value && ids.size > limit_value
488
+ limit_value
489
+ else
490
+ ids.size
491
+ end
448
492
 
449
- def find_some(ids)
450
- result = where(primary_key => ids).to_a
493
+ # 11 ids with limit 3, offset 9 should give 2 results.
494
+ if offset_value && (ids.size - offset_value < expected_size)
495
+ expected_size = ids.size - offset_value
496
+ end
451
497
 
452
- expected_size =
453
- if limit_value && ids.size > limit_value
454
- limit_value
498
+ if result.size == expected_size
499
+ result
455
500
  else
456
- ids.size
501
+ raise_record_not_found_exception!(ids, result.size, expected_size)
457
502
  end
503
+ end
504
+
505
+ def find_some_ordered(ids)
506
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
507
+
508
+ result = except(:limit, :offset).where(primary_key => ids).records
458
509
 
459
- # 11 ids with limit 3, offset 9 should give 2 results.
460
- if offset_value && (ids.size - offset_value < expected_size)
461
- expected_size = ids.size - offset_value
510
+ if result.size == ids.size
511
+ pk_type = @klass.type_for_attribute(primary_key)
512
+
513
+ records_by_id = result.index_by(&:id)
514
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
515
+ else
516
+ raise_record_not_found_exception!(ids, result.size, ids.size)
517
+ end
462
518
  end
463
519
 
464
- if result.size == expected_size
465
- result
466
- else
467
- raise_record_not_found_exception!(ids, result.size, expected_size)
520
+ def find_take
521
+ if loaded?
522
+ records.first
523
+ else
524
+ @take ||= limit(1).records.first
525
+ end
468
526
  end
469
- end
470
527
 
471
- def find_take
472
- if loaded?
473
- @records.first
474
- else
475
- @take ||= limit(1).to_a.first
528
+ def find_take_with_limit(limit)
529
+ if loaded?
530
+ records.take(limit)
531
+ else
532
+ limit(limit).to_a
533
+ end
476
534
  end
477
- end
478
535
 
479
- def find_nth(index, offset)
480
- if loaded?
481
- @records[index]
482
- else
483
- offset += index
484
- @offsets[offset] ||= find_nth_with_limit(offset, 1).first
536
+ def find_nth(index)
537
+ @offsets ||= {}
538
+ @offsets[index] ||= find_nth_with_limit(index, 1).first
485
539
  end
486
- end
487
540
 
488
- def find_nth!(index)
489
- find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
490
- end
541
+ def find_nth_with_limit(index, limit)
542
+ if loaded?
543
+ records[index, limit] || []
544
+ else
545
+ relation = ordered_relation
491
546
 
492
- def find_nth_with_limit(offset, limit)
493
- relation = if order_values.empty? && primary_key
494
- order(arel_table[primary_key].asc)
495
- else
496
- self
497
- end
547
+ if limit_value
548
+ limit = [limit_value - index, limit].min
549
+ end
498
550
 
499
- relation = relation.offset(offset) unless offset.zero?
500
- relation.limit(limit).to_a
501
- end
551
+ if limit > 0
552
+ relation = relation.offset((offset_value || 0) + index) unless index.zero?
553
+ relation.limit(limit).to_a
554
+ else
555
+ []
556
+ end
557
+ end
558
+ end
502
559
 
503
- def find_last
504
- if loaded?
505
- @records.last
506
- else
507
- @last ||=
508
- if limit_value
509
- to_a.last
560
+ def find_nth_from_last(index)
561
+ if loaded?
562
+ records[-index]
563
+ else
564
+ relation = ordered_relation
565
+
566
+ if equal?(relation) || has_limit_or_offset?
567
+ relation.records[-index]
510
568
  else
511
- reverse_order.limit(1).to_a.first
569
+ relation.last(index)[-index]
512
570
  end
571
+ end
572
+ end
573
+
574
+ def find_last(limit)
575
+ limit ? records.last(limit) : records.last
576
+ end
577
+
578
+ def ordered_relation
579
+ if order_values.empty? && (implicit_order_column || primary_key)
580
+ if implicit_order_column && primary_key && implicit_order_column != primary_key
581
+ order(table[implicit_order_column].asc, table[primary_key].asc)
582
+ else
583
+ order(table[implicit_order_column || primary_key].asc)
584
+ end
585
+ else
586
+ self
587
+ end
513
588
  end
514
- end
515
589
  end
516
590
  end