activerecord 5.1.7 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +372 -765
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +16 -27
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +20 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +4 -5
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +43 -35
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +7 -18
  26. data/lib/active_record/associations/has_one_association.rb +4 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +23 -43
  32. data/lib/active_record/associations/preloader/association.rb +45 -61
  33. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +25 -10
  37. data/lib/active_record/associations.rb +31 -54
  38. data/lib/active_record/attribute_assignment.rb +2 -5
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +21 -9
  48. data/lib/active_record/attribute_methods.rb +65 -24
  49. data/lib/active_record/attributes.rb +6 -5
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -10
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -2
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -6
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +246 -110
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +58 -82
  117. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +80 -90
  126. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  127. data/lib/active_record/connection_handling.rb +4 -2
  128. data/lib/active_record/core.rb +39 -60
  129. data/lib/active_record/counter_cache.rb +15 -12
  130. data/lib/active_record/define_callbacks.rb +5 -3
  131. data/lib/active_record/dynamic_matchers.rb +9 -9
  132. data/lib/active_record/enum.rb +17 -13
  133. data/lib/active_record/errors.rb +54 -21
  134. data/lib/active_record/explain.rb +3 -1
  135. data/lib/active_record/explain_registry.rb +2 -0
  136. data/lib/active_record/explain_subscriber.rb +2 -0
  137. data/lib/active_record/fixture_set/file.rb +2 -0
  138. data/lib/active_record/fixtures.rb +67 -60
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +49 -19
  141. data/lib/active_record/integration.rb +58 -19
  142. data/lib/active_record/internal_metadata.rb +2 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  144. data/lib/active_record/locking/optimistic.rb +14 -17
  145. data/lib/active_record/locking/pessimistic.rb +9 -6
  146. data/lib/active_record/log_subscriber.rb +43 -0
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +40 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/migration.rb +189 -139
  151. data/lib/active_record/model_schema.rb +16 -21
  152. data/lib/active_record/nested_attributes.rb +18 -6
  153. data/lib/active_record/no_touching.rb +3 -1
  154. data/lib/active_record/null_relation.rb +2 -0
  155. data/lib/active_record/persistence.rb +166 -16
  156. data/lib/active_record/query_cache.rb +11 -6
  157. data/lib/active_record/querying.rb +3 -1
  158. data/lib/active_record/railtie.rb +61 -3
  159. data/lib/active_record/railties/console_sandbox.rb +2 -0
  160. data/lib/active_record/railties/controller_runtime.rb +2 -0
  161. data/lib/active_record/railties/databases.rake +46 -36
  162. data/lib/active_record/readonly_attributes.rb +3 -2
  163. data/lib/active_record/reflection.rb +110 -192
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  165. data/lib/active_record/relation/batches.rb +20 -5
  166. data/lib/active_record/relation/calculations.rb +30 -8
  167. data/lib/active_record/relation/delegation.rb +15 -27
  168. data/lib/active_record/relation/finder_methods.rb +75 -78
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +51 -20
  171. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  173. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  174. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  175. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  176. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  178. data/lib/active_record/relation/predicate_builder.rb +53 -78
  179. data/lib/active_record/relation/query_attribute.rb +26 -2
  180. data/lib/active_record/relation/query_methods.rb +89 -88
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +3 -1
  183. data/lib/active_record/relation/where_clause.rb +65 -68
  184. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  185. data/lib/active_record/relation.rb +95 -208
  186. data/lib/active_record/result.rb +2 -0
  187. data/lib/active_record/runtime_registry.rb +2 -0
  188. data/lib/active_record/sanitization.rb +129 -121
  189. data/lib/active_record/schema.rb +4 -2
  190. data/lib/active_record/schema_dumper.rb +36 -26
  191. data/lib/active_record/schema_migration.rb +2 -0
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +21 -7
  194. data/lib/active_record/scoping.rb +9 -8
  195. data/lib/active_record/secure_token.rb +2 -0
  196. data/lib/active_record/serialization.rb +2 -0
  197. data/lib/active_record/statement_cache.rb +22 -12
  198. data/lib/active_record/store.rb +3 -1
  199. data/lib/active_record/suppressor.rb +2 -0
  200. data/lib/active_record/table_metadata.rb +12 -3
  201. data/lib/active_record/tasks/database_tasks.rb +26 -15
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  204. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  205. data/lib/active_record/timestamp.rb +5 -12
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +9 -7
  208. data/lib/active_record/translation.rb +2 -0
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +2 -4
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type.rb +4 -1
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +3 -1
  224. data/lib/active_record/type_caster.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +35 -5
  230. data/lib/active_record/validations.rb +2 -0
  231. data/lib/active_record/version.rb +2 -0
  232. data/lib/active_record.rb +11 -4
  233. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  234. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/migration.rb +2 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  240. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record.rb +3 -1
  243. metadata +24 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  252. data/lib/active_record/attribute.rb +0 -240
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  254. data/lib/active_record/attribute_set/builder.rb +0 -126
  255. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  256. data/lib/active_record/attribute_set.rb +0 -113
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Calculations
3
5
  # Count the records.
