activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -54,8 +54,8 @@ module ActiveRecord
54
54
  length = column_names_with_alias.length
55
55
 
56
56
  while index < length
57
- column_name, alias_name = column_names_with_alias[index]
58
- hash[column_name] = row[alias_name]
57
+ column = column_names_with_alias[index]
58
+ hash[column.name] = row[column.alias]
59
59
  index += 1
60
60
  end
61
61
 
@@ -88,7 +88,6 @@ module ActiveRecord
88
88
  if records.empty?
89
89
  []
90
90
  else
91
- records.uniq!
92
91
  Array.wrap(associations).flat_map { |association|
93
92
  preloaders_on association, records, preload_scope
94
93
  }
@@ -96,36 +95,35 @@ module ActiveRecord
96
95
  end
97
96
 
98
97
  private
99
-
100
98
  # Loads all the given data into +records+ for the +association+.
101
- def preloaders_on(association, records, scope)
99
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
102
100
  case association
103
101
  when Hash
104
- preloaders_for_hash(association, records, scope)
105
- when Symbol
106
- preloaders_for_one(association, records, scope)
107
- when String
108
- preloaders_for_one(association.to_sym, records, scope)
102
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
103
+ when Symbol, String
104
+ preloaders_for_one(association, records, scope, polymorphic_parent)
109
105
  else
110
106
  raise ArgumentError, "#{association.inspect} was not recognized for preload"
111
107
  end
112
108
  end
113
109
 
114
- def preloaders_for_hash(association, records, scope)
110
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
115
111
  association.flat_map { |parent, child|
116
- loaders = preloaders_for_one parent, records, scope
117
-
118
- recs = loaders.flat_map(&:preloaded_records).uniq
119
- loaders.concat Array.wrap(child).flat_map { |assoc|
120
- preloaders_on assoc, recs, scope
121
- }
122
- loaders
112
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
113
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
114
+ recs = loaders.flat_map(&:preloaded_records).uniq
115
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
116
+ loaders.concat Array.wrap(child).flat_map { |assoc|
117
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
118
+ }
119
+ loaders
120
+ end
123
121
  }
124
122
  end
125
123
 
126
124
  # Loads all the given data into +records+ for a singular +association+.
127
125
  #
128
- # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
126
+ # Functions by instantiating a preloader class such as Preloader::Association and
129
127
  # call the +run+ method for each passed in class in the +records+ argument.
130
128
  #
131
129
  # Not all records have the same class, so group then preload group on the reflection
@@ -135,24 +133,25 @@ module ActiveRecord
135
133
  # Additionally, polymorphic belongs_to associations can have multiple associated
136
134
  # classes, depending on the polymorphic_type field. So we group by the classes as
137
135
  # well.
138
- def preloaders_for_one(association, records, scope)
139
- grouped_records(association, records).flat_map do |reflection, klasses|
140
- klasses.map do |rhs_klass, rs|
141
- loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
142
- loader.run self
143
- loader
136
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
137
+ grouped_records(association, records, polymorphic_parent)
138
+ .flat_map do |reflection, reflection_records|
139
+ preloaders_for_reflection reflection, reflection_records, scope
144
140
  end
141
+ end
142
+
143
+ def preloaders_for_reflection(reflection, records, scope)
144
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
145
+ preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
145
146
  end
146
147
  end
147
148
 
148
- def grouped_records(association, records)
149
+ def grouped_records(association, records, polymorphic_parent)
149
150
  h = {}
150
151
  records.each do |record|
151
- next unless record
152
- assoc = record.association(association)
153
- next unless assoc.klass
154
- klasses = h[assoc.reflection] ||= {}
155
- (klasses[assoc.klass] ||= []) << record
152
+ reflection = record.class._reflect_on_association(association)
153
+ next if polymorphic_parent && !reflection || !record.association(association).klass
154
+ (h[reflection] ||= []) << record
156
155
  end
157
156
  h
