activerecord 5.1.7 → 5.2.8.1

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 +629 -661
  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 +15 -1
  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 +51 -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 +5 -3
  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 +80 -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 +26 -39
  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
  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,41 +117,57 @@ 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
+ joins_dependency = other.joins_values.map do |join|
114
121
  case join
115
122
  when Hash, Symbol, Array
116
- true
123
+ ActiveRecord::Associations::JoinDependency.new(
124
+ other.klass, other.table, join
125
+ )
117
126
  else
118
- false
127
+ join
119
128
  end
120
129
  end
121
130
 
122
- join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
123
- joins_dependency,
124
- [])
125
- relation.joins! rest
131
+ relation.joins!(*joins_dependency)
132
+ end
133
+ end
134
+
135
+ def merge_outer_joins
136
+ return if other.left_outer_joins_values.blank?
137
+
138
+ if other.klass == relation.klass
139
+ relation.left_outer_joins!(*other.left_outer_joins_values)
140
+ else
141
+ joins_dependency = other.left_outer_joins_values.map do |join|
142
+ case join
143
+ when Hash, Symbol, Array
144
+ ActiveRecord::Associations::JoinDependency.new(
145
+ other.klass, other.table, join
146
+ )
147
+ else
148
+ join
149
+ end
150
+ end
126
151
 
127
- @relation = relation.joins join_dependency
152
+ relation.left_outer_joins!(*joins_dependency)
128
153
  end
129
154
  end
130
155
 
131
156
  def merge_multi_values
132
157
  if other.reordering_value
133
158
  # override any order specified in the original relation
134
- relation.reorder! other.order_values
135
- elsif other.order_values
159
+ relation.reorder!(*other.order_values)
160
+ elsif other.order_values.any?
136
161
  # merge in order_values from relation
137
- relation.order! other.order_values
162
+ relation.order!(*other.order_values)
138
163
  end
139
164
 
140
- relation.extend(*other.extending_values) unless other.extending_values.blank?
165
+ extensions = other.extensions - relation.extensions
166
+ relation.extending!(*extensions) if extensions.any?
141
167
  end
142
168
 
143
169
  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
170
+ relation.lock_value ||= other.lock_value if other.lock_value
148
171
 
149
172
  unless other.create_with_value.blank?
150
173
  relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
@@ -152,11 +175,18 @@ module ActiveRecord
152
175
  end
153
176
 
154
177
  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))
159
- end
178
+ relation.from_clause = other.from_clause if replace_from_clause?
179
+
180
+ where_clause = relation.where_clause.merge(other.where_clause)
181
+ relation.where_clause = where_clause unless where_clause.empty?
182
+
183
+ having_clause = relation.having_clause.merge(other.having_clause)
184
+ relation.having_clause = having_clause unless having_clause.empty?
185
+ end
186
+
187
+ def replace_from_clause?
188
+ relation.from_clause.empty? && !other.from_clause.empty? &&
189
+ relation.klass.base_class == other.klass.base_class
160
190
  end
161
191
  end
162
192
  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
+ values.map! do |v|
23
+ predicate_builder.build_bind_attribute(attribute.name, v)
24
+ end
25
+ values.empty? ? NullPredicate : attribute.in(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,56 @@
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) do |value, hash|
29
+ hash[klass(value).polymorphic_name] << convert_to_id(value)
30
+ end
31
+ end
32
+
33
+ def primary_key(value)
34
+ associated_table.association_join_primary_key(klass(value))
35
+ end
36
+
37
+ def klass(value)
38
+ case value
39
+ when Base
40
+ value.class
41
+ when Relation
42
+ value.klass
43
+ end
44
+ end
45
+
46
+ def convert_to_id(value)
47
+ case value
48
+ when Base
49
+ value._read_attribute(primary_key(value))
50
+ when Relation
51
+ value.select(primary_key(value))
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,25 +1,42 @@
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)
7
- if value.begin.respond_to?(:infinite?) && value.begin.infinite?
8
- if value.end.respond_to?(:infinite?) && value.end.infinite?
17
+ begin_bind = predicate_builder.build_bind_attribute(attribute.name, value.begin)
18
+ end_bind = predicate_builder.build_bind_attribute(attribute.name, value.end)
19
+
20
+ if begin_bind.value.infinity?
21
+ if end_bind.value.infinity?
9
22
  attribute.not_in([])
10
23
  elsif value.exclude_end?
11
- attribute.lt(value.end)
24
+ attribute.lt(end_bind)
12
25
  else
13
- attribute.lteq(value.end)
26
+ attribute.lteq(end_bind)
14
27
  end
15
- elsif value.end.respond_to?(:infinite?) && value.end.infinite?
16
- attribute.gteq(value.begin)
28
+ elsif end_bind.value.infinity?
29
+ attribute.gteq(begin_bind)
17
30
  elsif value.exclude_end?
18
- attribute.gteq(value.begin).and(attribute.lt(value.end))
31
+ attribute.gteq(begin_bind).and(attribute.lt(end_bind))
19
32
  else