@@ -37,7 +39,16 @@ module ActiveRecord
37
39
  # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
38
40
  # between databases. In invalid cases, an error from the database is thrown.
39
41
  def count(column_name = nil)
40
- return super() if block_given?
42
+ if block_given?
43
+ unless column_name.nil?
44
+ ActiveSupport::Deprecation.warn \
45
+ "When `count' is called with a block, it ignores other arguments. " \
46
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
47
+ end
48
+
49
+ return super()
50
+ end
51
+
41
52
  calculate(:count, column_name)
42
53
  end
43
54
 
@@ -73,7 +84,16 @@ module ActiveRecord
73
84
  #
74
85
  # Person.sum(:age) # => 4562
75
86
  def sum(column_name = nil)
76
- return super() if block_given?
87
+ if block_given?
88
+ unless column_name.nil?
89
+ ActiveSupport::Deprecation.warn \
90
+ "When `sum' is called with a block, it ignores other arguments. " \
91
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
92
+ end
93
+
94
+ return super()
95
+ end
96
+
77
97
  calculate(:sum, column_name)
78
98
  end
79
99
 
@@ -110,7 +130,7 @@ module ActiveRecord
110
130
  # end
111
131
  def calculate(operation, column_name)
112
132
  if has_include?(column_name)
113
- relation = construct_relation_for_association_calculations
133
+ relation = apply_join_dependency
114
134
 
115
135
  if operation.to_s.downcase == "count"
116
136
  relation.distinct!
@@ -167,13 +187,15 @@ module ActiveRecord
167
187
  end
168
188
 
169
189
  if has_include?(column_names.first)
170
- construct_relation_for_association_calculations.pluck(*column_names)
190
+ relation = apply_join_dependency
191
+ relation.pluck(*column_names)
171
192
  else
193
+ enforce_raw_sql_whitelist(column_names)
172
194
  relation = spawn
173
195
  relation.select_values = column_names.map { |cn|
174
196
  @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
175
197
  }
176
- result = klass.connection.select_all(relation.arel, nil, bound_attributes)
198
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
177
199
  result.cast_values(klass.attribute_types)
178
200
  end
179
201
  end
@@ -220,7 +242,7 @@ module ActiveRecord
220
242
  def aggregate_column(column_name)
221
243
  return column_name if Arel::Expressions === column_name
222
244
 
223
- if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
245
+ if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
224
246
  @klass.arel_attribute(column_name)
225
247
  else
226
248
  Arel.sql(column_name == :all ? "*" : column_name.to_s)
@@ -257,7 +279,7 @@ module ActiveRecord
257
279
  query_builder = relation.arel
258
280
  end
259
281
 
260
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
282
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
261
283
  row = result.first
262
284
  value = row && row.values.first
263
285
  type = result.column_types.fetch(column_alias) do
