activerecord 6.0.0 → 6.1.4

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1178 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +120 -13
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +63 -44
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +12 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  104. data/lib/active_record/connection_adapters/schema_cache.rb +106 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +219 -81
  114. data/lib/active_record/core.rb +268 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +124 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +2 -2
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +71 -20
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +344 -181
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -82
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +3 -3
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +79 -16
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +26 -73
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +15 -12
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +27 -24
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  :order, :joins, :left_outer_joins, :references,
8
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
13
  CLAUSE_METHODS = [:where, :having, :from]
@@ -28,7 +28,6 @@ module ActiveRecord
28
28
  @klass = klass
29
29
  @table = table
30
30
  @values = values
31
- @offsets = {}
32
31
  @loaded = false
33
32
  @predicate_builder = predicate_builder
34
33
  @delegate_to_klass = false
@@ -40,8 +39,9 @@ module ActiveRecord
40
39
  end
41
40
 
42
41
  def arel_attribute(name) # :nodoc:
43
- klass.arel_attribute(name, table)
42
+ table[name]
44
43
  end
44
+ deprecate :arel_attribute
45
45
 
46
46
  def bind_attribute(name, value) # :nodoc:
47
47
  if reflection = klass._reflect_on_association(name)
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
50
50
  end
51
51
 
52
- attr = arel_attribute(name)
52
+ attr = table[name]
53
53
  bind = predicate_builder.build_bind_attribute(attr.name, value)
54
54
  yield attr, bind
55
55
  end
@@ -67,10 +67,9 @@ module ActiveRecord
67
67
  # user = users.new { |user| user.name = 'Oscar' }
68
68
  # user.name # => Oscar
69
69
  def new(attributes = nil, &block)
70
- block = _deprecated_scope_block("new", &block)
71
- scoping { klass.new(attributes, &block) }
70
+ block = current_scope_restoring_block(&block)
71
+ scoping { _new(attributes, &block) }
72
72
  end
73
-
74
73
  alias build new
75
74
 
76
75
  # Tries to create a new record with the same scoped attributes
@@ -96,8 +95,8 @@ module ActiveRecord
96
95
  if attributes.is_a?(Array)
97
96
  attributes.collect { |attr| create(attr, &block) }
98
97
  else
99
- block = _deprecated_scope_block("create", &block)
100
- scoping { klass.create(attributes, &block) }
98
+ block = current_scope_restoring_block(&block)
99
+ scoping { _create(attributes, &block) }
101
100
  end
102
101
  end
103
102
 
@@ -111,8 +110,8 @@ module ActiveRecord
111
110
  if attributes.is_a?(Array)
112
111
  attributes.collect { |attr| create!(attr, &block) }
113
112
  else
114
- block = _deprecated_scope_block("create!", &block)
115
- scoping { klass.create!(attributes, &block) }
113
+ block = current_scope_restoring_block(&block)
114
+ scoping { _create!(attributes, &block) }
116
115
  end
117
116
  end
118
117
 
@@ -308,7 +307,7 @@ module ActiveRecord
308
307
  # last updated record.
309
308
  #
310
309
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
311
- def cache_key(timestamp_column = :updated_at)
310
+ def cache_key(timestamp_column = "updated_at")
312
311
  @cache_keys ||= {}
313
312
  @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
314
313
  end
@@ -317,7 +316,7 @@ module ActiveRecord
317
316
  query_signature = ActiveSupport::Digest.hexdigest(to_sql)
318
317
  key = "#{klass.model_name.cache_key}/query-#{query_signature}"
319
318
 
320
- if cache_version(timestamp_column)
319
+ if collection_cache_versioning
321
320
  key
322
321
  else
323
322
  "#{key}-#{compute_cache_version(timestamp_column)}"
@@ -343,15 +342,17 @@ module ActiveRecord
343
342
  end
344
343
 
345
344
  def compute_cache_version(timestamp_column) # :nodoc:
345
+ timestamp_column = timestamp_column.to_s
346
+
346
347
  if loaded? || distinct_value
