activerecord 5.1.7 → 5.2.0.beta1

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 (259) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +221 -900
  3. data/README.rdoc +3 -3
  4. data/examples/performance.rb +2 -0
  5. data/examples/simple.rb +2 -0
  6. data/lib/active_record.rb +10 -3
  7. data/lib/active_record/aggregations.rb +2 -0
  8. data/lib/active_record/association_relation.rb +2 -0
  9. data/lib/active_record/associations.rb +13 -42
  10. data/lib/active_record/associations/alias_tracker.rb +17 -17
  11. data/lib/active_record/associations/association.rb +11 -22
  12. data/lib/active_record/associations/association_scope.rb +32 -44
  13. data/lib/active_record/associations/belongs_to_association.rb +6 -4
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -1
  15. data/lib/active_record/associations/builder/association.rb +2 -5
  16. data/lib/active_record/associations/builder/belongs_to.rb +7 -12
  17. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  19. data/lib/active_record/associations/builder/has_many.rb +2 -0
  20. data/lib/active_record/associations/builder/has_one.rb +2 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  22. data/lib/active_record/associations/collection_association.rb +41 -33
  23. data/lib/active_record/associations/collection_proxy.rb +11 -14
  24. data/lib/active_record/associations/foreign_association.rb +2 -0
  25. data/lib/active_record/associations/has_many_association.rb +4 -2
  26. data/lib/active_record/associations/has_many_through_association.rb +4 -2
  27. data/lib/active_record/associations/has_one_association.rb +3 -1
  28. data/lib/active_record/associations/has_one_through_association.rb +3 -1
  29. data/lib/active_record/associations/join_dependency.rb +22 -40
  30. data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
  31. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  33. data/lib/active_record/associations/preloader.rb +17 -37
  34. data/lib/active_record/associations/preloader/association.rb +42 -58
  35. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  36. data/lib/active_record/associations/singular_association.rb +14 -10
  37. data/lib/active_record/associations/through_association.rb +3 -1
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods.rb +47 -7
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  42. data/lib/active_record/attribute_methods/dirty.rb +25 -214
  43. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  44. data/lib/active_record/attribute_methods/query.rb +2 -0
  45. data/lib/active_record/attribute_methods/read.rb +8 -2
  46. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  48. data/lib/active_record/attribute_methods/write.rb +21 -9
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +5 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +6 -8
  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 +10 -5
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +120 -28
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -33
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +13 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +40 -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 +103 -63
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +62 -90
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +75 -138
  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 +3 -1
  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 -6
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +91 -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.rb +2 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -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 +3 -1
  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 +3 -5
  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/quoting.rb +10 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +2 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +11 -7
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +79 -65
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +47 -82
  116. data/lib/active_record/connection_adapters/schema_cache.rb +2 -0
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +19 -2
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -89
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +27 -57
  128. data/lib/active_record/counter_cache.rb +15 -12
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +15 -13
  132. data/lib/active_record/errors.rb +54 -21
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +40 -24
  138. data/lib/active_record/gem_version.rb +5 -3
  139. data/lib/active_record/inheritance.rb +6 -5
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +31 -20
  144. data/lib/active_record/locking/pessimistic.rb +10 -7
  145. data/lib/active_record/log_subscriber.rb +2 -0
  146. data/lib/active_record/migration.rb +47 -21
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +20 -2
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/model_schema.rb +29 -38
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +184 -40
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +3 -1
  157. data/lib/active_record/railtie.rb +54 -1
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +41 -28
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +100 -182
  163. data/lib/active_record/relation.rb +61 -193
  164. data/lib/active_record/relation/batches.rb +20 -5
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  166. data/lib/active_record/relation/calculations.rb +40 -23
  167. data/lib/active_record/relation/delegation.rb +10 -27
  168. data/lib/active_record/relation/finder_methods.rb +53 -49
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +22 -19
  171. data/lib/active_record/relation/predicate_builder.rb +42 -79
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  179. data/lib/active_record/relation/query_attribute.rb +9 -2
  180. data/lib/active_record/relation/query_methods.rb +80 -69
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +2 -0
  183. data/lib/active_record/relation/where_clause.rb +50 -67
  184. data/lib/active_record/relation/where_clause_factory.rb +4 -46
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +15 -9
  188. data/lib/active_record/schema.rb +3 -1
  189. data/lib/active_record/schema_dumper.rb +24 -23
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping.rb +9 -8
  192. data/lib/active_record/scoping/default.rb +6 -7
  193. data/lib/active_record/scoping/named.rb +15 -7
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +2 -0
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +3 -1
  200. data/lib/active_record/tasks/database_tasks.rb +23 -12
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -12
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type.rb +4 -1
  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_caster.rb +2 -0
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +2 -0
  224. data/lib/active_record/validations.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 +36 -6
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/rails/generators/active_record.rb +3 -1
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration.rb +2 -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/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. metadata +25 -38
  242. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  243. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  244. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  245. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  246. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  248. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  249. data/lib/active_record/attribute.rb +0 -240
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  252. data/lib/active_record/attribute_set.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -126
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  256. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  257. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  258. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  259. 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
  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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class PredicateBuilder
