activerecord 4.2.0 → 6.1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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