activerecord 5.1.7 → 5.2.6

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 +583 -673
  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 +7 -5
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +41 -37
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +27 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +12 -4
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  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 +59 -47
  22. data/lib/active_record/associations/collection_proxy.rb +20 -49
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +12 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +36 -30
  26. data/lib/active_record/associations/has_one_association.rb +12 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  31. data/lib/active_record/associations/join_dependency.rb +48 -93
  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 +18 -38
  35. data/lib/active_record/associations/singular_association.rb +14 -16
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +40 -63
  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 +30 -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 +9 -3
  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 +35 -19
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -6
  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 +12 -8
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
  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 +64 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  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 +13 -2
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -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 +2 -0
  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 +5 -3
  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 -2
  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 +50 -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 +233 -111
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  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 +22 -0
  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 +75 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +81 -94
  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 +41 -61
  129. data/lib/active_record/counter_cache.rb +10 -3
  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 +18 -13
  133. data/lib/active_record/errors.rb +42 -3
  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 +47 -9
  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 +167 -16
  156. data/lib/active_record/query_cache.rb +6 -8
  157. data/lib/active_record/querying.rb +4 -2
  158. data/lib/active_record/railtie.rb +62 -6
  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 +108 -194
  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 +45 -19
  167. data/lib/active_record/relation/delegation.rb +45 -27
  168. data/lib/active_record/relation/finder_methods.rb +75 -76
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +53 -23
  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 +60 -79
  179. data/lib/active_record/relation/query_attribute.rb +28 -2
  180. data/lib/active_record/relation/query_methods.rb +128 -99
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +4 -2
  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 +120 -214
  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 +8 -9
  193. data/lib/active_record/scoping/named.rb +23 -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 +23 -13
  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 +25 -14
  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 +6 -6
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +33 -28
  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 -0
  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 +23 -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,14 +130,15 @@ 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
- relation.distinct!
117
- # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
118
- if (column_name == :all || column_name.nil?) && select_values.empty?
119
- relation.order_values = []
136
+ unless distinct_value || distinct_select?(column_name || select_for_count)
137
+ relation.distinct!
138
+ relation.select_values = [ klass.primary_key || table[Arel.star] ]
120
139
  end
140
+ # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
141
+ relation.order_values = []
121
142
  end
122
143
 
123
144
  relation.calculate(operation, column_name)
@@ -167,13 +188,13 @@ module ActiveRecord
167
188
  end
168
189
 
169
190
  if has_include?(column_names.first)
170
- construct_relation_for_association_calculations.pluck(*column_names)
191
+ relation = apply_join_dependency
192
+ relation.pluck(*column_names)
171
193
  else
194
+ klass.enforce_raw_sql_whitelist(column_names)
172
195
  relation = spawn
173
- relation.select_values = column_names.map { |cn|
174
- @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
175
- }
176
- result = klass.connection.select_all(relation.arel, nil, bound_attributes)
196
+ relation.select_values = column_names
197
+ result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
177
198
  result.cast_values(klass.attribute_types)
178
199
  end
179
200
  end
@@ -187,7 +208,6 @@ module ActiveRecord
187
208
  end
188
209
 
189
210
  private
190
-
191
211
  def has_include?(column_name)
192
212
  eager_loading? || (includes_values.present? && column_name && column_name != :all)
193
213
  end
@@ -202,10 +222,12 @@ module ActiveRecord
202
222
  if operation == "count"
203
223
  column_name ||= select_for_count
204
224
  if column_name == :all
205
- if distinct && (group_values.any? || select_values.empty? && order_values.empty?)
225
+ if !distinct
226
+ distinct = distinct_select?(select_for_count) if group_values.empty?
227
+ elsif group_values.any? || select_values.empty? && order_values.empty?
206
228
  column_name = primary_key
207
229
  end
