activerecord 5.2.4.2 → 6.0.2.2

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 (269) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +715 -566
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +15 -6
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +61 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +12 -23
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +2 -2
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +5 -19
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +104 -16
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
  89. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  90. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +129 -141
  95. data/lib/active_record/connection_handling.rb +155 -26
  96. data/lib/active_record/core.rb +103 -59
  97. data/lib/active_record/counter_cache.rb +4 -29
  98. data/lib/active_record/database_configurations.rb +233 -0
  99. data/lib/active_record/database_configurations/database_config.rb +37 -0
  100. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  101. data/lib/active_record/database_configurations/url_config.rb +79 -0
  102. data/lib/active_record/dynamic_matchers.rb +1 -1
  103. data/lib/active_record/enum.rb +37 -7
  104. data/lib/active_record/errors.rb +15 -7
  105. data/lib/active_record/explain.rb +1 -1
  106. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  107. data/lib/active_record/fixture_set/render_context.rb +17 -0
  108. data/lib/active_record/fixture_set/table_row.rb +153 -0
  109. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  110. data/lib/active_record/fixtures.rb +145 -472
  111. data/lib/active_record/gem_version.rb +3 -3
  112. data/lib/active_record/inheritance.rb +13 -3
  113. data/lib/active_record/insert_all.rb +179 -0
  114. data/lib/active_record/integration.rb +68 -16
  115. data/lib/active_record/internal_metadata.rb +10 -2
  116. data/lib/active_record/locking/optimistic.rb +5 -6
  117. data/lib/active_record/locking/pessimistic.rb +3 -3
  118. data/lib/active_record/log_subscriber.rb +7 -26
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  121. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/migration/command_recorder.rb +50 -6
  124. data/lib/active_record/migration/compatibility.rb +76 -49
  125. data/lib/active_record/model_schema.rb +33 -9
  126. data/lib/active_record/nested_attributes.rb +2 -2
  127. data/lib/active_record/no_touching.rb +7 -0
  128. data/lib/active_record/persistence.rb +228 -24
  129. data/lib/active_record/query_cache.rb +11 -4
  130. data/lib/active_record/querying.rb +32 -20
  131. data/lib/active_record/railtie.rb +80 -43
  132. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  133. data/lib/active_record/railties/controller_runtime.rb +30 -35
  134. data/lib/active_record/railties/databases.rake +199 -46
  135. data/lib/active_record/reflection.rb +32 -30
  136. data/lib/active_record/relation.rb +311 -80
  137. data/lib/active_record/relation/batches.rb +13 -10
  138. data/lib/active_record/relation/calculations.rb +53 -47
  139. data/lib/active_record/relation/delegation.rb +26 -43
  140. data/lib/active_record/relation/finder_methods.rb +23 -27
  141. data/lib/active_record/relation/merger.rb +11 -20
  142. data/lib/active_record/relation/predicate_builder.rb +4 -6
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  145. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  148. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  149. data/lib/active_record/relation/query_attribute.rb +13 -8
  150. data/lib/active_record/relation/query_methods.rb +213 -64
  151. data/lib/active_record/relation/spawn_methods.rb +1 -1
  152. data/lib/active_record/relation/where_clause.rb +14 -10
  153. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  154. data/lib/active_record/result.rb +30 -11
  155. data/lib/active_record/sanitization.rb +32 -40
  156. data/lib/active_record/schema.rb +2 -11
  157. data/lib/active_record/schema_dumper.rb +22 -7
  158. data/lib/active_record/schema_migration.rb +5 -1
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/scoping/default.rb +4 -5
  161. data/lib/active_record/scoping/named.rb +20 -15
  162. data/lib/active_record/statement_cache.rb +30 -3
  163. data/lib/active_record/store.rb +87 -8
  164. data/lib/active_record/table_metadata.rb +10 -17
  165. data/lib/active_record/tasks/database_tasks.rb +194 -25
  166. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  167. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  168. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  169. data/lib/active_record/test_databases.rb +23 -0
  170. data/lib/active_record/test_fixtures.rb +225 -0
  171. data/lib/active_record/timestamp.rb +39 -25
  172. data/lib/active_record/touch_later.rb +4 -2
  173. data/lib/active_record/transactions.rb +56 -65
  174. data/lib/active_record/translation.rb +1 -1
  175. data/lib/active_record/type.rb +3 -4
  176. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  177. data/lib/active_record/type_caster/connection.rb +15 -14
  178. data/lib/active_record/type_caster/map.rb +1 -4
  179. data/lib/active_record/validations.rb +1 -0
  180. data/lib/active_record/validations/uniqueness.rb +15 -27
  181. data/lib/arel.rb +58 -0
  182. data/lib/arel/alias_predication.rb +9 -0
  183. data/lib/arel/attributes.rb +22 -0
  184. data/lib/arel/attributes/attribute.rb +37 -0
  185. data/lib/arel/collectors/bind.rb +24 -0
  186. data/lib/arel/collectors/composite.rb +31 -0
  187. data/lib/arel/collectors/plain_string.rb +20 -0
  188. data/lib/arel/collectors/sql_string.rb +20 -0
  189. data/lib/arel/collectors/substitute_binds.rb +28 -0
  190. data/lib/arel/crud.rb +42 -0
  191. data/lib/arel/delete_manager.rb +18 -0
  192. data/lib/arel/errors.rb +9 -0
  193. data/lib/arel/expressions.rb +29 -0
  194. data/lib/arel/factory_methods.rb +49 -0
  195. data/lib/arel/insert_manager.rb +49 -0
  196. data/lib/arel/math.rb +45 -0
  197. data/lib/arel/nodes.rb +68 -0
  198. data/lib/arel/nodes/and.rb +32 -0
  199. data/lib/arel/nodes/ascending.rb +23 -0
  200. data/lib/arel/nodes/binary.rb +52 -0
  201. data/lib/arel/nodes/bind_param.rb +36 -0
  202. data/lib/arel/nodes/case.rb +55 -0
  203. data/lib/arel/nodes/casted.rb +50 -0
  204. data/lib/arel/nodes/comment.rb +29 -0
  205. data/lib/arel/nodes/count.rb +12 -0
  206. data/lib/arel/nodes/delete_statement.rb +45 -0
  207. data/lib/arel/nodes/descending.rb +23 -0
  208. data/lib/arel/nodes/equality.rb +18 -0
  209. data/lib/arel/nodes/extract.rb +24 -0
  210. data/lib/arel/nodes/false.rb +16 -0
  211. data/lib/arel/nodes/full_outer_join.rb +8 -0
  212. data/lib/arel/nodes/function.rb +44 -0
  213. data/lib/arel/nodes/grouping.rb +8 -0
  214. data/lib/arel/nodes/in.rb +8 -0
  215. data/lib/arel/nodes/infix_operation.rb +80 -0
  216. data/lib/arel/nodes/inner_join.rb +8 -0
  217. data/lib/arel/nodes/insert_statement.rb +37 -0
  218. data/lib/arel/nodes/join_source.rb +20 -0
  219. data/lib/arel/nodes/matches.rb +18 -0
  220. data/lib/arel/nodes/named_function.rb +23 -0
  221. data/lib/arel/nodes/node.rb +50 -0
  222. data/lib/arel/nodes/node_expression.rb +13 -0
  223. data/lib/arel/nodes/outer_join.rb +8 -0
  224. data/lib/arel/nodes/over.rb +15 -0
  225. data/lib/arel/nodes/regexp.rb +16 -0
  226. data/lib/arel/nodes/right_outer_join.rb +8 -0
  227. data/lib/arel/nodes/select_core.rb +67 -0
  228. data/lib/arel/nodes/select_statement.rb +41 -0
  229. data/lib/arel/nodes/sql_literal.rb +16 -0
  230. data/lib/arel/nodes/string_join.rb +11 -0
  231. data/lib/arel/nodes/table_alias.rb +27 -0
  232. data/lib/arel/nodes/terminal.rb +16 -0
  233. data/lib/arel/nodes/true.rb +16 -0
  234. data/lib/arel/nodes/unary.rb +45 -0
  235. data/lib/arel/nodes/unary_operation.rb +20 -0
  236. data/lib/arel/nodes/unqualified_column.rb +22 -0
  237. data/lib/arel/nodes/update_statement.rb +41 -0
  238. data/lib/arel/nodes/values_list.rb +9 -0
  239. data/lib/arel/nodes/window.rb +126 -0
  240. data/lib/arel/nodes/with.rb +11 -0
  241. data/lib/arel/order_predications.rb +13 -0
  242. data/lib/arel/predications.rb +257 -0
  243. data/lib/arel/select_manager.rb +271 -0
  244. data/lib/arel/table.rb +110 -0
  245. data/lib/arel/tree_manager.rb +72 -0
  246. data/lib/arel/update_manager.rb +34 -0
  247. data/lib/arel/visitors.rb +20 -0
  248. data/lib/arel/visitors/depth_first.rb +204 -0
  249. data/lib/arel/visitors/dot.rb +297 -0
  250. data/lib/arel/visitors/ibm_db.rb +34 -0
  251. data/lib/arel/visitors/informix.rb +62 -0
  252. data/lib/arel/visitors/mssql.rb +157 -0
  253. data/lib/arel/visitors/mysql.rb +83 -0
  254. data/lib/arel/visitors/oracle.rb +159 -0
  255. data/lib/arel/visitors/oracle12.rb +66 -0
  256. data/lib/arel/visitors/postgresql.rb +110 -0
  257. data/lib/arel/visitors/sqlite.rb +39 -0
  258. data/lib/arel/visitors/to_sql.rb +889 -0
  259. data/lib/arel/visitors/visitor.rb +46 -0
  260. data/lib/arel/visitors/where_sql.rb +23 -0
  261. data/lib/arel/window_predications.rb +9 -0
  262. data/lib/rails/generators/active_record/migration.rb +14 -1
  263. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  264. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  265. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  266. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  267. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  268. metadata +109 -24
  269. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -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,7 +4,6 @@ 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