@@ -308,7 +330,7 @@ module ActiveRecord
308
330
  relation.group_values = group_fields
309
331
  relation.select_values = select_values
310
332
 
311
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
333
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
312
334
 
313
335
  if association
314
336
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Delegation # :nodoc:
3
5
  module DelegateCache # :nodoc:
@@ -36,13 +38,12 @@ module ActiveRecord
36
38
  # may vary depending on the klass of a relation, so we create a subclass of Relation
37
39
  # for each different klass, and the delegations are compiled into that subclass only.
38
40
 
39
- delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
40
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
41
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
42
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
41
43
  :to_sentence, :to_formatted_s, :as_json,
42
- :shuffle, :split, :index, to: :records
44
+ :shuffle, :split, :slice, :index, :rindex, to: :records
43
45
 
44
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
45
- :connection, :columns_hash, to: :klass
46
+ delegate :primary_key, :connection, to: :klass
46
47
 
47
48
  module ClassSpecificRelation # :nodoc:
48
49
  extend ActiveSupport::Concern
@@ -73,13 +74,6 @@ module ActiveRecord
73
74
  end
74
75
  end
75
76
  end
76
-
77
- def delegate(method, opts = {})
78
- @delegation_mutex.synchronize do
79
- return if method_defined?(method)
80
- super
81
- end
82
- end
83
77
  end
84
78
 
85
79
  private
@@ -88,8 +82,14 @@ module ActiveRecord
88
82
  if @klass.respond_to?(method)
89
83
  self.class.delegate_to_scoped_klass(method)
90
84
  scoping { @klass.public_send(method, *args, &block) }
85
+ elsif @delegate_to_klass && @klass.respond_to?(method, true)
86
+ ActiveSupport::Deprecation.warn \
87
+ "Delegating missing #{method} method to #{@klass}. " \
88
+ "Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
89
+ @klass.send(method, *args, &block)
91
90
  elsif arel.respond_to?(method)
92
- self.class.delegate method, to: :arel
91
+ ActiveSupport::Deprecation.warn \
92
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
93
93
  arel.public_send(method, *args, &block)
94
94
  else
95
95
  super
@@ -109,21 +109,9 @@ module ActiveRecord
109
109
  end
110
110
  end
111
111
 
112
- def respond_to_missing?(method, include_private = false)
113
- super || @klass.respond_to?(method, include_private) ||
114
- arel.respond_to?(method, include_private)
115
- end
116
-
117
112
  private
118
-
119
- def method_missing(method, *args, &block)
120
- if @klass.respond_to?(method)
121
- scoping { @klass.public_send(method, *args, &block) }
122
- elsif arel.respond_to?(method)
123
- arel.public_send(method, *args, &block)
124
- else
125
- super
126
- end
113
+ def respond_to_missing?(method, _)
114
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
127
115
  end
128
116
  end
129
117
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
@@ -16,9 +18,10 @@ module ActiveRecord
16
18
  # Person.find([1]) # returns an array for the object with ID = 1
17
19
  # Person.where("administrator = 1").order("created_on DESC").find(1)
18
20
  #
19
- # NOTE: The returned records may not be in the same order as the ids you
20
- # provide since database rows are unordered. You will need to provide an explicit QueryMethods#order
21
- # option if you want the results to be sorted.
21
+ # NOTE: The returned records are in the same order as the ids you provide.
22
+ # If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
23
+ # method and provide an explicit ActiveRecord::QueryMethods#order option.
24
+ # But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
22
25
  #
23
26
  # ==== Find with lock
24
27
  #
@@ -86,7 +89,7 @@ module ActiveRecord
86
89
  where(arg, *args).take!
87
90
  rescue ::RangeError
88
91
  raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
- @klass.name)
92
+ @klass.name, @klass.primary_key)
90
93
  end
91
94
 
92
95
  # Gives a record (or N records if a parameter is supplied) without any implied
@@ -145,10 +148,9 @@ module ActiveRecord
145
148
  #
146
149
  # [#<Person id:4>, #<Person id:3>, #<Person id:2>]
