activerecord 4.2.0 → 6.1.7.1

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 (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1221 -796
  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 +267 -249
  8. data/lib/active_record/association_relation.rb +45 -7
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +172 -67
  11. data/lib/active_record/associations/association_scope.rb +105 -129
  12. data/lib/active_record/associations/belongs_to_association.rb +85 -59
  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 -33
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
  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 +168 -279
  22. data/lib/active_record/associations/collection_proxy.rb +263 -155
  23. data/lib/active_record/associations/foreign_association.rb +33 -0
  24. data/lib/active_record/associations/has_many_association.rb +57 -84
  25. data/lib/active_record/associations/has_many_through_association.rb +70 -82
  26. data/lib/active_record/associations/has_one_association.rb +74 -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 -73
  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 +175 -164
  32. data/lib/active_record/associations/preloader/association.rb +107 -112
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +99 -96
  35. data/lib/active_record/associations/singular_association.rb +18 -45
  36. data/lib/active_record/associations/through_association.rb +49 -24
  37. data/lib/active_record/associations.rb +1845 -1597
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -138
  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 +59 -36
  46. data/lib/active_record/attribute_methods/write.rb +25 -56
  47. data/lib/active_record/attribute_methods.rb +153 -162
  48. data/lib/active_record/attributes.rb +234 -70
  49. data/lib/active_record/autosave_association.rb +157 -69
  50. data/lib/active_record/base.rb +49 -50
  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 +46 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
  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 +196 -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 +271 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
  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 +78 -161
  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 -57
  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 +5 -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 +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  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 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  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 +171 -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 +499 -293
  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 +595 -382
  119. data/lib/active_record/connection_adapters/schema_cache.rb +191 -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 +146 -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 -389
  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 +488 -243
  133. data/lib/active_record/counter_cache.rb +71 -50
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +273 -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 +212 -94
  143. data/lib/active_record/errors.rb +225 -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 +11 -6
  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 +273 -496
  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 +64 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +52 -0
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +103 -95
  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 +298 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +685 -309
  170. data/lib/active_record/model_schema.rb +420 -113
  171. data/lib/active_record/nested_attributes.rb +265 -216
  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 +574 -135
  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 +175 -54
  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 +533 -216
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +485 -310
  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 +326 -244
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +318 -256
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +99 -84
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
  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 +139 -96
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -409
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +23 -21
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -342
  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 -26
  209. data/lib/active_record/scoping/default.rb +96 -82
  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 +133 -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 +366 -129
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
  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 +291 -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 +181 -152
  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 -41
  231. data/lib/active_record/type/date_time.rb +4 -38
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  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 +33 -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 +65 -48
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +44 -28
  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 -10
  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.rb +7 -5
  339. metadata +175 -65
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  347. data/lib/active_record/attribute.rb +0 -149
  348. data/lib/active_record/attribute_decorators.rb +0 -66
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -30
  363. data/lib/active_record/type/decimal.rb +0 -40
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -55
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -36
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -101
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  374. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,48 +95,38 @@ 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).
115
108
  # If no order is defined it will order by primary key.
116
109
  #
117
- # Person.first # returns the first object fetched by SELECT * FROM people
110
+ # Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
118
111
  # Person.where(["user_name = ?", user_name]).first
119
112
  # Person.where(["user_name = :u", { u: user_name }]).first
120
113
  # Person.order("created_on DESC").offset(5).first
121
- # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
122
- #
123
- # ==== Rails 3
124
- #
125
- # Person.first # SELECT "people".* FROM "people" LIMIT 1
126
- #
127
- # NOTE: Rails 3 may not order this query by the primary key and the order
128
- # will depend on the database implementation. In order to ensure that behavior,
129
- # use <tt>User.order(:id).first</tt> instead.
130
- #
131
- # ==== Rails 4
132
- #
133
- # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
114
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
134
115
  #
135
116
  def first(limit = nil)
117
+ check_reorder_deprecation unless loaded?
118
+
136
119
  if limit
137
- find_nth_with_limit(offset_index, limit)
120
+ find_nth_with_limit(0, limit)
138
121
  else
139
- find_nth(0, offset_index)
122
+ find_nth 0
140
123
  end
141
124
  end
142
125
 
143
- # Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
144
- # 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.
145
128
  def first!
146
- find_nth! 0
129
+ first || raise_record_not_found_exception!
147
130
  end
148
131
 
149
132
  # Find the last record (or last N records if a parameter is supplied).
