activerecord 4.2.11.1 → 5.0.0

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

Potentially problematic release.


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

Files changed (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
- # = Active Record Belongs To Has One Association
2
+ # = Active Record Has One Association
3
3
  module Associations
4
4
  class HasOneAssociation < SingularAssociation #:nodoc:
5
5
  include ForeignAssociation
@@ -11,9 +11,16 @@ module ActiveRecord
11
11
 
12
12
  when :restrict_with_error
13
13
  if load_target
14
- record = klass.human_attribute_name(reflection.name).downcase
15
- owner.errors.add(:base, :"restrict_dependent_destroy.one", record: record)
16
- false
14
+ record = owner.class.human_attribute_name(reflection.name).downcase
15
+ message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
16
+ if message
17
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
18
+ The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
19
+ Please use `:'restrict_dependent_destroy.has_one'` instead.
20
+ MESSAGE
21
+ end
22
+ owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
23
+ throw(:abort)
17
24
  end
18
25
 
19
26
  else
@@ -58,7 +65,7 @@ module ActiveRecord
58
65
  when :destroy
59
66
  target.destroy
60
67
  when :nullify
61
- target.update_columns(reflection.foreign_key => nil)
68
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
62
69
  end
63
70
  end
64
71
  end
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  end
21
21
 
22
22
  def columns
23
- @tables.flat_map { |t| t.column_aliases }
23
+ @tables.flat_map(&:column_aliases)
24
24
  end
25
25
 
26
26
  # An array of [column_name, alias] pairs for the table
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  @alias_cache[node][column]
33
33
  end
34
34
 
35
- class Table < Struct.new(:node, :columns)
35
+ class Table < Struct.new(:node, :columns) # :nodoc:
36
36
  def table
37
37
  Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
38
38
  end
@@ -93,8 +93,7 @@ module ActiveRecord
93
93
  # joins # => []
94
94
  #
95
95
  def initialize(base, associations, joins)
96
- @alias_tracker = AliasTracker.create(base.connection, joins)
97
- @alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
96
+ @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
98
97
  tree = self.class.make_tree associations
99
98
  @join_root = JoinBase.new base, build(tree, base)
100
99
  @join_root.children.each { |child| construct_tables! @join_root, child }
@@ -104,9 +103,14 @@ module ActiveRecord
104
103
  join_root.drop(1).map!(&:reflection)
105
104
  end
106
105
 
107
- def join_constraints(outer_joins)
106
+ def join_constraints(outer_joins, join_type)
108
107
  joins = join_root.children.flat_map { |child|
109
- make_inner_joins join_root, child
108
+
109
+ if join_type == Arel::Nodes::OuterJoin
110
+ make_left_outer_joins join_root, child
111
+ else
112
+ make_inner_joins join_root, child
113
+ end
110
114
  }
111
115
 
112
116
  joins.concat outer_joins.flat_map { |oj|
@@ -132,9 +136,9 @@ module ActiveRecord
132
136
  def instantiate(result_set, aliases)
133
137
  primary_key = aliases.column_alias(join_root, join_root.primary_key)
134
138
 
135
- seen = Hash.new { |h,parent_klass|
136
- h[parent_klass] = Hash.new { |i,parent_id|
137
- i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
139
+ seen = Hash.new { |i, object_id|
140
+ i[object_id] = Hash.new { |j, child_class|
141
+ j[child_class] = {}
138
142
  }
139
143
  }
140
144
 
@@ -177,6 +181,14 @@ module ActiveRecord
177
181
  [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
178
182
  end
179
183
 
184
+ def make_left_outer_joins(parent, child)
185
+ tables = child.tables
186
+ join_type = Arel::Nodes::OuterJoin
187
+ info = make_constraints parent, child, tables, join_type
188
+
189
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
190
+ end
191
+
180
192
  def make_inner_joins(parent, child)
181
193
  tables = child.tables
182
194
  join_type = Arel::Nodes::InnerJoin
@@ -216,7 +228,7 @@ module ActiveRecord
216
228
 
217
229
  def find_reflection(klass, name)
218
230
  klass._reflect_on_association(name) or
219
- raise ConfigurationError, "Association named '#{ name }' was not found on #{ klass.name }; perhaps you misspelled it?"
231
+ raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
220
232
  end
221
233
 
222
234
  def build(associations, base_klass)
@@ -235,18 +247,15 @@ module ActiveRecord
235
247
 
236
248
  def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
237
249
  return if ar_parent.nil?
238
- primary_id = ar_parent.id
239
250
 
240
251
  parent.children.each do |node|
241
252
  if node.reflection.collection?
242
253
  other = ar_parent.association(node.reflection.name)
243
254
  other.loaded!
244
- else
245
- if ar_parent.association_cache.key?(node.reflection.name)
246
- model = ar_parent.association(node.reflection.name).target
247
- construct(model, node, row, rs, seen, model_cache, aliases)
248
- next
249
- end
255
+ elsif ar_parent.association_cached?(node.reflection.name)
256
+ model = ar_parent.association(node.reflection.name).target
257
+ construct(model, node, row, rs, seen, model_cache, aliases)
258
+ next
250
259
  end
251
260
 
252
261
  key = aliases.column_alias(node, node.primary_key)
@@ -257,13 +266,14 @@ module ActiveRecord
257
266
  next
258
267
  end
259
268
 
260
- model = seen[parent.base_klass][primary_id][node.base_klass][id]
269
+ model = seen[ar_parent.object_id][node.base_klass][id]
261
270
 
262
271
  if model
263
272
  construct(model, node, row, rs, seen, model_cache, aliases)
264
273
  else
265
274
  model = construct_model(ar_parent, node, row, model_cache, id, aliases)
266
- seen[parent.base_klass][primary_id][node.base_klass][id] = model
275
+ model.readonly!
276
+ seen[ar_parent.object_id][node.base_klass][id] = model
267
277
  construct(model, node, row, rs, seen, model_cache, aliases)
268
278
  end
269
279
  end
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
 
26
26
  def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
27
27
  joins = []
28
- bind_values = []
28
+ binds = []
29
29
  tables = tables.reverse
30
30
 
31
31
  scope_chain_index = 0
@@ -43,20 +43,27 @@ module ActiveRecord
43
43
 
44
44
  constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
45
45
 
46
+ predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
46
47
  scope_chain_items = scope_chain[scope_chain_index].map do |item|
47
48
  if item.is_a?(Relation)
48
49
  item
49
50
  else
50
- ActiveRecord::Relation.create(klass, table).instance_exec(node, &item)
51
+ ActiveRecord::Relation.create(klass, table, predicate_builder)
52
+ .instance_exec(node, &item)
51
53
  end
52
54
  end
53
55
  scope_chain_index += 1
54
56
 
55
57
  klass_scope =
56
- if klass.current_scope && klass.current_scope.values.blank?
57
- klass.unscoped
58
+ if klass.current_scope
59
+ klass.current_scope.clone
58
60
  else
59
- klass.send(:build_default_scope, ActiveRecord::Relation.create(klass, table))
61
+ relation = ActiveRecord::Relation.create(
62
+ klass,
63
+ table,
64
+ predicate_builder,
65
+ )
66
+ klass.send(:build_default_scope, relation)
60
67
  end
61
68
  scope_chain_items.concat [klass_scope].compact
62
69
 
@@ -65,7 +72,7 @@ module ActiveRecord
65
72
  end
66
73
 
67
74
  if rel && !rel.arel.constraints.empty?
68
- bind_values.concat rel.bind_values
75
+ binds += rel.bound_attributes
69
76
  constraint = constraint.and rel.arel.constraints
70
77
  end
71
78
 
@@ -73,9 +80,8 @@ module ActiveRecord
73
80
  value = foreign_klass.base_class.name
74
81
  column = klass.columns_hash[reflection.type.to_s]
75
82
 
76
- substitute = klass.connection.substitute_at(column)
77
- bind_values.push [column, value]
78
- constraint = constraint.and table[reflection.type].eq substitute
83
+ binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
84
+ constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
79
85
  end
80
86
 
81
87
  joins << table.create_join(table, table.create_on(constraint), join_type)
@@ -84,7 +90,7 @@ module ActiveRecord
84
90
  foreign_table, foreign_klass = table, klass
85
91
  end
86
92
 
87
- JoinInformation.new joins, bind_values
93
+ JoinInformation.new joins, binds
88
94
  end
89
95
 
90
96
  # Builds equality condition.
@@ -54,6 +54,8 @@ module ActiveRecord
54
54
  autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
55
55
  end
56
56
 
57
+ NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
58
+
57
59
  # Eager loads the named associations for the given Active Record record(s).
58
60
  #
59
61
  # In this description, 'association name' shall refer to the name passed
@@ -88,9 +90,6 @@ module ActiveRecord
88
90
  # [ :books, :author ]
89
91
  # { author: :avatar }
90
92
  # [ :books, { author: :avatar } ]
91
-
92
- NULL_RELATION = Struct.new(:values, :bind_values).new({}, [])
93
-
94
93
  def preload(records, associations, preload_scope = nil)
95
94
  records = Array.wrap(records).compact.uniq
96
95
  associations = Array.wrap(associations)
@@ -107,6 +106,7 @@ module ActiveRecord
107
106
 
108
107
  private
109
108
 
109
+ # Loads all the given data into +records+ for the +association+.
110
110
  def preloaders_on(association, records, scope)
111
111
  case association
112
112
  when Hash
@@ -116,7 +116,7 @@ module ActiveRecord
116
116
  when String
117
117
  preloaders_for_one(association.to_sym, records, scope)
118
118
  else
119
- raise ArgumentError, "#{association.inspect} was not recognised for preload"
119
+ raise ArgumentError, "#{association.inspect} was not recognized for preload"
120
120
  end
121
121
  end
122
122
 
@@ -132,6 +132,11 @@ module ActiveRecord
132
132
  }
133
133
  end
134
134
 
135
+ # Loads all the given data into +records+ for a singular +association+.
136
+ #
137
+ # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
138
+ # call the +run+ method for each passed in class in the +records+ argument.
139
+ #
135
140
  # Not all records have the same class, so group then preload group on the reflection
136
141
  # itself so that if various subclass share the same association then we do not split
137
142
  # them unnecessarily
@@ -179,8 +184,13 @@ module ActiveRecord
179
184
  def self.new(klass, owners, reflection, preload_scope); self; end
180
185
  def self.run(preloader); end
181
186
  def self.preloaded_records; []; end
187
+ def self.owners; []; end
182
188
  end
183
189
 
190
+ # Returns a class containing the logic needed to load preload the data
191
+ # and attach it to a relation. For example +Preloader::Association+ or
192
+ # +Preloader::HasManyThrough+. The class returned implements a `run` method
193
+ # that accepts a preloader.
184
194
  def preloader_for(reflection, owners, rhs_klass)
185
195
  return NullPreloader unless rhs_klass
186
196
 
@@ -12,7 +12,6 @@ module ActiveRecord
12
12
  @preload_scope = preload_scope
13
13
  @model = owners.first && owners.first.class
14
14
  @scope = nil
15
- @owners_by_key = nil
16
15
  @preloaded_records = []
17
16
  end
18
17
 
@@ -33,7 +32,7 @@ module ActiveRecord
33
32
  end
34
33
 
35
34
  def query_scope(ids)
36
- scope.where(association_key.in(ids))
35
+ scope.where(association_key_name => ids)
37
36
  end
38
37
 
39
38
  def table
@@ -48,7 +47,7 @@ module ActiveRecord
48
47
  # This is overridden by HABTM as the condition should be on the foreign_key column in
49
48
  # the join table
50
49
  def association_key
51
- table[association_key_name]
50
+ klass.arel_attribute(association_key_name, table)
52
51
  end
53
52
 
54
53
  # The name of the key on the model which declares the association
@@ -56,18 +55,6 @@ module ActiveRecord
56
55
  raise NotImplementedError
57
56
  end
58
57
 
59
- def owners_by_key
60
- @owners_by_key ||= if key_conversion_required?
61
- owners.group_by do |owner|
62
- owner[owner_key_name].to_s
63
- end
64
- else
65
- owners.group_by do |owner|
66
- owner[owner_key_name]
67
- end
68
- end
69
- end
70
-
71
58
  def options
72
59
  reflection.options
73
60
  end
@@ -75,32 +62,33 @@ module ActiveRecord
75
62
  private
76
63
 
77
64
  def associated_records_by_owner(preloader)
78
- owners_map = owners_by_key
79
- owner_keys = owners_map.keys.compact
80
-
81
- # Each record may have multiple owners, and vice-versa
82
- records_by_owner = owners.each_with_object({}) do |owner,h|
83
- h[owner] = []
65
+ records = load_records
66
+ owners.each_with_object({}) do |owner, result|
67
+ result[owner] = records[convert_key(owner[owner_key_name])] || []
84
68
  end
69
+ end
85
70
 
86
- if owner_keys.any?
87
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
88
- # Make several smaller queries if necessary or make one query if the adapter supports it
89
- sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
90
-
91
- records = load_slices sliced
92
- records.each do |record, owner_key|
93
- owners_map[owner_key].each do |owner|
94
- records_by_owner[owner] << record
95
- end
71
+ def owner_keys
72
+ unless defined?(@owner_keys)
73
+ @owner_keys = owners.map do |owner|
74
+ owner[owner_key_name]
96
75
  end
76
+ @owner_keys.uniq!
77
+ @owner_keys.compact!
97
78
  end
98
-
99
- records_by_owner
79
+ @owner_keys
100
80
  end
101
81
 
102
82
  def key_conversion_required?
103
- association_key_type != owner_key_type
83
+ @key_conversion_required ||= association_key_type != owner_key_type
84
+ end
85
+
86
+ def convert_key(key)
87
+ if key_conversion_required?
88
+ key.to_s
89
+ else
90
+ key
91
+ end
104
92
  end
105
93
 
106
94
  def association_key_type
@@ -111,17 +99,17 @@ module ActiveRecord
111
99
  @model.type_for_attribute(owner_key_name.to_s).type
112
100
  end
113
101
 
114
- def load_slices(slices)
115
- @preloaded_records = slices.flat_map { |slice|
102
+ def load_records
103
+ return {} if owner_keys.empty?
104
+ # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
105
+ # Make several smaller queries if necessary or make one query if the adapter supports it
106
+ slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
107
+ @preloaded_records = slices.flat_map do |slice|
116
108
  records_for(slice)
117
- }
118
-
119
- @preloaded_records.map { |record|
120
- key = record[association_key_name]
121
- key = key.to_s if key_conversion_required?
122
-
123
- [record, key]
124
- }
109
+ end
110
+ @preloaded_records.group_by do |record|
111
+ convert_key(record[association_key_name])
112
+ end
125
113
  end
126
114
 
127
115
  def reflection_scope
@@ -131,19 +119,25 @@ module ActiveRecord
131
119
  def build_scope
132
120
  scope = klass.unscoped
133
121
 
134
- values = reflection_scope.values
135
- reflection_binds = reflection_scope.bind_values
122
+ values = reflection_scope.values
136
123
  preload_values = preload_scope.values
137
- preload_binds = preload_scope.bind_values
138
124
 
139
- scope.where_values = Array(values[:where]) + Array(preload_values[:where])
125
+ scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
140
126
  scope.references_values = Array(values[:references]) + Array(preload_values[:references])
141
- scope.bind_values = (reflection_binds + preload_binds)
142
127
 
143
- scope._select! preload_values[:select] || values[:select] || table[Arel.star]
128
+ if preload_values[:select] || values[:select]
129
+ scope._select!(preload_values[:select] || values[:select])
130
+ end
144
131
  scope.includes! preload_values[:includes] || values[:includes]
145
- scope.joins! preload_values[:joins] || values[:joins]
146
- scope.order! preload_values[:order] || values[:order]
132
+ if preload_scope.joins_values.any?
133
+ scope.joins!(preload_scope.joins_values)
134
+ else
135
+ scope.joins!(reflection_scope.joins_values)
136
+ end
137
+
138
+ if order_values = preload_values[:order] || values[:order]
139
+ scope.order!(order_values)
140
+ end
147
141
 
148
142
  if preload_values[:reordering] || values[:reordering]
149
143
  scope.reordering_value = true
@@ -2,13 +2,8 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  class CollectionAssociation < Association #:nodoc:
5
-
6
5
  private
7
6
 
8
- def build_scope
9
- super.order(preload_scope.values[:order] || reflection_scope.values[:order])
10
- end
11
-
12
7
  def preload(preloader)
13
8
  associated_records_by_owner(preloader).each do |owner, records|
14
9
  association = owner.association(reflection.name)
@@ -17,7 +12,6 @@ module ActiveRecord
17
12
  records.each { |record| association.set_inverse_instance(record) }
18
13
  end
19
14
  end
20
-
21
15
  end
22
16
  end
23
17
  end