activerecord 5.2.6 → 6.1.3.2

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1038 -571
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +13 -12
  7. data/lib/active_record/aggregations.rb +9 -8
  8. data/lib/active_record/association_relation.rb +30 -10
  9. data/lib/active_record/associations.rb +137 -25
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +95 -42
  12. data/lib/active_record/associations/association_scope.rb +23 -21
  13. data/lib/active_record/associations/belongs_to_association.rb +54 -46
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  15. data/lib/active_record/associations/builder/association.rb +45 -22
  16. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  17. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  19. data/lib/active_record/associations/builder/has_many.rb +8 -2
  20. data/lib/active_record/associations/builder/has_one.rb +33 -2
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  22. data/lib/active_record/associations/collection_association.rb +31 -29
  23. data/lib/active_record/associations/collection_proxy.rb +25 -21
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +26 -13
  26. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  27. data/lib/active_record/associations/has_one_association.rb +43 -31
  28. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  31. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/preloader/association.rb +71 -43
  34. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  35. data/lib/active_record/associations/singular_association.rb +3 -17
  36. data/lib/active_record/associations/through_association.rb +1 -1
  37. data/lib/active_record/attribute_assignment.rb +17 -19
  38. data/lib/active_record/attribute_methods.rb +81 -143
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  42. data/lib/active_record/attribute_methods/query.rb +4 -8
  43. data/lib/active_record/attribute_methods/read.rb +14 -56
  44. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  46. data/lib/active_record/attribute_methods/write.rb +18 -34
  47. data/lib/active_record/attributes.rb +46 -9
  48. data/lib/active_record/autosave_association.rb +57 -42
  49. data/lib/active_record/base.rb +4 -17
  50. data/lib/active_record/callbacks.rb +158 -43
  51. data/lib/active_record/coders/yaml_column.rb +1 -2
  52. data/lib/active_record/connection_adapters.rb +50 -0
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  58. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  60. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
  61. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  63. data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
  64. data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
  65. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  66. data/lib/active_record/connection_adapters/column.rb +30 -12
  67. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  68. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  69. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
  71. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  77. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  79. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  80. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
  83. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  101. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  103. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  104. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  106. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  107. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  108. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  109. data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
  110. data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
  111. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  112. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  113. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  114. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  116. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  117. data/lib/active_record/connection_handling.rb +293 -33
  118. data/lib/active_record/core.rb +323 -97
  119. data/lib/active_record/counter_cache.rb +8 -30
  120. data/lib/active_record/database_configurations.rb +272 -0
  121. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  122. data/lib/active_record/database_configurations/database_config.rb +80 -0
  123. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  124. data/lib/active_record/database_configurations/url_config.rb +53 -0
  125. data/lib/active_record/delegated_type.rb +209 -0
  126. data/lib/active_record/destroy_association_async_job.rb +36 -0
  127. data/lib/active_record/dynamic_matchers.rb +3 -4
  128. data/lib/active_record/enum.rb +111 -37
  129. data/lib/active_record/errors.rb +62 -19
  130. data/lib/active_record/explain.rb +10 -6
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +10 -17
  133. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  134. data/lib/active_record/fixture_set/render_context.rb +17 -0
  135. data/lib/active_record/fixture_set/table_row.rb +152 -0
  136. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  137. data/lib/active_record/fixtures.rb +200 -481
  138. data/lib/active_record/gem_version.rb +4 -4
  139. data/lib/active_record/inheritance.rb +53 -24
  140. data/lib/active_record/insert_all.rb +208 -0
  141. data/lib/active_record/integration.rb +67 -17
  142. data/lib/active_record/internal_metadata.rb +26 -9
  143. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  144. data/lib/active_record/locking/optimistic.rb +37 -23
  145. data/lib/active_record/locking/pessimistic.rb +9 -5
  146. data/lib/active_record/log_subscriber.rb +35 -35
  147. data/lib/active_record/middleware/database_selector.rb +77 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  149. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  150. data/lib/active_record/migration.rb +206 -157
  151. data/lib/active_record/migration/command_recorder.rb +96 -44
  152. data/lib/active_record/migration/compatibility.rb +142 -64
  153. data/lib/active_record/migration/join_table.rb +0 -1
  154. data/lib/active_record/model_schema.rb +148 -22
  155. data/lib/active_record/nested_attributes.rb +4 -7
  156. data/lib/active_record/no_touching.rb +8 -1
  157. data/lib/active_record/null_relation.rb +0 -1
  158. data/lib/active_record/persistence.rb +267 -59
  159. data/lib/active_record/query_cache.rb +21 -4
  160. data/lib/active_record/querying.rb +40 -23
  161. data/lib/active_record/railtie.rb +115 -58
  162. data/lib/active_record/railties/console_sandbox.rb +2 -4
  163. data/lib/active_record/railties/controller_runtime.rb +30 -35
  164. data/lib/active_record/railties/databases.rake +408 -78
  165. data/lib/active_record/readonly_attributes.rb +4 -0
  166. data/lib/active_record/reflection.rb +109 -93
  167. data/lib/active_record/relation.rb +374 -104
  168. data/lib/active_record/relation/batches.rb +44 -35
  169. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  170. data/lib/active_record/relation/calculations.rb +153 -90
  171. data/lib/active_record/relation/delegation.rb +35 -50
  172. data/lib/active_record/relation/finder_methods.rb +64 -39
  173. data/lib/active_record/relation/from_clause.rb +5 -1
  174. data/lib/active_record/relation/merger.rb +32 -40
  175. data/lib/active_record/relation/predicate_builder.rb +62 -45
  176. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  177. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  182. data/lib/active_record/relation/query_attribute.rb +13 -8
  183. data/lib/active_record/relation/query_methods.rb +475 -186
  184. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  185. data/lib/active_record/relation/spawn_methods.rb +9 -9
  186. data/lib/active_record/relation/where_clause.rb +111 -61
  187. data/lib/active_record/result.rb +64 -38
  188. data/lib/active_record/runtime_registry.rb +2 -2
  189. data/lib/active_record/sanitization.rb +22 -41
  190. data/lib/active_record/schema.rb +2 -11
  191. data/lib/active_record/schema_dumper.rb +54 -9
  192. data/lib/active_record/schema_migration.rb +7 -9
  193. data/lib/active_record/scoping.rb +8 -9
  194. data/lib/active_record/scoping/default.rb +4 -6
  195. data/lib/active_record/scoping/named.rb +17 -24
  196. data/lib/active_record/secure_token.rb +16 -8
  197. data/lib/active_record/serialization.rb +5 -3
  198. data/lib/active_record/signed_id.rb +116 -0
  199. data/lib/active_record/statement_cache.rb +49 -6
  200. data/lib/active_record/store.rb +88 -9
  201. data/lib/active_record/suppressor.rb +2 -2
  202. data/lib/active_record/table_metadata.rb +42 -43
  203. data/lib/active_record/tasks/database_tasks.rb +277 -81
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  207. data/lib/active_record/test_databases.rb +24 -0
  208. data/lib/active_record/test_fixtures.rb +246 -0
  209. data/lib/active_record/timestamp.rb +43 -32
  210. data/lib/active_record/touch_later.rb +23 -22
  211. data/lib/active_record/transactions.rb +62 -118
  212. data/lib/active_record/translation.rb +1 -1
  213. data/lib/active_record/type.rb +10 -5
  214. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  215. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  216. data/lib/active_record/type/serialized.rb +6 -3
  217. data/lib/active_record/type/time.rb +10 -0
  218. data/lib/active_record/type/type_map.rb +0 -1
  219. data/lib/active_record/type/unsigned_integer.rb +0 -1
  220. data/lib/active_record/type_caster/connection.rb +15 -15
  221. data/lib/active_record/type_caster/map.rb +8 -8
  222. data/lib/active_record/validations.rb +4 -3
  223. data/lib/active_record/validations/associated.rb +1 -2
  224. data/lib/active_record/validations/numericality.rb +35 -0
  225. data/lib/active_record/validations/uniqueness.rb +38 -30
  226. data/lib/arel.rb +54 -0
  227. data/lib/arel/alias_predication.rb +9 -0
  228. data/lib/arel/attributes/attribute.rb +41 -0
  229. data/lib/arel/collectors/bind.rb +29 -0
  230. data/lib/arel/collectors/composite.rb +39 -0
  231. data/lib/arel/collectors/plain_string.rb +20 -0
  232. data/lib/arel/collectors/sql_string.rb +27 -0
  233. data/lib/arel/collectors/substitute_binds.rb +35 -0
  234. data/lib/arel/crud.rb +42 -0
  235. data/lib/arel/delete_manager.rb +18 -0
  236. data/lib/arel/errors.rb +9 -0
  237. data/lib/arel/expressions.rb +29 -0
  238. data/lib/arel/factory_methods.rb +49 -0
  239. data/lib/arel/insert_manager.rb +49 -0
  240. data/lib/arel/math.rb +45 -0
  241. data/lib/arel/nodes.rb +70 -0
  242. data/lib/arel/nodes/and.rb +32 -0
  243. data/lib/arel/nodes/ascending.rb +23 -0
  244. data/lib/arel/nodes/binary.rb +126 -0
  245. data/lib/arel/nodes/bind_param.rb +44 -0
  246. data/lib/arel/nodes/case.rb +55 -0
  247. data/lib/arel/nodes/casted.rb +62 -0
  248. data/lib/arel/nodes/comment.rb +29 -0
  249. data/lib/arel/nodes/count.rb +12 -0
  250. data/lib/arel/nodes/delete_statement.rb +45 -0
  251. data/lib/arel/nodes/descending.rb +23 -0
  252. data/lib/arel/nodes/equality.rb +15 -0
  253. data/lib/arel/nodes/extract.rb +24 -0
  254. data/lib/arel/nodes/false.rb +16 -0
  255. data/lib/arel/nodes/full_outer_join.rb +8 -0
  256. data/lib/arel/nodes/function.rb +44 -0
  257. data/lib/arel/nodes/grouping.rb +11 -0
  258. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  259. data/lib/arel/nodes/in.rb +15 -0
  260. data/lib/arel/nodes/infix_operation.rb +92 -0
  261. data/lib/arel/nodes/inner_join.rb +8 -0
  262. data/lib/arel/nodes/insert_statement.rb +37 -0
  263. data/lib/arel/nodes/join_source.rb +20 -0
  264. data/lib/arel/nodes/matches.rb +18 -0
  265. data/lib/arel/nodes/named_function.rb +23 -0
  266. data/lib/arel/nodes/node.rb +51 -0
  267. data/lib/arel/nodes/node_expression.rb +13 -0
  268. data/lib/arel/nodes/ordering.rb +27 -0
  269. data/lib/arel/nodes/outer_join.rb +8 -0
  270. data/lib/arel/nodes/over.rb +15 -0
  271. data/lib/arel/nodes/regexp.rb +16 -0
  272. data/lib/arel/nodes/right_outer_join.rb +8 -0
  273. data/lib/arel/nodes/select_core.rb +67 -0
  274. data/lib/arel/nodes/select_statement.rb +41 -0
  275. data/lib/arel/nodes/sql_literal.rb +19 -0
  276. data/lib/arel/nodes/string_join.rb +11 -0
  277. data/lib/arel/nodes/table_alias.rb +31 -0
  278. data/lib/arel/nodes/terminal.rb +16 -0
  279. data/lib/arel/nodes/true.rb +16 -0
  280. data/lib/arel/nodes/unary.rb +44 -0
  281. data/lib/arel/nodes/unary_operation.rb +20 -0
  282. data/lib/arel/nodes/unqualified_column.rb +22 -0
  283. data/lib/arel/nodes/update_statement.rb +41 -0
  284. data/lib/arel/nodes/values_list.rb +9 -0
  285. data/lib/arel/nodes/window.rb +126 -0
  286. data/lib/arel/nodes/with.rb +11 -0
  287. data/lib/arel/order_predications.rb +13 -0
  288. data/lib/arel/predications.rb +250 -0
  289. data/lib/arel/select_manager.rb +270 -0
  290. data/lib/arel/table.rb +118 -0
  291. data/lib/arel/tree_manager.rb +72 -0
  292. data/lib/arel/update_manager.rb +34 -0
  293. data/lib/arel/visitors.rb +13 -0
  294. data/lib/arel/visitors/dot.rb +308 -0
  295. data/lib/arel/visitors/mysql.rb +93 -0
  296. data/lib/arel/visitors/postgresql.rb +120 -0
  297. data/lib/arel/visitors/sqlite.rb +38 -0
  298. data/lib/arel/visitors/to_sql.rb +899 -0
  299. data/lib/arel/visitors/visitor.rb +45 -0
  300. data/lib/arel/window_predications.rb +9 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration.rb +19 -2
  303. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  304. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  305. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +119 -34
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -19,6 +19,10 @@ module ActiveRecord
19
19
  def readonly_attributes
