activerecord 3.2.6 → 6.0.0

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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,136 +1,230 @@
1
- # -*- coding: utf-8 -*-
2
- require 'active_support/core_ext/object/blank'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module ActiveRecord
5
- # = Active Record Relation
4
+ # = Active Record \Relation
6
5
  class Relation
7
- JoinOperation = Struct.new(:relation, :join_class, :on)
8
- ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
9
- MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
10
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq]
6
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
+ :order, :joins, :left_outer_joins, :references,
8
+ :extending, :unscope, :optimizer_hints, :annotate]
11
9
 
12
- include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
+ :reverse_order, :distinct, :create_with, :skip_query_cache]
13
12
 
14
- attr_reader :table, :klass, :loaded
15
- attr_accessor :extensions, :default_scoped
16
- alias :loaded? :loaded
17
- alias :default_scoped? :default_scoped
13
+ CLAUSE_METHODS = [:where, :having, :from]
14
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
18
15
 
19
- def initialize(klass, table)
20
- @klass, @table = klass, table
16
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
21
17
 
22
- @implicit_readonly = nil
23
- @loaded = false
24
- @default_scoped = false
18
+ include Enumerable
19
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
25
20
 
26
- SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
27
- (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
28
- @extensions = []
29
- @create_with_value = {}
21
+ attr_reader :table, :klass, :loaded, :predicate_builder
22
+ attr_accessor :skip_preloading_value
23
+ alias :model :klass
24
+ alias :loaded? :loaded
25
+ alias :locked? :lock_value
26
+
27
+ def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
28
+ @klass = klass
29
+ @table = table
30
+ @values = values
31
+ @offsets = {}
32
+ @loaded = false
33
+ @predicate_builder = predicate_builder
34
+ @delegate_to_klass = false
30
35
  end
31
36
 
32
- def insert(values)
33
- primary_key_value = nil
37
+ def initialize_copy(other)
38
+ @values = @values.dup
39
+ reset
40
+ end
34
41
 
35
- if primary_key && Hash === values
36
- primary_key_value = values[values.keys.find { |k|
37
- k.name == primary_key
38
- }]
42
+ def arel_attribute(name) # :nodoc:
43
+ klass.arel_attribute(name, table)
44
+ end
39
45
 
40
- if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
41
- primary_key_value = connection.next_sequence_value(klass.sequence_name)
42
- values[klass.arel_table[klass.primary_key]] = primary_key_value
43
- end
46
+ def bind_attribute(name, value) # :nodoc:
47
+ if reflection = klass._reflect_on_association(name)
48
+ name = reflection.foreign_key
49
+ value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
44
50
  end
45
51
 
46
- im = arel.create_insert
47
- im.into @table
48
-
49
- conn = @klass.connection
52
+ attr = arel_attribute(name)
53
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
54
+ yield attr, bind
55
+ end
50
56
 
51
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
52
- binds = substitutes.map do |arel_attr, value|
53
- [@klass.columns_hash[arel_attr.name], value]
54
- end
57
+ # Initializes new record from relation while maintaining the current
58
+ # scope.
59
+ #
60
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
61
+ #
62
+ # users = User.where(name: 'DHH')
63
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
64
+ #
65
+ # You can also pass a block to new with the new record as argument:
66
+ #
67
+ # user = users.new { |user| user.name = 'Oscar' }
68
+ # user.name # => Oscar
69
+ def new(attributes = nil, &block)
70
+ block = _deprecated_scope_block("new", &block)
71
+ scoping { klass.new(attributes, &block) }
72
+ end
55
73
 
56
- substitutes.each_with_index do |tuple, i|
57
- tuple[1] = conn.substitute_at(binds[i][0], i)
58
- end
74
+ alias build new
59
75
 
60
- if values.empty? # empty insert
61
- im.values = Arel.sql(connection.empty_insert_statement_value)
76
+ # Tries to create a new record with the same scoped attributes
77
+ # defined in the relation. Returns the initialized object if validation fails.
78
+ #
79
+ # Expects arguments in the same format as
80
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
81
+ #
82
+ # ==== Examples
83
+ #
84
+ # users = User.where(name: 'Oscar')
85
+ # users.create # => #<User id: 3, name: "Oscar", ...>
86
+ #
87
+ # users.create(name: 'fxn')
88
+ # users.create # => #<User id: 4, name: "fxn", ...>
89
+ #
90
+ # users.create { |user| user.name = 'tenderlove' }
91
+ # # => #<User id: 5, name: "tenderlove", ...>
92
+ #
93
+ # users.create(name: nil) # validation on name
94
+ # # => #<User id: nil, name: nil, ...>
95
+ def create(attributes = nil, &block)
96
+ if attributes.is_a?(Array)
97
+ attributes.collect { |attr| create(attr, &block) }
62
98
  else
63
- im.insert substitutes
99
+ block = _deprecated_scope_block("create", &block)
100
+ scoping { klass.create(attributes, &block) }
64
101
  end
65
-
66
- conn.insert(
67
- im,
68
- 'SQL',
69
- primary_key,
70
- primary_key_value,
71
- nil,
72
- binds)
73
102
  end
74
103
 
75
- def new(*args, &block)
76
- scoping { @klass.new(*args, &block) }
104
+ # Similar to #create, but calls
105
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
106
+ # on the base class. Raises an exception if a validation error occurs.
107
+ #
108
+ # Expects arguments in the same format as
109
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
110
+ def create!(attributes = nil, &block)
111
+ if attributes.is_a?(Array)
112
+ attributes.collect { |attr| create!(attr, &block) }
113
+ else
114
+ block = _deprecated_scope_block("create!", &block)
115
+ scoping { klass.create!(attributes, &block) }
116
+ end
77
117
  end
78
118
 
79
- def initialize_copy(other)
80
- @bind_values = @bind_values.dup
81
- reset
119
+ def first_or_create(attributes = nil, &block) # :nodoc:
120
+ first || create(attributes, &block)
82
121
  end
83
122
 
84
- alias build new
85
-
86
- def create(*args, &block)
87
- scoping { @klass.create(*args, &block) }
123
+ def first_or_create!(attributes = nil, &block) # :nodoc:
124
+ first || create!(attributes, &block)
88
125
  end
89
126
 
90
- def create!(*args, &block)
91
- scoping { @klass.create!(*args, &block) }
127
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
128
+ first || new(attributes, &block)
92
129
  end
93
130
 
94
- # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
95
- #
96
- # Expects arguments in the same format as <tt>Base.create</tt>.
131
+ # Finds the first record with the given attributes, or creates a record
132
+ # with the attributes if one is not found:
97
133
  #
98
- # ==== Examples
99
- # # Find the first user named Penélope or create a new one.
100
- # User.where(:first_name => 'Penélope').first_or_create
101
- # # => <User id: 1, first_name: 'Penélope', last_name: nil>
134
+ # # Find the first user named "Penélope" or create a new one.
135
+ # User.find_or_create_by(first_name: 'Penélope')
136
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
102
137
  #
103
- # # Find the first user named Penélope or create a new one.
138
+ # # Find the first user named "Penélope" or create a new one.
104
139
  # # We already have one so the existing record will be returned.
105
- # User.where(:first_name => 'Penélope').first_or_create
106
- # # => <User id: 1, first_name: 'Penélope', last_name: nil>
107
- #
108
- # # Find the first user named Scarlett or create a new one with a particular last name.
109
- # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
110
- # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
140
+ # User.find_or_create_by(first_name: 'Penélope')
141
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
111
142
  #
112
- # # Find the first user named Scarlett or create a new one with a different last name.
113
- # # We already have one so the existing record will be returned.
114
- # User.where(:first_name => 'Scarlett').first_or_create do |user|
115
- # user.last_name = "O'Hara"
116
- # end
117
- # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
118
- def first_or_create(attributes = nil, options = {}, &block)
119
- first || create(attributes, options, &block)
120
- end
121
-
122
- # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
143
+ # # Find the first user named "Scarlett" or create a new one with
144
+ # # a particular last name.
145
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
146
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
123
147
  #
124
- # Expects arguments in the same format as <tt>Base.create!</tt>.
125
- def first_or_create!(attributes = nil, options = {}, &block)
126
- first || create!(attributes, options, &block)
127
- end
128
-
129
- # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
148
+ # This method accepts a block, which is passed down to #create. The last example
149
+ # above can be alternatively written this way:
130
150
  #
131
- # Expects arguments in the same format as <tt>Base.new</tt>.
132
- def first_or_initialize(attributes = nil, options = {}, &block)
133
- first || new(attributes, options, &block)
151
+ # # Find the first user named "Scarlett" or create a new one with a
152
+ # # different last name.
153
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
154
+ # user.last_name = 'Johansson'
155
+ # end
156
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
157
+ #
158
+ # This method always returns a record, but if creation was attempted and
159
+ # failed due to validation errors it won't be persisted, you get what
160
+ # #create returns in such situation.
161
+ #
162
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
163
+ # there are no results an INSERT is attempted. If there are other threads
164
+ # or processes there is a race condition between both calls and it could
165
+ # be the case that you end up with two similar records.
166
+ #
167
+ # If this might be a problem for your application, please see #create_or_find_by.
168
+ def find_or_create_by(attributes, &block)
169
+ find_by(attributes) || create(attributes, &block)
170
+ end
171
+
172
+ # Like #find_or_create_by, but calls
173
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
174
+ # is raised if the created record is invalid.
175
+ def find_or_create_by!(attributes, &block)
176
+ find_by(attributes) || create!(attributes, &block)
177
+ end
178
+
179
+ # Attempts to create a record with the given attributes in a table that has a unique constraint
180
+ # on one or several of its columns. If a row already exists with one or several of these
181
+ # unique constraints, the exception such an insertion would normally raise is caught,
182
+ # and the existing record with those attributes is found using #find_by!.
183
+ #
184
+ # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
185
+ # and the INSERT, as that method needs to first query the table, then attempt to insert a row
186
+ # if none is found.
187
+ #
188
+ # There are several drawbacks to #create_or_find_by, though:
189
+ #
190
+ # * The underlying table must have the relevant columns defined with unique constraints.
191
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
192
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
193
+ # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
194
+ # rather than a record with the given attributes.
195
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
196
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
197
+ # if a DELETE between those two statements is run by another client. But for most applications,
198
+ # that's a significantly less likely condition to hit.
199
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
200
+ # * The primary key may auto-increment on each create, even if it fails. This can accelerate
201
+ # the problem of running out of integers, if the underlying table is still stuck on a primary
202
+ # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
203
+ # to this problem).
204
+ #
205
+ # This method will return a record if all given attributes are covered by unique constraints
206
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
207
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
208
+ # such situation.
209
+ def create_or_find_by(attributes, &block)
210
+ transaction(requires_new: true) { create(attributes, &block) }
211
+ rescue ActiveRecord::RecordNotUnique
212
+ find_by!(attributes)
213
+ end
214
+
215
+ # Like #create_or_find_by, but calls
216
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
+ # is raised if the created record is invalid.
218
+ def create_or_find_by!(attributes, &block)
219
+ transaction(requires_new: true) { create!(attributes, &block) }
220
+ rescue ActiveRecord::RecordNotUnique
221
+ find_by!(attributes)
222
+ end
223
+
224
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
225
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
226
+ def find_or_initialize_by(attributes, &block)
227
+ find_by(attributes) || new(attributes, &block)
134
228
  end
135
229
 
136
230
  # Runs EXPLAIN on the query or queries triggered by this relation and
@@ -141,397 +235,625 @@ module ActiveRecord
141
235
  # are needed by the next ones when eager loading is going on.
142
236
  #
143
237
  # Please see further details in the
144
- # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
238
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
145
239
  def explain
146
- _, queries = collecting_queries_for_explain { exec_queries }
147
- exec_explain(queries)
148
- end
149
-
150
- def to_a
151
- # We monitor here the entire execution rather than individual SELECTs
152
- # because from the point of view of the user fetching the records of a
153
- # relation is a single unit of work. You want to know if this call takes
154
- # too long, not if the individual queries take too long.
155
- #
156
- # It could be the case that none of the queries involved surpass the
157
- # threshold, and at the same time the sum of them all does. The user
158
- # should get a query plan logged in that case.
159
- logging_query_plan do
160
- exec_queries
161
- end
240
+ exec_explain(collecting_queries_for_explain { exec_queries })
162
241
  end
163
242
 
164
- def exec_queries
165
- return @records if loaded?
166
-
167
- default_scoped = with_default_scope
168
-
169
- if default_scoped.equal?(self)
170
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
171
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
172
- else
173
- IdentityMap.without do
174
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
175
- end
176
- end
177
-
178
- preload = @preload_values
179
- preload += @includes_values unless eager_loading?
180
- preload.each do |associations|
181
- ActiveRecord::Associations::Preloader.new(@records, associations).run
182
- end
183
-
184
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
185
- # are JOINS and no explicit SELECT.
186
- readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
187
- @records.each { |record| record.readonly! } if readonly
188
- else
189
- @records = default_scoped.to_a
190
- end
243
+ # Converts relation objects to Array.
244
+ def to_ary
245
+ records.dup
246
+ end
247
+ alias to_a to_ary
191
248
 
192
- @loaded = true
249
+ def records # :nodoc:
250
+ load
193
251
  @records
194
252
  end
195
- private :exec_queries
196
253
 
197
- def as_json(options = nil) #:nodoc:
198
- to_a.as_json(options)
254
+ # Serializes the relation objects Array.
255
+ def encode_with(coder)
256
+ coder.represent_seq(nil, records)
199
257
  end
200
258
 
201
259
  # Returns size of the records.
202
260
  def size
203
- loaded? ? @records.length : count
261
+ loaded? ? @records.length : count(:all)
204
262
  end
205
263
 
206
264
  # Returns true if there are no records.
207
265
  def empty?
208
266
  return @records.empty? if loaded?
267
+ !exists?
268
+ end
209
269
 
210
- c = count
211
- c.respond_to?(:zero?) ? c.zero? : c.empty?
270
+ # Returns true if there are no records.
271
+ def none?
272
+ return super if block_given?
273
+ empty?
212
274
  end
213
275
 
276
+ # Returns true if there are any records.
214
277
  def any?
215
- if block_given?
216
- to_a.any? { |*block_args| yield(*block_args) }
278
+ return super if block_given?
279
+ !empty?
280
+ end
281
+
282
+ # Returns true if there is exactly one record.
283
+ def one?
284
+ return super if block_given?
285
+ limit_value ? records.one? : size == 1
286
+ end
287
+
288
+ # Returns true if there is more than one record.
289
+ def many?
290
+ return super if block_given?
291
+ limit_value ? records.many? : size > 1
292
+ end
293
+
294
+ # Returns a stable cache key that can be used to identify this query.
295
+ # The cache key is built with a fingerprint of the SQL query.
296
+ #
297
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
298
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
299
+ #
300
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
301
+ # in Rails 6.0 and earlier, the cache key will also include a version.
302
+ #
303
+ # ActiveRecord::Base.collection_cache_versioning = false
304
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
305
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
306
+ #
307
+ # You can also pass a custom timestamp column to fetch the timestamp of the
308
+ # last updated record.
309
+ #
310
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
311
+ def cache_key(timestamp_column = :updated_at)
312
+ @cache_keys ||= {}
313
+ @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
314
+ end
315
+
316
+ def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
317
+ query_signature = ActiveSupport::Digest.hexdigest(to_sql)
318
+ key = "#{klass.model_name.cache_key}/query-#{query_signature}"
319
+
320
+ if cache_version(timestamp_column)
321
+ key
217
322
  else
218
- !empty?
323
+ "#{key}-#{compute_cache_version(timestamp_column)}"
219
324
  end
220
325
  end
326
+ private :compute_cache_key
221
327
 
222
- def many?
223
- if block_given?
224
- to_a.many? { |*block_args| yield(*block_args) }
328
+ # Returns a cache version that can be used together with the cache key to form
329
+ # a recyclable caching scheme. The cache version is built with the number of records
330
+ # matching the query, and the timestamp of the last updated record. When a new record
331
+ # comes to match the query, or any of the existing records is updated or deleted,
332
+ # the cache version changes.
333
+ #
334
+ # If the collection is loaded, the method will iterate through the records
335
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
336
+ #
337
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
338
+ def cache_version(timestamp_column = :updated_at)
339
+ if collection_cache_versioning
340
+ @cache_versions ||= {}
341
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
342
+ end
343
+ end
344
+
345
+ def compute_cache_version(timestamp_column) # :nodoc:
346
+ if loaded? || distinct_value
347
+ size = records.size
348
+ if size > 0
349
+ timestamp = max_by(&timestamp_column)._read_attribute(timestamp_column)
350
+ end
351
+ else
352
+ collection = eager_loading? ? apply_join_dependency : self
353
+
354
+ column = connection.visitor.compile(arel_attribute(timestamp_column))
355
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
356
+
357
+ if collection.has_limit_or_offset?
358
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
359
+ subquery_alias = "subquery_for_cache_key"
360
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
361
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
362
+ else
363
+ query = collection.unscope(:order)
364
+ query.select_values = [select_values % column]
365
+ arel = query.arel
366
+ end
367
+
368
+ result = connection.select_one(arel, nil)
369
+
370
+ if result
371
+ column_type = klass.type_for_attribute(timestamp_column)
372
+ timestamp = column_type.deserialize(result["timestamp"])
373
+ size = result["size"]
374
+ else
375
+ timestamp = nil
376
+ size = 0
377
+ end
378
+ end
379
+
380
+ if timestamp
381
+ "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
225
382
  else
226
- @limit_value ? to_a.many? : size > 1
383
+ "#{size}"
227
384
  end
228
385
  end
386
+ private :compute_cache_version
229
387
 
230
388
  # Scope all queries to the current scope.
231
389
  #
232
- # ==== Example
233
- #
234
- # Comment.where(:post_id => 1).scoping do
235
- # Comment.first # SELECT * FROM comments WHERE post_id = 1
390
+ # Comment.where(post_id: 1).scoping do
391
+ # Comment.first
236
392
  # end
393
+ # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
237
394
  #
238
395
  # Please check unscoped if you want to remove all previous scopes (including
239
396
  # the default_scope) during the execution of a block.
240
397
  def scoping
241
- @klass.with_scope(self, :overwrite) { yield }
398
+ already_in_scope? ? yield : _scoping(self) { yield }
399
+ end
400
+
401
+ def _exec_scope(name, *args, &block) # :nodoc:
402
+ @delegate_to_klass = true
403
+ _scoping(_deprecated_spawn(name)) { instance_exec(*args, &block) || self }
404
+ ensure
405
+ @delegate_to_klass = false
242
406
  end
243
407
 
244
- # Updates all records with details given if they match a set of conditions supplied, limits and order can
245
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
246
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
247
- # or validations.
408
+ # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
409
+ # statement and sends it straight to the database. It does not instantiate the involved models and it does not
410
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
411
+ # Active Record's normal type casting and serialization.
412
+ #
413
+ # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
248
414
  #
249
415
  # ==== Parameters
250
416
  #
251
417
  # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
252
- # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
253
- # See conditions in the intro.
254
- # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
255
418
  #
256
419
  # ==== Examples
257
420
  #
258
421
  # # Update all customers with the given attributes
259
- # Customer.update_all :wants_email => true
422
+ # Customer.update_all wants_email: true
260
423
  #
261
424
  # # Update all books with 'Rails' in their title
262
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
263
- #
264
- # # Update all avatars migrated more than a week ago
265
- # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
425
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
266
426
  #
267
427
  # # Update all books that match conditions, but limit it to 5 ordered by date
268
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
269
- #
270
- # # Conditions from the current relation also works
271
- # Book.where('title LIKE ?', '%Rails%').update_all(:author => 'David')
428
+ # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
272
429
  #
273
- # # The same idea applies to limit and order
274
- # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
275
- def update_all(updates, conditions = nil, options = {})
276
- IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
277
- if conditions || options.present?
278
- where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
279
- else
280
- stmt = Arel::UpdateManager.new(arel.engine)
430
+ # # Update all invoices and set the number column to its id value.
431
+ # Invoice.update_all('number = id')
432
+ def update_all(updates)
433
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
281
434
 
282
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
283
- stmt.table(table)
284
- stmt.key = table[primary_key]
435
+ if eager_loading?
436
+ relation = apply_join_dependency
437
+ return relation.update_all(updates)
438
+ end
285
439
 
286
- if joins_values.any?
287
- @klass.connection.join_to_update(stmt, arel)
288
- else
289
- stmt.take(arel.limit)
290
- stmt.order(*arel.orders)
291
- stmt.wheres = arel.constraints
440
+ stmt = Arel::UpdateManager.new
441
+ stmt.table(arel.join_sources.empty? ? table : arel.source)
442
+ stmt.key = arel_attribute(primary_key)
443
+ stmt.take(arel.limit)
444
+ stmt.offset(arel.offset)
445
+ stmt.order(*arel.orders)
446
+ stmt.wheres = arel.constraints
447
+
448
+ if updates.is_a?(Hash)
449
+ if klass.locking_enabled? &&
450
+ !updates.key?(klass.locking_column) &&
451
+ !updates.key?(klass.locking_column.to_sym)
452
+ attr = arel_attribute(klass.locking_column)
453
+ updates[attr.name] = _increment_attribute(attr)
292
454
  end
455
+ stmt.set _substitute_values(updates)
456
+ else
457
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
458
+ end
459
+
460
+ @klass.connection.update stmt, "#{@klass} Update All"
461
+ end
293
462
 
294
- @klass.connection.update stmt, 'SQL', bind_values
463
+ def update(id = :all, attributes) # :nodoc:
464
+ if id == :all
465
+ each { |record| record.update(attributes) }
466
+ else
467
+ klass.update(id, attributes)
295
468
  end
296
469
  end
297
470
 
298
- # Updates an object (or multiple objects) and saves it to the database, if validations pass.
299
- # The resulting object is returned whether the object was saved successfully to the database or not.
471
+ def update_counters(counters) # :nodoc:
472
+ touch = counters.delete(:touch)
473
+
474
+ updates = {}
475
+ counters.each do |counter_name, value|
476
+ attr = arel_attribute(counter_name)
477
+ updates[attr.name] = _increment_attribute(attr, value)
478
+ end
479
+
480
+ if touch
481
+ names = touch if touch != true
482
+ touch_updates = klass.touch_attributes_with_time(*names)
483
+ updates.merge!(touch_updates) unless touch_updates.empty?
484
+ end
485
+
486
+ update_all updates
487
+ end
488
+
489
+ # Touches all records in the current relation without instantiating records first with the +updated_at+/+updated_on+ attributes
490
+ # set to the current time or the time specified.
491
+ # This method can be passed attribute names and an optional time argument.
492
+ # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
493
+ # If no time argument is passed, the current time is used as default.
300
494
  #
301
- # ==== Parameters
495
+ # === Examples
302
496
  #
303
- # * +id+ - This should be the id or an array of ids to be updated.
304
- # * +attributes+ - This should be a hash of attributes or an array of hashes.
497
+ # # Touch all records
498
+ # Person.all.touch_all
499
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
305
500
  #
306
- # ==== Examples
501
+ # # Touch multiple records with a custom attribute
502
+ # Person.all.touch_all(:created_at)
503
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
307
504
  #
308
- # # Updates one record
309
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
505
+ # # Touch multiple records with a specified time
506
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
507
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
310
508
  #
311
- # # Updates multiple records
312
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
313
- # Person.update(people.keys, people.values)
314
- def update(id, attributes)
315
- if id.is_a?(Array)
316
- id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
317
- else
318
- object = find(id)
319
- object.update_attributes(attributes)
320
- object
321
- end
509
+ # # Touch records with scope
510
+ # Person.where(name: 'David').touch_all
511
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
512
+ def touch_all(*names, time: nil)
513
+ update_all klass.touch_attributes_with_time(*names, time: time)
322
514
  end
323
515
 
324
- # Destroys the records matching +conditions+ by instantiating each
325
- # record and calling its +destroy+ method. Each object's callbacks are
326
- # executed (including <tt>:dependent</tt> association options and
327
- # +before_destroy+/+after_destroy+ Observer methods). Returns the
328
- # collection of objects that were destroyed; each will be frozen, to
329
- # reflect that no changes should be made (since they can't be
330
- # persisted).
516
+ # Destroys the records by instantiating each
517
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
518
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
519
+ # Returns the collection of objects that were destroyed; each will be frozen, to
520
+ # reflect that no changes should be made (since they can't be persisted).
331
521
  #
332
522
  # Note: Instantiation, callback execution, and deletion of each
333
523
  # record can be time consuming when you're removing many records at
334
524
  # once. It generates at least one SQL +DELETE+ query per record (or
335
525
  # possibly more, to enforce your callbacks). If you want to delete many
336
526
  # rows quickly, without concern for their associations or callbacks, use
337
- # +delete_all+ instead.
338
- #
339
- # ==== Parameters
340
- #
341
- # * +conditions+ - A string, array, or hash that specifies which records
342
- # to destroy. If omitted, all records are destroyed. See the
343
- # Conditions section in the introduction to ActiveRecord::Base for
344
- # more information.
527
+ # #delete_all instead.
345
528
  #
346
529
  # ==== Examples
347
530
  #
348
- # Person.destroy_all("last_login < '2004-04-04'")
349
- # Person.destroy_all(:status => "inactive")
350
- # Person.where(:age => 0..18).destroy_all
351
- def destroy_all(conditions = nil)
352
- if conditions
353
- where(conditions).destroy_all
354
- else
355
- to_a.each {|object| object.destroy }.tap { reset }
356
- end
531
+ # Person.where(age: 0..18).destroy_all
532
+ def destroy_all
533
+ records.each(&:destroy).tap { reset }
357
534
  end
358
535
 
359
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
360
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
361
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
362
- #
363
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
364
- # from the attributes, and then calls destroy on it.
536
+ # Deletes the records without instantiating the records
537
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
538
+ # method nor invoking callbacks.
539
+ # This is a single SQL DELETE statement that goes straight to the database, much more
540
+ # efficient than #destroy_all. Be careful with relations though, in particular
541
+ # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
542
+ # number of rows affected.
365
543
  #
366
- # ==== Parameters
544
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
367
545
  #
368
- # * +id+ - Can be either an Integer or an Array of Integers.
369
- #
370
- # ==== Examples
546
+ # Both calls delete the affected posts all at once with a single DELETE statement.
547
+ # If you need to destroy dependent associations or call your <tt>before_*</tt> or
548
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
371
549
  #
372
- # # Destroy a single object
373
- # Todo.destroy(1)
550
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
374
551
  #
375
- # # Destroy multiple objects
376
- # todos = [1,2,3]
377
- # Todo.destroy(todos)
378
- def destroy(id)
379
- if id.is_a?(Array)
380
- id.map { |one_id| destroy(one_id) }
381
- else
382
- find(id).destroy
552
+ # Post.distinct.delete_all
553
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
554
+ def delete_all
555
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
556
+ value = @values[method]
557
+ method == :distinct ? value : value&.any?
558
+ end
559
+ if invalid_methods.any?
560
+ raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
383
561
  end
384
- end
385
562
 
386
- # Deletes the records matching +conditions+ without instantiating the records first, and hence not
387
- # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
388
- # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
389
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
390
- # the number of rows affected.
391
- #
392
- # ==== Parameters
393
- #
394
- # * +conditions+ - Conditions are specified the same way as with +find+ method.
395
- #
396
- # ==== Example
397
- #
398
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
399
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
400
- # Post.where(:person_id => 5).where(:category => ['Something', 'Else']).delete_all
401
- #
402
- # Both calls delete the affected posts all at once with a single DELETE statement.
403
- # If you need to destroy dependent associations or call your <tt>before_*</tt> or
404
- # +after_destroy+ callbacks, use the +destroy_all+ method instead.
405
- def delete_all(conditions = nil)
406
- raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
563
+ if eager_loading?
564
+ relation = apply_join_dependency
565
+ return relation.delete_all
566
+ end
407
567
 
408
- IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
409
- if conditions
410
- where(conditions).delete_all
411
- else
412
- statement = arel.compile_delete
413
- affected = @klass.connection.delete(statement, 'SQL', bind_values)
568
+ stmt = Arel::DeleteManager.new
569
+ stmt.from(arel.join_sources.empty? ? table : arel.source)
570
+ stmt.key = arel_attribute(primary_key)
571
+ stmt.take(arel.limit)
572
+ stmt.offset(arel.offset)
573
+ stmt.order(*arel.orders)
574
+ stmt.wheres = arel.constraints
414
575
 
415
- reset
416
- affected
417
- end
576
+ affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
577
+
578
+ reset
579
+ affected
418
580
  end
419
581
 
420
- # Deletes the row with a primary key matching the +id+ argument, using a
421
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
422
- # Record objects are not instantiated, so the object's callbacks are not
423
- # executed, including any <tt>:dependent</tt> association options or
424
- # Observer methods.
582
+ # Finds and destroys all records matching the specified conditions.
583
+ # This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
584
+ # Returns the collection of objects that were destroyed.
425
585
  #
426
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
586
+ # If no record is found, returns empty array.
427
587
  #
428
- # Note: Although it is often much faster than the alternative,
429
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
430
- # your application that ensures referential integrity or performs other
431
- # essential jobs.
588
+ # Person.destroy_by(id: 13)
589
+ # Person.destroy_by(name: 'Spartacus', rating: 4)
590
+ # Person.destroy_by("published_at < ?", 2.weeks.ago)
591
+ def destroy_by(*args)
592
+ where(*args).destroy_all
593
+ end
594
+
595
+ # Finds and deletes all records matching the specified conditions.
596
+ # This is short-hand for <tt>relation.where(condition).delete_all</tt>.
597
+ # Returns the number of rows affected.
432
598
  #
433
- # ==== Examples
599
+ # If no record is found, returns <tt>0</tt> as zero rows were affected.
434
600
  #
435
- # # Delete a single row
436
- # Todo.delete(1)
601
+ # Person.delete_by(id: 13)
602
+ # Person.delete_by(name: 'Spartacus', rating: 4)
603
+ # Person.delete_by("published_at < ?", 2.weeks.ago)
604
+ def delete_by(*args)
605
+ where(*args).delete_all
606
+ end
607
+
608
+ # Causes the records to be loaded from the database if they have not
609
+ # been loaded already. You can use this if for some reason you need
610
+ # to explicitly load some records before actually using them. The
611
+ # return value is the relation itself, not the records.
437
612
  #
438
- # # Delete multiple rows
439
- # Todo.delete([2,3,4])
440
- def delete(id_or_array)
441
- IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
442
- where(primary_key => id_or_array).delete_all
613
+ # Post.where(published: true).load # => #<ActiveRecord::Relation>
614
+ def load(&block)
615
+ exec_queries(&block) unless loaded?
616
+
617
+ self
443
618
  end
444
619
 
620
+ # Forces reloading of relation.
445
621
  def reload
446
622
  reset
447
- to_a # force reload
448
- self
623
+ load
449
624
  end
450
625
 
451
626
  def reset
452
- @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
453
- @should_eager_load = @join_dependency = nil
454
- @records = []
627
+ @delegate_to_klass = false
628
+ @_deprecated_scope_source = nil
629
+ @to_sql = @arel = @loaded = @should_eager_load = nil
630
+ @records = [].freeze
631
+ @offsets = {}
455
632
  self
456
633
  end
457
634
 
635
+ # Returns sql statement for the relation.
636
+ #
637
+ # User.where(name: 'Oscar').to_sql
638
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
458
639
  def to_sql
459
- @to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
640
+ @to_sql ||= begin
641
+ if eager_loading?
642
+ apply_join_dependency do |relation, join_dependency|
643
+ relation = join_dependency.apply_column_aliases(relation)
644
+ relation.to_sql
645
+ end
646
+ else
647
+ conn = klass.connection
648
+ conn.unprepared_statement { conn.to_sql(arel) }
649
+ end
650
+ end
460
651
  end
461
652
 
462
- def where_values_hash
463
- equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
464
- node.left.relation.name == table_name
465
- }
466
-
467
- Hash[equalities.map { |where| [where.left.name, where.right] }]
653
+ # Returns a hash of where conditions.
654
+ #
655
+ # User.where(name: 'Oscar').where_values_hash
656
+ # # => {name: "Oscar"}
657
+ def where_values_hash(relation_table_name = klass.table_name)
658
+ where_clause.to_h(relation_table_name)
468
659
  end
