activerecord 6.0.0 → 6.1.4

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

Potentially problematic release.


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

Files changed (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1178 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +120 -13
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +63 -44
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +12 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  104. data/lib/active_record/connection_adapters/schema_cache.rb +106 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +219 -81
  114. data/lib/active_record/core.rb +268 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +124 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +2 -2
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +71 -20
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +344 -181
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -82
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +3 -3
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +79 -16
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +26 -73
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +15 -12
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +27 -24
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -115,8 +115,17 @@ module ActiveRecord
115
115
  #
116
116
  # Sets the column to sort records by when no explicit order clause is used
117
117
  # during an ordered finder call. Useful when the primary key is not an
118
- # auto-incrementing integer, for example when it's a UUID. Note that using
119
- # a non-unique column can result in non-deterministic results.
118
+ # auto-incrementing integer, for example when it's a UUID. Records are subsorted
119
+ # by the primary key if it exists to ensure deterministic results.
120
+
121
+ ##
122
+ # :singleton-method: immutable_strings_by_default=
123
+ # :call-seq: immutable_strings_by_default=(bool)
124
+ #
125
+ # Determines whether columns should infer their type as +:string+ or
126
+ # +:immutable_string+. This setting does not affect the behavior of
127
+ # <tt>attribute :foo, :string</tt>. Defaults to false.
128
+
120
129
  included do
121
130
  mattr_accessor :primary_key_prefix_type, instance_writer: false
122
131
 
@@ -126,12 +135,13 @@ module ActiveRecord
126
135
  class_attribute :internal_metadata_table_name, instance_accessor: false, default: "ar_internal_metadata"
127
136
  class_attribute :pluralize_table_names, instance_writer: false, default: true
128
137
  class_attribute :implicit_order_column, instance_accessor: false
138
+ class_attribute :immutable_strings_by_default, instance_accessor: false
129
139
 
130
140
  self.protected_environments = ["production"]
131
141
  self.inheritance_column = "type"
132
142
  self.ignored_columns = [].freeze
133
143
 
134
- delegate :type_for_attribute, to: :class
144
+ delegate :type_for_attribute, :column_for_attribute, to: :class
135
145
 
136
146
  initialize_load_schema_monitor
137
147
  end
@@ -287,8 +297,38 @@ module ActiveRecord
287
297
 
288
298
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
289
299
  # accessors defined, and won't be referenced in SQL queries.
300
+ #
301
+ # A common usage pattern for this method is to ensure all references to an attribute
302
+ # have been removed and deployed, before a migration to drop the column from the database
303
+ # has been deployed and run. Using this two step approach to dropping columns ensures there
304
+ # is no code that raises errors due to having a cached schema in memory at the time the
305
+ # schema migration is run.
306
+ #
307
+ # For example, given a model where you want to drop the "category" attribute, first mark it
308
+ # as ignored:
309
+ #
310
+ # class Project < ActiveRecord::Base
311
+ # # schema:
312
+ # # id :bigint
313
+ # # name :string, limit: 255
314
+ # # category :string, limit: 255
315
+ #
316
+ # self.ignored_columns = [:category]
317
+ # end
318
+ #
319
+ # The schema still contains "category", but now the model omits it, so any meta-driven code or
320
+ # schema caching will not attempt to use the column:
321
+ #
322
+ # Project.columns_hash["category"] => nil
323
+ #
324
+ # You will get an error if accessing that attribute directly, so ensure all usages of the
325
+ # column are removed (automated tests can help you find any usages).
326
+ #
327
+ # user = Project.create!(name: "First Project")
328
+ # user.category # => raises NoMethodError
290
329
  def ignored_columns=(columns)
291
- @ignored_columns = columns.map(&:to_s)
330
+ reload_schema_from_cache
331
+ @ignored_columns = columns.map(&:to_s).freeze
292
332
  end
293
333
 
294
334
  def sequence_name
@@ -355,7 +395,7 @@ module ActiveRecord
355
395
 
356
396
  def columns
357
397
  load_schema
358
- @columns ||= columns_hash.values
398
+ @columns ||= columns_hash.values.freeze
359
399
  end
360
400
 
361
401
  def attribute_types # :nodoc:
@@ -380,6 +420,8 @@ module ActiveRecord
380
420
  # a string or a symbol.
381
421
  def type_for_attribute(attr_name, &block)
382
422
  attr_name = attr_name.to_s
423
+ attr_name = attribute_aliases[attr_name] || attr_name
424
+
383
425
  if block
384
426
  attribute_types.fetch(attr_name, &block)
385
427
  else
@@ -387,11 +429,31 @@ module ActiveRecord
387
429
  end
388
430
  end
389
431
 
432
+ # Returns the column object for the named attribute.
433
+ # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
434
+ # named attribute does not exist.
435
+ #
436
+ # class Person < ActiveRecord::Base
437
+ # end
438
+ #
439
+ # person = Person.new
440
+ # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
441
+ # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
442
+ #
443
+ # person.column_for_attribute(:nothing)
444
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
445
+ def column_for_attribute(name)
446
+ name = name.to_s
447
+ columns_hash.fetch(name) do
448
+ ConnectionAdapters::NullColumn.new(name)
449
+ end
450
+ end
451
+
390
452
  # Returns a hash where the keys are column names and the values are
391
453
  # default values when instantiating the Active Record object for this table.
392
454
  def column_defaults
393
455
  load_schema
394
- @column_defaults ||= _default_attributes.deep_dup.to_hash
456
+ @column_defaults ||= _default_attributes.deep_dup.to_hash.freeze
395
457
  end
396
458
 
397
459
  def _default_attributes # :nodoc:
@@ -401,7 +463,7 @@ module ActiveRecord
401
463
 
402
464
  # Returns an array of column names as strings.
403
465
  def column_names
404
- @column_names ||= columns.map(&:name)
466
+ @column_names ||= columns.map(&:name).freeze
405
467
  end
406
468
 
407
469
  def symbol_column_to_string(name_symbol) # :nodoc:
@@ -415,9 +477,8 @@ module ActiveRecord
415
477
  @content_columns ||= columns.reject do |c|
416
478
  c.name == primary_key ||
417
479
  c.name == inheritance_column ||
418
- c.name.end_with?("_id") ||
419
- c.name.end_with?("_count")
420
- end
480
+ c.name.end_with?("_id", "_count")
481
+ end.freeze
421
482
  end
422
483
 
423
484
  # Resets all the cached information about columns, which will cause them
@@ -427,7 +488,7 @@ module ActiveRecord
427
488
  # when just after creating a table you want to populate it with some default
428
489
  # values, eg:
429
490
  #
430
- # class CreateJobLevels < ActiveRecord::Migration[5.0]
491
+ # class CreateJobLevels < ActiveRecord::Migration[6.0]
431
492
  # def up
432
493
  # create_table :job_levels do |t|
433
494
  # t.integer :id
@@ -456,13 +517,11 @@ module ActiveRecord
456
517
  end
457
518
 
458
519
  protected
459
-
460
520
  def initialize_load_schema_monitor
461
521
  @load_schema_monitor = Monitor.new
462
522
  end
463
523
 
464
524
  private
465
-
466
525
  def inherited(child_class)
467
526
  super
468
527
  child_class.initialize_load_schema_monitor
@@ -480,15 +539,27 @@ module ActiveRecord
480
539
  load_schema!
481
540
 
482
541
  @schema_loaded = true
542
+ rescue
543
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
544
+ raise
483
545
  end
484
546
  end
485
547
 
486
548
  def load_schema!
487
- @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
549
+ unless table_name
550
+ raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
551
+ end
552
+
553
+ columns_hash = connection.schema_cache.columns_hash(table_name)
554
+ columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
555
+ @columns_hash = columns_hash.freeze
488
556
  @columns_hash.each do |name, column|
557
+ type = connection.lookup_cast_type_from_column(column)
558
+ type = _convert_type_from_options(type)
559
+ warn_if_deprecated_type(column)
489
560
  define_attribute(
490
561
  name,
491
- connection.lookup_cast_type_from_column(column),
562
+ type,
492
563
  default: column.default,
493
564
  user_provided_default: false
494
565
  )
@@ -537,6 +608,40 @@ module ActiveRecord
537
608
  base_class.table_name
538
609
  end
539
610
  end
611
+
612
+ def _convert_type_from_options(type)
613
+ if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
614
+ type.to_immutable_string
615
+ else
616
+ type
617
+ end
618
+ end
619
+
620
+ def warn_if_deprecated_type(column)
621
+ return if attributes_to_define_after_schema_loads.key?(column.name)
622
+ return unless column.respond_to?(:oid)
623
+
624
+ if column.array?
625
+ array_arguments = ", array: true"
626
+ else
627
+ array_arguments = ""
628
+ end
629
+
630
+ if column.sql_type.start_with?("interval")
631
+ precision_arguments = column.precision.presence && ", precision: #{column.precision}"
632
+ ActiveSupport::Deprecation.warn(<<~WARNING)
633
+ The behavior of the `:interval` type will be changing in Rails 6.2
634
+ to return an `ActiveSupport::Duration` object. If you'd like to keep
635
+ the old behavior, you can add this line to #{self.name} model:
636
+
637
+ attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
638
+
639
+ If you'd like the new behavior today, you can add this line:
640
+
641
+ attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
642
+ WARNING
643
+ end
644
+ end
540
645
  end
541
646
  end
542
647
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "active_support/core_ext/hash/except"
4
4
  require "active_support/core_ext/module/redefine_method"
5
- require "active_support/core_ext/object/try"
6
5
  require "active_support/core_ext/hash/indifferent_access"
7
6
 
8
7
  module ActiveRecord
@@ -289,7 +288,7 @@ module ActiveRecord
289
288
  # [:allow_destroy]
290
289
  # If true, destroys any members from the attributes hash with a
291
290
  # <tt>_destroy</tt> key and a value that evaluates to +true+
292
- # (eg. 1, '1', true, or 'true'). This option is off by default.
291
+ # (e.g. 1, '1', true, or 'true'). This option is off by default.
293
292
  # [:reject_if]
294
293
  # Allows you to specify a Proc or a Symbol pointing to a method
295
294
  # that checks whether a record should be built for a certain attribute
@@ -354,7 +353,6 @@ module ActiveRecord
354
353
  end
355
354
 
356
355
  private
357
-
358
356
  # Generates a writer method for this association. Serves as a point for
359
357
  # accessing the objects in the association. For example, this method
360
358
  # could generate the following:
@@ -386,7 +384,6 @@ module ActiveRecord
386
384
  end
387
385
 
388
386
  private
389
-
390
387
  # Attribute hash keys that should not be assigned as normal attributes.
391
388
  # These hash keys are nested attributes implementation details.
392
389
  UNASSIGNABLE_KEYS = %w( id _destroy )
@@ -512,7 +509,7 @@ module ActiveRecord
512
509
  if target_record
513
510
  existing_record = target_record
514
511
  else
515
- association.add_to_target(existing_record, :skip_callbacks)
512
+ association.add_to_target(existing_record, skip_callbacks: true)
516
513
  end
517
514
 
518
515
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
@@ -58,7 +58,7 @@ module ActiveRecord
58
58
  super unless no_touching?
59
59
  end
60
60
 
61
- def touch(*) # :nodoc:
61
+ def touch(*, **) # :nodoc:
62
62
  super unless no_touching?
63
63
  end
64
64
  end
@@ -60,7 +60,6 @@ module ActiveRecord
60
60
  end
61
61
 
62
62
  private
63
-
64
63
  def exec_queries
65
64
  @records = [].freeze
66
65
  end
@@ -414,8 +414,8 @@ module ActiveRecord
414
414
 
415
415
  def _substitute_values(values)
416
416
  values.map do |name, value|
417
- attr = arel_attribute(name)
418
- bind = predicate_builder.build_bind_attribute(name, value)
417
+ attr = arel_table[name]
418
+ bind = predicate_builder.build_bind_attribute(attr.name, value)
419
419
  [attr, bind]
420
420
  end
421
421
  end
@@ -424,26 +424,30 @@ module ActiveRecord
424
424
  # Returns true if this object hasn't been saved yet -- that is, a record
425
425
  # for the object doesn't exist in the database yet; otherwise, returns false.
426
426
  def new_record?
427
- sync_with_transaction_state if @transaction_state&.finalized?
428
427
  @new_record
429
428
  end
430
429
 
430
+ # Returns true if this object was just created -- that is, prior to the last
431
+ # save, the object didn't exist in the database and new_record? would have
432
+ # returned true.
433
+ def previously_new_record?
434
+ @previously_new_record
435
+ end
436
+
431
437
  # Returns true if this object has been destroyed, otherwise returns false.
432
438
  def destroyed?
433
- sync_with_transaction_state if @transaction_state&.finalized?
434
439
  @destroyed
435
440
  end
436
441
 
437
442
  # Returns true if the record is persisted, i.e. it's not a new record and it was
438
443
  # not destroyed, otherwise returns false.
439
444
  def persisted?
440
- sync_with_transaction_state if @transaction_state&.finalized?
441
445
  !(@new_record || @destroyed)
442
446
  end
443
447
 
444
448
  ##
445
449
  # :call-seq:
446
- # save(*args)
450
+ # save(**options)
447
451
  #
448
452
  # Saves the model.
449
453
  #
@@ -466,15 +470,15 @@ module ActiveRecord
466
470
  #
467
471
  # Attributes marked as readonly are silently ignored if the record is
468
472
  # being updated.
469
- def save(*args, &block)
470
- create_or_update(*args, &block)
473
+ def save(**options, &block)
474
+ create_or_update(**options, &block)
471
475
  rescue ActiveRecord::RecordInvalid
472
476
  false
473
477
  end
474
478
 
475
479
  ##
476
480
  # :call-seq:
477
- # save!(*args)
481
+ # save!(**options)
478
482
  #
479
483
  # Saves the model.
480
484
  #
@@ -499,8 +503,8 @@ module ActiveRecord
499
503
  # being updated.
500
504
  #
501
505
  # Unless an error is raised, returns true.
502
- def save!(*args, &block)
503
- create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
506
+ def save!(**options, &block)
507
+ create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
504
508
  end
505
509
 
506
510
  # Deletes the record in the database and freezes this instance to
@@ -514,7 +518,7 @@ module ActiveRecord
514
518
  #
515
519
  # To enforce the object's +before_destroy+ and +after_destroy+
516
520
  # callbacks or any <tt>:dependent</tt> association
517
- # options, use <tt>#destroy</tt>.
521
+ # options, use #destroy.
518
522
  def delete
519
523
  _delete_row if persisted?
520
524
  @destroyed = true
@@ -565,12 +569,15 @@ module ActiveRecord
565
569
  # If you want to change the sti column as well, use #becomes! instead.
566
570
  def becomes(klass)
567
571
  became = klass.allocate
568
- became.send(:initialize)
569
- became.instance_variable_set("@attributes", @attributes)
570
- became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
571
- became.instance_variable_set("@new_record", new_record?)
572
- became.instance_variable_set("@destroyed", destroyed?)
573
- became.errors.copy!(errors)
572
+
573
+ became.send(:initialize) do |becoming|
574
+ becoming.instance_variable_set(:@attributes, @attributes)
575
+ becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
576
+ becoming.instance_variable_set(:@new_record, new_record?)
577
+ becoming.instance_variable_set(:@destroyed, destroyed?)
578
+ becoming.errors.copy!(errors)
579
+ end
580
+
574
581
  became
575
582
  end
576
583
 
@@ -622,9 +629,6 @@ module ActiveRecord
622
629
  end
623
630
  end
624
631
 
625
- alias update_attributes update
626
- deprecate update_attributes: "please, use update instead"
627
-
628
632
  # Updates its receiver just like #update but calls #save! instead
629
633
  # of +save+, so an exception is raised if the record is invalid and saving will fail.
630
634
  def update!(attributes)
@@ -636,9 +640,6 @@ module ActiveRecord
636
640
  end
637
641
  end
638
642
 
639
- alias update_attributes! update!
640
- deprecate update_attributes!: "please, use update! instead"
641
-
642
643
  # Equivalent to <code>update_columns(name => value)</code>.
643
644
  def update_column(name, value)
644
645
  update_columns(name => value)
@@ -666,11 +667,8 @@ module ActiveRecord
666
667
 
667
668
  attributes = attributes.transform_keys do |key|
668
669
  name = key.to_s
669
- self.class.attribute_aliases[name] || name
670
- end
671
-
672
- attributes.each_key do |key|
673
- verify_readonly_attribute(key)
670
+ name = self.class.attribute_aliases[name] || name
671
+ verify_readonly_attribute(name) || name
674
672
  end
675
673
 
676
674
  id_in_database = self.id_in_database
@@ -703,9 +701,9 @@ module ActiveRecord
703
701
  # Returns +self+.
704
702
  def increment!(attribute, by = 1, touch: nil)
705
703
  increment(attribute, by)
706
- change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
704
+ change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
707
705
  self.class.update_counters(id, attribute => change, touch: touch)
708
- clear_attribute_change(attribute) # eww
706
+ public_send(:"clear_#{attribute}_change")
709
707
  self
710
708
  end
711
709
 
@@ -809,8 +807,9 @@ module ActiveRecord
809
807
  self.class.unscoped { self.class.find(id) }
810
808
  end
811
809
 
812
- @attributes = fresh_object.instance_variable_get("@attributes")
810
+ @attributes = fresh_object.instance_variable_get(:@attributes)
813
811
  @new_record = false
812
+ @previously_new_record = false
814
813
  self
815
814
  end
816
815
 
@@ -849,17 +848,13 @@ module ActiveRecord
849
848
  # ball.touch(:updated_at) # => raises ActiveRecordError
850
849
  #
851
850
  def touch(*names, time: nil)
852
- unless persisted?
853
- raise ActiveRecordError, <<-MSG.squish
854
- cannot touch on a new or destroyed record object. Consider using
855
- persisted?, new_record?, or destroyed? before touching
856
- MSG
857
- end
851
+ _raise_record_not_touched_error unless persisted?
858
852
 
859
853
  attribute_names = timestamp_attributes_for_update_in_model
860
- attribute_names |= names.map!(&:to_s).map! { |name|
854
+ attribute_names |= names.map! do |name|
855
+ name = name.to_s
861
856
  self.class.attribute_aliases[name] || name
862
- }
857
+ end unless names.empty?
863
858
 
864
859
  unless attribute_names.empty?
865
860
  affected_rows = _touch_row(attribute_names, time)
@@ -870,7 +865,6 @@ module ActiveRecord
870
865
  end
871
866
 
872
867
  private
873
-
874
868
  # A hook to be overridden by association modules.
875
869
  def destroy_associations
876
870
  end
@@ -920,6 +914,8 @@ module ActiveRecord
920
914
  @_trigger_update_callback = affected_rows == 1
921
915
  end
922
916
 
917
+ @previously_new_record = false
918
+
923
919
  yield(self) if block_given?
924
920
 
925
921
  affected_rows
@@ -937,6 +933,7 @@ module ActiveRecord
937
933
  self.id ||= new_id if @primary_key
938
934
 
939
935
  @new_record = false
936
+ @previously_new_record = true
940
937
 
941
938
  yield(self) if block_given?
942
939
 
@@ -944,7 +941,7 @@ module ActiveRecord
944
941
  end
945
942
 
946
943
  def verify_readonly_attribute(name)
947
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
944
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
948
945
  end
949
946
 
950
947
  def _raise_record_not_destroyed
@@ -954,14 +951,21 @@ module ActiveRecord
954
951
  @_association_destroy_exception = nil
955
952
  end
956
953
 
954
+ def _raise_readonly_record_error
955
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
956
+ end
957
+
958
+ def _raise_record_not_touched_error
959
+ raise ActiveRecordError, <<~MSG.squish
960
+ Cannot touch on a new or destroyed record object. Consider using
961
+ persisted?, new_record?, or destroyed? before touching.
962
+ MSG
963
+ end
964
+
957
965
  # The name of the method used to touch a +belongs_to+ association when the
958
966
  # +:touch+ option is used.
959
967
  def belongs_to_touch_method
960
968
  :touch
961
969
  end
962
-
963
- def _raise_readonly_record_error
964
- raise ReadOnlyRecord, "#{self.class} is marked as readonly"
965
- end
966
970
  end
967
971
  end
@@ -28,18 +28,28 @@ module ActiveRecord
28
28
  def self.run
29
29
  pools = []
30
30
 
31
- ActiveRecord::Base.connection_handlers.each do |key, handler|
32
- pools << handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
31
+ if ActiveRecord::Base.legacy_connection_handling
32
+ ActiveRecord::Base.connection_handlers.each do |key, handler|
33
+ pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
34
+ end
35
+ else
36
+ pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
33
37
  end
34
38
 
35
- pools.flatten
39
+ pools
36
40
  end
37
41
 
38
42
  def self.complete(pools)
39
43
  pools.each { |pool| pool.disable_query_cache! }
40
44
 
41
- ActiveRecord::Base.connection_handlers.each do |_, handler|
42
- handler.connection_pool_list.each do |pool|
45
+ if ActiveRecord::Base.legacy_connection_handling
46
+ ActiveRecord::Base.connection_handlers.each do |_, handler|
47
+ handler.connection_pool_list.each do |pool|
48
+ pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
49
+ end
50
+ end
51
+ else
52
+ ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
43
53
  pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
44
54
  end
45
55
  end
@@ -13,10 +13,11 @@ module ActiveRecord
13
13
  :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
15
  :select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
- :where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly, :extending, :or,
17
- :having, :create_with, :distinct, :references, :none, :unscope, :optimizer_hints, :merge, :except, :only,
18
- :count, :average, :minimum, :maximum, :sum, :calculate, :annotate,
19
- :pluck, :pick, :ids
16
+ :where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
17
+ :and, :or, :annotate, :optimizer_hints, :extending,
18
+ :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
+ :count, :average, :minimum, :maximum, :sum, :calculate,
20
+ :pluck, :pick, :ids, :strict_loading
20
21
  ].freeze # :nodoc:
21
22
  delegate(*QUERYING_METHODS, to: :all)
22
23
 
@@ -36,7 +37,7 @@ module ActiveRecord
36
37
  #
37
38
  # # A simple SQL query spanning multiple tables
38
39
  # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
39
- # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
40
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "author"=>"Quentin"}>, ...]
40
41
  #
41
42
  # You can use the same string replacement techniques as you can with <tt>ActiveRecord::QueryMethods#where</tt>:
42
43
  #
@@ -44,8 +45,12 @@ module ActiveRecord
44
45
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
45
46
  def find_by_sql(sql, binds = [], preparable: nil, &block)
46
47
  result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
47
- column_types = result_set.column_types.dup
48
- attribute_types.each_key { |k| column_types.delete k }
48
+ column_types = result_set.column_types
49
+
50
+ unless column_types.empty?
51
+ column_types = column_types.reject { |k, _| attribute_types.key?(k) }
52
+ end
53
+
49
54
  message_bus = ActiveSupport::Notifications.instrumenter
50
55
 
51
56
  payload = {