20
20
  _attr_readonly
21
21
  end
22
+
23
+ def readonly_attribute?(name) # :nodoc:
24
+ _attr_readonly.include?(name)
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/filters"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Reflection
@@ -13,33 +12,37 @@ module ActiveRecord
13
12
  class_attribute :aggregate_reflections, instance_writer: false, default: {}
14
13
  end
15
14
 
16
- def self.create(macro, name, scope, options, ar)
17
- klass = \
18
- case macro
19
- when :composed_of
20
- AggregateReflection
21
- when :has_many
22
- HasManyReflection
23
- when :has_one
24
- HasOneReflection
25
- when :belongs_to
26
- BelongsToReflection
27
- else
28
- raise "Unsupported Macro: #{macro}"
29
- end
15
+ class << self
16
+ def create(macro, name, scope, options, ar)
17
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
18
+ options[:through] ? ThroughReflection.new(reflection) : reflection
19
+ end
30
20
 
31
- reflection = klass.new(name, scope, options, ar)
32
- options[:through] ? ThroughReflection.new(reflection) : reflection
33
- end
21
+ def add_reflection(ar, name, reflection)
22
+ ar.clear_reflections_cache
23
+ name = -name.to_s
24
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
25
+ end
34
26
 
35
- def self.add_reflection(ar, name, reflection)
36
- ar.clear_reflections_cache
37
- name = name.to_s
38
- ar._reflections = ar._reflections.except(name).merge!(name => reflection)
39
- end
27
+ def add_aggregate_reflection(ar, name, reflection)
28
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
29
+ end
40
30
 