469
660
 
470
661
  def scope_for_create
471
- @scope_for_create ||= where_values_hash.merge(create_with_value)
662
+ where_values_hash.merge!(create_with_value.stringify_keys)
472
663
  end
473
664
 
665
+ # Returns true if relation needs eager loading.
474
666
  def eager_loading?
475
667
  @should_eager_load ||=
476
- @eager_load_values.any? ||
477
- @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
668
+ eager_load_values.any? ||
669
+ includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
478
670
  end
479
671
 
480
672
  # Joins that are also marked for preloading. In which case we should just eager load them.
481
673
  # Note that this is a naive implementation because we could have strings and symbols which
482
674
  # represent the same association, but that aren't matched by this. Also, we could have
483
- # nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
675
+ # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
484
676
  def joined_includes_values
485
- @includes_values & @joins_values
677
+ includes_values & joins_values
486
678
  end
487
679
 
680
+ # Compares two relations for equality.
488
681
  def ==(other)
489
682
  case other
683
+ when Associations::CollectionProxy, AssociationRelation
684
+ self == other.records
490
685
  when Relation
491
686
  other.to_sql == to_sql
492
687
  when Array
493
- to_a == other
688
+ records == other
494
689
  end
495
690
  end
496
691
 
692
+ def pretty_print(q)
693
+ q.pp(records)
694
+ end
695
+
696
+ # Returns true if relation is blank.
697
+ def blank?
698
+ records.blank?
699
+ end
700
+
701
+ def values
702
+ @values.dup
703
+ end
704
+
497
705
  def inspect