5
+ class AssociationQueryValue # :nodoc:
6
+ def initialize(associated_table, value)
7
+ @associated_table = associated_table
8
+ @value = value
9
+ end
10
+
11
+ def queries
12
+ [associated_table.association_join_foreign_key.to_s => ids]
13
+ end
14
+
15
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
16
+ # Workaround for Ruby 2.2 "private attribute?" warning.
17
+ protected
18
+ attr_reader :associated_table, :value
19
+
20
+ private
21
+ def ids
22
+ case value
23
+ when Relation
24
+ value.select_values.empty? ? value.select(primary_key) : value
25
+ when Array
26
+ value.map { |v| convert_to_id(v) }
27
+ else
28
+ convert_to_id(value)
29
+ end
30
+ end
31
+
32
+ def primary_key
33
+ associated_table.association_join_keys.key
34
+ end
35
+
36
+ def convert_to_id(value)
37
+ case value
38
+ when Base
39
+ value._read_attribute(primary_key)
40
+ else
41
+ value
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class BaseHandler # :nodoc:
@@ -9,8 +11,6 @@ module ActiveRecord
9
11
  predicate_builder.build(attribute, value.id)
10
12
  end
11
13
 
12
- # TODO Change this to private once we've dropped Ruby 2.2 support.
13
- # Workaround for Ruby 2.2 "private attribute?" warning.
14
14
  protected
15
15
 
16
16
  attr_reader :predicate_builder
@@ -1,9 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class BasicObjectHandler # :nodoc:
6
+ def initialize(predicate_builder)
7
+ @predicate_builder = predicate_builder
8
+ end
9
+
4
10
  def call(attribute, value)
5
- attribute.eq(value)
11
+ bind = predicate_builder.build_bind_attribute(attribute.name, value)
12
+ attribute.eq(bind)
6
13
  end
14
+
15
+ protected
16
+
17
+ attr_reader :predicate_builder
7
18
  end
8
19
  end