41
- def self.add_aggregate_reflection(ar, name, reflection)
42
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
31
+ private
32
+ def reflection_class_for(macro)
33
+ case macro
34
+ when :composed_of
35
+ AggregateReflection
36
+ when :has_many
37
+ HasManyReflection
38
+ when :has_one
39
+ HasOneReflection
40
+ when :belongs_to
41
+ BelongsToReflection
42
+ else
43
+ raise "Unsupported Macro: #{macro}"
44
+ end
45
+ end
43
46
  end
44
47
 
45
48
  # \Reflection enables the ability to examine the associations and aggregations of
@@ -159,13 +162,7 @@ module ActiveRecord
159
162
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
160
163
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
161
164
  def class_name
162
- @class_name ||= (options[:class_name] || derive_class_name).to_s
163
- end
164
-
165
- JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
166
-
167
- def join_keys
168
- @join_keys ||= get_join_keys(klass)
165
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
169
166
  end
170
167
 
171
168
  # Returns a list of scopes that should be applied for this Reflection
@@ -179,25 +176,27 @@ module ActiveRecord
179
176
  scope_chain_items = join_scopes(table, predicate_builder)
180
177
  klass_scope = klass_join_scope(table, predicate_builder)
181
178
 
182
- key = join_keys.key
183
- foreign_key = join_keys.foreign_key
184
-
185
- klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
186
-
187
179
  if type
188
180
  klass_scope.where!(type => foreign_klass.polymorphic_name)
189
181
  end
190
182
 
183
+ scope_chain_items.inject(klass_scope, &:merge!)
184
+
185
+ primary_key = join_primary_key
186
+ foreign_key = join_foreign_key
187
+
188
+ klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
189
+
191
190
  if klass.finder_needs_type_condition?
192
191
  klass_scope.where!(klass.send(:type_condition, table))
193
192
  end
194
193
 
195
- scope_chain_items.inject(klass_scope, &:merge!)
194
+ klass_scope
196
195
  end
197
196
 
198
- def join_scopes(table, predicate_builder) # :nodoc:
197
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
199
198
  if scope
200
- [scope_for(build_scope(table, predicate_builder))]
199
+ [scope_for(build_scope(table, predicate_builder, klass))]
201
200
  else
202
201
  []
203
202
  end
@@ -213,14 +212,14 @@ module ActiveRecord
213
212
  end
214
213
 
215
214
  def counter_cache_column
216
- if belongs_to?
215
+ @counter_cache_column ||= if belongs_to?
217
216
  if options[:counter_cache] == true
218
- "#{active_record.name.demodulize.underscore.pluralize}_count"
217
+ -"#{active_record.name.demodulize.underscore.pluralize}_count"
219
218
  elsif options[:counter_cache]
220
- options[:counter_cache].to_s
219
+ -options[:counter_cache].to_s
221
220
  end
222
221
  else
223
- options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
222
+ -(options[:counter_cache]&.to_s || "#{name}_count")
224
223
  end
225
224
  end
226
225
 
@@ -267,7 +266,7 @@ module ActiveRecord
267
266
  def has_cached_counter?
268
267
  options[:counter_cache] ||
269
268
  inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
270
- !!active_record.columns_hash[counter_cache_column]
269
+ active_record.has_attribute?(counter_cache_column)
271
270
  end
272
271
 
273
272
  def counter_must_be_updated_by_has_many?
@@ -282,11 +281,7 @@ module ActiveRecord
282
281
  collect_join_chain
283
282
  end
284
283
 
285
- def get_join_keys(association_klass)
286
- JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
287
- end
288
-
289
- def build_scope(table, predicate_builder = predicate_builder(table))
284
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
290
285
  Relation.create(
291
286
  klass,
292
287
  table: table,
@@ -294,12 +289,8 @@ module ActiveRecord
294
289
  )
295
290
  end
296
291
 
297
- def join_primary_key(*)
298
- foreign_key
299
- end
300
-
301
- def join_foreign_key
302
- active_record_primary_key
292
+ def strict_loading?
293
+ options[:strict_loading]
303
294
  end
304
295
 
305
296
  protected
@@ -413,7 +404,7 @@ module ActiveRecord
413
404
  class AssociationReflection < MacroReflection #:nodoc:
414
405
  def compute_class(name)
415
406
  if polymorphic?
416
- raise ArgumentError, "Polymorphic association does not support to compute class."
407
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
417
408
  end
418
409
  active_record.send(:compute_type, name)
419
410
  end
@@ -423,22 +414,21 @@ module ActiveRecord
423
414
 
424
415
  def initialize(name, scope, options, active_record)
425
416
  super
426
- @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
427
- @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
417
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
418
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
428
419
  @constructable = calculate_constructable(macro, options)
429
- @association_scope_cache = Concurrent::Map.new
430
420
 
431
421
  if options[:class_name] && options[:class_name].class == Class
432
422
  raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
433
423
  end
434
424
  end
435
425
 
436
- def association_scope_cache(conn, owner, &block)
437
- key = conn.prepared_statements
426
+ def association_scope_cache(klass, owner, &block)
427
+ key = self
438
428
  if polymorphic?
