activerecord 5.2.4.5 → 6.0.0.beta1

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -739
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +32 -29
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +7 -21
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +76 -43
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations.rb +184 -0
  83. data/lib/active_record/database_configurations/database_config.rb +37 -0
  84. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  85. data/lib/active_record/database_configurations/url_config.rb +74 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration.rb +38 -37
  102. data/lib/active_record/migration/command_recorder.rb +35 -5
  103. data/lib/active_record/migration/compatibility.rb +34 -16
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -42
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation.rb +150 -69
  116. data/lib/active_record/relation/batches.rb +13 -10
  117. data/lib/active_record/relation/calculations.rb +38 -28
  118. data/lib/active_record/relation/delegation.rb +4 -13
  119. data/lib/active_record/relation/finder_methods.rb +12 -25
  120. data/lib/active_record/relation/merger.rb +2 -6
  121. data/lib/active_record/relation/predicate_builder.rb +4 -6
  122. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  123. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  126. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  127. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  128. data/lib/active_record/relation/query_attribute.rb +15 -12
  129. data/lib/active_record/relation/query_methods.rb +29 -52
  130. data/lib/active_record/relation/where_clause.rb +4 -0
  131. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping.rb +9 -8
  138. data/lib/active_record/scoping/default.rb +10 -3
  139. data/lib/active_record/scoping/named.rb +10 -14
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/arel.rb +44 -0
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/attributes/attribute.rb +37 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes.rb +67 -0
  174. data/lib/arel/nodes/and.rb +32 -0
  175. data/lib/arel/nodes/ascending.rb +23 -0
  176. data/lib/arel/nodes/binary.rb +52 -0
  177. data/lib/arel/nodes/bind_param.rb +36 -0
  178. data/lib/arel/nodes/case.rb +55 -0
  179. data/lib/arel/nodes/casted.rb +50 -0
  180. data/lib/arel/nodes/count.rb +12 -0
  181. data/lib/arel/nodes/delete_statement.rb +45 -0
  182. data/lib/arel/nodes/descending.rb +23 -0
  183. data/lib/arel/nodes/equality.rb +18 -0
  184. data/lib/arel/nodes/extract.rb +24 -0
  185. data/lib/arel/nodes/false.rb +16 -0
  186. data/lib/arel/nodes/full_outer_join.rb +8 -0
  187. data/lib/arel/nodes/function.rb +44 -0
  188. data/lib/arel/nodes/grouping.rb +8 -0
  189. data/lib/arel/nodes/in.rb +8 -0
  190. data/lib/arel/nodes/infix_operation.rb +80 -0
  191. data/lib/arel/nodes/inner_join.rb +8 -0
  192. data/lib/arel/nodes/insert_statement.rb +37 -0
  193. data/lib/arel/nodes/join_source.rb +20 -0
  194. data/lib/arel/nodes/matches.rb +18 -0
  195. data/lib/arel/nodes/named_function.rb +23 -0
  196. data/lib/arel/nodes/node.rb +50 -0
  197. data/lib/arel/nodes/node_expression.rb +13 -0
  198. data/lib/arel/nodes/outer_join.rb +8 -0
  199. data/lib/arel/nodes/over.rb +15 -0
  200. data/lib/arel/nodes/regexp.rb +16 -0
  201. data/lib/arel/nodes/right_outer_join.rb +8 -0
  202. data/lib/arel/nodes/select_core.rb +63 -0
  203. data/lib/arel/nodes/select_statement.rb +41 -0
  204. data/lib/arel/nodes/sql_literal.rb +16 -0
  205. data/lib/arel/nodes/string_join.rb +11 -0
  206. data/lib/arel/nodes/table_alias.rb +27 -0
  207. data/lib/arel/nodes/terminal.rb +16 -0
  208. data/lib/arel/nodes/true.rb +16 -0
  209. data/lib/arel/nodes/unary.rb +44 -0
  210. data/lib/arel/nodes/unary_operation.rb +20 -0
  211. data/lib/arel/nodes/unqualified_column.rb +22 -0
  212. data/lib/arel/nodes/update_statement.rb +41 -0
  213. data/lib/arel/nodes/values.rb +16 -0
  214. data/lib/arel/nodes/values_list.rb +24 -0
  215. data/lib/arel/nodes/window.rb +126 -0
  216. data/lib/arel/nodes/with.rb +11 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors.rb +20 -0
  224. data/lib/arel/visitors/depth_first.rb +199 -0
  225. data/lib/arel/visitors/dot.rb +292 -0
  226. data/lib/arel/visitors/ibm_db.rb +21 -0
  227. data/lib/arel/visitors/informix.rb +56 -0
  228. data/lib/arel/visitors/mssql.rb +143 -0
  229. data/lib/arel/visitors/mysql.rb +83 -0
  230. data/lib/arel/visitors/oracle.rb +159 -0
  231. data/lib/arel/visitors/oracle12.rb +67 -0
  232. data/lib/arel/visitors/postgresql.rb +116 -0
  233. data/lib/arel/visitors/sqlite.rb +39 -0
  234. data/lib/arel/visitors/to_sql.rb +913 -0
  235. data/lib/arel/visitors/visitor.rb +42 -0
  236. data/lib/arel/visitors/where_sql.rb +23 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/rails/generators/active_record/migration.rb +14 -1
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -14,10 +14,8 @@ module ActiveRecord
14
14
  i[column.name] = column.alias