147
150
  def last(limit = nil)
148
- return find_last(limit) if loaded? || limit_value
151
+ return find_last(limit) if loaded? || has_limit_or_offset?
149
152
 
150
- result = limit(limit)
151
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
153
+ result = ordered_relation.limit(limit)
152
154
  result = result.reverse_order!
153
155
 
154
156
  limit ? result.reverse : result.first
@@ -283,7 +285,7 @@ module ActiveRecord
283
285
  # * Hash - Finds the record that matches these +find+-style conditions
284
286
  # (such as <tt>{name: 'David'}</tt>).
285
287
  # * +false+ - Returns always +false+.
286
- # * No args - Returns +false+ if the table is empty, +true+ otherwise.
288
+ # * No args - Returns +false+ if the relation is empty, +true+ otherwise.
287
289
  #
288
290
  # For more information about specifying conditions as a hash or array,
289
291
  # see the Conditions section in the introduction to ActiveRecord::Base.
@@ -299,6 +301,7 @@ module ActiveRecord
299
301
  # Person.exists?(name: 'David')
300
302
  # Person.exists?(false)
301
303
  # Person.exists?
304
+ # Person.where(name: 'Spartacus', rating: 4).exists?
302
305
  def exists?(conditions = :none)
303
306
  if Base === conditions
304
307
  raise ArgumentError, <<-MSG.squish
@@ -309,14 +312,14 @@ module ActiveRecord
309
312
 
310
313
  return false if !conditions || limit_value == 0
311
314
 
312
- relation = self unless eager_loading?
313
- relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
314
-
315
- return false if ActiveRecord::NullRelation === relation
315
+ if eager_loading?
316
+ relation = apply_join_dependency(eager_loading: false)
317
+ return relation.exists?(conditions)
318
+ end
316
319
 
317
- relation = construct_relation_for_exists(relation, conditions)
320
+ relation = construct_relation_for_exists(conditions)
318
321
 
319
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
322
+ skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
320
323
  rescue ::RangeError
321
324
  false
322
325
  end
@@ -329,23 +332,23 @@ module ActiveRecord
329
332
  # of results obtained should be provided in the +result_size+ argument and
330
333
  # the expected number of results should be provided in the +expected_size+
331
334
  # argument.
332
- def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
333
- conditions = arel.where_sql(@klass.arel_engine)
335
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
336
+ conditions = arel.where_sql(@klass)
334
337
  conditions = " [#{conditions}]" if conditions
335
338
  name = @klass.name
336
339
 
337
340
  if ids.nil?
338
- error = "Couldn't find #{name}"
341
+ error = "Couldn't find #{name}".dup
339
342
  error << " with#{conditions}" if conditions
340
- raise RecordNotFound.new(error, name)
343
+ raise RecordNotFound.new(error, name, key)
341
344
  elsif Array(ids).size == 1
342
345
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
343
346
  raise RecordNotFound.new(error, name, key, ids)
344
347
  else
345
- error = "Couldn't find all #{name.pluralize} with '#{key}': "
346
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
347
-
348
- raise RecordNotFound.new(error, name, primary_key, ids)
348
+ error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
349
+ error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
350
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
351
+ raise RecordNotFound.new(error, name, key, ids)
349
352
  end
350
353
  end
351
354
 
@@ -355,31 +358,12 @@ module ActiveRecord
355
358
  offset_value || 0
356
359
  end
357
360
 
358
- def find_with_associations
359
- # NOTE: the JoinDependency constructed here needs to know about
360
- # any joins already present in `self`, so pass them in
361
- #
362
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
363
- # incorrect SQL is generated. In that case, the join dependency for
364
- # SpecialCategorizations is constructed without knowledge of the
365
- # preexisting join in joins_values to categorizations (by way of
366
- # the `has_many :through` for categories).
367
- #
368
- join_dependency = construct_join_dependency(joins_values)
369
-
370
- aliases = join_dependency.aliases
371
- relation = select aliases.columns
372
- relation = apply_join_dependency(relation, join_dependency)
373
-
374
- yield relation, join_dependency
375
- end
376
-
377
- def construct_relation_for_exists(relation, conditions)
378
- relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
361
+ def construct_relation_for_exists(conditions)
362
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
379
363
 