439
429
  key = [key, owner._read_attribute(@foreign_type)]
440
430
  end
441
- @association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
431
+ klass.cached_find_by_statement(key, &block)
442
432
  end
443
433
 
444
434
  def constructable? # :nodoc:
@@ -446,24 +436,31 @@ module ActiveRecord
446
436
  end
447
437
 
448
438
  def join_table
449
- @join_table ||= options[:join_table] || derive_join_table
439
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
450
440
  end
451
441
 
452
442
  def foreign_key
453
- @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
443
+ @foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
454
444
  end
455
445
 
456
446
  def association_foreign_key
457
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
447
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
458
448
  end
459
449
 
460
- # klass option is necessary to support loading polymorphic associations
461
450
  def association_primary_key(klass = nil)
462
- options[:primary_key] || primary_key(klass || self.klass)
451
+ primary_key(klass || self.klass)
463
452
  end
464
453
 
465
454
  def active_record_primary_key
466
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
455
+ @active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
456
+ end
457
+
458
+ def join_primary_key(klass = nil)
459
+ foreign_key
460
+ end
461
+
462
+ def join_foreign_key
463
+ active_record_primary_key
467
464
  end
468
465
 
469
466
  def check_validity!
@@ -473,7 +470,7 @@ module ActiveRecord
473
470
  def check_preloadable!
474
471
  return unless scope
475
472
 
476
- if scope.arity > 0
473
+ unless scope.arity == 0
477
474
  raise ArgumentError, <<-MSG.squish
478
475
  The association scope '#{name}' is instance dependent (the scope
479
476
  block takes an argument). Preloading instance dependent scopes is
@@ -504,7 +501,7 @@ module ActiveRecord
504
501
  # This is for clearing cache on the reflection. Useful for tests that need to compare
505
502
  # SQL queries on associations.
506
503
  def clear_association_scope_cache # :nodoc:
507
- @association_scope_cache.clear
504
+ klass.initialize_find_by_cache
508
505
  end
509
506
 
510
507
  def nested?
@@ -586,7 +583,6 @@ module ActiveRecord
586
583
  end
587
584
 
588
585
  private
589
-
590
586
  def calculate_constructable(macro, options)
591
587
  true
592
588
  end
@@ -616,7 +612,7 @@ module ActiveRecord
616
612
  end
617
613
 
618
614
  if valid_inverse_reflection?(reflection)
619
- return inverse_name
615
+ inverse_name
620
616
  end
621
617
  end
622
618
  end
@@ -627,6 +623,7 @@ module ActiveRecord
627
623
  # with the current reflection's klass name.
628
624
  def valid_inverse_reflection?(reflection)
629
625
  reflection &&
626
+ foreign_key == reflection.foreign_key &&
630
627
  klass <= reflection.active_record &&
631
628
  can_find_inverse_of_automatically?(reflection)
632
629
  end
@@ -680,10 +677,6 @@ module ActiveRecord
680
677
  Associations::HasManyAssociation
681
678
  end
682
679
  end
683
-
684
- def association_primary_key(klass = nil)
685
- primary_key(klass || self.klass)
686
- end
687
680
  end
688
681
 
689
682
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -700,7 +693,6 @@ module ActiveRecord
700
693
  end
701
694
 
702
695
  private
703
-
704
696
  def calculate_constructable(macro, options)
705
697
  !options[:through]
706
698
  end
@@ -719,6 +711,15 @@ module ActiveRecord
719
711
  end
720
712
  end
721
713
 
714
+ # klass option is necessary to support loading polymorphic associations
715
+ def association_primary_key(klass = nil)
716
+ if primary_key = options[:primary_key]
717
+ @association_primary_key ||= -primary_key.to_s
718
+ else
719
+ primary_key(klass || self.klass)
720
+ end
721
+ end
722
+
722
723
  def join_primary_key(klass = nil)
723
724
  polymorphic? ? association_primary_key(klass) : association_primary_key
724
725
  end
@@ -727,6 +728,10 @@ module ActiveRecord
727
728
  foreign_key
728
729
  end
729
730
 
731
+ def join_foreign_type
732
+ foreign_type
733
+ end
734
+
730
735
  private
731
736
  def can_find_inverse_of_automatically?(_)
732
737
  !polymorphic? && super
@@ -748,8 +753,8 @@ module ActiveRecord
748
753
  # Holds all the metadata about a :through association as it was specified
749
754
  # in the Active Record class.
750
755
  class ThroughReflection < AbstractReflection #:nodoc:
751
- delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
752
- :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
756
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
757
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
753
758
 
754
759
  def initialize(delegate_reflection)
755
760
  @delegate_reflection = delegate_reflection
@@ -835,8 +840,8 @@ module ActiveRecord
835
840
  source_reflection.scopes + super
836
841
  end
837
842
 
838
- def join_scopes(table, predicate_builder) # :nodoc:
839
- source_reflection.join_scopes(table, predicate_builder) + super
843
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
844
+ source_reflection.join_scopes(table, predicate_builder, klass) + super
840
845
  end
841
846
 
842
847
  def has_scope?
@@ -856,7 +861,15 @@ module ActiveRecord
856
861
  def association_primary_key(klass = nil)
857
862
  # Get the "actual" source reflection if the immediate source reflection has a
858
863
  # source reflection itself
859
- actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
864
+ if primary_key = actual_source_reflection.options[:primary_key]
865
+ @association_primary_key ||= -primary_key.to_s
866
+ else
867
+ primary_key(klass || self.klass)
868
+ end
869
+ end
870
+
871
+ def join_primary_key(klass = self.klass)
872
+ source_reflection.join_primary_key(klass)
860
873
  end
861
874
 
862
875
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
@@ -905,7 +918,7 @@ module ActiveRecord
905
918
 
906
919
  def check_validity!
907
920
  if through_reflection.nil?
908
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
921
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
909
922
  end
910
923
 
911
924
  if through_reflection.polymorphic?
@@ -961,16 +974,14 @@ module ActiveRecord
961
974
  collect_join_reflections(seed + [self])
962
975
  end
963
976
 
964
- # TODO Change this to private once we've dropped Ruby 2.2 support.
965
- # Workaround for Ruby 2.2 "private attribute?" warning.
966
977
  protected
967
- attr_reader :delegate_reflection
968
-
969
978
  def actual_source_reflection # FIXME: this is a horrible name
970
979
  source_reflection.actual_source_reflection
971
980
  end
972
981
 
973
982
  private