498
- to_a.inspect
706
+ subject = loaded? ? records : self
707
+ entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
708
+
709
+ entries[10] = "..." if entries.size == 11
710
+
711
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
499
712
  end
500
713
 
501
- def with_default_scope #:nodoc:
502
- if default_scoped? && default_scope = klass.send(:build_default_scope)
503
- default_scope = default_scope.merge(self)
504
- default_scope.default_scoped = false
505
- default_scope
506
- else
507
- self
714
+ def empty_scope? # :nodoc:
715
+ @values == klass.unscoped.values
716
+ end
717
+
718
+ def has_limit_or_offset? # :nodoc:
719
+ limit_value || offset_value
720
+ end
721
+
722
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
723
+ joins += [aliases] if aliases
724
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
725
+ end
726
+
727
+ def preload_associations(records) # :nodoc:
728
+ preload = preload_values
729
+ preload += includes_values unless eager_loading?
730
+ preloader = nil
731
+ preload.each do |associations|
732
+ preloader ||= build_preloader
733
+ preloader.preload records, associations
508
734
  end
509
735
  end
510
736
 
737
+ attr_reader :_deprecated_scope_source # :nodoc:
738
+
739
+ protected
740
+ attr_writer :_deprecated_scope_source # :nodoc:
741
+
742
+ def load_records(records)
743
+ @records = records.freeze
744
+ @loaded = true
745
+ end
746
+
747
+ def null_relation? # :nodoc:
748
+ is_a?(NullRelation)
749
+ end
750
+
511
751
  private