15
15
  }
16
16
  }
17
- @name_and_alias_cache = tables.each_with_object({}) { |table, h|
18
- h[table.node] = table.columns.map { |column|
19
- [column.name, column.alias]
20
- }
17
+ @columns_cache = tables.each_with_object({}) { |table, h|
18
+ h[table.node] = table.columns
21
19
  }
22
20
  end
23
21
 
@@ -25,9 +23,8 @@ module ActiveRecord
25
23
  @tables.flat_map(&:column_aliases)
26
24
  end
27
25
 
28
- # An array of [column_name, alias] pairs for the table
29
26
  def column_aliases(node)
30
- @name_and_alias_cache[node]
27
+ @columns_cache[node]
31
28
  end
32
29
 
33
30
  def column_alias(node, column)
@@ -116,7 +113,7 @@ module ActiveRecord
116
113
  result_set.each { |row_hash|
117
114
  parent_key = primary_key ? row_hash[primary_key] : row_hash
118
115
  parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
119
- construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
116
+ construct(parent, join_root, row_hash, seen, model_cache)
120
117
  }
121
118
  end
122
119
 
@@ -128,9 +125,11 @@ module ActiveRecord
128
125
  end
129
126
 
130
127
  protected
131
- attr_reader :alias_tracker, :join_root
128
+ attr_reader :join_root
132
129
 
133
130
  private
131
+ attr_reader :alias_tracker
132
+
134
133
  def aliases
135
134
  @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
136
135
  columns = join_part.column_names.each_with_index.map { |column_name, j|
@@ -170,7 +169,7 @@ module ActiveRecord
170
169
  end
171
170
 
172
171
  def table_alias_for(reflection, parent, join)
173
- name = "#{reflection.plural_name}_#{parent.table_name}"
172
+ name = reflection.alias_candidate(parent.table_name)
174
173
  join ? "#{name}_join" : name
175
174
  end
176
175
 
@@ -202,7 +201,7 @@ module ActiveRecord
202
201
  end
203
202
  end
204
203
 
205
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
204
+ def construct(ar_parent, parent, row, seen, model_cache)
206
205
  return if ar_parent.nil?
207
206
 
208
207
  parent.children.each do |node|
@@ -211,7 +210,7 @@ module ActiveRecord
211
210
  other.loaded!
212
211
  elsif ar_parent.association_cached?(node.reflection.name)
213
212
  model = ar_parent.association(node.reflection.name).target
214
- construct(model, node, row, rs, seen, model_cache, aliases)
213
+ construct(model, node, row, seen, model_cache)
215
214
  next
216
215
  end
217
216
 
@@ -226,22 +225,17 @@ module ActiveRecord
226
225
  model = seen[ar_parent.object_id][node][id]
227
226
 
228
227
  if model
229
- construct(model, node, row, rs, seen, model_cache, aliases)
228
+ construct(model, node, row, seen, model_cache)
230
229
  else
231
- model = construct_model(ar_parent, node, row, model_cache, id, aliases)
232
-
233
- if node.reflection.scope &&
234
- node.reflection.scope_for(node.base_klass.unscoped).readonly_value
235
- model.readonly!
236
- end
230
+ model = construct_model(ar_parent, node, row, model_cache, id)
237
231
 
238
232
  seen[ar_parent.object_id][node][id] = model
239
- construct(model, node, row, rs, seen, model_cache, aliases)
233
+ construct(model, node, row, seen, model_cache)
240
234
  end
241
235
  end
242
236
  end
243
237
 
244
- def construct_model(record, node, row, model_cache, id, aliases)
238
+ def construct_model(record, node, row, model_cache, id)
245
239
  other = record.association(node.reflection.name)
246
240
 
247
241
  model = model_cache[node][id] ||=
@@ -255,6 +249,7 @@ module ActiveRecord
255
249
  other.target = model
256
250
  end
257
251
 
252
+ model.readonly! if node.readonly?
258
253
  model
259
254
  end
260
255
  end
@@ -30,21 +30,17 @@ module ActiveRecord
30
30
  table = tables[-i]
31
31
  klass = reflection.klass
32
32
 
33
- join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
33
+ constraint = reflection.build_join_constraint(table, foreign_table)
34
34
 
35
- arel = join_scope.arel(alias_tracker.aliases)
36
- nodes = arel.constraints.first
37
-
38
- others, children = nodes.children.partition do |node|
39
- !fetch_arel_attribute(node) { |attr| attr.relation.name == table.name }
40
- end
41
- nodes = table.create_and(children)
35
+ joins << table.create_join(table, table.create_on(constraint), join_type)
42
36
 
43
- joins << table.create_join(table, table.create_on(nodes), join_type)
37
+ join_scope = reflection.join_scope(table, foreign_klass)
38
+ arel = join_scope.arel(alias_tracker.aliases)
44
39
 
45
- unless others.empty?
40
+ if arel.constraints.any?
46
41
  joins.concat arel.join_sources
47
- append_constraints(joins.last, others)
42
+ right = joins.last.right
43
+ right.expr = right.expr.and(arel.constraints)
48
44
  end
49
45
 
50
46
  # The current table in this iteration becomes the foreign table in the next
@@ -59,22 +55,11 @@ module ActiveRecord
59
55
  @table = tables.first
60
56
  end
61
57
 
62
- private
63
- def fetch_arel_attribute(value)
64
- case value
65
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
66
- yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
67
- end
68
- end
58
+ def readonly?
59
+ return @readonly if defined?(@readonly)
69
60
 
70
- def append_constraints(join, constraints)
71
- if join.is_a?(Arel::Nodes::StringJoin)
72
- join_string = table.create_and(constraints.unshift(join.left))
73
- join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
74
- else
75
- join.right.expr.children.concat(constraints)
76
- end
77
- end
61
+ @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
62
+ end
78
63
  end
79
64
  end
80
65
  end
@@ -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
  }
@@ -98,34 +97,34 @@ module ActiveRecord
98
97
  private
99
98
 
100
99
  # Loads all the given data into +records+ for the +association+.
101
- def preloaders_on(association, records, scope)
100
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
102
101
  case association
103
102
  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)