347
348
  size = records.size
348
349
  if size > 0
349
- timestamp = max_by(&timestamp_column)._read_attribute(timestamp_column)
350
+ timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
350
351
  end
351
352
  else
352
353
  collection = eager_loading? ? apply_join_dependency : self
353
354
 
354
- column = connection.visitor.compile(arel_attribute(timestamp_column))
355
+ column = connection.visitor.compile(table[timestamp_column])
355
356
  select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
356
357
 
357
358
  if collection.has_limit_or_offset?
@@ -365,14 +366,12 @@ module ActiveRecord
365
366
  arel = query.arel
366
367
  end
367
368
 
368
- result = connection.select_one(arel, nil)
369
+ size, timestamp = connection.select_rows(arel, nil).first
369
370
 
370
- if result
371
+ if size
371
372
  column_type = klass.type_for_attribute(timestamp_column)
372
- timestamp = column_type.deserialize(result["timestamp"])
373
- size = result["size"]
373
+ timestamp = column_type.deserialize(timestamp)
374
374
  else
375
- timestamp = nil
376
375
  size = 0
377
376
  end
378
377
  end
@@ -385,6 +384,15 @@ module ActiveRecord
385
384
  end
386
385
  private :compute_cache_version
387
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
394
+ end
395
+
388
396
  # Scope all queries to the current scope.
389
397
  #
390
398
  # Comment.where(post_id: 1).scoping do
@@ -398,9 +406,9 @@ module ActiveRecord
398
406
  already_in_scope? ? yield : _scoping(self) { yield }
399
407
  end
400
408
 
401
- def _exec_scope(name, *args, &block) # :nodoc:
409
+ def _exec_scope(*args, &block) # :nodoc:
402
410
  @delegate_to_klass = true
403
- _scoping(_deprecated_spawn(name)) { instance_exec(*args, &block) || self }
411
+ _scoping(nil) { instance_exec(*args, &block) || self }
404
412
  ensure
405
413
  @delegate_to_klass = false
406
414
  end
@@ -408,7 +416,7 @@ module ActiveRecord
408
416
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
409
417
  # statement and sends it straight to the database. It does not instantiate the involved models and it does not
410
418
  # 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.
419
+ # Active Record's normal type casting and serialization. Returns the number of rows affected.
412
420
  #
413
421
  # Note: As Active Record callbacks are not triggered, this method will not automatically update +updated_at+/+updated_on+ columns.
414
422
  #
@@ -432,14 +440,12 @@ module ActiveRecord
432
440
  def update_all(updates)
433
441
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
434
442
 
435
- if eager_loading?
436
- relation = apply_join_dependency
437
- return relation.update_all(updates)
438
- end
443
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
444
+ arel.source.left = table
439
445
 
440
446
  stmt = Arel::UpdateManager.new
441
- stmt.table(arel.join_sources.empty? ? table : arel.source)
442
- stmt.key = arel_attribute(primary_key)
447
+ stmt.table(arel.source)
448
+ stmt.key = table[primary_key]
443
449
  stmt.take(arel.limit)
444
450
  stmt.offset(arel.offset)
445
451
  stmt.order(*arel.orders)
@@ -449,7 +455,7 @@ module ActiveRecord
449
455
  if klass.locking_enabled? &&
450
456
  !updates.key?(klass.locking_column) &&
451
457
  !updates.key?(klass.locking_column.to_sym)
452
- attr = arel_attribute(klass.locking_column)
458
+ attr = table[klass.locking_column]
453
459
  updates[attr.name] = _increment_attribute(attr)
454
460
  end
455
461
  stmt.set _substitute_values(updates)
@@ -457,7 +463,7 @@ module ActiveRecord
457
463
  stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
458
464
  end
459
465
 
460
- @klass.connection.update stmt, "#{@klass} Update All"
466
+ klass.connection.update(stmt, "#{klass} Update All").tap { reset }
461
467
  end
462
468
 
463
469
  def update(id = :all, attributes) # :nodoc:
@@ -468,26 +474,40 @@ module ActiveRecord
468
474
  end
469
475
  end
470
476
 
471
- def update_counters(counters) # :nodoc:
477
+ # Updates the counters of the records in the current relation.
478
+ #
479
+ # ==== Parameters
480
+ #
481
+ # * +counter+ - A Hash containing the names of the fields to update as keys and the amount to update as values.
482
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
483
+ # * If attributes names are passed, they are updated along with update_at/on attributes.
484
+ #
485
+ # ==== Examples
486
+ #
487
+ # # For Posts by a given author increment the comment_count by 1.
488
+ # Post.where(author_id: author.id).update_counters(comment_count: 1)
489
+ def update_counters(counters)
472
490
  touch = counters.delete(:touch)
473
491
 
474
492
  updates = {}
475
493
  counters.each do |counter_name, value|
476
- attr = arel_attribute(counter_name)
494
+ attr = table[counter_name]
477
495
  updates[attr.name] = _increment_attribute(attr, value)
478
496
  end
479
497
 
480
498
  if touch
481
499
  names = touch if touch != true
482
- touch_updates = klass.touch_attributes_with_time(*names)
500
+ names = Array.wrap(names)
501
+ options = names.extract_options!
502
+ touch_updates = klass.touch_attributes_with_time(*names, **options)
483
503
  updates.merge!(touch_updates) unless touch_updates.empty?
484
504
  end
485
505
 
486
506
  update_all updates
487
507
  end
488
508
 
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.
509
+ # Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
510
+ # It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
491
511
  # This method can be passed attribute names and an optional time argument.
492
512
  # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
493
513
  # If no time argument is passed, the current time is used as default.
@@ -560,23 +580,18 @@ module ActiveRecord
560
580
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
561
581
  end
562
582
 
563
- if eager_loading?
564
- relation = apply_join_dependency
565
- return relation.delete_all
566
- end
583
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
584
+ arel.source.left = table
567
585
 
568
586
  stmt = Arel::DeleteManager.new
569
- stmt.from(arel.join_sources.empty? ? table : arel.source)
570
- stmt.key = arel_attribute(primary_key)
587
+ stmt.from(arel.source)
588
+ stmt.key = table[primary_key]
571
589
  stmt.take(arel.limit)
572
590
  stmt.offset(arel.offset)
573
591
  stmt.order(*arel.orders)
574
592
  stmt.wheres = arel.constraints
575
593
 
576
- affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
577
-
578
- reset
579
- affected
594
+ klass.connection.delete(stmt, "#{klass} Destroy").tap { reset }
580
595
  end
581
596
 
582
597
  # Finds and destroys all records matching the specified conditions.
@@ -612,7 +627,10 @@ module ActiveRecord
612
627
  #
613
628
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
614
629
  def load(&block)
615
- exec_queries(&block) unless loaded?
630
+ unless loaded?
631
+ @records = exec_queries(&block)
632
+ @loaded = true
633
+ end
616
634
 
617
635
  self
618
636
  end
@@ -625,10 +643,10 @@ module ActiveRecord
625
643
 
626
644
  def reset
627
645
  @delegate_to_klass = false
628
- @_deprecated_scope_source = nil
629
646
  @to_sql = @arel = @loaded = @should_eager_load = nil
647
+ @offsets = @take = nil
648
+ @cache_keys = nil
630
649
  @records = [].freeze
631
- @offsets = {}
632
650
  self
633
651
  end
634
652
 
@@ -659,7 +677,9 @@ module ActiveRecord
659
677
  end
660
678
 
661
679
  def scope_for_create
662
- where_values_hash.merge!(create_with_value.stringify_keys)
680
+ hash = where_clause.to_h(klass.table_name, equality_only: true)
681
+ create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
682
+ hash
663
683
  end
664
684
 
665
685
  # Returns true if relation needs eager loading.
@@ -703,7 +723,7 @@ module ActiveRecord
703
723
  end
704
724
 
705
725
  def inspect
