activerecord 6.0.1 → 6.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1363 -647
  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 +26 -15
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -37
  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 +38 -13
  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 +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  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 +119 -12
  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 +56 -41
  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 +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  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 +145 -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 +63 -77
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  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 +20 -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 +30 -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 +80 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -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 +57 -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 +218 -87
  114. data/lib/active_record/core.rb +276 -68
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +125 -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 +3 -3
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +42 -9
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -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 +6 -2
  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 +75 -21
  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 +117 -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 +45 -16
  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 +341 -188
  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 -83
  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 +9 -4
  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 +87 -20
  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 +25 -72
  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 +5 -9
  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 +30 -29
  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,11 +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
- @take = nil
633
650
  self
634
651
  end
635
652
 
@@ -660,7 +677,9 @@ module ActiveRecord
660
677
  end
661
678
 
662
679
  def scope_for_create
663
- 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
664
683
  end
665
684
 
666
685
  # Returns true if relation needs eager loading.
@@ -704,7 +723,7 @@ module ActiveRecord
704
723
  end
705
724
 
706
725
  def inspect
707
- subject = loaded? ? records : self
726
+ subject = loaded? ? records : annotate("loading for inspect")
708
727
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
709
728
 
710
729
  entries[10] = "..." if entries.size == 11
@@ -721,25 +740,31 @@ module ActiveRecord
721
740
  end
722
741
 
723
742
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
724
- joins += [aliases] if aliases
725
- 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
726
754
  end
727
755
 
728
756
  def preload_associations(records) # :nodoc:
729
757
  preload = preload_values
730
758
  preload += includes_values unless eager_loading?
731
759
  preloader = nil
760
+ scope = strict_loading_value ? StrictLoadingScope : nil
732
761
  preload.each do |associations|
733
762
  preloader ||= build_preloader
734
- preloader.preload records, associations
763
+ preloader.preload records, associations, scope
735
764
  end
736
765
  end
737
766
 
738
- attr_reader :_deprecated_scope_source # :nodoc:
739
-
740
767
  protected
741
- attr_writer :_deprecated_scope_source # :nodoc:
742
-
743
768
  def load_records(records)
744
769
  @records = records.freeze
745
770
  @loaded = true
@@ -751,23 +776,29 @@ module ActiveRecord
751
776
 
752
777
  private
753
778
  def already_in_scope?
754
- @delegate_to_klass && begin
755
- scope = klass.current_scope(true)
756
- scope && !scope._deprecated_scope_source
757
- end
779
+ @delegate_to_klass && klass.current_scope(true)
758
780
  end
759
781
 
760
- def _deprecated_spawn(name)
761
- spawn.tap { |scope| scope._deprecated_scope_source = name }
762
- end
763
-
764
- def _deprecated_scope_block(name, &block)
782
+ def current_scope_restoring_block(&block)
783
+ current_scope = klass.current_scope(true)
765
784
  -> record do
766
- klass.current_scope = _deprecated_spawn(name)
785
+ klass.current_scope = current_scope
767
786
  yield record if block_given?
768
787
  end
769
788
  end
770
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
+
771
802
  def _scoping(scope)
772
803
  previous, klass.current_scope = klass.current_scope(true), scope
773
804
  yield
@@ -777,7 +808,7 @@ module ActiveRecord
777
808
 
778
809
  def _substitute_values(values)
779
810
  values.map do |name, value|
780
- attr = arel_attribute(name)
811
+ attr = table[name]
781
812
  unless Arel.arel_node?(value)
782
813
  type = klass.type_for_attribute(attr.name)
783
814
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
@@ -795,27 +826,29 @@ module ActiveRecord
795
826
 
796
827
  def exec_queries(&block)
797
828
  skip_query_cache_if_necessary do
798
- @records =
799
- if eager_loading?
829
+ records =
830
+ if where_clause.contradiction?
831
+ []
832
+ elsif eager_loading?
800
833
  apply_join_dependency do |relation, join_dependency|
801
834
  if relation.null_relation?
802
835
  []
803
836
  else
804
837
  relation = join_dependency.apply_column_aliases(relation)
805
838
  rows = connection.select_all(relation.arel, "SQL")
806
- join_dependency.instantiate(rows, &block)
839
+ join_dependency.instantiate(rows, strict_loading_value, &block)
807
840
  end.freeze
808
841
  end
809
842
  else
810
843
  klass.find_by_sql(arel, &block).freeze
811
844
  end
812
845
 
813
- preload_associations(@records) unless skip_preloading_value
846
+ preload_associations(records) unless skip_preloading_value
814
847
 
815
- @records.each(&:readonly!) if readonly_value
848
+ records.each(&:readonly!) if readonly_value
849
+ records.each(&:strict_loading!) if strict_loading_value
816
850
 
817
- @loaded = true
818
- @records
851
+ records
819
852
  end
820
853
  end
821
854
 
@@ -834,27 +867,27 @@ module ActiveRecord
834
867
  end
835
868
 
836
869
  def references_eager_loaded_tables?
837
- joined_tables = arel.join_sources.map do |join|
870
+ joined_tables = build_joins([]).flat_map do |join|
838
871
  if join.is_a?(Arel::Nodes::StringJoin)
839
872
  tables_in_string(join.left)
840
873
  else
841
- [join.left.table_name, join.left.table_alias]
874
+ join.left.name
842
875
  end
843
876
  end
844
877
 
845
- joined_tables += [table.name, table.table_alias]
878
+ joined_tables << table.name
846
879
 
847
880
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
848
- joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
881
+ joined_tables.map!(&:downcase)
849
882
 
850
- (references_values - joined_tables).any?
883
+ !(references_values.map(&:to_s) - joined_tables).empty?
851
884
  end
852
885
 
853
886
  def tables_in_string(string)
854
887
  return [] if string.blank?
855
888
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
856
889
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
857
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
890
+ string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
858
891
  end
859
892
  end
860
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