activerecord 5.2.1.1 → 6.0.1

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

Potentially problematic release.


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

Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +738 -445
  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 +18 -9
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +69 -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 +5 -15
  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 +15 -29
  22. data/lib/active_record/associations/collection_proxy.rb +19 -48
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +11 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +42 -25
  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 +27 -7
  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 +114 -38
  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 +27 -13
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +6 -20
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
  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 +135 -56
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
  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 +8 -2
  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 +47 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
  90. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  91. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  92. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  93. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  96. data/lib/active_record/connection_handling.rb +155 -26
  97. data/lib/active_record/core.rb +104 -59
  98. data/lib/active_record/counter_cache.rb +4 -29
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/database_configurations/database_config.rb +37 -0
  101. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  102. data/lib/active_record/database_configurations/url_config.rb +79 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/enum.rb +38 -7
  105. data/lib/active_record/errors.rb +30 -16
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  108. data/lib/active_record/fixture_set/render_context.rb +17 -0
  109. data/lib/active_record/fixture_set/table_row.rb +153 -0
  110. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  111. data/lib/active_record/fixtures.rb +145 -472
  112. data/lib/active_record/gem_version.rb +3 -3
  113. data/lib/active_record/inheritance.rb +13 -3
  114. data/lib/active_record/insert_all.rb +179 -0
  115. data/lib/active_record/integration.rb +68 -16
  116. data/lib/active_record/internal_metadata.rb +10 -2
  117. data/lib/active_record/locking/optimistic.rb +5 -6
  118. data/lib/active_record/locking/pessimistic.rb +3 -3
  119. data/lib/active_record/log_subscriber.rb +7 -26
  120. data/lib/active_record/middleware/database_selector.rb +75 -0
  121. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  122. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  123. data/lib/active_record/migration.rb +100 -81
  124. data/lib/active_record/migration/command_recorder.rb +50 -6
  125. data/lib/active_record/migration/compatibility.rb +91 -64
  126. data/lib/active_record/model_schema.rb +34 -10
  127. data/lib/active_record/nested_attributes.rb +2 -2
  128. data/lib/active_record/no_touching.rb +7 -0
  129. data/lib/active_record/persistence.rb +233 -28
  130. data/lib/active_record/query_cache.rb +11 -4
  131. data/lib/active_record/querying.rb +33 -21
  132. data/lib/active_record/railtie.rb +81 -46
  133. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  134. data/lib/active_record/railties/controller_runtime.rb +30 -35
  135. data/lib/active_record/railties/databases.rake +196 -46
  136. data/lib/active_record/reflection.rb +42 -44
  137. data/lib/active_record/relation.rb +320 -70
  138. data/lib/active_record/relation/batches.rb +13 -10
  139. data/lib/active_record/relation/calculations.rb +67 -57
  140. data/lib/active_record/relation/delegation.rb +48 -35
  141. data/lib/active_record/relation/finder_methods.rb +30 -30
  142. data/lib/active_record/relation/merger.rb +19 -25
  143. data/lib/active_record/relation/predicate_builder.rb +18 -15
  144. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
  145. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  146. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  148. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  149. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  150. data/lib/active_record/relation/query_attribute.rb +17 -10
  151. data/lib/active_record/relation/query_methods.rb +236 -73
  152. data/lib/active_record/relation/spawn_methods.rb +1 -1
  153. data/lib/active_record/relation/where_clause.rb +14 -10
  154. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  155. data/lib/active_record/result.rb +30 -11
  156. data/lib/active_record/sanitization.rb +32 -40
  157. data/lib/active_record/schema.rb +2 -11
  158. data/lib/active_record/schema_dumper.rb +22 -7
  159. data/lib/active_record/schema_migration.rb +5 -1
  160. data/lib/active_record/scoping.rb +8 -8
  161. data/lib/active_record/scoping/default.rb +6 -7
  162. data/lib/active_record/scoping/named.rb +21 -15
  163. data/lib/active_record/statement_cache.rb +32 -5
  164. data/lib/active_record/store.rb +87 -8
  165. data/lib/active_record/table_metadata.rb +10 -17
  166. data/lib/active_record/tasks/database_tasks.rb +195 -26
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  170. data/lib/active_record/test_databases.rb +23 -0
  171. data/lib/active_record/test_fixtures.rb +224 -0
  172. data/lib/active_record/timestamp.rb +39 -25
  173. data/lib/active_record/touch_later.rb +4 -2
  174. data/lib/active_record/transactions.rb +57 -66
  175. data/lib/active_record/translation.rb +1 -1
  176. data/lib/active_record/type.rb +3 -4
  177. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  178. data/lib/active_record/type_caster/connection.rb +15 -14
  179. data/lib/active_record/type_caster/map.rb +1 -4
  180. data/lib/active_record/validations.rb +1 -0
  181. data/lib/active_record/validations/uniqueness.rb +15 -27
  182. data/lib/arel.rb +58 -0
  183. data/lib/arel/alias_predication.rb +9 -0
  184. data/lib/arel/attributes.rb +22 -0
  185. data/lib/arel/attributes/attribute.rb +37 -0
  186. data/lib/arel/collectors/bind.rb +24 -0
  187. data/lib/arel/collectors/composite.rb +31 -0
  188. data/lib/arel/collectors/plain_string.rb +20 -0
  189. data/lib/arel/collectors/sql_string.rb +20 -0
  190. data/lib/arel/collectors/substitute_binds.rb +28 -0
  191. data/lib/arel/crud.rb +42 -0
  192. data/lib/arel/delete_manager.rb +18 -0
  193. data/lib/arel/errors.rb +9 -0
  194. data/lib/arel/expressions.rb +29 -0
  195. data/lib/arel/factory_methods.rb +49 -0
  196. data/lib/arel/insert_manager.rb +49 -0
  197. data/lib/arel/math.rb +45 -0
  198. data/lib/arel/nodes.rb +68 -0
  199. data/lib/arel/nodes/and.rb +32 -0
  200. data/lib/arel/nodes/ascending.rb +23 -0
  201. data/lib/arel/nodes/binary.rb +52 -0
  202. data/lib/arel/nodes/bind_param.rb +36 -0
  203. data/lib/arel/nodes/case.rb +55 -0
  204. data/lib/arel/nodes/casted.rb +50 -0
  205. data/lib/arel/nodes/comment.rb +29 -0
  206. data/lib/arel/nodes/count.rb +12 -0
  207. data/lib/arel/nodes/delete_statement.rb +45 -0
  208. data/lib/arel/nodes/descending.rb +23 -0
  209. data/lib/arel/nodes/equality.rb +18 -0
  210. data/lib/arel/nodes/extract.rb +24 -0
  211. data/lib/arel/nodes/false.rb +16 -0
  212. data/lib/arel/nodes/full_outer_join.rb +8 -0
  213. data/lib/arel/nodes/function.rb +44 -0
  214. data/lib/arel/nodes/grouping.rb +8 -0
  215. data/lib/arel/nodes/in.rb +8 -0
  216. data/lib/arel/nodes/infix_operation.rb +80 -0
  217. data/lib/arel/nodes/inner_join.rb +8 -0
  218. data/lib/arel/nodes/insert_statement.rb +37 -0
  219. data/lib/arel/nodes/join_source.rb +20 -0
  220. data/lib/arel/nodes/matches.rb +18 -0
  221. data/lib/arel/nodes/named_function.rb +23 -0
  222. data/lib/arel/nodes/node.rb +50 -0
  223. data/lib/arel/nodes/node_expression.rb +13 -0
  224. data/lib/arel/nodes/outer_join.rb +8 -0
  225. data/lib/arel/nodes/over.rb +15 -0
  226. data/lib/arel/nodes/regexp.rb +16 -0
  227. data/lib/arel/nodes/right_outer_join.rb +8 -0
  228. data/lib/arel/nodes/select_core.rb +67 -0
  229. data/lib/arel/nodes/select_statement.rb +41 -0
  230. data/lib/arel/nodes/sql_literal.rb +16 -0
  231. data/lib/arel/nodes/string_join.rb +11 -0
  232. data/lib/arel/nodes/table_alias.rb +27 -0
  233. data/lib/arel/nodes/terminal.rb +16 -0
  234. data/lib/arel/nodes/true.rb +16 -0
  235. data/lib/arel/nodes/unary.rb +45 -0
  236. data/lib/arel/nodes/unary_operation.rb +20 -0
  237. data/lib/arel/nodes/unqualified_column.rb +22 -0
  238. data/lib/arel/nodes/update_statement.rb +41 -0
  239. data/lib/arel/nodes/values_list.rb +9 -0
  240. data/lib/arel/nodes/window.rb +126 -0
  241. data/lib/arel/nodes/with.rb +11 -0
  242. data/lib/arel/order_predications.rb +13 -0
  243. data/lib/arel/predications.rb +257 -0
  244. data/lib/arel/select_manager.rb +271 -0
  245. data/lib/arel/table.rb +110 -0
  246. data/lib/arel/tree_manager.rb +72 -0
  247. data/lib/arel/update_manager.rb +34 -0
  248. data/lib/arel/visitors.rb +20 -0
  249. data/lib/arel/visitors/depth_first.rb +204 -0
  250. data/lib/arel/visitors/dot.rb +297 -0
  251. data/lib/arel/visitors/ibm_db.rb +34 -0
  252. data/lib/arel/visitors/informix.rb +62 -0
  253. data/lib/arel/visitors/mssql.rb +157 -0
  254. data/lib/arel/visitors/mysql.rb +83 -0
  255. data/lib/arel/visitors/oracle.rb +159 -0
  256. data/lib/arel/visitors/oracle12.rb +66 -0
  257. data/lib/arel/visitors/postgresql.rb +110 -0
  258. data/lib/arel/visitors/sqlite.rb +39 -0
  259. data/lib/arel/visitors/to_sql.rb +889 -0
  260. data/lib/arel/visitors/visitor.rb +46 -0
  261. data/lib/arel/visitors/where_sql.rb +23 -0
  262. data/lib/arel/window_predications.rb +9 -0
  263. data/lib/rails/generators/active_record/migration.rb +14 -1
  264. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  265. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  266. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  267. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  268. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  269. metadata +111 -27
  270. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -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