706
- subject = loaded? ? records : self
726
+ subject = loaded? ? records : annotate("loading for inspect")
707
727
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
708
728
 
709
729
  entries[10] = "..." if entries.size == 11
@@ -720,25 +740,31 @@ module ActiveRecord
720
740
  end
721
741
 
722
742
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
723
- joins += [aliases] if aliases
724
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
743
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
744
+ end
745
+
746
+ class StrictLoadingScope # :nodoc:
747
+ def self.empty_scope?
748
+ true
749
+ end
750
+
751
+ def self.strict_loading_value
752
+ true
753
+ end
725
754
  end
726
755
 
727
756
  def preload_associations(records) # :nodoc:
728
757
  preload = preload_values
729
758
  preload += includes_values unless eager_loading?
730
759
  preloader = nil
760
+ scope = strict_loading_value ? StrictLoadingScope : nil
731
761
  preload.each do |associations|
732
762
  preloader ||= build_preloader
733
- preloader.preload records, associations
763
+ preloader.preload records, associations, scope
734
764
  end
735
765
  end
736
766
 
737
- attr_reader :_deprecated_scope_source # :nodoc:
738
-
739
767
  protected
740
- attr_writer :_deprecated_scope_source # :nodoc:
741
-
742
768
  def load_records(records)
743
769
  @records = records.freeze
744
770
  @loaded = true
@@ -750,23 +776,29 @@ module ActiveRecord
750
776
 
751
777
  private
752
778
  def already_in_scope?
753
- @delegate_to_klass && begin
754
- scope = klass.current_scope(true)
755
- scope && !scope._deprecated_scope_source
756
- end
779
+ @delegate_to_klass && klass.current_scope(true)
757
780
  end
758
781
 
759
- def _deprecated_spawn(name)
760
- spawn.tap { |scope| scope._deprecated_scope_source = name }
761
- end
762
-
763
- def _deprecated_scope_block(name, &block)
782
+ def current_scope_restoring_block(&block)
783
+ current_scope = klass.current_scope(true)
764
784
  -> record do
765
- klass.current_scope = _deprecated_spawn(name)
785
+ klass.current_scope = current_scope
766
786
  yield record if block_given?
767
787
  end
768
788
  end
769
789
 
790
+ def _new(attributes, &block)
791
+ klass.new(attributes, &block)
792
+ end
793
+
794
+ def _create(attributes, &block)
795
+ klass.create(attributes, &block)
796
+ end
797
+
798
+ def _create!(attributes, &block)
799
+ klass.create!(attributes, &block)
800
+ end
801
+
770
802
  def _scoping(scope)
771
803
  previous, klass.current_scope = klass.current_scope(true), scope
772
804
  yield
@@ -776,7 +808,7 @@ module ActiveRecord
776
808
 
777
809
  def _substitute_values(values)
778
810
  values.map do |name, value|
779
- attr = arel_attribute(name)
811
+ attr = table[name]
780
812
  unless Arel.arel_node?(value)
781
813
  type = klass.type_for_attribute(attr.name)
782
814
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
@@ -794,27 +826,29 @@ module ActiveRecord
794
826
 
795
827
  def exec_queries(&block)
796
828
  skip_query_cache_if_necessary do
797
- @records =
798
- if eager_loading?
829
+ records =
830
+ if where_clause.contradiction?
831
+ []
832
+ elsif eager_loading?
799
833
  apply_join_dependency do |relation, join_dependency|
800
834
  if relation.null_relation?
801
835
  []
802
836
  else
803
837
  relation = join_dependency.apply_column_aliases(relation)
804
838
  rows = connection.select_all(relation.arel, "SQL")
805
- join_dependency.instantiate(rows, &block)
839
+ join_dependency.instantiate(rows, strict_loading_value, &block)
806
840
  end.freeze
807
841
  end
808
842
  else
809
843
  klass.find_by_sql(arel, &block).freeze
810
844
  end
811
845
 
812
- preload_associations(@records) unless skip_preloading_value
846
+ preload_associations(records) unless skip_preloading_value
813
847
 