@@ -162,21 +145,18 @@ module ActiveRecord
162
145
  #
163
146
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
164
147
  def last(limit = nil)
165
- if limit
166
- if order_values.empty? && primary_key
167
- order(arel_table[primary_key].desc).limit(limit).reverse
168
- else
169
- to_a.last(limit)
170
- end
171
- else
172
- find_last
173
- 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
174
154
  end
175
155
 
176
- # Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
177
- # 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.
178
158
  def last!
179
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
159
+ last || raise_record_not_found_exception!
180
160
  end
181
161
 
182
162
  # Find the second record.
@@ -186,13 +166,13 @@ module ActiveRecord
186
166
  # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
187
167
  # Person.where(["user_name = :u", { u: user_name }]).second
188
168
  def second
189
- find_nth(1, offset_index)
169
+ find_nth 1
190
170
  end
191
171
 
192
- # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
172
+ # Same as #second but raises ActiveRecord::RecordNotFound if no record
193
173
  # is found.
194
174
  def second!
195
- find_nth! 1
175
+ second || raise_record_not_found_exception!
196
176
  end
197
177
 
198
178
  # Find the third record.
@@ -202,13 +182,13 @@ module ActiveRecord
202
182
  # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
203
183
  # Person.where(["user_name = :u", { u: user_name }]).third
204
184
  def third
205
- find_nth(2, offset_index)
185
+ find_nth 2
206
186
  end
207
187
 
208
- # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
188
+ # Same as #third but raises ActiveRecord::RecordNotFound if no record
209
189
  # is found.
210
190
  def third!
211
- find_nth! 2
191
+ third || raise_record_not_found_exception!
212
192
  end
213
193
 
214
194
  # Find the fourth record.
@@ -218,13 +198,13 @@ module ActiveRecord
218
198
  # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
219
199
  # Person.where(["user_name = :u", { u: user_name }]).fourth
220
200
  def fourth
221
- find_nth(3, offset_index)
201
+ find_nth 3
222
202
  end
223
203
 
224
- # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
204
+ # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
225
205
  # is found.
226
206
  def fourth!
227
- find_nth! 3
207
+ fourth || raise_record_not_found_exception!
228
208
  end
229
209
 
230
210
  # Find the fifth record.
@@ -234,13 +214,13 @@ module ActiveRecord
234
214
  # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
235
215
  # Person.where(["user_name = :u", { u: user_name }]).fifth
236
216
  def fifth
237
- find_nth(4, offset_index)
217
+ find_nth 4
238
218
  end
239
219
 
240
- # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
220
+ # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
241
221
  # is found.
242
222
  def fifth!
243
- find_nth! 4
223
+ fifth || raise_record_not_found_exception!
244
224
  end
245
225
 
246
226
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -250,30 +230,62 @@ module ActiveRecord
250
230
  # Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
251
231
  # Person.where(["user_name = :u", { u: user_name }]).forty_two
252
232
  def forty_two
253
- find_nth(41, offset_index)
233
+ find_nth 41
254
234
  end
255
235
 
256
- # 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
257
237
  # is found.
258
238
  def forty_two!
259
- 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!
260
256
  end
261
257
 
262
- # Returns +true+ if a record exists in the table that matches the +id+ or
263
- # conditions given, or +false+ otherwise. The argument can take six forms:
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
266
+ end
267
+
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:
264
276
  #
265
277
  # * Integer - Finds the record with this primary key.
266
278
  # * String - Finds the record with a primary key corresponding to this
267
279
  # string (such as <tt>'5'</tt>).
268
- # * Array - Finds the record that matches these +find+-style conditions
280
+ # * Array - Finds the record that matches these +where+-style conditions
269
281
  # (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
270
- # * Hash - Finds the record that matches these +find+-style conditions
282
+ # * Hash - Finds the record that matches these +where+-style conditions
271
283
  # (such as <tt>{name: 'David'}</tt>).
272
284
  # * +false+ - Returns always +false+.
273
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
285
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
274
286
  #
275
287
  # For more information about specifying conditions as a hash or array,
276
- # see the Conditions section in the introduction to <tt>ActiveRecord::Base</tt>.
288
+ # see the Conditions section in the introduction to ActiveRecord::Base.
277
289
  #
278
290
  # Note: You can't pass in a condition as a string (like <tt>name =
279
291
  # 'Jamie'</tt>), since it would be sanitized and then queried against
@@ -286,243 +298,293 @@ module ActiveRecord
286
298
  # Person.exists?(name: 'David')