983
+ attr_reader :delegate_reflection
984
+
974
985
  def collect_join_reflections(seed)
975
986
  a = source_reflection.add_as_source seed
976
987
  if options[:source_type]
@@ -994,16 +1005,17 @@ module ActiveRecord
994
1005
  end
995
1006
 
996
1007
  class PolymorphicReflection < AbstractReflection # :nodoc:
997
- delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1008
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1009
+ :name, :scope_for, to: :@reflection
998
1010
 
999
1011
  def initialize(reflection, previous_reflection)
1000
1012
  @reflection = reflection
1001
1013
  @previous_reflection = previous_reflection
1002
1014
  end
1003
1015
 
1004
- def join_scopes(table, predicate_builder) # :nodoc:
1016
+ def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
1005
1017
  scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1006
- scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
1018
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
1007
1019
  end
1008
1020
 
1009
1021
  def constraints
@@ -1019,7 +1031,7 @@ module ActiveRecord
1019
1031
  end
1020
1032
 
1021
1033
  class RuntimeReflection < AbstractReflection # :nodoc:
1022
- delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1034
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1023
1035
 
1024
1036
  def initialize(reflection, association)
1025
1037
  @reflection = reflection
@@ -1031,7 +1043,11 @@ module ActiveRecord
1031
1043
  end
1032
1044
 
1033
1045
  def aliased_table
1034
- @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1046
+ klass.arel_table
1047
+ end
1048
+
1049
+ def join_primary_key(klass = self.klass)
1050
+ @reflection.join_primary_key(klass)
1035
1051
  end
1036
1052
 
1037
1053
  def all_includes; yield; end
@@ -5,10 +5,11 @@ module ActiveRecord
5
5
  class Relation
6
6
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
7
  :order, :joins, :left_outer_joins, :references,
8
- :extending, :unscope]
8
+ :extending, :unscope, :optimizer_hints, :annotate]
9
9
 
10
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
11
11
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
+
12
13
  CLAUSE_METHODS = [:where, :having, :from]
13
14
  INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
15
 
@@ -18,6 +19,7 @@ module ActiveRecord
18
19
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
19
20
 
20
21
  attr_reader :table, :klass, :loaded, :predicate_builder
22
+ attr_accessor :skip_preloading_value
21
23
  alias :model :klass
22
24
  alias :loaded? :loaded
23
25
  alias :locked? :lock_value
@@ -26,7 +28,6 @@ module ActiveRecord
26
28
  @klass = klass
27
29
  @table = table
28
30
  @values = values
29
- @offsets = {}
30
31
  @loaded = false
31
32
  @predicate_builder = predicate_builder
32
33
  @delegate_to_klass = false
@@ -38,7 +39,19 @@ module ActiveRecord
38
39
  end
39
40
 
40
41
  def arel_attribute(name) # :nodoc:
41
- klass.arel_attribute(name, table)
42
+ table[name]
43
+ end
44
+ deprecate :arel_attribute
45
+
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?
50
+ end
51
+
52
+ attr = table[name]
53
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
54
+ yield attr, bind
42
55
  end
43
56
 
44
57
  # Initializes new record from relation while maintaining the current
@@ -54,9 +67,9 @@ module ActiveRecord
54
67
  # user = users.new { |user| user.name = 'Oscar' }
55
68
  # user.name # => Oscar
56
69
  def new(attributes = nil, &block)
57
- scoping { klass.new(values_for_create(attributes), &block) }
70
+ block = current_scope_restoring_block(&block)
71
+ scoping { _new(attributes, &block) }
58
72
  end
59
-
60
73
  alias build new
61
74
 
62
75
  # Tries to create a new record with the same scoped attributes
@@ -82,7 +95,8 @@ module ActiveRecord
82
95
  if attributes.is_a?(Array)
83
96
  attributes.collect { |attr| create(attr, &block) }
84
97
  else
85
- scoping { klass.create(values_for_create(attributes), &block) }
98
+ block = current_scope_restoring_block(&block)
99
+ scoping { _create(attributes, &block) }
86
100
  end
87
101
  end
88
102
 
@@ -96,7 +110,8 @@ module ActiveRecord
96
110
  if attributes.is_a?(Array)
97
111
  attributes.collect { |attr| create!(attr, &block) }
98
112
  else
99
- scoping { klass.create!(values_for_create(attributes), &block) }
113
+ block = current_scope_restoring_block(&block)
114
+ scoping { _create!(attributes, &block) }
100
115
  end
101
116
  end
102
117
 
@@ -143,23 +158,12 @@ module ActiveRecord
143
158
  # failed due to validation errors it won't be persisted, you get what
144
159
  # #create returns in such situation.
145
160
  #
146
- # Please note *this method is not atomic*, it runs first a SELECT, and if
161
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
147
162
  # there are no results an INSERT is attempted. If there are other threads
148
163
  # or processes there is a race condition between both calls and it could
149
164
  # be the case that you end up with two similar records.
150
165
  #
151
- # Whether that is a problem or not depends on the logic of the
152
- # application, but in the particular case in which rows have a UNIQUE
153
- # constraint an exception may be raised, just retry:
154
- #
155
- # begin
156
- # CreditAccount.transaction(requires_new: true) do
157
- # CreditAccount.find_or_create_by(user_id: user.id)
158
- # end
159
- # rescue ActiveRecord::RecordNotUnique
160
- # retry
161
- # end
162
- #
166
+ # If this might be a problem for your application, please see #create_or_find_by.
163
167
  def find_or_create_by(attributes, &block)
164
168
  find_by(attributes) || create(attributes, &block)
165
169
  end
@@ -171,6 +175,51 @@ module ActiveRecord
171
175
  find_by(attributes) || create!(attributes, &block)
172
176
  end
173
177
 