380
364
  case conditions
381
365
  when Array, Hash
382
- relation.where!(conditions) unless conditions.empty?
366
+ relation.where!(conditions)
383
367
  else
384
368
  relation.where!(primary_key => conditions) unless conditions == :none
385
369
  end
@@ -387,37 +371,43 @@ module ActiveRecord
387
371
  relation
388
372
  end
389
373
 
390
- def construct_join_dependency(joins = [], eager_loading: true)
374
+ def construct_join_dependency
391
375
  including = eager_load_values + includes_values
392
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
393
- end
394
-
395
- def construct_relation_for_association_calculations
396
- apply_join_dependency(self, construct_join_dependency(joins_values))
376
+ joins = joins_values.select { |join| join.is_a?(Arel::Nodes::Join) }
377
+ ActiveRecord::Associations::JoinDependency.new(
378
+ klass, table, including, alias_tracker(joins)
379
+ )
397
380
  end
398
381
 
399
- def apply_join_dependency(relation, join_dependency)
400
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
382
+ def apply_join_dependency(eager_loading: true)
383
+ join_dependency = construct_join_dependency
384
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
401
385
 
402
- if using_limitable_reflections?(join_dependency.reflections)
403
- relation
404
- else
405
- if relation.limit_value
386
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
387
+ if has_limit_or_offset?
406
388
  limited_ids = limited_ids_for(relation)
407
389
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
408
390
  end
409
- relation.except(:limit, :offset)
391
+ relation.limit_value = relation.offset_value = nil
392
+ end
393
+
394
+ if block_given?
395
+ relation._select!(join_dependency.aliases.columns)
396
+ yield relation, join_dependency
397
+ else
398
+ relation
410
399
  end
411
400
  end
412
401
 
413
402
  def limited_ids_for(relation)
414
403
  values = @klass.connection.columns_for_distinct(
415
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
404
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
405
+ relation.order_values
406
+ )
416
407
 
417
408
  relation = relation.except(:select).select(values).distinct!
418
- arel = relation.arel
419
409
 
420
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
410
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
421
411
  id_rows.map { |row| row[primary_key] }
422
412
  end
423
413
 
@@ -429,13 +419,16 @@ module ActiveRecord
429
419
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
430
420
 
431
421
  expects_array = ids.first.kind_of?(Array)
432
- return [] if expects_array && ids.first.empty?
422
+ return ids.first if expects_array && ids.first.empty?
433
423
 
434
424
  ids = ids.flatten.compact.uniq
435
425
 
426
+ model_name = @klass.name
427
+
436
428
  case ids.size
437
429
  when 0
438
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
430
+ error_message = "Couldn't find #{model_name} without an ID"
431
+ raise RecordNotFound.new(error_message, model_name, primary_key)
439
432
  when 1
440
433
  result = find_one(ids.first)
441
434
  expects_array ? [ result ] : result
@@ -443,7 +436,8 @@ module ActiveRecord
443
436
  find_some(ids)
444
437
  end
445
438
  rescue ::RangeError
446
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
439
+ error_message = "Couldn't find #{model_name} with an out of range ID"
440
+ raise RecordNotFound.new(error_message, model_name, primary_key, ids)
447
441
  end
448
442
 
449
443
  def find_one(id)
@@ -525,13 +519,13 @@ module ActiveRecord
525
519
  if loaded?
526
520
  records[index, limit] || []
527
521
  else
528
- relation = if order_values.empty? && primary_key
529
- order(arel_attribute(primary_key).asc)
530
- else
531
- self
522
+ relation = ordered_relation
523
+
524
+ if limit_value
525
+ limit = [limit_value - index, limit].min
532
526
  end
533
527
 