@@ -46,6 +46,7 @@ module ActiveRecord
46
46
  # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
47
47
  # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
48
48
  def read_attribute_before_type_cast(attr_name)
49
+ sync_with_transaction_state if @transaction_state&.finalized?
49
50
  @attributes[attr_name.to_s].value_before_type_cast
50
51
  end
51
52
 
@@ -60,17 +61,19 @@ module ActiveRecord
60
61
  # task.attributes_before_type_cast
61
62
  # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
62
63
  def attributes_before_type_cast
64
+ sync_with_transaction_state if @transaction_state&.finalized?
63
65
  @attributes.values_before_type_cast
64
66
  end
65
67
 
66
68
  private
67
69
 
68
- # Handle *_before_type_cast for method_missing.
70
+ # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
69
71
  def attribute_before_type_cast(attribute_name)
70
72
  read_attribute_before_type_cast(attribute_name)
71
73
  end
72
74
 
73
75
  def attribute_came_from_user?(attribute_name)
76
+ sync_with_transaction_state if @transaction_state&.finalized?
74
77
  @attributes[attribute_name].came_from_user?
75
78
  end
76
79
  end
@@ -16,9 +16,6 @@ module ActiveRecord
16
16
 
17
17
  class_attribute :partial_writes, instance_writer: false, default: true
