activerecord 4.2.0 → 6.0.5.1

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

Potentially problematic release.


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

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