158
157
  end
@@ -163,13 +162,21 @@ module ActiveRecord
163
162
  @reflection = reflection
164
163
  end
165
164
 
166
- def run(preloader); end
165
+ def run
166
+ self
167
+ end
167
168
 
168
169
  def preloaded_records
169
- owners.flat_map { |owner| owner.association(reflection.name).target }
170
+ @preloaded_records ||= records_by_owner.flat_map(&:last)
171
+ end
172
+
173
+ def records_by_owner
174
+ @records_by_owner ||= owners.each_with_object({}) do |owner, result|
175
+ result[owner] = Array(owner.association(reflection.name).target)
176
+ end
170
177
  end
171
178
 
172
- protected
179
+ private
173
180
  attr_reader :owners, :reflection
174
181
  end
175
182
 
@@ -4,33 +4,46 @@ module ActiveRecord
4
4
  module Associations
5
5
  class Preloader
6
6
  class Association #:nodoc:
7
- attr_reader :preloaded_records
8
-
9
7
  def initialize(klass, owners, reflection, preload_scope)
10
8
  @klass = klass
11
9
  @owners = owners
12
10
  @reflection = reflection
13
11
  @preload_scope = preload_scope
14
12
  @model = owners.first && owners.first.class
15
- @preloaded_records = []
16
13
  end
17
14
 
18
- def run(preloader)
19
- records = load_records do |record|
20
- owner = owners_by_key[convert_key(record[association_key_name])]
21
- association = owner.association(reflection.name)
22
- association.set_inverse_instance(record)
15
+ def run
16
+ if !preload_scope || preload_scope.empty_scope?
17
+ owners.each do |owner|
18
+ associate_records_to_owner(owner, records_by_owner[owner] || [])
19
+ end
20
+ else
21
+ # Custom preload scope is used and
22
+ # the association can not be marked as loaded
23
+ # Loading into a Hash instead
24
+ records_by_owner
23
25
  end
26
+ self
27
+ end
24
28
 
25
- owners.each do |owner|
26
- associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || [])
29
+ def records_by_owner
30
+ # owners can be duplicated when a relation has a collection association join
31
+ # #compare_by_identity makes such owners different hash keys
32
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
33
+ owners_by_key[convert_key(record[association_key_name])].each do |owner|
34
+ (result[owner] ||= []) << record
35
+ end
27
36
  end
28
37
  end
29
38
 
30
- protected
31
- attr_reader :owners, :reflection, :preload_scope, :model, :klass
39
+ def preloaded_records
40
+ return @preloaded_records if defined?(@preloaded_records)
41
+ @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
42
+ end
32
43
 
33
44
  private
45
+ attr_reader :owners, :reflection, :preload_scope, :model, :klass
46
+
34
47
  # The name of the key on the associated records
35
48
  def association_key_name
36
49
  reflection.join_primary_key(klass)
@@ -43,11 +56,10 @@ module ActiveRecord
43
56
 
44
57
  def associate_records_to_owner(owner, records)
45
58
  association = owner.association(reflection.name)
46
- association.loaded!
47
59
  if reflection.collection?
48
- association.target.concat(records)
60
+ association.target = records
49
61
  else
50
- association.target = records.first unless records.empty?
62
+ association.target = records.first
51
63
  end
52
64
  end
53
65
 
@@ -56,13 +68,10 @@ module ActiveRecord
56
68
  end
57
69
 
58
70
  def owners_by_key
59
- unless defined?(@owners_by_key)
60
- @owners_by_key = owners.each_with_object({}) do |owner, h|
61
- key = convert_key(owner[owner_key_name])
62
- h[key] = owner if key
63
- end
71
+ @owners_by_key ||= owners.each_with_object({}) do |owner, result|
72
+ key = convert_key(owner[owner_key_name])
73
+ (result[key] ||= []) << owner if key
64
74
  end
65
- @owners_by_key
66
75
  end
67
76
 