534
- if limit_value.nil? || index < limit_value
528
+ if limit > 0
535
529
  relation = relation.offset(offset_index + index) unless index.zero?
536
530
  relation.limit(limit).to_a
537
531
  else
@@ -544,23 +538,26 @@ module ActiveRecord
544
538
  if loaded?
545
539
  records[-index]
546
540
  else
547
- relation = if order_values.empty? && primary_key
548
- order(arel_attribute(primary_key).asc)
541
+ relation = ordered_relation
542
+
543
+ if equal?(relation) || has_limit_or_offset?
544
+ relation.records[-index]
549
545
  else
550
- self
546
+ relation.last(index)[-index]
551
547
  end
552
-
553
- relation.to_a[-index]
554
- # TODO: can be made more performant on large result sets by
555
- # for instance, last(index)[-index] (which would require
556
- # refactoring the last(n) finder method to make test suite pass),
557
- # or by using a combination of reverse_order, limit, and offset,
558
- # e.g., reverse_order.offset(index-1).first
559
548
  end
560
549
  end
561
550
 
562
551
  def find_last(limit)
563
552
  limit ? records.last(limit) : records.last
564
553
  end
554
+
555
+ def ordered_relation
556
+ if order_values.empty? && primary_key
557
+ order(arel_attribute(primary_key).asc)
558
+ else
559
+ self
560
+ end
561
+ end
565
562
  end
566
563
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class Relation
3
5
  class FromClause # :nodoc:
@@ -8,14 +10,6 @@ module ActiveRecord
8
10
  @name = name
9
11
  end
10
12
 
11
- def binds
12
- if value.is_a?(Relation)
13
- value.bound_attributes
14
- else
15
- []
16
- end
17
- end
18
-
19
13
  def merge(other)
20
14
  self
21
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash/keys"
2
4
 
3
5
  module ActiveRecord
@@ -21,7 +23,11 @@ module ActiveRecord
21
23
  # build a relation to merge in rather than directly merging
22
24
  # the values.
23
25
  def other