178
+ # Attempts to create a record with the given attributes in a table that has a unique constraint
179
+ # on one or several of its columns. If a row already exists with one or several of these
180
+ # unique constraints, the exception such an insertion would normally raise is caught,
181
+ # and the existing record with those attributes is found using #find_by!.
182
+ #
183
+ # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
184
+ # and the INSERT, as that method needs to first query the table, then attempt to insert a row
185
+ # if none is found.
186
+ #
187
+ # There are several drawbacks to #create_or_find_by, though:
188
+ #
189
+ # * The underlying table must have the relevant columns defined with unique constraints.
190
+ # * A unique constraint violation may be triggered by only one, or at least less than all,
191
+ # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
+ # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
193
+ # rather than a record with the given attributes.
194
+ # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
195
+ # we actually have another race condition between INSERT -> SELECT, which can be triggered
196
+ # if a DELETE between those two statements is run by another client. But for most applications,
197
+ # that's a significantly less likely condition to hit.
198
+ # * It relies on exception handling to handle control flow, which may be marginally slower.
199
+ # * The primary key may auto-increment on each create, even if it fails. This can accelerate
200
+ # the problem of running out of integers, if the underlying table is still stuck on a primary
201
+ # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
202
+ # to this problem).
203
+ #
204
+ # This method will return a record if all given attributes are covered by unique constraints
205
+ # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
206
+ # and failed due to validation errors it won't be persisted, you get what #create returns in
207
+ # such situation.
208
+ def create_or_find_by(attributes, &block)
209
+ transaction(requires_new: true) { create(attributes, &block) }
210
+ rescue ActiveRecord::RecordNotUnique
211
+ find_by!(attributes)
212
+ end
213
+
214
+ # Like #create_or_find_by, but calls
215
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
216
+ # is raised if the created record is invalid.
217
+ def create_or_find_by!(attributes, &block)
218
+ transaction(requires_new: true) { create!(attributes, &block) }
219
+ rescue ActiveRecord::RecordNotUnique
220
+ find_by!(attributes)
221
+ end
222
+
174
223
  # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
175
224
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
176
225
  def find_or_initialize_by(attributes, &block)
@@ -185,7 +234,7 @@ module ActiveRecord
185
234
  # are needed by the next ones when eager loading is going on.
186
235
  #
187
236
  # Please see further details in the
188
- # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
237
+ # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
189
238
  def explain
190
239
  exec_explain(collecting_queries_for_explain { exec_queries })
191
240
  end
@@ -241,30 +290,107 @@ module ActiveRecord
241
290
  limit_value ? records.many? : size > 1
242
291
  end
243
292
 
244
- # Returns a cache key that can be used to identify the records fetched by
245
- # this query. The cache key is built with a fingerprint of the sql query,
246
- # the number of records matched by the query and a timestamp of the last
247
- # updated record. When a new record comes to match the query, or any of
248
- # the existing records is updated or deleted, the cache key changes.
293
+ # Returns a stable cache key that can be used to identify this query.
294
+ # The cache key is built with a fingerprint of the SQL query.
249
295
  #
250
- # Product.where("name like ?", "%Cosmic Encounter%").cache_key
251
- # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
296
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
297
+ # # => "products/query-1850ab3d302391b85b8693e941286659"
252
298
  #
253
- # If the collection is loaded, the method will iterate through the records
254
- # to generate the timestamp, otherwise it will trigger one SQL query like:
299
+ # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
300
+ # in Rails 6.0 and earlier, the cache key will also include a version.
255
301
  #
256
- # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
302
+ # ActiveRecord::Base.collection_cache_versioning = false
303
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
304
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
257
305
  #
258
306
  # You can also pass a custom timestamp column to fetch the timestamp of the
259
307
  # last updated record.
260
308
  #
261
309
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
262
- #
263
- # You can customize the strategy to generate the key on a per model basis
264
- # overriding ActiveRecord::Base#collection_cache_key.
265
- def cache_key(timestamp_column = :updated_at)
310
+ def cache_key(timestamp_column = "updated_at")
266
311
  @cache_keys ||= {}
267
- @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
312
+ @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
313
+ end
314
+
315
+ def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
316
+ query_signature = ActiveSupport::Digest.hexdigest(to_sql)
317
+ key = "#{klass.model_name.cache_key}/query-#{query_signature}"
318
+
319
+ if collection_cache_versioning
320
+ key
321
+ else
322
+ "#{key}-#{compute_cache_version(timestamp_column)}"
323
+ end
324
+ end
325
+ private :compute_cache_key
326
+
327
+ # Returns a cache version that can be used together with the cache key to form
328
+ # a recyclable caching scheme. The cache version is built with the number of records
329
+ # matching the query, and the timestamp of the last updated record. When a new record
330
+ # comes to match the query, or any of the existing records is updated or deleted,
331
+ # the cache version changes.
332
+ #
333
+ # If the collection is loaded, the method will iterate through the records
334
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
335
+ #
336
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
337
+ def cache_version(timestamp_column = :updated_at)
338
+ if collection_cache_versioning
339
+ @cache_versions ||= {}
340
+ @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
341
+ end
342
+ end
343
+
344
+ def compute_cache_version(timestamp_column) # :nodoc:
345
+ timestamp_column = timestamp_column.to_s
346
+
347
+ if loaded? || distinct_value
348
+ size = records.size
349
+ if size > 0
350
+ timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
351
+ end
352
+ else
353
+ collection = eager_loading? ? apply_join_dependency : self
354
+
355
+ column = connection.visitor.compile(table[timestamp_column])
356
+ select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
357
+
358
+ if collection.has_limit_or_offset?
359
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
360
+ subquery_alias = "subquery_for_cache_key"
361
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
363
+ else
364
+ query = collection.unscope(:order)
365
+ query.select_values = [select_values % column]
366
+ arel = query.arel
367
+ end
368
+
369
+ size, timestamp = connection.select_rows(arel, nil).first
370
+
371
+ if size
372
+ column_type = klass.type_for_attribute(timestamp_column)
373
+ timestamp = column_type.deserialize(timestamp)
374
+ else
375
+ size = 0
376
+ end
377
+ end
378
+
379
+ if timestamp
380
+ "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
381
+ else
382
+ "#{size}"
383
+ end
384
+ end
385
+ private :compute_cache_version
386
+
387
+ # Returns a cache key along with the version.
388
+ def cache_key_with_version
389
+ if version = cache_version
390
+ "#{cache_key}-#{version}"
391
+ else
392
+ cache_key
393
+ end
268
394
  end
269
395
 
270
396
  # Scope all queries to the current scope.
@@ -277,15 +403,12 @@ module ActiveRecord
277
403
  # Please check unscoped if you want to remove all previous scopes (including
278
404
  # the default_scope) during the execution of a block.
279
405
  def scoping
280
- previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
281
- yield
282
- ensure
283
- klass.current_scope = previous unless @delegate_to_klass
406
+ already_in_scope? ? yield : _scoping(self) { yield }
284
407
  end
285
408
 
286
409
  def _exec_scope(*args, &block) # :nodoc:
287
410
  @delegate_to_klass = true
288
- instance_exec(*args, &block) || self
411
+ _scoping(nil) { instance_exec(*args, &block) || self }
289
412
  ensure
290
413
  @delegate_to_klass = false
291
414
  end