814
- @records.each(&:readonly!) if readonly_value
848
+ records.each(&:readonly!) if readonly_value
849
+ records.each(&:strict_loading!) if strict_loading_value
815
850
 
816
- @loaded = true
817
- @records
851
+ records
818
852
  end
819
853
  end
820
854
 
@@ -833,27 +867,27 @@ module ActiveRecord
833
867
  end
834
868
 
835
869
  def references_eager_loaded_tables?
836
- joined_tables = arel.join_sources.map do |join|
870
+ joined_tables = build_joins([]).flat_map do |join|
837
871
  if join.is_a?(Arel::Nodes::StringJoin)
838
872
  tables_in_string(join.left)
839
873
  else
840
- [join.left.table_name, join.left.table_alias]
874
+ join.left.name
841
875
  end
842
876
  end
843
877
 
844
- joined_tables += [table.name, table.table_alias]
878
+ joined_tables << table.name
845
879
 
846
880
  # 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
881
+ joined_tables.map!(&:downcase)
848
882
 
849
- (references_values - joined_tables).any?
883
+ !(references_values.map(&:to_s) - joined_tables).empty?
850
884
  end
851
885
 
852
886
  def tables_in_string(string)
853
887
  return [] if string.blank?
854
888
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
855
889
  # 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_"]
890
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
857
891
  end
858
892
  end
859
893
  end
@@ -65,16 +65,10 @@ module ActiveRecord
65
65
  end
66
66
  end
67
67
 
68
- def to_hash
69
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
70
- `ActiveRecord::Result#to_hash` has been renamed to `to_a`.
71
- `to_hash` is deprecated and will be removed in Rails 6.1.
72
- MSG
73
- to_a
74
- end
75
-
76
68
  alias :map! :map
77
69
  alias :collect! :map
70
+ deprecate "map!": :map
71
+ deprecate "collect!": :map
78
72
 
79
73
  # Returns true if there are no records, otherwise false.
80
74
  def empty?
@@ -92,31 +86,30 @@ module ActiveRecord
92
86
  hash_rows[idx]
93
87
  end
94
88
 
95
- # Returns the first record from the rows collection.
96
- # If the rows collection is empty, returns +nil+.
97
- def first
98
- return nil if @rows.empty?
99
- Hash[@columns.zip(@rows.first)]
100
- end
101
-
102
89
  # Returns the last record from the rows collection.
103
- # If the rows collection is empty, returns +nil+.
104
- def last
105
- return nil if @rows.empty?
106
- Hash[@columns.zip(@rows.last)]
90
+ def last(n = nil)
91
+ n ? hash_rows.last(n) : hash_rows.last
107
92
  end
108
93
 
109
94
  def cast_values(type_overrides = {}) # :nodoc:
110
95
  if columns.one?
111
96
  # Separated to avoid allocating an array per row
112
97
 
113
- type = column_type(columns.first, type_overrides)
98
+ type = if type_overrides.is_a?(Array)
99
+ type_overrides.first
100
+ else
101
+ column_type(columns.first, type_overrides)
102
+ end
114
103
 
115
104
  rows.map do |(value)|
116
105
  type.deserialize(value)
117
106
  end
118
107
  else
119
- types = columns.map { |name| column_type(name, type_overrides) }
108
+ types = if type_overrides.is_a?(Array)
109
+ type_overrides
110
+ else
111
+ columns.map { |name| column_type(name, type_overrides) }
112
+ end
120
113
 
121
114
  rows.map do |values|
122
115
  Array.new(values.size) { |i| types[i].deserialize(values[i]) }
@@ -132,7 +125,6 @@ module ActiveRecord
132
125
  end
133
126
 
134
127
  private
135
-
136
128
  def column_type(name, type_overrides = {})
137
129
  type_overrides.fetch(name) do
138
130
  column_types.fetch(name, Type.default_value)
@@ -146,21 +138,36 @@ module ActiveRecord
146
138
  # used as keys in ActiveRecord::Base's @attributes hash