20
- attribute.between(value)
33
+ attribute.between(RangeWithBinds.new(begin_bind, end_bind))
21
34
  end
22
35
  end
36
+
37
+ protected
38
+
39
+ attr_reader :predicate_builder
23
40
  end
24
41
  end
25
42
  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)
@@ -61,11 +48,19 @@ module ActiveRecord
61
48
  end
62
49
 
63
50
  def build(attribute, value)
64
- handler_for(value).call(attribute, value)
51
+ if table.type(attribute.name).force_equality?(value)
52
+ bind = build_bind_attribute(attribute.name, value)
53
+ attribute.eq(bind)
54
+ else
55
+ handler_for(value).call(attribute, value)
56
+ end
57
+ end
58
+
59
+ def build_bind_attribute(column_name, value)
60
+ attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
61
+ Arel::Nodes::BindParam.new(attr)
65
62
  end
66
63
 
67
- # TODO Change this to private once we've dropped Ruby 2.2 support.
68
- # Workaround for Ruby 2.2 "private attribute?" warning.
69
64
  protected
70
65
 
71
66
  attr_reader :table
@@ -76,56 +71,47 @@ module ActiveRecord
76
71
  attributes.flat_map do |key, value|
77
72
  if value.is_a?(Hash) && !table.has_column?(key)
78
73
  associated_predicate_builder(key).expand_from_hash(value)
79
- else
80
- build(table.arel_attribute(key), value)
81
- end
82
- end
83
- end
74
+ elsif table.associated_with?(key)
75
+ # Find the foreign key when using queries such as:
76
+ # Post.where(author: author)
77
+ #
78
+ # For polymorphic relationships, find the foreign key and type:
79
+ # PriceEstimate.where(estimate_of: treasure)
80
+ associated_table = table.associated_table(key)
81
+ if associated_table.polymorphic_association?
82
+ case value.is_a?(Array) ? value.first : value
83
+ when Base, Relation
84
+ value = [value] unless value.is_a?(Array)
85
+ klass = PolymorphicArrayValue
86
+ end
87
+ end
84
88
 
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).force_equality?(value)
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
89
+ klass ||= AssociationQueryValue
90
+ queries = klass.new(associated_table, value).queries.map do |query|
91
+ expand_from_hash(query).reduce(&:and)
104
92
  end
105
- unless last.respond_to?(:infinite?) && last.infinite?
106
- binds << build_bind_param(column_name, last)
107
- last = Arel::Nodes::BindParam.new
93
+ queries.reduce(&:or)
94
+ elsif table.aggregated_with?(key)
95
+ mapping = table.reflect_on_aggregation(key).mapping
96
+ values = value.nil? ? [nil] : Array.wrap(value)
97
+ if mapping.length == 1 || values.empty?
98
+ column_name, aggr_attr = mapping.first
99
+ values = values.map do |object|
100
+ object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
101
+ end
102
+ build(table.arel_attribute(column_name), values)
103
+ else
104
+ queries = values.map do |object|
105
+ mapping.map do |field_attr, aggregate_attr|
106
+ build(table.arel_attribute(field_attr), object.try!(aggregate_attr))
107
+ end.reduce(&:and)
108
+ end
109
+ queries.reduce(&:or)
108
110
  end
109
-
110
- result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
111
111
  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)
112
+ build(table.arel_attribute(key), value)
125
113
  end
126
114
  end
127
-
128
- [result, binds]
129
115
  end
130
116
 
131
117
  private
@@ -153,19 +139,14 @@ module ActiveRecord
153
139
  def handler_for(object)
154
140
  @handlers.detect { |klass, _| klass === object }.last
155
141
  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).force_equality?(value)
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
142
  end
171
143
  end
144
+
145
+ require "active_record/relation/predicate_builder/array_handler"
146
+ require "active_record/relation/predicate_builder/base_handler"
147
+ require "active_record/relation/predicate_builder/basic_object_handler"
148
+ require "active_record/relation/predicate_builder/range_handler"
149
+ require "active_record/relation/predicate_builder/relation_handler"
150
+
151
+ require "active_record/relation/predicate_builder/association_query_value"
152
+ 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,30 @@ module ActiveRecord
14
16
  def with_cast_value(value)
15
17
  QueryAttribute.new(name, value, type)
16
18
  end
19
+
20
+ def nil?
21
+ unless value_before_type_cast.is_a?(StatementCache::Substitute)
22
+ value_before_type_cast.nil? ||
23
+ type.respond_to?(:subtype, true) && value_for_database.nil?
24
+ end
25
+ end
26
+
27
+ def boundable?
28
+ return @_boundable if defined?(@_boundable)
29
+ value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
30
+ @_boundable = true
31
+ rescue ::RangeError
32
+ @_boundable = false
33
+ end
34
+
35
+ def infinity?
36
+ _infinity?(value_before_type_cast) || boundable? && _infinity?(value_for_database)
37
+ end
38
+
39
+ private
40
+ def _infinity?(value)
41
+ value.respond_to?(:infinite?) && value.infinite?
42
+ end
17
43
  end
18
44
  end
19
45
  end