103
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
104
+ when Symbol, String
105
+ preloaders_for_one(association, records, scope, polymorphic_parent)
109
106
  else
110
107
  raise ArgumentError, "#{association.inspect} was not recognized for preload"
111
108
  end
112
109
  end
113
110
 
114
- def preloaders_for_hash(association, records, scope)
111
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
115
112
  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
113
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
+ recs = loaders.flat_map(&:preloaded_records)
116
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
+ loaders.concat Array.wrap(child).flat_map { |assoc|
118
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
119
+ }
120
+ loaders
121
+ end
123
122
  }
124
123
  end
125
124
 
126
125
  # Loads all the given data into +records+ for a singular +association+.
127
126
  #
128
- # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
127
+ # Functions by instantiating a preloader class such as Preloader::Association and
129
128
  # call the +run+ method for each passed in class in the +records+ argument.
130
129
  #
131
130
  # Not all records have the same class, so group then preload group on the reflection
@@ -135,24 +134,28 @@ module ActiveRecord
135
134
  # Additionally, polymorphic belongs_to associations can have multiple associated
136
135
  # classes, depending on the polymorphic_type field. So we group by the classes as
137
136
  # 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
137
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
138
+ grouped_records(association, records, polymorphic_parent)
139
+ .flat_map do |reflection, reflection_records|
140
+ preloaders_for_reflection reflection, reflection_records, scope
144
141
  end