@@ -293,7 +416,9 @@ module ActiveRecord
293
416
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
294
417
  # statement and sends it straight to the database. It does not instantiate the involved models and it does not
295
418
  # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
296
- # Active Record's normal type casting and serialization.
419
+ # Active Record's normal type casting and serialization. Returns the number of rows affected.
420
+ #
421
+ # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
297
422
  #
298
423
  # ==== Parameters
299
424
  #
@@ -321,17 +446,23 @@ module ActiveRecord
321
446
  end
322
447
 
323
448
  stmt = Arel::UpdateManager.new
324
-
325
- stmt.set Arel.sql(@klass.sanitize_sql_for_assignment(updates))
326
- stmt.table(table)
327
-
328
- if has_join_values? || offset_value
329
- @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
449
+ stmt.table(arel.join_sources.empty? ? table : arel.source)
450
+ stmt.key = table[primary_key]
451
+ stmt.take(arel.limit)
452
+ stmt.offset(arel.offset)
453
+ stmt.order(*arel.orders)
454
+ stmt.wheres = arel.constraints
455
+
456
+ if updates.is_a?(Hash)
457
+ if klass.locking_enabled? &&
458
+ !updates.key?(klass.locking_column) &&
459
+ !updates.key?(klass.locking_column.to_sym)
460
+ attr = table[klass.locking_column]
461
+ updates[attr.name] = _increment_attribute(attr)
462
+ end
463
+ stmt.set _substitute_values(updates)
330
464
  else
331
- stmt.key = arel_attribute(primary_key)
332
- stmt.take(arel.limit)
333
- stmt.order(*arel.orders)
334
- stmt.wheres = arel.constraints
465
+ stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
335
466
  end
336
467
 
337
468
  @klass.connection.update stmt, "#{@klass} Update All"
@@ -345,6 +476,65 @@ module ActiveRecord
345
476
  end
346
477
  end
347
478
 
479
+ # Updates the counters of the records in the current relation.
480
+ #
481
+ # ==== Parameters
482
+ #
483
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
484
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
485
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
486
+ #
487
+ # ==== Examples
488
+ #
489
+ # # For Posts by a given author increment the comment_count by 1.
490
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
491
+ def update_counters(counters)
492
+ touch = counters.delete(:touch)
493
+
494
+ updates = {}
495
+ counters.each do |counter_name, value|
496
+ attr = table[counter_name]
497
+ updates[attr.name] = _increment_attribute(attr, value)
498
+ end
499
+
500
+ if touch
501
+ names = touch if touch != true
502
+ names = Array.wrap(names)
503
+ options = names.extract_options!
504
+ touch_updates = klass.touch_attributes_with_time(*names, **options)
505
+ updates.merge!(touch_updates) unless touch_updates.empty?
506
+ end
507
+
508
+ update_all updates
509
+ end
510
+
511
+ # Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
512
+ # It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
513
+ # This method can be passed attribute names and an optional time argument.
514
+ # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
515
+ # If no time argument is passed, the current time is used as default.
516
+ #
517
+ # === Examples
518
+ #
519
+ # # Touch all records
520
+ # Person.all.touch_all
521
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
522
+ #
523
+ # # Touch multiple records with a custom attribute
524
+ # Person.all.touch_all(:created_at)
525
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
526
+ #
527
+ # # Touch multiple records with a specified time
528
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
529
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
530
+ #
531
+ # # Touch records with scope
532
+ # Person.where(name: 'David').touch_all
533
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
534
+ def touch_all(*names, time: nil)
535
+ update_all klass.touch_attributes_with_time(*names, time: time)
536
+ end
537
+
348
538
  # Destroys the records by instantiating each
349
539
  # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
350
540
  # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
@@ -385,8 +575,8 @@ module ActiveRecord
385
575
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
386
576
  def delete_all
387
577
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
388
- value = get_value(method)
389
- SINGLE_VALUE_METHODS.include?(method) ? value : value.any?
578
+ value = @values[method]
579
+ method == :distinct ? value : value&.any?
390
580
  end
391
581
  if invalid_methods.any?
392
582
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
@@ -398,13 +588,12 @@ module ActiveRecord
398
588
  end
399
589
 
400
590
  stmt = Arel::DeleteManager.new
401
- stmt.from(table)
402
-
403
- if has_join_values? || has_limit_or_offset?
404
- @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
405
- else
406
- stmt.wheres = arel.constraints
407
- end
591
+ stmt.from(arel.join_sources.empty? ? table : arel.source)
592
+ stmt.key = table[primary_key]
593
+ stmt.take(arel.limit)
594
+ stmt.offset(arel.offset)
595
+ stmt.order(*arel.orders)
596
+ stmt.wheres = arel.constraints
408
597
 
409
598
  affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
410
599
 
@@ -412,6 +601,32 @@ module ActiveRecord
412
601
  affected
413
602
  end
414
603
 
604
+ # Finds and destroys all records matching the specified conditions.
605
+ # This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
606
+ # Returns the collection of objects that were destroyed.
607
+ #
608
+ # If no record is found, returns empty array.
609
+ #
610
+ # Person.destroy_by(id: 13)
611
+ # Person.destroy_by(name: 'Spartacus', rating: 4)
612
+ # Person.destroy_by("published_at < ?", 2.weeks.ago)
613
+ def destroy_by(*args)
614
+ where(*args).destroy_all
615
+ end
616
+
617
+ # Finds and deletes all records matching the specified conditions.
618
+ # This is short-hand for <tt>relation.where(condition).delete_all</tt>.
619
+ # Returns the number of rows affected.
620
+ #
621
+ # If no record is found, returns <tt>0</tt> as zero rows were affected.
622
+ #
623
+ # Person.delete_by(id: 13)
624
+ # Person.delete_by(name: 'Spartacus', rating: 4)
625
+ # Person.delete_by("published_at < ?", 2.weeks.ago)
626
+ def delete_by(*args)
627
+ where(*args).delete_all
628
+ end
629
+
415
630
  # Causes the records to be loaded from the database if they have not
416
631
  # been loaded already. You can use this if for some reason you need
417
632
  # to explicitly load some records before actually using them. The
@@ -419,7 +634,10 @@ module ActiveRecord
419
634
  #
420
635
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
421
636
  def load(&block)
422
- exec_queries(&block) unless loaded?
637
+ unless loaded?
638
+ @records = exec_queries(&block)
639
+ @loaded = true
640
+ end
423
641
 
424
642
  self
425
643
  end
@@ -433,8 +651,8 @@ module ActiveRecord
433
651
  def reset
434
652
  @delegate_to_klass = false