9
20
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class PredicateBuilder
5
+ class PolymorphicArrayValue # :nodoc:
6
+ def initialize(associated_table, values)
7
+ @associated_table = associated_table
8
+ @values = values
9
+ end
10
+
11
+ def queries
12
+ type_to_ids_mapping.map do |type, ids|
13
+ {
14
+ associated_table.association_foreign_type.to_s => type,
15
+ associated_table.association_foreign_key.to_s => ids
16
+ }
17
+ end
18
+ end
19
+
20
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
21
+ # Workaround for Ruby 2.2 "private attribute?" warning.
22
+ protected
23
+ attr_reader :associated_table, :values
24
+
25
+ private
26
+ def type_to_ids_mapping
27
+ default_hash = Hash.new { |hsh, key| hsh[key] = [] }
28
+ values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) }
29
+ end
30
+
31
+ def primary_key(value)
32
+ associated_table.association_primary_key(base_class(value))
33
+ end
34
+
35
+ def base_class(value)
36
+ case value
37
+ when Base
38
+ value.class.base_class
39
+ when Relation
40
+ value.klass.base_class
41
+ end
42
+ end
43
+
44
+ def convert_to_id(value)
45
+ case value
46
+ when Base
47
+ value._read_attribute(primary_key(value))
48
+ when Relation
49
+ value.select(primary_key(value))
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,25 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class RangeHandler # :nodoc:
4
- RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
6
+ class RangeWithBinds < Struct.new(:begin, :end)
7
+ def exclude_end?
8
+ false
9
+ end
10
+ end
11
+
12
+ def initialize(predicate_builder)
13
+ @predicate_builder = predicate_builder
14
+ end
5
15
 
6
16
  def call(attribute, value)
17
+ begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
18
+ end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
7
19
  if value.begin.respond_to?(:infinite?) && value.begin.infinite?
8
20
  if value.end.respond_to?(:infinite?) && value.end.infinite?
9
21
  attribute.not_in([])
10
22
  elsif value.exclude_end?
11
- attribute.lt(value.end)
23
+ attribute.lt(end_bind)
12
24
  else
13
- attribute.lteq(value.end)
25
+ attribute.lteq(end_bind)
14
26
  end
15
27
  elsif value.end.respond_to?(:infinite?) && value.end.infinite?
16
- attribute.gteq(value.begin)
28
+ attribute.gteq(begin_bind)
17
29
  elsif value.exclude_end?
18
- attribute.gteq(value.begin).and(attribute.lt(value.end))
30
+ attribute.gteq(begin_bind).and(attribute.lt(end_bind))
19
31
  else
20
- attribute.between(value)
32
+ attribute.between(RangeWithBinds.new(begin_bind, end_bind))
21
33
  end
22
34
  end
35
+
36
+ protected
37
+
38
+ attr_reader :predicate_builder
23
39
  end
24
40
  end
25
41
  end
@@ -1,7 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder
3
5
  class RelationHandler # :nodoc:
4
6
  def call(attribute, value)
7
+ if value.eager_loading?
8
+ value = value.send(:apply_join_dependency)
9
+ end
10
+
5
11
  if value.select_values.empty?
6
12
  value = value.select(value.arel_attribute(value.klass.primary_key))
7
13
  end
@@ -1,8 +1,10 @@
1
- require "active_record/attribute"
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/attribute"
2
4
 
3
5
  module ActiveRecord
4
6
  class Relation
5
- class QueryAttribute < Attribute # :nodoc:
7
+ class QueryAttribute < ActiveModel::Attribute # :nodoc:
6
8
  def type_cast(value)
7
9
  value
8
10
  end
@@ -14,6 +16,11 @@ module ActiveRecord
14
16
  def with_cast_value(value)
15
17
  QueryAttribute.new(name, value, type)
16
18
  end
19
+
20
+ def nil?
21
+ !value_before_type_cast.is_a?(StatementCache::Substitute) &&
22
+ (value_before_type_cast.nil? || value_for_database.nil?)
23
+ end
17
24
  end
18
25
  end
19
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/relation/from_clause"
2
4
  require "active_record/relation/query_attribute"
3
5
  require "active_record/relation/where_clause"
@@ -74,31 +76,6 @@ module ActiveRecord
74
76
  CODE
75
77
  end
76
78
 
77
- def bound_attributes
78
- if limit_value
79
- limit_bind = Attribute.with_cast_value(
80
- "LIMIT".freeze,
81
- connection.sanitize_limit(limit_value),
82
- Type.default_value,
83
- )
84
- end
85
- if offset_value
86
- offset_bind = Attribute.with_cast_value(
87
- "OFFSET".freeze,
88
- offset_value.to_i,
89
- Type.default_value,
90
- )
91
- end
92
- connection.combine_bind_parameters(
93
- from_clause: from_clause.binds,
94
- join_clause: arel.bind_values,
95
- where_clause: where_clause.binds,
96
- having_clause: having_clause.binds,
97
- limit: limit_bind,
98
- offset: offset_bind,
99
- )
100
- end
101
-
102
79
  alias extensions extending_values