18
18
 
19
- after_create { changes_applied }
20
- after_update { changes_applied }
21
-
22
19
  # Attribute methods for "changed in last call to save?"
23
20
  attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
24
21
  attribute_method_prefix("saved_change_to_")
@@ -32,18 +29,17 @@ module ActiveRecord
32
29
  # <tt>reload</tt> the record and clears changed attributes.
33
30
  def reload(*)
34
31
  super.tap do
35
- @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
36
32
  @mutations_before_last_save = nil
37
- @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
38
33
  @mutations_from_database = nil
39
34
  end
40
35
  end
41
36
 
42
- # Did this attribute change when we last saved? This method can be invoked
43
- # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
44
- # Behaves similarly to +attribute_changed?+. This method is useful in
45
- # after callbacks to determine if the call to save changed a certain
46
- # attribute.
37
+ # Did this attribute change when we last saved?
38
+ #
39
+ # This method is useful in after callbacks to determine if an attribute
40
+ # was changed during the save that triggered the callbacks to run. It can
41
+ # be invoked as +saved_change_to_name?+ instead of
42
+ # <tt>saved_change_to_attribute?("name")</tt>.
47
43
  #
48
44
  # ==== Options
49
45
  #
@@ -53,28 +49,29 @@ module ActiveRecord
53
49
  # +to+ When passed, this method will return false unless the value was