147
139
  columns = @columns.map(&:-@)
148
140
  length = columns.length
141
+ template = nil
149
142
 
150
143
  @rows.map { |row|
151
- # In the past we used Hash[columns.zip(row)]
152
- # though elegant, the verbose way is much more efficient
153
- # both time and memory wise cause it avoids a big array allocation
154
- # this method is called a lot and needs to be micro optimised
155
- hash = {}
156
-
157
- index = 0
158
- while index < length
159
- hash[columns[index]] = row[index]
160
- index += 1
144
+ if template
145
+ # We use transform_values to build subsequent rows from the
146
+ # hash of the first row. This is faster because we avoid any
147
+ # reallocs and in Ruby 2.7+ avoid hashing entirely.
148
+ index = -1
149
+ template.transform_values do
150
+ row[index += 1]
151
+ end
152
+ else
153
+ # In the past we used Hash[columns.zip(row)]
154
+ # though elegant, the verbose way is much more efficient
155
+ # both time and memory wise cause it avoids a big array allocation
156
+ # this method is called a lot and needs to be micro optimised
157
+ hash = {}
158
+
159
+ index = 0
160
+ while index < length
161
+ hash[columns[index]] = row[index]
162
+ index += 1
163
+ end
164
+
165
+ # It's possible to select the same column twice, in which case
166
+ # we can't use a template
167
+ template = hash if hash.length == length
168
+
169
+ hash
161
170
  end
162
-
163
- hash
164
171
  }
165
172
  end
166
173
  end
@@ -14,9 +14,9 @@ module ActiveRecord
14
14
  class RuntimeRegistry # :nodoc:
15
15
  extend ActiveSupport::PerThreadRegistry
16
16
 
17
- attr_accessor :connection_handler, :sql_runtime
17
+ attr_accessor :sql_runtime
18
18
 
19
- [:connection_handler, :sql_runtime].each do |val|
19
+ [:sql_runtime].each do |val|
20
20
  class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
21
  class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
22
22
  end
@@ -67,7 +67,7 @@ module ActiveRecord
67
67
  )
68
68
 
69
69
  # Ensure we aren't dealing with a subclass of String that might
70
- # override methods we use (eg. Arel::Nodes::SqlLiteral).
70
+ # override methods we use (e.g. Arel::Nodes::SqlLiteral).
71
71
  if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
72
72
  condition = [String.new(condition.first), *condition[1..-1]]
73
73
  end
@@ -141,19 +141,7 @@ module ActiveRecord
141
141
  (unexpected ||= []) << arg
142
142
  end
143
143
 
144
- return unless unexpected
145
-
146
- if allow_unsafe_raw_sql == :deprecated
147
- ActiveSupport::Deprecation.warn(
148
- "Dangerous query method (method whose arguments are used as raw " \
149
- "SQL) called with non-attribute argument(s): " \
150
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
151
- "arguments will be disallowed in Rails 6.1. This method should " \
152
- "not be called with user-provided values, such as request " \
153
- "parameters or model attributes. Known-safe values can be passed " \
154
- "by wrapping them in Arel.sql()."
155
- )
156
- else
144
+ if unexpected
157
145
  raise(ActiveRecord::UnknownAttributeReference,
158
146
  "Query method called with non-attribute argument(s): " +
159
147
  unexpected.map(&:inspect).join(", ")
@@ -193,13 +181,14 @@ module ActiveRecord
193
181
 
194
182
  def quote_bound_value(value, c = connection)
195
183
  if value.respond_to?(:map) && !value.acts_like?(:string)
196
- quoted = value.map { |v| c.quote(v) }
197
- if quoted.empty?
184
+ values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
185
+ if values.empty?
198
186
  c.quote(nil)
199
187
  else
200
- quoted.join(",")
188
+ values.map! { |v| c.quote(v) }.join(",")
201
189
  end
202
190
  else
191
+ value = value.id_for_database if value.respond_to?(:id_for_database)
203
192
  c.quote(value)
204
193
  end
205
194
  end