287
299
  # Person.exists?(false)
288
300
  # Person.exists?
301
+ # Person.where(name: 'Spartacus', rating: 4).exists?
289
302
  def exists?(conditions = :none)
290
303
  if Base === conditions
291
- conditions = conditions.id
292
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
304
+ raise ArgumentError, <<-MSG.squish
293
305
  You are passing an instance of ActiveRecord::Base to `exists?`.
294
- Please pass the id of the object by calling `.id`
306
+ Please pass the id of the object by calling `.id`.
295
307
  MSG
296
308
  end
297
309
 
298
- return false if !conditions
310
+ return false if !conditions || limit_value == 0
299
311
 
300
- relation = apply_join_dependency(self, construct_join_dependency)
301
- 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
302
316
 
303
- 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?
304
319
 
305
- case conditions
306
- when Array, Hash
307
- 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)
308
331
  else
309
- unless conditions == :none
310
- relation = where(primary_key => conditions)
311
- end
332
+ record.is_a?(klass) && exists?(record.id)
312
333
  end
313
-
314
- connection.select_value(relation, "#{name} Exists", relation.arel.bind_values + relation.bind_values) ? true : false
315
334
  end
316
335
 
336
+ alias :member? :include?
337
+
317
338
  # This method is called whenever no records are found with either a single
318
- # id or multiple ids and raises a +ActiveRecord::RecordNotFound+ exception.
339
+ # id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
319
340
  #
320
341
  # The error message is different depending on whether a single id or
321
342
  # multiple ids are provided. If multiple ids are provided, then the number
322
343
  # of results obtained should be provided in the +result_size+ argument and
323
344
  # the expected number of results should be provided in the +expected_size+
324
345
  # argument.
325
- def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
326
- conditions = arel.where_sql
327
- conditions = " [#{conditions}]" if conditions
328
-
329
- if Array(ids).size == 1
330
- 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)
331
358
  else
332
- error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
333
- 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)
334
363
  end
335
-
336
- raise RecordNotFound, error
337
364
  end
338
365
 
339
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 7.0.
373
+ To continue taking non-deterministic result, use `.take` / `.take!` instead.
374
+ MSG
375
+ end
376
+ end
340
377
 
341
- def offset_index
342
- offset_value || 0
343
- end
378
+ def construct_relation_for_exists(conditions)
379
+ conditions = sanitize_forbidden_attributes(conditions)
344
380
 
345
- def find_with_associations
346
- # NOTE: the JoinDependency constructed here needs to know about
347
- # any joins already present in `self`, so pass them in
348
- #
349
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
350
- # incorrect SQL is generated. In that case, the join dependency for
351
- # SpecialCategorizations is constructed without knowledge of the
352
- # preexisting join in joins_values to categorizations (by way of
353
- # the `has_many :through` for categories).
354
- #
355
- join_dependency = construct_join_dependency(joins_values)
356
-
357
- aliases = join_dependency.aliases
358
- relation = select aliases.columns
359
- relation = apply_join_dependency(relation, join_dependency)
360
-
361
- if block_given?
362
- yield relation
363
- else
364
- if ActiveRecord::NullRelation === relation
365
- []
381
+ if distinct_value && offset_value
382
+ relation = except(:order).limit!(1)
366
383
  else
367
- arel = relation.arel
368
- rows = connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
369
- join_dependency.instantiate(rows, aliases)
384
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
370
385
  end
371
- end
372
- end
373
386
 
374
- def construct_join_dependency(joins = [])
375
- including = eager_load_values + includes_values
376
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
377
- 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
378
393
 
379
- def construct_relation_for_association_calculations
380
- from = arel.froms.first
381
- if Arel::Table === from
382
- apply_join_dependency(self, construct_join_dependency)
383
- else
384
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
385
- # There are no tests that test this branch, but presumably it's
386
- # possible for `from` to be a list?
387
- apply_join_dependency(self, construct_join_dependency(from))
394
+ relation
388
395
  end
389
- end
390
396
 
391
- def apply_join_dependency(relation, join_dependency)
392
- relation = relation.except(:includes, :eager_load, :preload)
393
- 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
394
419
 
395
- if using_limitable_reflections?(join_dependency.reflections)
396
- relation
397
- else
398
- if relation.limit_value
399
- limited_ids = limited_ids_for(relation)
400
- 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
401
424
  end
402
- relation.except(:limit, :offset)
403
425
  end
404
- end
405
426
 