752
+ def already_in_scope?
753
+ @delegate_to_klass && begin
754
+ scope = klass.current_scope(true)
755
+ scope && !scope._deprecated_scope_source
756
+ end
757
+ end
512
758
 
513
- def references_eager_loaded_tables?
514
- joined_tables = arel.join_sources.map do |join|
515
- if join.is_a?(Arel::Nodes::StringJoin)
516
- tables_in_string(join.left)
759
+ def _deprecated_spawn(name)
760
+ spawn.tap { |scope| scope._deprecated_scope_source = name }
761
+ end
762
+
763
+ def _deprecated_scope_block(name, &block)
764
+ -> record do
765
+ klass.current_scope = _deprecated_spawn(name)
766
+ yield record if block_given?
767
+ end
768
+ end
769
+
770
+ def _scoping(scope)
771
+ previous, klass.current_scope = klass.current_scope(true), scope
772
+ yield
773
+ ensure
774
+ klass.current_scope = previous
775
+ end
776
+
777
+ def _substitute_values(values)
778
+ values.map do |name, value|
779
+ attr = arel_attribute(name)
780
+ unless Arel.arel_node?(value)
781
+ type = klass.type_for_attribute(attr.name)
782
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
783
+ end
784
+ [attr, value]
785
+ end
786
+ end
787
+
788
+ def _increment_attribute(attribute, value = 1)
789
+ bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
790
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
791
+ expr = value < 0 ? expr - bind : expr + bind
792
+ expr.expr
793
+ end
794
+
795
+ def exec_queries(&block)
796
+ skip_query_cache_if_necessary do
797
+ @records =
798
+ if eager_loading?
799
+ apply_join_dependency do |relation, join_dependency|
800
+ if relation.null_relation?
801
+ []
802
+ else
803
+ relation = join_dependency.apply_column_aliases(relation)
804
+ rows = connection.select_all(relation.arel, "SQL")
805
+ join_dependency.instantiate(rows, &block)
806
+ end.freeze
807
+ end
808
+ else
809
+ klass.find_by_sql(arel, &block).freeze
810
+ end
811
+
812
+ preload_associations(@records) unless skip_preloading_value
813
+
814
+ @records.each(&:readonly!) if readonly_value
815
+
816
+ @loaded = true
817
+ @records
818
+ end
819
+ end
820
+
821
+ def skip_query_cache_if_necessary
822
+ if skip_query_cache_value
823
+ uncached do
824
+ yield
825
+ end
517
826
  else