68
77
  def key_conversion_required?
@@ -89,21 +98,14 @@ module ActiveRecord
89
98
  @model.type_for_attribute(owner_key_name).type
90
99
  end
91
100
 
92
- def load_records(&block)
93
- return {} if owner_keys.empty?
94
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
95
- # Make several smaller queries if necessary or make one query if the adapter supports it
96
- slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
97
- @preloaded_records = slices.flat_map do |slice|
98
- records_for(slice, &block)
101
+ def records_for(ids)
102
+ scope.where(association_key_name => ids).load do |record|
103
+ # Processing only the first owner
104
+ # because the record is modified but not an owner
105
+ owner = owners_by_key[convert_key(record[association_key_name])].first
106
+ association = owner.association(reflection.name)
107
+ association.set_inverse_instance(record)
99
108
  end
100
- @preloaded_records.group_by do |record|
101
- convert_key(record[association_key_name])
102
- end
103
- end
104
-
105
- def records_for(ids, &block)
106
- scope.where(association_key_name => ids).load(&block)
107
109
  end
108
110
 
109
111
  def scope
@@ -117,7 +119,7 @@ module ActiveRecord
117
119
  def build_scope
118
120
  scope = klass.scope_for_association
119
121
 
120
- if reflection.type
122
+ if reflection.type && !reflection.through_reflection?
121
123
  scope.where!(reflection.type => model.polymorphic_name)
122
124
  end
123
125
 
@@ -4,42 +4,57 @@ module ActiveRecord
4
4
  module Associations
5
5
  class Preloader
6
6
  class ThroughAssociation < Association # :nodoc:
7
- def run(preloader)
8
- already_loaded = owners.first.association(through_reflection.name).loaded?
9
- through_scope = through_scope()
10
- reflection_scope = target_reflection_scope
11
- through_preloaders = preloader.preload(owners, through_reflection.name, through_scope)
12
- middle_records = through_preloaders.flat_map(&:preloaded_records)
13
- preloaders = preloader.preload(middle_records, source_reflection.name, reflection_scope)
14
- @preloaded_records = preloaders.flat_map(&:preloaded_records)
15
-
16
- owners.each do |owner|
17
- through_records = Array(owner.association(through_reflection.name).target)
18
- if already_loaded
7
+ PRELOADER = ActiveRecord::Associations::Preloader.new
8
+
9
+ def initialize(*)
10
+ super
11
+ @already_loaded = owners.first.association(through_reflection.name).loaded?
12
+ end
13
+
14
+ def preloaded_records
15
+ @preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
16
+ end
17
+
18
+ def records_by_owner
19
+ return @records_by_owner if defined?(@records_by_owner)
20
+ source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
21
+ through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
22
+
23
+ @records_by_owner = owners.each_with_object({}) do |owner, result|
24
+ through_records = through_records_by_owner[owner] || []
25
+
26
+ if @already_loaded
19
27
  if source_type = reflection.options[:source_type]
20
28
  through_records = through_records.select do |record|
21
29
  record[reflection.foreign_type] == source_type
22
30
  end
23
31
  end
24
- else
25
- owner.association(through_reflection.name).reset if through_scope
26
- end
27
- result = through_records.flat_map do |record|
28
- association = record.association(source_reflection.name)
29
- target = association.target
30
- association.reset if preload_scope
31
- target
32
32
  end
33
- result.compact!
34
- if reflection_scope
35
- result.sort_by! { |rhs| preload_index[rhs] } if reflection_scope.order_values.any?
36
- result.uniq! if reflection_scope.distinct_value
33
+
34
+ records = through_records.flat_map do |record|
35
+ source_records_by_owner[record]
37
36
  end
38
- associate_records_to_owner(owner, result)
37
+
38
+ records.compact!
39
+ records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
40
+ records.uniq! if scope.distinct_value
41
+ result[owner] = records
39
42
  end
40
43
  end
41
44
 
