activerecord 4.2.11.3 → 5.0.0.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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1281 -1204
  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/aggregations.rb +35 -24
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +49 -41
  21. data/lib/active_record/associations/collection_proxy.rb +67 -27
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +19 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +13 -37
  49. data/lib/active_record/attribute_methods.rb +76 -47
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -81
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +4 -4
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +363 -133
  141. data/lib/active_record/model_schema.rb +129 -41
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +23 -16
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +69 -46
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +79 -108
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -14
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +57 -43
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -45
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +8 -4
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  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 +60 -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,3 @@
1
- require 'active_support/core_ext/string/filters'
2
-
3
1
  module ActiveRecord
4
2
  # = Active Record Has Many Through Association
5
3
  module Associations
@@ -13,21 +11,6 @@ module ActiveRecord
13
11
  @through_association = nil
14
12
  end
15
13
 
16
- # Returns the size of the collection by executing a SELECT COUNT(*) query
17
- # if the collection hasn't been loaded, and by calling collection.size if
18
- # it has. If the collection will likely have a size greater than zero,
19
- # and if fetching the collection will be needed afterwards, one less
20
- # SELECT query will be generated by using #length instead.
21
- def size
22
- if has_cached_counter?
23
- owner._read_attribute cached_counter_attribute_name(reflection)
24
- elsif loaded?
25
- target.size
26
- else
27
- super
28
- end
29
- end
30
-
31
14
  def concat(*records)
32
15
  unless owner.new_record?
33
16
  records.flatten.each do |record|
@@ -55,25 +38,14 @@ module ActiveRecord
55
38
  def insert_record(record, validate = true, raise = false)
56
39
  ensure_not_nested
57
40
 
58
- if record.new_record?
59
- if raise
60
- record.save!(:validate => validate)
61
- else
62
- return unless record.save(:validate => validate)
63
- end
41
+ if raise
42
+ record.save!(:validate => validate)
43
+ else
44
+ return unless record.save(:validate => validate)
64
45
  end
65
46
 
66
47
  save_through_record(record)
67
- if has_cached_counter? && !through_reflection_updates_counter_cache?
68
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
69
- Automatic updating of counter caches on through associations has been
70
- deprecated, and will be removed in Rails 5. Instead, please set the
71
- appropriate `counter_cache` options on the `has_many` and `belongs_to`
72
- for your associations to #{through_reflection.name}.
73
- MSG
74
48
 
75
- update_counter_in_database(1)
76
- end
77
49
  record
78
50
  end
79
51
 
@@ -143,7 +115,7 @@ module ActiveRecord
143
115
  def update_through_counter?(method)
144
116
  case method
145
117
  when :destroy
146
- !inverse_updates_counter_cache?(through_reflection)
118
+ !through_reflection.inverse_updates_counter_cache?
147
119
  when :nullify
148
120
  false
149
121
  else
@@ -166,17 +138,15 @@ module ActiveRecord
166
138
  if scope.klass.primary_key
167
139
  count = scope.destroy_all.length
168
140
  else
169
- scope.each do |record|
170
- record._run_destroy_callbacks
171
- end
141
+ scope.each(&:_run_destroy_callbacks)
172
142
 
173
143
  arel = scope.arel
174
144
 
175
- stmt = Arel::DeleteManager.new arel.engine
145
+ stmt = Arel::DeleteManager.new
176
146
  stmt.from scope.klass.arel_table
177
147
  stmt.wheres = arel.constraints
178
148
 
179
- count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
149
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
180
150
  end
181
151
  when :nullify
182
152
  count = scope.update_all(source_reflection.foreign_key => nil)
@@ -233,15 +203,6 @@ module ActiveRecord
233
203
  def invertible_for?(record)
234
204
  false
235
205
  end
236
-
237
- def has_cached_counter?(reflection = reflection())
238
- owner.attribute_present?(cached_counter_attribute_name(reflection))
239
- end
240
-
241
- def through_reflection_updates_counter_cache?
242
- counter_name = cached_counter_attribute_name
243
- inverse_updates_counter_named?(counter_name, through_reflection)
244
- end
245
206
  end