24
- other = Relation.create(relation.klass, relation.table, relation.predicate_builder)
26
+ other = Relation.create(
27
+ relation.klass,
28
+ table: relation.table,
29
+ predicate_builder: relation.predicate_builder
30
+ )
25
31
  hash.each { |k, v|
26
32
  if k == :joins
27
33
  if Hash === v
@@ -50,7 +56,7 @@ module ActiveRecord
50
56
 
51
57
  NORMAL_VALUES = Relation::VALUE_METHODS -
52
58
  Relation::CLAUSE_METHODS -
53
- [:includes, :preload, :joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
59
+ [:includes, :preload, :joins, :left_outer_joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
54
60
 
55
61
  def normal_values
56
62
  NORMAL_VALUES
@@ -77,6 +83,7 @@ module ActiveRecord
77
83
  merge_clauses
78
84
  merge_preloads
79
85
  merge_joins
86
+ merge_outer_joins
80
87
 
81
88
  relation
82
89
  end
@@ -110,21 +117,43 @@ module ActiveRecord
110
117
  if other.klass == relation.klass
111
118
  relation.joins!(*other.joins_values)
112
119
  else
113
- joins_dependency, rest = other.joins_values.partition do |join|
120
+ alias_tracker = nil
121
+ joins_dependency = other.joins_values.map do |join|
114
122
  case join
115
123
  when Hash, Symbol, Array
116
- true
124
+ alias_tracker ||= other.alias_tracker
125
+ ActiveRecord::Associations::JoinDependency.new(
126
+ other.klass, other.table, join, alias_tracker
127
+ )
117
128
  else
118
- false
129
+ join
119
130
  end
120
131
  end
121
132
 
122
- join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
123
- joins_dependency,
124
- [])
125
- relation.joins! rest
133
+ relation.joins!(*joins_dependency)
134
+ end
135
+ end
126
136
 
127
- @relation = relation.joins join_dependency
137
+ def merge_outer_joins
138
+ return if other.left_outer_joins_values.blank?
139
+
140
+ if other.klass == relation.klass
141
+ relation.left_outer_joins!(*other.left_outer_joins_values)
142
+ else
143
+ alias_tracker = nil
144
+ joins_dependency = other.left_outer_joins_values.map do |join|
145
+ case join
146
+ when Hash, Symbol, Array
147
+ alias_tracker ||= other.alias_tracker
148
+ ActiveRecord::Associations::JoinDependency.new(
149
+ other.klass, other.table, join, alias_tracker
150
+ )
151
+ else
152
+ join
153
+ end
154
+ end
155
+
156
+ relation.left_outer_joins!(*joins_dependency)
128
157
  end
129
158
  end
130
159
 
@@ -132,19 +161,17 @@ module ActiveRecord
132
161
  if other.reordering_value
133
162
  # override any order specified in the original relation
134
163
  relation.reorder! other.order_values
135
- elsif other.order_values
164
+ elsif other.order_values.any?
136
165
  # merge in order_values from relation
137
166
  relation.order! other.order_values
138
167
  end
139
168
 
140
- relation.extend(*other.extending_values) unless other.extending_values.blank?
169
+ extensions = other.extensions - relation.extensions
170
+ relation.extending!(*extensions) if extensions.any?
141
171
  end
142
172
 
143
173
  def merge_single_values
144
- if relation.from_clause.empty?
145
- relation.from_clause = other.from_clause
146
- end
147
- relation.lock_value ||= other.lock_value
174
+ relation.lock_value ||= other.lock_value if other.lock_value
148
175
 
149
176
  unless other.create_with_value.blank?
150
177
  relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
@@ -152,11 +179,15 @@ module ActiveRecord
152
179
  end
153
180
 
154
181
  def merge_clauses
155
- CLAUSE_METHODS.each do |method|
156
- clause = relation.get_value(method)
157
- other_clause = other.get_value(method)
158
- relation.set_value(method, clause.merge(other_clause))
182
+ if relation.from_clause.empty? && !other.from_clause.empty?
183
+ relation.from_clause = other.from_clause
159
184
  end
185
+
186
+ where_clause = relation.where_clause.merge(other.where_clause)
187
+ relation.where_clause = where_clause unless where_clause.empty?
188
+
189
+ having_clause = relation.having_clause.merge(other.having_clause)
190
+ relation.having_clause = having_clause unless having_clause.empty?
160
191
  end
161
192
  end
162
193
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class ArrayHandler # :nodoc:
@@ -6,18 +8,21 @@ module ActiveRecord
6
8
  end
7
9
 
8
10
  def call(attribute, value)
11
+ return attribute.in([]) if value.empty?
12
+
9
13
  values = value.map { |x| x.is_a?(Base) ? x.id : x }
10
14
  nils, values = values.partition(&:nil?)
11
-
12
- return attribute.in([]) if values.empty? && nils.empty?
13
-
14
15
  ranges, values = values.partition { |v| v.is_a?(Range) }
15
16
 
16
17
  values_predicate =
17
18
  case values.length
18
19
  when 0 then NullPredicate
19
20
  when 1 then predicate_builder.build(attribute, values.first)
20
- else attribute.in(values)
21
+ else
22
+ bind_values = values.map do |v|
23
+ predicate_builder.build_bind_attribute(attribute.name, v)
24
+ end
25
+ attribute.in(bind_values)
21
26
  end
22
27
 
23
28
  unless nils.empty?
@@ -26,11 +31,9 @@ module ActiveRecord
26
31
 
27
32
  array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
28
33
  array_predicates.unshift(values_predicate)
29
- array_predicates.inject { |composite, predicate| composite.or(predicate) }
34
+ array_predicates.inject(&:or)
30
35
  end
31
36
 
32
- # TODO Change this to private once we've dropped Ruby 2.2 support.
33
- # Workaround for Ruby 2.2 "private attribute?" warning.
34
37
  protected
35
38
 
36
39
  attr_reader :predicate_builder