42
45
  private
46
+ def source_preloaders
47
+ @source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
48
+ end
49
+
50
+ def middle_records
51
+ through_preloaders.flat_map(&:preloaded_records)
52
+ end
53
+
54
+ def through_preloaders
55
+ @through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
56
+ end
57
+
43
58
  def through_reflection
44
59
  reflection.through_reflection
45
60
  end
@@ -49,8 +64,8 @@ module ActiveRecord
49
64
  end
50
65
 
51
66
  def preload_index
52
- @preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index|
53
- result[id] = index
67
+ @preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
68
+ result[record] = index
54
69
  end
55
70
  end
56
71
 
@@ -58,11 +73,15 @@ module ActiveRecord
58
73
  scope = through_reflection.klass.unscoped
59
74
  options = reflection.options
60
75
 
76
+ values = reflection_scope.values
77
+ if annotations = values[:annotate]
78
+ scope.annotate!(*annotations)
79
+ end
80
+
61
81
  if options[:source_type]
62
82
  scope.where! reflection.foreign_type => options[:source_type]
63
83
  elsif !reflection_scope.where_clause.empty?
64
84
  scope.where_clause = reflection_scope.where_clause
65
- values = reflection_scope.values
66
85
 
67
86
  if includes = values[:includes]
68
87
  scope.includes!(source_reflection.name => includes)
@@ -89,17 +108,7 @@ module ActiveRecord
89
108
  end
90
109
  end
91
110
 
92
- scope unless scope.empty_scope?
93
- end
94
-
95
- def target_reflection_scope
96
- if preload_scope
97
- reflection_scope.merge(preload_scope)
98
- elsif reflection.scope
99
- reflection_scope
100
- else
101
- nil
102
- end
111
+ scope
103
112
  end
104
113
  end
105
114
  end
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  # Implements the reload reader method, e.g. foo.reload_bar for
27
27
  # Foo.has_one :bar
28
28
  def force_reload_reader
29
- klass.uncached { reload }
29
+ reload(true)
30
30
  target
31
31
  end
32
32
 
@@ -36,21 +36,7 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  def find_target
39
- scope = self.scope
40
- return scope.take if skip_statement_cache?(scope)
41
-
42
- conn = klass.connection
43
- sc = reflection.association_scope_cache(conn, owner) do |params|
44
- as = AssociationScope.create { params.bind }
45
- target_scope.merge!(as.scope(self)).limit(1)
46
- end
47
-
48
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
49
- sc.execute(binds, conn) do |record|
50
- set_inverse_instance record
51
- end.first
52
- rescue ::RangeError
53
- nil
39
+ super.first
54
40
  end
55
41
 
56
42
  def replace(record)
@@ -4,11 +4,9 @@ require "active_model/forbidden_attributes_protection"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeAssignment
7
- extend ActiveSupport::Concern
8
7
  include ActiveModel::AttributeAssignment
9
8
 
10
9
  private
11
-
12
10
  def _assign_attributes(attributes)
13
11
  multi_parameter_attributes = {}
14
12
  nested_parameter_attributes = {}
@@ -46,16 +44,14 @@ module ActiveRecord
46
44
  def execute_callstack_for_multiparameter_attributes(callstack)
47
45
  errors = []
48
46
  callstack.each do |name, values_with_empty_parameters|
49
- begin
50
- if values_with_empty_parameters.each_value.all?(&:nil?)
51
- values = nil
52
- else
53
- values = values_with_empty_parameters
54
- end
55
- send("#{name}=", values)
56
- rescue => ex
57
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
47
+ if values_with_empty_parameters.each_value.all?(&:nil?)
48
+ values = nil
49
+ else
50
+ values = values_with_empty_parameters
58
51
  end
52
+ send("#{name}=", values)
53
+ rescue => ex
54
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
59
55
  end
60
56
  unless errors.empty?
61
57
  error_descriptions = errors.map(&:message).join(",")