518
- [join.left.table_name, join.left.table_alias]
827
+ yield
519
828
  end
520
829
  end
521
830
 
522
- joined_tables += [table.name, table.table_alias]
831
+ def build_preloader
832
+ ActiveRecord::Associations::Preloader.new
833
+ end
523
834
 
524
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
525
- joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
835
+ def references_eager_loaded_tables?
836
+ joined_tables = arel.join_sources.map do |join|
837
+ if join.is_a?(Arel::Nodes::StringJoin)
838
+ tables_in_string(join.left)
839
+ else
840
+ [join.left.table_name, join.left.table_alias]
841
+ end
842
+ end
526
843
 
527
- (tables_in_string(to_sql) - joined_tables).any?
528
- end
844
+ joined_tables += [table.name, table.table_alias]
529
845
 
530
- def tables_in_string(string)
531
- return [] if string.blank?
532
- # always convert table names to downcase as in Oracle quoted table names are in uppercase
533
- # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
534
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
535
- end
846
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
847
+ joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
848
+
849
+ (references_values - joined_tables).any?
850
+ end
851
+
852
+ def tables_in_string(string)
853
+ return [] if string.blank?
854
+ # always convert table names to downcase as in Oracle quoted table names are in uppercase
855
+ # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
856
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
857
+ end
536
858
  end
537
859
  end