435
653
  @to_sql = @arel = @loaded = @should_eager_load = nil
654
+ @offsets = @take = nil
436
655
  @records = [].freeze
437
- @offsets = {}
438
656
  self
439
657
  end
440
658
 
@@ -465,7 +683,9 @@ module ActiveRecord
465
683
  end
466
684
 
467
685
  def scope_for_create
468
- where_values_hash.merge!(create_with_value.stringify_keys)
686
+ hash = where_clause.to_h(klass.table_name, equality_only: true)
687
+ create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
688
+ hash
469
689
  end
470
690
 
471
691
  # Returns true if relation needs eager loading.
@@ -509,7 +729,7 @@ module ActiveRecord
509
729
  end
510
730
 
511
731
  def inspect
512
- subject = loaded? ? records : self
732
+ subject = loaded? ? records : annotate("loading for inspect")
513
733
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
514
734
 
515
735
  entries[10] = "..." if entries.size == 11
@@ -526,52 +746,115 @@ module ActiveRecord
526
746
  end
527
747
 
528
748
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
529
- joins += [aliases] if aliases
530
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
749
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
531
750
  end
532
751
 
533
- protected
752
+ class StrictLoadingScope # :nodoc:
753
+ def self.empty_scope?
754
+ true
755
+ end
756
+
757
+ def self.strict_loading_value
758
+ true
759
+ end
760
+ end
761
+
762
+ def preload_associations(records) # :nodoc:
763
+ preload = preload_values
764
+ preload += includes_values unless eager_loading?
765
+ preloader = nil
766
+ scope = strict_loading_value ? StrictLoadingScope : nil
767
+ preload.each do |associations|
768
+ preloader ||= build_preloader
769
+ preloader.preload records, associations, scope
770
+ end
771
+ end
534
772
 
773
+ protected
535
774
  def load_records(records)
536
775
  @records = records.freeze
537
776
  @loaded = true
538
777
  end
539
778
 
779
+ def null_relation? # :nodoc:
780
+ is_a?(NullRelation)
781
+ end
782
+
540
783
  private
784
+ def already_in_scope?
785
+ @delegate_to_klass && klass.current_scope(true)
786
+ end
787
+
788
+ def current_scope_restoring_block(&block)
789
+ current_scope = klass.current_scope(true)
790
+ -> record do
791
+ klass.current_scope = current_scope
792
+ yield record if block_given?
793
+ end
794
+ end
795
+
796
+ def _new(attributes, &block)
797
+ klass.new(attributes, &block)
798
+ end
799
+
800
+ def _create(attributes, &block)
801
+ klass.create(attributes, &block)
802
+ end
803
+
804
+ def _create!(attributes, &block)
805
+ klass.create!(attributes, &block)
806
+ end
807
+
808
+ def _scoping(scope)
809
+ previous, klass.current_scope = klass.current_scope(true), scope
810
+ yield
811
+ ensure
812
+ klass.current_scope = previous
813
+ end
541
814
 
542
- def has_join_values?
543
- joins_values.any? || left_outer_joins_values.any?
815
+ def _substitute_values(values)
816
+ values.map do |name, value|
817
+ attr = table[name]
818
+ unless Arel.arel_node?(value)
819
+ type = klass.type_for_attribute(attr.name)
820
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
821
+ end
822
+ [attr, value]
823
+ end
824
+ end
825
+
826
+ def _increment_attribute(attribute, value = 1)
827
+ bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
828
+ expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
829
+ expr = value < 0 ? expr - bind : expr + bind
830
+ expr.expr
544
831
  end
545
832
 
546
833
  def exec_queries(&block)
547
834
  skip_query_cache_if_necessary do
548
- @records =
549
- if eager_loading?
835
+ records =
836
+ if where_clause.contradiction?
837
+ []
838
+ elsif eager_loading?
550
839
  apply_join_dependency do |relation, join_dependency|
551
- if ActiveRecord::NullRelation === relation
840
+ if relation.null_relation?
552
841
  []
553
842
  else
554
843
  relation = join_dependency.apply_column_aliases(relation)
555
844
  rows = connection.select_all(relation.arel, "SQL")
556
- join_dependency.instantiate(rows, &block)
845
+ join_dependency.instantiate(rows, strict_loading_value, &block)
557
846
  end.freeze
558
847
  end
559
848
  else
560
849
  klass.find_by_sql(arel, &block).freeze
561
850
  end
562
851
 
563
- preload = preload_values
564
- preload += includes_values unless eager_loading?
565
- preloader = nil
566
- preload.each do |associations|
567
- preloader ||= build_preloader
568
- preloader.preload @records, associations
569
- end
852
+ preload_associations(records) unless skip_preloading_value
570
853
 
571
- @records.each(&:readonly!) if readonly_value
854
+ records.each(&:readonly!) if readonly_value
855
+ records.each(&:strict_loading!) if strict_loading_value
572
856
 
573
- @loaded = true
574
- @records
857
+ records
575
858
  end
576
859
  end
577
860
 
@@ -590,40 +873,27 @@ module ActiveRecord
590
873
  end
591
874
 
592
875
  def references_eager_loaded_tables?
593
- joined_tables = arel.join_sources.map do |join|
876
+ joined_tables = build_joins([]).flat_map do |join|
594
877
  if join.is_a?(Arel::Nodes::StringJoin)
595
878
  tables_in_string(join.left)
596
879
  else
597
- [join.left.table_name, join.left.table_alias]
880
+ join.left.name
598
881
  end
599
882
  end
600
883
 
601
- joined_tables += [table.name, table.table_alias]
884
+ joined_tables << table.name
602
885
 
603
886
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
604
- joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
887
+ joined_tables.map!(&:downcase)
605
888
 
606
- (references_values - joined_tables).any?
889
+ !(references_values.map(&:to_s) - joined_tables).empty?
607
890
  end
608
891
 
609
892
  def tables_in_string(string)
610
893
  return [] if string.blank?
611
894
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
612
895
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
613
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
614
- end
615
-
616
- def values_for_create(attributes = nil)
617
- result = attributes ? where_values_hash.merge!(attributes) : where_values_hash
618
-
619
- # NOTE: if there are same keys in both create_with and result, create_with should be used.
620
- # This is to make sure nested attributes don't get passed to the klass.new,
621
- # while keeping the precedence of the duplicate keys in create_with.
622
- create_with_value.stringify_keys.each do |k, v|
623
- result[k] = v if result.key?(k)
624
- end
625
-
626
- result
896
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
627
897
  end
628
898
  end
629
899
  end