208
- elsif column_name =~ /\s*DISTINCT[\s(]+/i
230
+ elsif distinct_select?(column_name)
209
231
  distinct = nil
210
232
  end
211
233
  end
@@ -217,10 +239,14 @@ module ActiveRecord
217
239
  end
218
240
  end
219
241
 
242
+ def distinct_select?(column_name)
243
+ column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
244
+ end
245
+
220
246
  def aggregate_column(column_name)
221
247
  return column_name if Arel::Expressions === column_name
222
248
 
223
- if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
249
+ if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
224
250
  @klass.arel_attribute(column_name)
225
251
  else
226
252
  Arel.sql(column_name == :all ? "*" : column_name.to_s)
@@ -257,7 +283,7 @@ module ActiveRecord
257
283
  query_builder = relation.arel
258
284
  end
259
285
 
260
- result = @klass.connection.select_all(query_builder, nil, bound_attributes)
286
+ result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
261
287
  row = result.first
262
288
  value = row && row.values.first
263
289
  type = result.column_types.fetch(column_alias) do
@@ -308,7 +334,7 @@ module ActiveRecord
308
334
  relation.group_values = group_fields
309
335
  relation.select_values = select_values
310
336
 
311
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
337
+ calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
312
338
 
313
339
  if association
314
340
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -361,7 +387,7 @@ module ActiveRecord
361
387
  case operation
362
388
  when "count" then value.to_i
363
389
  when "sum" then type.deserialize(value || 0)
364
- when "average" then value.respond_to?(:to_d) ? value.to_d : value
390
+ when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
365
391
  else type.deserialize(value)
366
392
  end
367
393
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Delegation # :nodoc:
3
5
  module DelegateCache # :nodoc:
@@ -15,6 +17,7 @@ module ActiveRecord
15
17
  delegate = Class.new(klass) {
16
18
  include ClassSpecificRelation
17
19
  }
20
+ include_relation_methods(delegate)
18
21
  mangled_name = klass.name.gsub("::".freeze, "_".freeze)
19
22
  const_set mangled_name, delegate
20
23
  private_constant mangled_name
@@ -27,6 +30,35 @@ module ActiveRecord
27
30
  child_class.initialize_relation_delegate_cache
28
31
  super
29
32
  end
33
+
34
+ protected
35
+ def include_relation_methods(delegate)
36
+ superclass.include_relation_methods(delegate) unless base_class == self
37
+ delegate.include generated_relation_methods
38
+ end
39
+
40
+ private
41
+ def generated_relation_methods
42
+ @generated_relation_methods ||= Module.new.tap do |mod|
43
+ mod_name = "GeneratedRelationMethods"
44
+ const_set mod_name, mod
45
+ private_constant mod_name
46
+ end
47
+ end
48
+
49
+ def generate_relation_method(method)
50
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
51
+ generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
52
+ def #{method}(*args, &block)
53
+ scoping { klass.#{method}(*args, &block) }
54
+ end
55
+ RUBY
56
+ else
57
+ generated_relation_methods.send(:define_method, method) do |*args, &block|
58
+ scoping { klass.public_send(method, *args, &block) }
59
+ end
60
+ end
61
+ end
30
62
  end
31
63
 
32
64
  extend ActiveSupport::Concern
@@ -36,13 +68,12 @@ module ActiveRecord
36
68
  # may vary depending on the klass of a relation, so we create a subclass of Relation
37
69
  # for each different klass, and the delegations are compiled into that subclass only.
38
70
 
39
- delegate :to_xml, :encode_with, :length, :each, :uniq, :to_ary, :join,
40
- :[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
71
+ delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
72
+ :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
41
73
  :to_sentence, :to_formatted_s, :as_json,
42
- :shuffle, :split, :index, to: :records
74
+ :shuffle, :split, :slice, :index, :rindex, to: :records
43
75
 
44
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
45
- :connection, :columns_hash, to: :klass
76
+ delegate :primary_key, :connection, to: :klass
46
77
 
47
78
  module ClassSpecificRelation # :nodoc:
48
79
  extend ActiveSupport::Concern
@@ -73,13 +104,6 @@ module ActiveRecord
73
104
  end
74
105
  end
75
106
  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
107
  end
84
108
 
85
109
  private
@@ -88,8 +112,14 @@ module ActiveRecord
88
112
  if @klass.respond_to?(method)
89
113
  self.class.delegate_to_scoped_klass(method)
90
114
  scoping { @klass.public_send(method, *args, &block) }
115
+ elsif @delegate_to_klass && @klass.respond_to?(method, true)
116
+ ActiveSupport::Deprecation.warn \
117
+ "Delegating missing #{method} method to #{@klass}. " \
118
+ "Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
119
+ @klass.send(method, *args, &block)
91
120
  elsif arel.respond_to?(method)
92
- self.class.delegate method, to: :arel
121
+ ActiveSupport::Deprecation.warn \
122
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
93
123
  arel.public_send(method, *args, &block)
94
124
  else
95
125
  super
@@ -109,21 +139,9 @@ module ActiveRecord
109
139
  end
110
140
  end
111
141
 
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
142
  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
143
+ def respond_to_missing?(method, _)
144
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
127
145
  end
128
146
  end
129
147
  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_one(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,27 +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
+ if distinct_value && offset_value
363
+ relation = except(:order).limit!(1)
364
+ else
365
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
366
+ end
379
367
 
380
368
  case conditions
381
369
  when Array, Hash
@@ -387,37 +375,41 @@ module ActiveRecord
387
375
  relation
388
376
  end
389
377
 
390
- def construct_join_dependency(joins = [], eager_loading: true)
378
+ def construct_join_dependency
391
379
  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))
380
+ ActiveRecord::Associations::JoinDependency.new(
381
+ klass, table, including
382
+ )
397
383
  end
398
384
 
399
- def apply_join_dependency(relation, join_dependency)
400
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
385
+ def apply_join_dependency(eager_loading: group_values.empty?)
386
+ join_dependency = construct_join_dependency
387
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
401
388
 
402
- if using_limitable_reflections?(join_dependency.reflections)
403
- relation
404
- else
405
- if relation.limit_value
389
+ if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
390
+ if has_limit_or_offset?
406
391
  limited_ids = limited_ids_for(relation)
407
392
  limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
408
393
  end
409
- relation.except(:limit, :offset)
394
+ relation.limit_value = relation.offset_value = nil
395
+ end
396
+
397
+ if block_given?
398
+ yield relation, join_dependency
399
+ else
400
+ relation
410
401
  end
411
402
  end
412
403
 
413
404
  def limited_ids_for(relation)
414
405
  values = @klass.connection.columns_for_distinct(
415
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
406
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
407
+ relation.order_values
408
+ )
416
409
 
417
410
  relation = relation.except(:select).select(values).distinct!
418
- arel = relation.arel
419
411
 
420
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
412
+ id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
421
413
  id_rows.map { |row| row[primary_key] }
422
414
  end
423
415
 
@@ -433,9 +425,12 @@ module ActiveRecord
433
425
 
434
426
  ids = ids.flatten.compact.uniq
435
427
 
428
+ model_name = @klass.name
429
+
436
430
  case ids.size
437
431
  when 0
438
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
432
+ error_message = "Couldn't find #{model_name} without an ID"
433
+ raise RecordNotFound.new(error_message, model_name, primary_key)
439
434
  when 1
440
435
  result = find_one(ids.first)
441
436
  expects_array ? [ result ] : result
@@ -443,7 +438,8 @@ module ActiveRecord
443
438
  find_some(ids)
444
439
  end
445
440
  rescue ::RangeError
446
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
441
+ error_message = "Couldn't find #{model_name} with an out of range ID"
442
+ raise RecordNotFound.new(error_message, model_name, primary_key, ids)
447
443
  end
448
444
 
449
445
  def find_one(id)
@@ -525,13 +521,13 @@ module ActiveRecord
525
521
  if loaded?
526
522
  records[index, limit] || []
527
523
  else
528
- relation = if order_values.empty? && primary_key
529
- order(arel_attribute(primary_key).asc)
530
- else
531
- self
524
+ relation = ordered_relation
525
+
526
+ if limit_value
527
+ limit = [limit_value - index, limit].min
532
528
  end
533
529
 
534
- if limit_value.nil? || index < limit_value
530
+ if limit > 0
535
531
  relation = relation.offset(offset_index + index) unless index.zero?
536
532
  relation.limit(limit).to_a
537
533
  else
@@ -544,23 +540,26 @@ module ActiveRecord
544
540
  if loaded?
545
541
  records[-index]
546
542
  else
547
- relation = if order_values.empty? && primary_key
548
- order(arel_attribute(primary_key).asc)
543
+ relation = ordered_relation
544
+
545
+ if equal?(relation) || has_limit_or_offset?
546
+ relation.records[-index]
549
547
  else
550
- self
548
+ relation.last(index)[-index]
551
549
  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
550
  end
560
551
  end
561
552
 
562
553
  def find_last(limit)
563
554
  limit ? records.last(limit) : records.last
564
555
  end
556
+
557
+ def ordered_relation
558
+ if order_values.empty? && primary_key
559
+ order(arel_attribute(primary_key).asc)
560
+ else
561
+ self
562
+ end
563
+ end
565
564
  end
566
565
  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