103
80
 
104
81
  # Specify relationships to be included in the result set. For
@@ -202,12 +179,13 @@ module ActiveRecord
202
179
 
203
180
  # Works in two unique ways.
204
181
  #
205
- # First: takes a block so it can be used just like +Array#select+.
182
+ # First: takes a block so it can be used just like <tt>Array#select</tt>.
206
183
  #
207
184
  # Model.all.select { |m| m.field == value }
208
185
  #
209
186
  # This will build an array of objects from the database for the scope,
210
- # converting them into an array and iterating through them using +Array#select+.
187
+ # converting them into an array and iterating through them using
188
+ # <tt>Array#select</tt>.
211
189
  #
212
190
  # Second: Modifies the SELECT statement for the query so that only certain
213
191
  # fields are retrieved:
@@ -248,7 +226,7 @@ module ActiveRecord
248
226
  return super()
249
227
  end
250
228
 
251
- raise ArgumentError, "Call this with at least one field" if fields.empty?
229
+ raise ArgumentError, "Call `select' with at least one field" if fields.empty?
252
230
  spawn._select!(*fields)
253
231
  end
254
232
 
@@ -317,6 +295,7 @@ module ActiveRecord
317
295
  spawn.order!(*args)
318
296
  end
319
297
 
298
+ # Same as #order but operates on relation in-place instead of copying.
320
299
  def order!(*args) # :nodoc:
321
300
  preprocess_order_args(args)
322
301
 
@@ -338,6 +317,7 @@ module ActiveRecord
338
317
  spawn.reorder!(*args)
339
318
  end
340
319
 
320
+ # Same as #reorder but operates on relation in-place instead of copying.
341
321
  def reorder!(*args) # :nodoc:
342
322
  preprocess_order_args(args)
343
323
 
@@ -463,7 +443,7 @@ module ActiveRecord
463
443
  # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
464
444
  #
465
445
  def left_outer_joins(*args)
466
- check_if_method_has_arguments!(:left_outer_joins, args)
446
+ check_if_method_has_arguments!(__callee__, args)
467
447
 
468
448
  args.compact!
469
449
  args.flatten!
@@ -657,6 +637,7 @@ module ActiveRecord
657
637
 
658
638
  self.where_clause = self.where_clause.or(other.where_clause)
659
639
  self.having_clause = having_clause.or(other.having_clause)
640
+ self.references_values += other.references_values
660
641
 
661
642
  self
662
643
  end
@@ -797,7 +778,7 @@ module ActiveRecord
797
778
  value = sanitize_forbidden_attributes(value)
798
779
  self.create_with_value = create_with_value.merge(value)
799
780
  else
800
- self.create_with_value = {}
781
+ self.create_with_value = FROZEN_EMPTY_HASH
801
782
  end
802
783
 
803
784
  self
@@ -913,21 +894,27 @@ module ActiveRecord
913
894
  self
914
895
  end
915
896
 
916
- # Returns the Arel object associated with the relation.
917
- def arel # :nodoc:
918
- @arel ||= build_arel
897
+ def skip_query_cache! # :nodoc:
898
+ self.skip_query_cache_value = true
899
+ self
919
900
  end
920
901
 
921
- # Returns a relation value with a given name
922
- def get_value(name) # :nodoc:
923
- @values[name] || default_value_for(name)
902
+ # Returns the Arel object associated with the relation.
903
+ def arel(aliases = nil) # :nodoc:
904
+ @arel ||= build_arel(aliases)
924
905
  end
925
906
 