@@ -46,16 +45,14 @@ module ActiveRecord
46
45
  def execute_callstack_for_multiparameter_attributes(callstack)
47
46
  errors = []
48
47
  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)
48
+ if values_with_empty_parameters.each_value.all?(&:nil?)
49
+ values = nil
50
+ else
51
+ values = values_with_empty_parameters
58
52
  end
53
+ send("#{name}=", values)
54
+ rescue => ex
55
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
59
56
  end
60
57
  unless errors.empty?
61
58
  error_descriptions = errors.map(&:message).join(",")
@@ -22,16 +22,7 @@ module ActiveRecord
22
22
  delegate :column_for_attribute, to: :class
23
23
  end
24
24
 
25
- AttrNames = Module.new {
26
- def self.set_name_cache(name, value)
27
- const_name = "ATTR_#{name}"
28
- unless const_defined? const_name
29
- const_set const_name, value.dup.freeze
30
- end
31
- end
32
- }
33
-
34
- BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
25
+ RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
35
26
 
36
27
  class GeneratedAttributeMethods < Module #:nodoc:
37
28
  include Mutex_m
@@ -44,7 +35,8 @@ module ActiveRecord
44
35
  end
45
36
 
46
37
  def initialize_generated_modules # :nodoc:
47
- @generated_attribute_methods = GeneratedAttributeMethods.new
38
+ @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
39
+ private_constant :GeneratedAttributeMethods
48
40
  @attribute_methods_generated = false