54
50
  # changed to the given value
55
51
  def saved_change_to_attribute?(attr_name, **options)
56
- mutations_before_last_save.changed?(attr_name, **options)
52
+ mutations_before_last_save.changed?(attr_name.to_s, options)
57
53
  end
58
54
 
59
55
  # Returns the change to an attribute during the last save. If the
60
56
  # attribute was changed, the result will be an array containing the
61
57
  # original value and the saved value.
62
58
  #
63
- # Behaves similarly to +attribute_change+. This method is useful in after
64
- # callbacks, to see the change in an attribute that just occurred
65
- #
66
- # This method can be invoked as +saved_change_to_name+ in instead of
67
- # <tt>saved_change_to_attribute("name")</tt>
59
+ # This method is useful in after callbacks, to see the change in an
60
+ # attribute during the save that triggered the callbacks to run. It can be
61
+ # invoked as +saved_change_to_name+ instead of
62
+ # <tt>saved_change_to_attribute("name")</tt>.
68
63
  def saved_change_to_attribute(attr_name)
69
- mutations_before_last_save.change_to_attribute(attr_name)
64
+ mutations_before_last_save.change_to_attribute(attr_name.to_s)
70
65
  end
71
66
 
72
67
  # Returns the original value of an attribute before the last save.
73
- # Behaves similarly to +attribute_was+. This method is useful in after
74
- # callbacks to get the original value of an attribute before the save that
75
- # just occurred
68
+ #
69
+ # This method is useful in after callbacks to get the original value of an
70
+ # attribute before the save that triggered the callbacks to run. It can be
71
+ # invoked as +name_before_last_save+ instead of
72
+ # <tt>attribute_before_last_save("name")</tt>.
76
73
  def attribute_before_last_save(attr_name)
77
- mutations_before_last_save.original_value(attr_name)
74
+ mutations_before_last_save.original_value(attr_name.to_s)
78
75
  end
79
76
 
80
77
  # Did the last call to +save+ have any changes to change?
@@ -87,58 +84,137 @@ module ActiveRecord
87
84
  mutations_before_last_save.changes
88
85
  end
89
86
 
90
- # Alias for +attribute_changed?+
87
+ # Will this attribute change the next time we save?
88
+ #
89
+ # This method is useful in validations and before callbacks to determine
90
+ # if the next call to +save+ will change a particular attribute. It can be
91
+ # invoked as +will_save_change_to_name?+ instead of
92
+ # <tt>will_save_change_to_attribute("name")</tt>.
93
+ #
94
+ # ==== Options
95
+ #
96
+ # +from+ When passed, this method will return false unless the original
97
+ # value is equal to the given option
98
+ #
99
+ # +to+ When passed, this method will return false unless the value will be
100
+ # changed to the given value
91
101
  def will_save_change_to_attribute?(attr_name, **options)
92
- mutations_from_database.changed?(attr_name, **options)
102
+ mutations_from_database.changed?(attr_name.to_s, options)
93
103
  end
94
104
 
95
- # Alias for +attribute_change+
105
+ # Returns the change to an attribute that will be persisted during the
106
+ # next save.
107
+ #
108
+ # This method is useful in validations and before callbacks, to see the
109
+ # change to an attribute that will occur when the record is saved. It can
110
+ # be invoked as +name_change_to_be_saved+ instead of
111
+ # <tt>attribute_change_to_be_saved("name")</tt>.
112
+ #
113
+ # If the attribute will change, the result will be an array containing the
114
+ # original value and the new value about to be saved.
96
115
  def attribute_change_to_be_saved(attr_name)
97
- mutations_from_database.change_to_attribute(attr_name)
116
+ mutations_from_database.change_to_attribute(attr_name.to_s)
98
117
  end