926
- # Sets the relation value with the given name
927
- def set_value(name, value) # :nodoc:
928
- assert_mutability!
929
- @values[name] = value
930
- end
907
+ protected
908
+ # Returns a relation value with a given name
909
+ def get_value(name) # :nodoc:
910
+ @values[name] || default_value_for(name)
911
+ end
912
+
913
+ # Sets the relation value with the given name
914
+ def set_value(name, value) # :nodoc:
915
+ assert_mutability!
916
+ @values[name] = value
917
+ end
931
918
 
932
919
  private
933
920
 
@@ -936,16 +923,30 @@ module ActiveRecord
936
923
  raise ImmutableRelation if defined?(@arel) && @arel
937
924
  end
938
925
 
939
- def build_arel
926
+ def build_arel(aliases)
940
927
  arel = Arel::SelectManager.new(table)
941
928
 
942
- build_joins(arel, joins_values.flatten) unless joins_values.empty?
943
- build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
929
+ aliases = build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty?
930
+ build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty?
944
931
 
945
932
  arel.where(where_clause.ast) unless where_clause.empty?
946
933
  arel.having(having_clause.ast) unless having_clause.empty?
947
- arel.take(Arel::Nodes::BindParam.new) if limit_value
948
- arel.skip(Arel::Nodes::BindParam.new) if offset_value
934
+ if limit_value
935
+ limit_attribute = ActiveModel::Attribute.with_cast_value(
936
+ "LIMIT".freeze,
937
+ connection.sanitize_limit(limit_value),
938
+ Type.default_value,
939
+ )
940
+ arel.take(Arel::Nodes::BindParam.new(limit_attribute))
941
+ end
942
+ if offset_value
943
+ offset_attribute = ActiveModel::Attribute.with_cast_value(
944
+ "OFFSET".freeze,
945
+ offset_value.to_i,
946
+ Type.default_value,
947
+ )
948
+ arel.skip(Arel::Nodes::BindParam.new(offset_attribute))
949
+ end
949
950
  arel.group(*arel_columns(group_values.uniq.reject(&:blank?))) unless group_values.empty?
950
951
 
951
952
  build_order(arel)
@@ -964,6 +965,9 @@ module ActiveRecord
964
965
  name = from_clause.name
965
966
  case opts
966
967
  when Relation
968
+ if opts.eager_loading?
969
+ opts = opts.send(:apply_join_dependency)
970
+ end
967
971
  name ||= "subquery"
968
972
  opts.arel.as(name.to_s)
969
973
  else
@@ -971,7 +975,7 @@ module ActiveRecord
971
975
  end
972
976
  end
973
977
 
974
- def build_left_outer_joins(manager, outer_joins)
978
+ def build_left_outer_joins(manager, outer_joins, aliases)
975
979
  buckets = outer_joins.group_by do |join|
976
980
  case join
977
981
  when Hash, Symbol, Array
@@ -981,10 +985,10 @@ module ActiveRecord
981
985
  end
982
986
  end
983
987
 
984
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
988
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
985
989
  end
986
990
 
987
- def build_joins(manager, joins)
991
+ def build_joins(manager, joins, aliases)
988
992
  buckets = joins.group_by do |join|
989
993
  case join
990
994
  when String
@@ -1000,10 +1004,10 @@ module ActiveRecord
1000
1004
  end
1001
1005
  end
1002
1006
 
1003
- build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
1007
+ build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
1004
1008
  end
1005
1009
 
1006
- def build_join_query(manager, buckets, join_type)
1010
+ def build_join_query(manager, buckets, join_type, aliases)
1007
1011
  buckets.default = []
1008
1012
 
1009
1013
  association_joins = buckets[:association_join]
@@ -1012,23 +1016,18 @@ module ActiveRecord
1012
1016
  string_joins = buckets[:string_join].map(&:strip).uniq
1013
1017
 
1014
1018
  join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
1019
+ alias_tracker = alias_tracker(join_list, aliases)
1015
1020
 
