activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  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 +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -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 +10 -6
  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 +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -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 -64
  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 +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  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 +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  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/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  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 +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  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 -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 +14 -10
  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 +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  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 -17
  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 -23
  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 +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  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_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  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 +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  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 +18 -0
  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 +92 -95
  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 +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  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 +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  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 +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  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 +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  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 +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  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 +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  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 +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  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 +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  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/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  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/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -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. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -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
@@ -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_primary_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_join_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,27 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class PredicateBuilder # :nodoc:
3
- require "active_record/relation/predicate_builder/array_handler"
4
- require "active_record/relation/predicate_builder/association_query_handler"
5
- require "active_record/relation/predicate_builder/base_handler"
6
- require "active_record/relation/predicate_builder/basic_object_handler"
7
- require "active_record/relation/predicate_builder/polymorphic_array_handler"
8
- require "active_record/relation/predicate_builder/range_handler"
9
- require "active_record/relation/predicate_builder/relation_handler"
10
-
11
5
  delegate :resolve_column_aliases, to: :table
12
6
 
13
7
  def initialize(table)
14
8
  @table = table
15
9
  @handlers = []
16
10
 
17
- register_handler(BasicObject, BasicObjectHandler.new)
11
+ register_handler(BasicObject, BasicObjectHandler.new(self))
18
12
  register_handler(Base, BaseHandler.new(self))
19
- register_handler(Range, RangeHandler.new)
20
- register_handler(RangeHandler::RangeWithBinds, RangeHandler.new)
13
+ register_handler(Range, RangeHandler.new(self))
21
14
  register_handler(Relation, RelationHandler.new)
22
15
  register_handler(Array, ArrayHandler.new(self))
23
- register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
24
- register_handler(PolymorphicArrayValue, PolymorphicArrayHandler.new(self))
16
+ register_handler(Set, ArrayHandler.new(self))
25
17
  end
26
18
 
27
19
  def build_from_hash(attributes)
@@ -29,11 +21,6 @@ module ActiveRecord
29
21
  expand_from_hash(attributes)
30
22
  end
31
23
 
32
- def create_binds(attributes)
33
- attributes = convert_dot_notation_to_hash(attributes)
34
- create_binds_for_hash(attributes)
35
- end
36
-
37
24
  def self.references(attributes)
38
25
  attributes.map do |key, value|
39
26
  if value.is_a?(Hash)
@@ -64,8 +51,11 @@ module ActiveRecord
64
51
  handler_for(value).call(attribute, value)
65
52
  end
66
53
 
67
- # TODO Change this to private once we've dropped Ruby 2.2 support.
68
- # Workaround for Ruby 2.2 "private attribute?" warning.
54
+ def build_bind_attribute(column_name, value)
55
+ attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
56
+ Arel::Nodes::BindParam.new(attr)
57
+ end
58
+
69
59
  protected
70
60
 
71
61
  attr_reader :table
@@ -76,56 +66,46 @@ module ActiveRecord
76
66
  attributes.flat_map do |key, value|
77
67
  if value.is_a?(Hash) && !table.has_column?(key)
78
68
  associated_predicate_builder(key).expand_from_hash(value)
79
- else
80
- build(table.arel_attribute(key), value)
81
- end
82
- end
83
- end
69
+ elsif table.associated_with?(key)
70
+ # Find the foreign key when using queries such as:
71
+ # Post.where(author: author)
72
+ #
73
+ # For polymorphic relationships, find the foreign key and type:
74
+ # PriceEstimate.where(estimate_of: treasure)
75
+ associated_table = table.associated_table(key)
76
+ if associated_table.polymorphic_association?
77
+ case value.is_a?(Array) ? value.first : value
78
+ when Base, Relation
79
+ value = [value] unless value.is_a?(Array)
80
+ klass = PolymorphicArrayValue
81
+ end
82
+ end
84
83
 
85
- def create_binds_for_hash(attributes)
86
- result = attributes.dup
87
- binds = []
88
-
89
- attributes.each do |column_name, value|
90
- case
91
- when value.is_a?(Hash) && !table.has_column?(column_name)
92
- attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
93
- result[column_name] = attrs
94
- binds += bvs
95
- next
96
- when value.is_a?(Relation)
97
- binds += value.bound_attributes
98
- when value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype)
99
- first = value.begin
100
- last = value.end
101
- unless first.respond_to?(:infinite?) && first.infinite?
102
- binds << build_bind_param(column_name, first)
103
- first = Arel::Nodes::BindParam.new
84
+ klass ||= AssociationQueryValue
85
+ queries = klass.new(associated_table, value).queries.map do |query|
86
+ expand_from_hash(query).reduce(&:and)
104
87
  end
105
- unless last.respond_to?(:infinite?) && last.infinite?
106
- binds << build_bind_param(column_name, last)
107
- last = Arel::Nodes::BindParam.new
88
+ queries.reduce(&:or)
89
+ elsif table.aggregated_with?(key)
90
+ mapping = table.reflect_on_aggregation(key).mapping
91
+ queries = Array.wrap(value).map do |object|
92
+ mapping.map do |field_attr, aggregate_attr|
93
+ if mapping.size == 1 && !object.respond_to?(aggregate_attr)
94
+ build(table.arel_attribute(field_attr), object)
95
+ else
96
+ build(table.arel_attribute(field_attr), object.send(aggregate_attr))
97
+ end
98
+ end.reduce(&:and)
108
99
  end
109
-
110
- result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
100
+ queries.reduce(&:or)
101
+ # FIXME: Deprecate this and provide a public API to force equality
102
+ elsif (value.is_a?(Range) || value.is_a?(Array)) &&
103
+ table.type(key.to_s).respond_to?(:subtype)
104
+ BasicObjectHandler.new(self).call(table.arel_attribute(key), value)
111
105
  else
112
- if can_be_bound?(column_name, value)
113
- result[column_name] = Arel::Nodes::BindParam.new
114
- binds << build_bind_param(column_name, value)
115
- end
116
- end
117
-
118
- # Find the foreign key when using queries such as:
119
- # Post.where(author: author)
120
- #
121
- # For polymorphic relationships, find the foreign key and type:
122
- # PriceEstimate.where(estimate_of: treasure)
123
- if table.associated_with?(column_name)
124
- result[column_name] = AssociationQueryHandler.value_for(table, column_name, value)
106
+ build(table.arel_attribute(key), value)
125
107
  end
126
108
  end
127
-
128
- [result, binds]
129
109
  end
130
110
 
131
111
  private
@@ -153,19 +133,14 @@ module ActiveRecord
153
133
  def handler_for(object)
154
134
  @handlers.detect { |klass, _| klass === object }.last
155
135
  end
156
-
157
- def can_be_bound?(column_name, value)
158
- return if table.associated_with?(column_name)
159
- case value
160
- when Array, Range
161
- table.type(column_name).respond_to?(:subtype)
162
- else
163
- !value.nil? && handler_for(value).is_a?(BasicObjectHandler)
164
- end
165
- end
166
-
167
- def build_bind_param(column_name, value)
168
- Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
169
- end
170
136
  end
171
137
  end
138
+
139
+ require "active_record/relation/predicate_builder/array_handler"
140
+ require "active_record/relation/predicate_builder/base_handler"
141
+ require "active_record/relation/predicate_builder/basic_object_handler"
142
+ require "active_record/relation/predicate_builder/range_handler"
143
+ require "active_record/relation/predicate_builder/relation_handler"
144
+
145
+ require "active_record/relation/predicate_builder/association_query_value"
146
+ require "active_record/relation/predicate_builder/polymorphic_array_value"
@@ -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