99
118
 
100
- # Alias for +attribute_was+
119
+ # Returns the value of an attribute in the database, as opposed to the
120
+ # in-memory value that will be persisted the next time the record is
121
+ # saved.
122
+ #
123
+ # This method is useful in validations and before callbacks, to see the
124
+ # original value of an attribute prior to any changes about to be
125
+ # saved. It can be invoked as +name_in_database+ instead of
126
+ # <tt>attribute_in_database("name")</tt>.
101
127
  def attribute_in_database(attr_name)
102
- mutations_from_database.original_value(attr_name)
128
+ mutations_from_database.original_value(attr_name.to_s)
103
129
  end
104
130
 
105
- # Alias for +changed?+
131
+ # Will the next call to +save+ have any changes to persist?
106
132
  def has_changes_to_save?
107
133
  mutations_from_database.any_changes?
108
134
  end
109
135
 
110
- # Alias for +changes+
136
+ # Returns a hash containing all the changes that will be persisted during
137
+ # the next save.
111
138
  def changes_to_save
112
139
  mutations_from_database.changes
113
140
  end
114
141
 
115
- # Alias for +changed+
142
+ # Returns an array of the names of any attributes that will change when
143
+ # the record is next saved.
116
144
  def changed_attribute_names_to_save
117
145
  mutations_from_database.changed_attribute_names
118
146
  end
119
147
 
120
- # Alias for +changed_attributes+
148
+ # Returns a hash of the attributes that will change when the record is
149
+ # next saved.
150
+ #
151
+ # The hash keys are the attribute names, and the hash values are the
152
+ # original attribute values in the database (as opposed to the in-memory
153
+ # values about to be saved).
121
154
  def attributes_in_database
122
155
  mutations_from_database.changed_values
123
156
  end
124
157
 
125
158
  private
126
- def write_attribute_without_type_cast(attr_name, _)
159
+ def mutations_from_database
160
+ sync_with_transaction_state if @transaction_state&.finalized?
161
+ super
162
+ end
163
+
164
+ def mutations_before_last_save
165
+ sync_with_transaction_state if @transaction_state&.finalized?
166
+ super
167
+ end
168
+
169
+ def write_attribute_without_type_cast(attr_name, value)
127
170
  result = super
128
171
  clear_attribute_change(attr_name)
129
172
  result
130
173
  end
131
174
 
132
- def _update_record(*)
133
- partial_writes? ? super(keys_for_partial_write) : super
175
+ def _touch_row(attribute_names, time)
176
+ @_touch_attr_names = Set.new(attribute_names)
177
+
178
+ affected_rows = super
179
+
180
+ if @_skip_dirty_tracking ||= false
181
+ clear_attribute_changes(@_touch_attr_names)
182
+ return affected_rows
183
+ end
184
+
185
+ changes = {}
186
+ @attributes.keys.each do |attr_name|
187
+ next if @_touch_attr_names.include?(attr_name)
188
+
189
+ if attribute_changed?(attr_name)
190
+ changes[attr_name] = _read_attribute(attr_name)
191
+ _write_attribute(attr_name, attribute_was(attr_name))
192
+ clear_attribute_change(attr_name)
193
+ end
194
+ end
195
+
196
+ changes_applied
197
+ changes.each { |attr_name, value| _write_attribute(attr_name, value) }
198
+
199
+ affected_rows
200
+ ensure
201
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
202
+ end
203
+
204
+ def _update_record(attribute_names = attribute_names_for_partial_writes)
205
+ affected_rows = super
206
+ changes_applied
207
+ affected_rows
134
208
  end
135
209
 
136
- def _create_record(*)
137
- partial_writes? ? super(keys_for_partial_write) : super
210
+ def _create_record(attribute_names = attribute_names_for_partial_writes)
211
+ id = super
212
+ changes_applied
213
+ id
138
214
  end
139
215
 
140
- def keys_for_partial_write
141
- changed_attribute_names_to_save & self.class.column_names
216
+ def attribute_names_for_partial_writes
217
+ partial_writes? ? changed_attribute_names_to_save : attribute_names
142
218
  end
143
219
  end
144
220
  end