49
41
  include @generated_attribute_methods
50
42
 
@@ -59,7 +51,7 @@ module ActiveRecord
59
51
  # attribute methods.
60
52
  generated_attribute_methods.synchronize do
61
53
  return false if @attribute_methods_generated
62
- superclass.define_attribute_methods unless self == base_class
54
+ superclass.define_attribute_methods unless base_class?
63
55
  super(attribute_names)
64
56
  @attribute_methods_generated = true
65
57
  end
@@ -123,7 +115,7 @@ module ActiveRecord
123
115
  # A class method is 'dangerous' if it is already (re)defined by Active Record, but
124
116
  # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
125
117
  def dangerous_class_method?(method_name)
126
- BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
118
+ RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
127
119
  end
128
120
 
129
121
  def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
@@ -167,57 +159,6 @@ module ActiveRecord
167
159
  end
168
160
  end
169
161
 
170
- # Regexp whitelist. Matches the following:
171
- # "#{table_name}.#{column_name}"
172
- # "#{column_name}"
173
- COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
174
-
175
- # Regexp whitelist. Matches the following:
176
- # "#{table_name}.#{column_name}"
177
- # "#{table_name}.#{column_name} #{direction}"
178
- # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
179
- # "#{table_name}.#{column_name} NULLS LAST"
180
- # "#{column_name}"
181
- # "#{column_name} #{direction}"
182
- # "#{column_name} #{direction} NULLS FIRST"
183
- # "#{column_name} NULLS LAST"
184
- COLUMN_NAME_ORDER_WHITELIST = /
185
- \A
186
- (?:\w+\.)?
187
- \w+
188
- (?:\s+asc|\s+desc)?
189
- (?:\s+nulls\s+(?:first|last))?
190
- \z
191
- /ix
192
-
193
- def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
194
- unexpected = args.reject do |arg|
195
- arg.kind_of?(Arel::Node) ||
196
- arg.is_a?(Arel::Nodes::SqlLiteral) ||
197
- arg.is_a?(Arel::Attributes::Attribute) ||
198
- arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
199
- end
200
-
201
- return if unexpected.none?
202
-
203
- if allow_unsafe_raw_sql == :deprecated
204
- ActiveSupport::Deprecation.warn(
205
- "Dangerous query method (method whose arguments are used as raw " \
206
- "SQL) called with non-attribute argument(s): " \
207
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
208
- "arguments will be disallowed in Rails 6.0. This method should " \
209
- "not be called with user-provided values, such as request " \
210
- "parameters or model attributes. Known-safe values can be passed " \
211
- "by wrapping them in Arel.sql()."
212
- )
213
- else
214
- raise(ActiveRecord::UnknownAttributeReference,
215
- "Query method called with non-attribute argument(s): " +
216
- unexpected.map(&:inspect).join(", ")
217
- )
218
- end
219
- end
220
-
221
162
  # Returns true if the given attribute exists, otherwise false.