406
- def limited_ids_for(relation)
407
- values = @klass.connection.columns_for_distinct(
408
- "#{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
+ )
409
432
 
410
- relation = relation.except(:select).select(values).distinct!
411
- arel = relation.arel
433
+ relation = relation.except(:select).select(values).distinct!
412
434
 
413
- id_rows = @klass.connection.select_all(arel, 'SQL', arel.bind_values + relation.bind_values)
414
- id_rows.map {|row| row[primary_key]}
415
- end
435
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
436
+ id_rows.map(&:last)
437
+ end
416
438
 
417
- def using_limitable_reflections?(reflections)
418
- reflections.none? { |r| r.collection? }
419
- end
439
+ def using_limitable_reflections?(reflections)
440
+ reflections.none?(&:collection?)
441
+ end
420
442
 
421
- protected
443
+ def find_with_ids(*ids)
444
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
422
445
 
423
- def find_with_ids(*ids)
424
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
446
+ expects_array = ids.first.kind_of?(Array)
447
+ return [] if expects_array && ids.first.empty?
425
448
 
426
- expects_array = ids.first.kind_of?(Array)
427
- return ids.first if expects_array && ids.first.empty?
449
+ ids = ids.flatten.compact.uniq
428
450
 
429
- ids = ids.flatten.compact.uniq
451
+ model_name = @klass.name
430
452
 
431
- case ids.size
432
- when 0
433
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
434
- when 1
435
- result = find_one(ids.first)
436
- expects_array ? [ result ] : result
437
- else
438
- 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
439
463
  end
440
- rescue RangeError
441
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
442
- end
443
464
 
444
- def find_one(id)
445
- if ActiveRecord::Base === id
446
- id = id.id
447
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
448
- You are passing an instance of ActiveRecord::Base to `find`.
449
- Please pass the id of the object by calling `.id`
450
- 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
451
479
  end
452
480
 
453
- relation = where(primary_key => id)
454
- record = relation.take
481
+ def find_some(ids)
482
+ return find_some_ordered(ids) unless order_values.present?
455
483
 
456
- raise_record_not_found_exception!(id, 0, 1) unless record
484
+ result = where(primary_key => ids).to_a
457
485
 
458
- record
459
- end
486
+ expected_size =
487
+ if limit_value && ids.size > limit_value
488
+ limit_value
489
+ else
490
+ ids.size
491
+ end
460
492
 
461
- def find_some(ids)
462
- 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
463
497
 
464
- expected_size =
465
- if limit_value && ids.size > limit_value
466
- limit_value
498
+ if result.size == expected_size
499
+ result
467
500
  else
468
- ids.size
501
+ raise_record_not_found_exception!(ids, result.size, expected_size)
469
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
509
+
510
+ if result.size == ids.size
511
+ pk_type = @klass.type_for_attribute(primary_key)
470
512
 
471
- # 11 ids with limit 3, offset 9 should give 2 results.
472
- if offset_value && (ids.size - offset_value < expected_size)
473
- expected_size = ids.size - offset_value
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
474
518
  end
475
519
 
476
- if result.size == expected_size
477
- result
478
- else
479
- 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
480
526
  end
481
- end
482
527
 
483
- def find_take
484
- if loaded?
485
- @records.first
486
- else
487
- @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
488
534
  end
489
- end
490
535
 
491
- def find_nth(index, offset)
492
- if loaded?
493
- @records[index]
494
- else
495
- offset += index
496
- @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
497
539
  end
498
- end
499
540
 
500
- def find_nth!(index)
501
- find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
502
- end
541
+ def find_nth_with_limit(index, limit)
542
+ if loaded?
543
+ records[index, limit] || []
544
+ else
545
+ relation = ordered_relation
503
546
 
504
- def find_nth_with_limit(offset, limit)
505
- relation = if order_values.empty? && primary_key
506
- order(arel_table[primary_key].asc)
507
- else
508
- self
509
- end
547
+ if limit_value
548
+ limit = [limit_value - index, limit].min
549
+ end
510
550
 
511
- relation = relation.offset(offset) unless offset.zero?
512
- relation.limit(limit).to_a
513
- 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
514
559
 
515
- def find_last
516
- if loaded?
517
- @records.last
518
- else
519
- @last ||=
520
- if limit_value
521
- 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]
522
568
  else
523
- reverse_order.limit(1).to_a.first
569
+ relation.last(index)[-index]
524
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
525
588
  end
526
- end
527
589
  end
528
590
  end