142
+ end
143
+
144
+ def preloaders_for_reflection(reflection, records, scope)
145
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
146
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
147
+ loader.run self
148
+ loader
145
149
  end
146
150
  end
147
151
 
148
- def grouped_records(association, records)
152
+ def grouped_records(association, records, polymorphic_parent)
149
153
  h = {}
150
154
  records.each do |record|
151
155
  next unless record
152
- assoc = record.association(association)
153
- next unless assoc.klass
154
- klasses = h[assoc.reflection] ||= {}
155
- (klasses[assoc.klass] ||= []) << record
156
+ reflection = record.class._reflect_on_association(association)
157
+ next if polymorphic_parent && !reflection || !record.association(association).klass
158
+ (h[reflection] ||= []) << record
156
159
  end
157
160
  h
158
161
  end
@@ -169,7 +172,7 @@ module ActiveRecord
169
172
  owners.flat_map { |owner| owner.association(reflection.name).target }
170
173
  end
171
174
 
172
- protected
175
+ private
173
176
  attr_reader :owners, :reflection
174
177
  end
175
178
 
@@ -177,7 +180,7 @@ module ActiveRecord
177
180
  # and attach it to a relation. The class returned implements a `run` method
178
181
  # that accepts a preloader.
179
182
  def preloader_for(reflection, owners)
180
- if owners.all? { |o| o.association(reflection.name).loaded? }
183
+ if owners.first.association(reflection.name).loaded?
181
184
  return AlreadyLoaded
182
185
  end
183
186
  reflection.check_preloadable!
@@ -27,10 +27,9 @@ module ActiveRecord
27
27
  end
28
28
  end
29
29
 
30
- protected
30
+ private
31
31
  attr_reader :owners, :reflection, :preload_scope, :model, :klass
32
32
 
33
- private
34
33
  # The name of the key on the associated records
35
34
  def association_key_name
36
35
  reflection.join_primary_key(klass)
@@ -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
@@ -59,7 +50,7 @@ module ActiveRecord
59
50
  # attribute methods.
60
51
  generated_attribute_methods.synchronize do
61
52
  return false if @attribute_methods_generated
62
- superclass.define_attribute_methods unless self == base_class
53
+ superclass.define_attribute_methods unless base_class?
63
54
  super(attribute_names)
64
55
  @attribute_methods_generated = true
65
56
  end
@@ -123,7 +114,7 @@ module ActiveRecord
123
114
  # A class method is 'dangerous' if it is already (re)defined by Active Record, but
124
115
  # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
125
116
  def dangerous_class_method?(method_name)
126
- BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
117
+ RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
127
118
  end
128
119
 
129
120
  def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
@@ -167,12 +158,14 @@ module ActiveRecord
167
158
  end
168
159
  end
169
160
 
170
- # Regexp whitelist. Matches the following:
161
+ # Regexp for column names (with or without a table name prefix). Matches
162
+ # the following:
171
163
  # "#{table_name}.#{column_name}"
172
164
  # "#{column_name}"
173
- COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
165
+ COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
174
166
 
