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