222
163
  #
223
164
  # class Person < ActiveRecord::Base
@@ -270,21 +211,14 @@ module ActiveRecord
270
211
  def respond_to?(name, include_private = false)
271
212
  return false unless super
272
213
 
273
- case name
274
- when :to_partial_path
275
- name = "to_partial_path".freeze
276
- when :to_model
277
- name = "to_model".freeze
278
- else
279
- name = name.to_s
280
- end
281
-
282
214
  # If the result is true then check for the select case.
283
215
  # For queries selecting a subset of columns, return false for unselected columns.
284
216
  # We check defined?(@attributes) not to issue warnings if called on objects that
285
217
  # have been allocated but not yet initialized.
286
- if defined?(@attributes) && self.class.column_names.include?(name)
287
- return has_attribute?(name)
218
+ if defined?(@attributes)
219
+ if name = self.class.symbol_column_to_string(name.to_sym)
220
+ return has_attribute?(name)
221
+ end
288
222
  end
289
223
 
290
224
  true
@@ -344,15 +278,8 @@ module ActiveRecord
344
278
  # person.attribute_for_inspect(:tag_ids)
345
279
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
346
280
  def attribute_for_inspect(attr_name)
347
- value = read_attribute(attr_name)
348
-
349
- if value.is_a?(String) && value.length > 50
350
- "#{value[0, 50]}...".inspect
351
- elsif value.is_a?(Date) || value.is_a?(Time)
352
- %("#{value.to_s(:db)}")
353
- else
354
- value.inspect
355
- end
281
+ value = _read_attribute(attr_name)
282
+ format_for_inspect(value)
356
283
  end
357
284
 
358
285
  # Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -443,23 +370,12 @@ module ActiveRecord
443
370
  @attributes.accessed
444
371
  end
445
372
 
446
- protected
447
-
448
- def attribute_method?(attr_name) # :nodoc:
373
+ private
374
+ def attribute_method?(attr_name)
449
375
  # We check defined? because Syck calls respond_to? before actually calling initialize.
450
376
  defined?(@attributes) && @attributes.key?(attr_name)
451
377
  end
452
378
 
453
- private
454
-
455
- def attributes_with_values_for_create(attribute_names)
456
- attributes_with_values(attributes_for_create(attribute_names))
457
- end
458
-
459
- def attributes_with_values_for_update(attribute_names)
460
- attributes_with_values(attributes_for_update(attribute_names))
461
- end
462
-
463
379
  def attributes_with_values(attribute_names)
464
380
  attribute_names.each_with_object({}) do |name, attrs|
465
381
  attrs[name] = _read_attribute(name)
@@ -468,7 +384,8 @@ module ActiveRecord
468
384
 
469
385
  # Filters the primary keys and readonly attributes from the attribute names.
470
386
  def attributes_for_update(attribute_names)
471
- attribute_names.reject do |name|
387
+ attribute_names &= self.class.column_names
388
+ attribute_names.delete_if do |name|
472
389
  readonly_attribute?(name)
473
390
  end
474
391
  end
@@ -476,17 +393,28 @@ module ActiveRecord
476
393
  # Filters out the primary keys, from the attribute names, when the primary
477
394
  # key is to be generated (e.g. the id attribute has no value).
478
395
  def attributes_for_create(attribute_names)
479
- attribute_names.reject do |name|
396
+ attribute_names &= self.class.column_names
397
+ attribute_names.delete_if do |name|
480
398
  pk_attribute?(name) && id.nil?
481
399
  end
482
400
  end
483
401
 
402
+ def format_for_inspect(value)
403
+ if value.is_a?(String) && value.length > 50
404
+ "#{value[0, 50]}...".inspect
405
+ elsif value.is_a?(Date) || value.is_a?(Time)
406
+ %("#{value.to_s(:db)}")
407
+ else
408
+ value.inspect
409
+ end
410
+ end
411
+
484
412
  def readonly_attribute?(name)
485
413
  self.class.readonly_attributes.include?(name)
486
414
  end
487
415
 
488
416
  def pk_attribute?(name)
489
- name == self.class.primary_key
417
+ name == @primary_key
490
418
  end
491
419
  end
492
420
  end