1016
1021
  join_dependency = ActiveRecord::Associations::JoinDependency.new(
1017
- @klass,
1018
- association_joins,
1019
- join_list
1022
+ klass, table, association_joins, alias_tracker
1020
1023
  )
1021
1024
 
1022
- join_infos = join_dependency.join_constraints stashed_association_joins, join_type
1023
-
1024
- join_infos.each do |info|
1025
- info.joins.each { |join| manager.from(join) }
1026
- manager.bind_values.concat info.binds
1027
- end
1025
+ joins = join_dependency.join_constraints(stashed_association_joins, join_type)
1026
+ joins.each { |join| manager.from(join) }
1028
1027
 
1029
1028
  manager.join_sources.concat(join_list)
1030
1029
 
1031
- manager
1030
+ alias_tracker.aliases
1032
1031
  end
1033
1032
 
1034
1033
  def convert_join_strings_to_ast(table, joins)
@@ -1041,10 +1040,10 @@ module ActiveRecord
1041
1040
  def build_select(arel)
1042
1041
  if select_values.any?
1043
1042
  arel.project(*arel_columns(select_values.uniq))
1044
- elsif klass.ignored_columns.any?
1045
- arel.project(*klass.column_names.map { |field| arel_attribute(field) })
1043
+ elsif @klass.ignored_columns.any?
1044
+ arel.project(*arel_columns(@klass.column_names))
1046
1045
  else
1047
- arel.project(@klass.arel_table[Arel.star])
1046
+ arel.project(table[Arel.star])
1048
1047
  end
1049
1048
  end
1050
1049
 
@@ -1088,6 +1087,10 @@ module ActiveRecord
1088
1087
  end
1089
1088
 
1090
1089
  def does_not_support_reverse?(order)
1090
+ # Account for String subclasses like Arel::Nodes::SqlLiteral that
1091
+ # override methods like #count.
1092
+ order = String.new(order) unless order.instance_of?(String)
1093
+
1091
1094
  # Uses SQL function with multiple arguments.
1092
1095
  (order.include?(",") && order.split(",").find { |section| section.count("(") != section.count(")") }) ||
1093
1096
  # Uses "nulls first" like construction.
@@ -1102,14 +1105,16 @@ module ActiveRecord
1102
1105
  end
1103
1106
 
1104
1107
  VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
1105
- "asc", "desc", "ASC", "DESC"] # :nodoc:
1108
+ "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
1106
1109
 
1107
1110
  def validate_order_args(args)
1108
1111
  args.each do |arg|
1109
1112
  next unless arg.is_a?(Hash)
1110
1113
  arg.each do |_key, value|
1111
- raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
1112
- "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
1114
+ unless VALID_DIRECTIONS.include?(value)
1115
+ raise ArgumentError,
1116
+ "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
1117
+ end
1113
1118
  end
1114
1119
  end
1115
1120
  end
@@ -1119,10 +1124,16 @@ module ActiveRecord
1119
1124
  klass.send(:sanitize_sql_for_order, arg)
1120
1125
  end
1121
1126
  order_args.flatten!
1127
+
1128
+ @klass.enforce_raw_sql_whitelist(
1129
+ order_args.flat_map { |a| a.is_a?(Hash) ? a.keys : a },
1130
+ whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST
1131
+ )
1132
+
1122
1133
  validate_order_args(order_args)
1123
1134
 
1124
1135
  references = order_args.grep(String)
1125
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
1136
+ references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
1126
1137
  references!(references) if references.any?
1127
1138
 
1128
1139
  # if a symbol is given we prepend the quoted table name
@@ -1167,7 +1178,7 @@ module ActiveRecord
1167
1178
  end
1168
1179
  end
1169
1180
 
1170
- STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having]
1181
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
1171
1182
  def structurally_incompatible_values_for_or(other)
1172
1183
  STRUCTURAL_OR_METHODS.reject do |method|
1173
1184
  get_value(method) == other.get_value(method)