175
- # Regexp whitelist. Matches the following:
167
+ # Regexp for column names with order (with or without a table name
168
+ # prefix, with or without various order modifiers). Matches the following:
176
169
  # "#{table_name}.#{column_name}"
177
170
  # "#{table_name}.#{column_name} #{direction}"
178
171
  # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
@@ -181,7 +174,7 @@ module ActiveRecord
181
174
  # "#{column_name} #{direction}"
182
175
  # "#{column_name} #{direction} NULLS FIRST"
183
176
  # "#{column_name} NULLS LAST"
184
- COLUMN_NAME_ORDER_WHITELIST = /
177
+ COLUMN_NAME_WITH_ORDER = /
185
178
  \A
186
179
  (?:\w+\.)?
187
180
  \w+
@@ -190,12 +183,10 @@ module ActiveRecord
190
183
  \z
191
184
  /ix
192
185
 
193
- def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
186
+ def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
194
187
  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) }
188
+ Arel.arel_node?(arg) ||
189
+ arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
199
190
  end
200
191
 
201
192
  return if unexpected.none?
@@ -270,21 +261,14 @@ module ActiveRecord
270
261
  def respond_to?(name, include_private = false)
271
262
  return false unless super
272
263
 
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
264
  # If the result is true then check for the select case.
283
265
  # For queries selecting a subset of columns, return false for unselected columns.
284
266
  # We check defined?(@attributes) not to issue warnings if called on objects that
285
267
  # have been allocated but not yet initialized.
286
- if defined?(@attributes) && self.class.column_names.include?(name)
287
- return has_attribute?(name)
268
+ if defined?(@attributes)
269
+ if name = self.class.symbol_column_to_string(name.to_sym)
270
+ return has_attribute?(name)
271
+ end
288
272
  end
289
273
 
290
274
  true
@@ -344,15 +328,8 @@ module ActiveRecord
344
328
  # person.attribute_for_inspect(:tag_ids)
345
329
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
346
330
  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
331
+ value = _read_attribute(attr_name)
332
+ format_for_inspect(value)
356
333
  end
357
334
 
358
335
  # Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -443,23 +420,12 @@ module ActiveRecord
443
420
  @attributes.accessed
444
421
  end
445
422
 
446
- protected
447
-
448
- def attribute_method?(attr_name) # :nodoc:
423
+ private
424
+ def attribute_method?(attr_name)
449
425
  # We check defined? because Syck calls respond_to? before actually calling initialize.
450
426
  defined?(@attributes) && @attributes.key?(attr_name)
451
427
  end
452
428
 
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
429
  def attributes_with_values(attribute_names)
464
430
  attribute_names.each_with_object({}) do |name, attrs|
465
431
  attrs[name] = _read_attribute(name)
@@ -468,7 +434,8 @@ module ActiveRecord
468
434
 
469
435
  # Filters the primary keys and readonly attributes from the attribute names.
470
436
  def attributes_for_update(attribute_names)
471
- attribute_names.reject do |name|
437
+ attribute_names &= self.class.column_names
438
+ attribute_names.delete_if do |name|
472
439
  readonly_attribute?(name)
473
440
  end
474
441
  end
@@ -476,11 +443,22 @@ module ActiveRecord
476
443
  # Filters out the primary keys, from the attribute names, when the primary
477
444
  # key is to be generated (e.g. the id attribute has no value).
478
445
  def attributes_for_create(attribute_names)
479
- attribute_names.reject do |name|
446
+ attribute_names &= self.class.column_names
447
+ attribute_names.delete_if do |name|
480
448
  pk_attribute?(name) && id.nil?
481
449
  end
482
450
  end
483
451
 
452
+ def format_for_inspect(value)
453
+ if value.is_a?(String) && value.length > 50
454
+ "#{value[0, 50]}...".inspect
455
+ elsif value.is_a?(Date) || value.is_a?(Time)
456
+ %("#{value.to_s(:db)}")
457
+ else
458
+ value.inspect
459
+ end
460
+ end
461
+
484
462
  def readonly_attribute?(name)
485
463
  self.class.readonly_attributes.include?(name)
486
464
  end