246
207
  end
247
208
  end
@@ -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
@@ -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.
@@ -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
@@ -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
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  records_by_owner = super
9
9
 
10
10
  if reflection_scope.distinct_value
11
- records_by_owner.each_value { |records| records.uniq! }
11
+ records_by_owner.each_value(&:uniq!)
12
12
  end
13
13
 
14
14
  records_by_owner
@@ -2,7 +2,6 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  class HasOne < SingularAssociation #:nodoc:
5
-
6
5
  def association_key_name
7
6
  reflection.foreign_key
8
7
  end
@@ -10,13 +9,6 @@ module ActiveRecord
10
9
  def owner_key_name
11
10
  reflection.active_record_primary_key
12
11
  end
13
-
14
- private
15
-
16
- def build_scope
17
- super.order(preload_scope.values[:order] || reflection_scope.values[:order])
18
- end
19
-
20
12
  end
21
13
  end
22
14
  end
@@ -18,7 +18,8 @@ module ActiveRecord
18
18
  through_records = owners.map do |owner|
19
19
  association = owner.association through_reflection.name
20
20
 
21
- [owner, Array(association.reader)]
21
+ center = target_records_from_association(association)
22
+ [owner, Array(center)]
22
23
  end
23
24
 
24
25
  reset_association owners, through_reflection.name
@@ -37,28 +38,35 @@ module ActiveRecord
37
38
  }
38
39
  end
39
40
 
40
- record_offset = {}
41
- @preloaded_records.each_with_index do |record,i|
42
- record_offset[record] = i
43
- end
44
-
45
- through_records.each_with_object({}) { |(lhs,center),records_by_owner|
41
+ through_records.each_with_object({}) do |(lhs,center), records_by_owner|
46
42
  pl_to_middle = center.group_by { |record| middle_to_pl[record] }
47
43
 
48
44
  records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
49
45
  rhs_records = middles.flat_map { |r|
50
46
  association = r.association source_reflection.name
51
47
 
52
- association.reader
48
+ target_records_from_association(association)
53
49
  }.compact
54
50
 
55
- rhs_records.sort_by { |rhs| record_offset[rhs] }
51
+ # Respect the order on `reflection_scope` if it exists, else use the natural order.
52
+ if reflection_scope.values[:order].present?
53
+ @id_map ||= id_to_index_map @preloaded_records
54
+ rhs_records.sort_by { |rhs| @id_map[rhs] }
55
+ else
56
+ rhs_records
57
+ end
56
58
  end
57
- }
59
+ end
58
60
  end
59
61
 
60
62
  private
61
63
 
64
+ def id_to_index_map(ids)
65
+ id_map = {}
66
+ ids.each_with_index { |id, index| id_map[id] = index }
67
+ id_map
68
+ end
69
+
62
70
  def reset_association(owners, association_name)
63
71
  should_reset = (through_scope != through_reflection.klass.unscoped) ||
64
72
  (reflection.options[:source_type] && through_reflection.collection?)
@@ -78,18 +86,23 @@ module ActiveRecord
78
86
  if options[:source_type]
79
87
  scope.where! reflection.foreign_type => options[:source_type]
80
88
  else
81
- unless reflection_scope.where_values.empty?
89
+ unless reflection_scope.where_clause.empty?
82
90
  scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
83
- scope.where_values = reflection_scope.values[:where]
84
- scope.bind_values = reflection_scope.bind_values
91
+ scope.where_clause = reflection_scope.where_clause
85
92
  end
86
93
 
87
94
  scope.references! reflection_scope.values[:references]
88
- scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
95
+ if scope.eager_loading? && order_values = reflection_scope.values[:order]
96
+ scope = scope.order(order_values)
97
+ end
89
98
  end
90
99
 
91
100
  scope
92
101
  end
102
+
103
+ def target_records_from_association(association)
104
+ association.loaded? ? association.target : association.reader
105
+ end
93
106
  end
94
107
  end
95
108
  end