activerecord 6.1.7 → 7.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -57,14 +57,44 @@ module ActiveRecord
57
57
  end
58
58
  end
59
59
 
60
+ # Builds an object (or multiple objects) and returns either the built object or a list of built
61
+ # objects.
62
+ #
63
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
64
+ # attributes on the objects that are to be built.
65
+ #
66
+ # ==== Examples
67
+ # # Build a single new object
68
+ # User.build(first_name: 'Jamie')
69
+ #
70
+ # # Build an Array of new objects
71
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
72
+ #
73
+ # # Build a single object and pass it into a block to set other attributes.
74
+ # User.build(first_name: 'Jamie') do |u|
75
+ # u.is_admin = false
76
+ # end
77
+ #
78
+ # # Building an Array of new objects using a block, where the block is executed for each object:
79
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
80
+ # u.is_admin = false
81
+ # end
82
+ def build(attributes = nil, &block)
83
+ if attributes.is_a?(Array)
84
+ attributes.collect { |attr| build(attr, &block) }
85
+ else
86
+ new(attributes, &block)
87
+ end
88
+ end
89
+
60
90
  # Inserts a single record into the database in a single SQL INSERT
61
91
  # statement. It does not instantiate any models nor does it trigger
62
92
  # Active Record callbacks or validations. Though passed values
63
93
  # go through Active Record's type casting and serialization.
64
94
  #
65
- # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66
- def insert(attributes, returning: nil, unique_by: nil)
67
- insert_all([ attributes ], returning: returning, unique_by: unique_by)
95
+ # See #insert_all for documentation.
96
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
97
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
68
98
  end
69
99
 
70
100
  # Inserts multiple records into the database in a single SQL INSERT
@@ -79,18 +109,21 @@ module ActiveRecord
79
109
  # duplicate rows are skipped.
80
110
  # Override with <tt>:unique_by</tt> (see below).
81
111
  #
82
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
112
+ # Returns an ActiveRecord::Result with its contents based on
83
113
  # <tt>:returning</tt> (see below).
84
114
  #
85
115
  # ==== Options
86
116
  #
87
117
  # [:returning]
88
- # (PostgreSQL only) An array of attributes to return for all successfully
118
+ # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
89
119
  # inserted records, which by default is the primary key.
90
120
  # Pass <tt>returning: %w[ id name ]</tt> for both id and name
91
121
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
92
122
  # clause entirely.
93
123
  #
124
+ # You can also pass an SQL string if you need more control on the return values
125
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
126
+ #
94
127
  # [:unique_by]
95
128
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
96
129
  # by every unique index on the table. Any duplicate rows are skipped.
@@ -99,7 +132,7 @@ module ActiveRecord
99
132
  #
100
133
  # Consider a Book model where no duplicate ISBNs make sense, but if any
101
134
  # row has an existing id, or is not unique by another unique index,
102
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
135
+ # ActiveRecord::RecordNotUnique is raised.
103
136
  #
104
137
  # Unique indexes can be identified by columns or name:
105
138
  #
@@ -107,6 +140,17 @@ module ActiveRecord
107
140
  # unique_by: %i[ author_id name ]
108
141
  # unique_by: :index_books_on_isbn
109
142
  #
143
+ # [:record_timestamps]
144
+ # By default, automatic setting of timestamp columns is controlled by
145
+ # the model's <tt>record_timestamps</tt> config, matching typical
146
+ # behavior.
147
+ #
148
+ # To override this and force automatic setting of timestamp columns one
149
+ # way or the other, pass <tt>:record_timestamps</tt>:
150
+ #
151
+ # record_timestamps: true # Always set timestamps automatically
152
+ # record_timestamps: false # Never set timestamps automatically
153
+ #
110
154
  # Because it relies on the index information from the database
111
155
  # <tt>:unique_by</tt> is recommended to be paired with
112
156
  # Active Record's schema_cache.
@@ -120,8 +164,16 @@ module ActiveRecord
120
164
  # { id: 1, title: "Rework", author: "David" },
121
165
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
122
166
  # ])
123
- def insert_all(attributes, returning: nil, unique_by: nil)
124
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
167
+ #
168
+ # # insert_all works on chained scopes, and you can use create_with
169
+ # # to set default attributes for all inserted records.
170
+ #
171
+ # author.books.create_with(created_at: Time.now).insert_all([
172
+ # { id: 1, title: "Rework" },
173
+ # { id: 2, title: "Eloquent Ruby" }
174
+ # ])
175
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
176
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
125
177
  end
126
178
 
127
179
  # Inserts a single record into the database in a single SQL INSERT
@@ -129,9 +181,9 @@ module ActiveRecord
129
181
  # Active Record callbacks or validations. Though passed values
130
182
  # go through Active Record's type casting and serialization.
131
183
  #
132
- # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
- def insert!(attributes, returning: nil)
134
- insert_all!([ attributes ], returning: returning)
184
+ # See #insert_all! for more.
185
+ def insert!(attributes, returning: nil, record_timestamps: nil)
186
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
135
187
  end
136
188
 
137
189
  # Inserts multiple records into the database in a single SQL INSERT
@@ -142,24 +194,37 @@ module ActiveRecord
142
194
  # The +attributes+ parameter is an Array of Hashes. Every Hash determines
143
195
  # the attributes for a single row and must have the same keys.
144
196
  #
145
- # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
197
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
146
198
  # unique index on the table. In that case, no rows are inserted.
147
199
  #
148
- # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
149
- # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
200
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
150
201
  #
151
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
202
+ # Returns an ActiveRecord::Result with its contents based on
152
203
  # <tt>:returning</tt> (see below).
153
204
  #
154
205
  # ==== Options
155
206
  #
156
207
  # [:returning]
157
- # (PostgreSQL only) An array of attributes to return for all successfully
208
+ # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
158
209
  # inserted records, which by default is the primary key.
159
210
  # Pass <tt>returning: %w[ id name ]</tt> for both id and name
160
211
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
212
  # clause entirely.
162
213
  #
214
+ # You can also pass an SQL string if you need more control on the return values
215
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
216
+ #
217
+ # [:record_timestamps]
218
+ # By default, automatic setting of timestamp columns is controlled by
219
+ # the model's <tt>record_timestamps</tt> config, matching typical
220
+ # behavior.
221
+ #
222
+ # To override this and force automatic setting of timestamp columns one
223
+ # way or the other, pass <tt>:record_timestamps</tt>:
224
+ #
225
+ # record_timestamps: true # Always set timestamps automatically
226
+ # record_timestamps: false # Never set timestamps automatically
227
+ #
163
228
  # ==== Examples
164
229
  #
165
230
  # # Insert multiple records
@@ -174,8 +239,8 @@ module ActiveRecord
174
239
  # { id: 1, title: "Rework", author: "David" },
175
240
  # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
241
  # ])
177
- def insert_all!(attributes, returning: nil)
178
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
242
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
243
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
179
244
  end
180
245
 
181
246
  # Updates or inserts (upserts) a single record into the database in a
@@ -183,9 +248,9 @@ module ActiveRecord
183
248
  # it trigger Active Record callbacks or validations. Though passed values
184
249
  # go through Active Record's type casting and serialization.
185
250
  #
186
- # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
187
- def upsert(attributes, returning: nil, unique_by: nil)
188
- upsert_all([ attributes ], returning: returning, unique_by: unique_by)
251
+ # See #upsert_all for documentation.
252
+ def upsert(attributes, **kwargs)
253
+ upsert_all([ attributes ], **kwargs)
189
254
  end
190
255
 
191
256
  # Updates or inserts (upserts) multiple records into the database in a
@@ -196,18 +261,25 @@ module ActiveRecord
196
261
  # The +attributes+ parameter is an Array of Hashes. Every Hash determines
197
262
  # the attributes for a single row and must have the same keys.
198
263
  #
199
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
264
+ # Returns an ActiveRecord::Result with its contents based on
200
265
  # <tt>:returning</tt> (see below).
201
266
  #
267
+ # By default, +upsert_all+ will update all the columns that can be updated when
268
+ # there is a conflict. These are all the columns except primary keys, read-only
269
+ # columns, and columns covered by the optional +unique_by+.
270
+ #
202
271
  # ==== Options
203
272
  #
204
273
  # [:returning]
205
- # (PostgreSQL only) An array of attributes to return for all successfully
274
+ # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
206
275
  # inserted records, which by default is the primary key.
207
276
  # Pass <tt>returning: %w[ id name ]</tt> for both id and name
208
277
  # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
278
  # clause entirely.
210
279
  #
280
+ # You can also pass an SQL string if you need more control on the return values
281
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
282
+ #
211
283
  # [:unique_by]
212
284
  # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
285
  # by every unique index on the table. Any duplicate rows are skipped.
@@ -216,7 +288,7 @@ module ActiveRecord
216
288
  #
217
289
  # Consider a Book model where no duplicate ISBNs make sense, but if any
218
290
  # row has an existing id, or is not unique by another unique index,
219
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
291
+ # ActiveRecord::RecordNotUnique is raised.
220
292
  #
221
293
  # Unique indexes can be identified by columns or name:
222
294
  #
@@ -228,6 +300,54 @@ module ActiveRecord
228
300
  # <tt>:unique_by</tt> is recommended to be paired with
229
301
  # Active Record's schema_cache.
230
302
  #
303
+ # [:on_duplicate]
304
+ # Configure the SQL update sentence that will be used in case of conflict.
305
+ #
306
+ # NOTE: If you use this option you must provide all the columns you want to update
307
+ # by yourself.
308
+ #
309
+ # Example:
310
+ #
311
+ # Commodity.upsert_all(
312
+ # [
313
+ # { id: 2, name: "Copper", price: 4.84 },
314
+ # { id: 4, name: "Gold", price: 1380.87 },
315
+ # { id: 6, name: "Aluminium", price: 0.35 }
316
+ # ],
317
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
318
+ # )
319
+ #
320
+ # See the related +:update_only+ option. Both options can't be used at the same time.
321
+ #
322
+ # [:update_only]
323
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
324
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
325
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
326
+ #
327
+ # Example:
328
+ #
329
+ # Commodity.upsert_all(
330
+ # [
331
+ # { id: 2, name: "Copper", price: 4.84 },
332
+ # { id: 4, name: "Gold", price: 1380.87 },
333
+ # { id: 6, name: "Aluminium", price: 0.35 }
334
+ # ],
335
+ # update_only: [:price] # Only prices will be updated
336
+ # )
337
+ #
338
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
339
+ #
340
+ # [:record_timestamps]
341
+ # By default, automatic setting of timestamp columns is controlled by
342
+ # the model's <tt>record_timestamps</tt> config, matching typical
343
+ # behavior.
344
+ #
345
+ # To override this and force automatic setting of timestamp columns one
346
+ # way or the other, pass <tt>:record_timestamps</tt>:
347
+ #
348
+ # record_timestamps: true # Always set timestamps automatically
349
+ # record_timestamps: false # Never set timestamps automatically
350
+ #
231
351
  # ==== Examples
232
352
  #
233
353
  # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
@@ -239,8 +359,8 @@ module ActiveRecord
239
359
  # ], unique_by: :isbn)
240
360
  #
241
361
  # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
242
- def upsert_all(attributes, returning: nil, unique_by: nil)
243
- InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
362
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
363
+ InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
244
364
  end
245
365
 
246
366
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -264,6 +384,7 @@ module ActiveRecord
264
384
  # ==== Parameters
265
385
  #
266
386
  # * +id+ - This should be the id or an array of ids to be updated.
387
+ # Optional argument, defaults to all records in the relation.
267
388
  # * +attributes+ - This should be a hash of attributes or an array of hashes.
268
389
  #
269
390
  # ==== Examples
@@ -286,6 +407,11 @@ module ActiveRecord
286
407
  # for updating all records in a single query.
287
408
  def update(id = :all, attributes)
288
409
  if id.is_a?(Array)
410
+ if id.any?(ActiveRecord::Base)
411
+ raise ArgumentError,
412
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
413
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
414
+ end
289
415
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
290
416
  object.update(attributes[idx])
291
417
  }
@@ -303,6 +429,88 @@ module ActiveRecord
303
429
  end
304
430
  end
305
431
 
432
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
433
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
434
+ def update!(id = :all, attributes)
435
+ if id.is_a?(Array)
436
+ if id.any?(ActiveRecord::Base)
437
+ raise ArgumentError,
438
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
439
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
440
+ end
441
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
442
+ object.update!(attributes[idx])
443
+ }
444
+ elsif id == :all
445
+ all.each { |record| record.update!(attributes) }
446
+ else
447
+ if ActiveRecord::Base === id
448
+ raise ArgumentError,
449
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
450
+ "Please pass the id of the object by calling `.id`."
451
+ end
452
+ object = find(id)
453
+ object.update!(attributes)
454
+ object
455
+ end
456
+ end
457
+
458
+ # Accepts a list of attribute names to be used in the WHERE clause
459
+ # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
460
+ #
461
+ # class Developer < ActiveRecord::Base
462
+ # query_constraints :company_id, :id
463
+ # end
464
+ #
465
+ # developer = Developer.first
466
+ # # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
467
+ # developer.inspect # => #<Developer id: 1, company_id: 1, ...>
468
+ #
469
+ # developer.update!(name: "Nikita")
470
+ # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
471
+ #
472
+ # # It is possible to update an attribute used in the query_constraints clause:
473
+ # developer.update!(company_id: 2)
474
+ # # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
475
+ #
476
+ # developer.name = "Bob"
477
+ # developer.save!
478
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
479
+ #
480
+ # developer.destroy!
481
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
482
+ #
483
+ # developer.delete
484
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
485
+ #
486
+ # developer.reload
487
+ # # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
488
+ def query_constraints(*columns_list)
489
+ raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
490
+
491
+ @query_constraints_list = columns_list.map(&:to_s)
492
+ @has_query_constraints = @query_constraints_list
493
+ end
494
+
495
+ def has_query_constraints? # :nodoc:
496
+ @has_query_constraints
497
+ end
498
+
499
+ def query_constraints_list # :nodoc:
500
+ @query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
501
+ primary_key if primary_key.is_a?(Array)
502
+ else
503
+ base_class.query_constraints_list
504
+ end
505
+ end
506
+
507
+ # Returns an array of column names to be used in queries. The source of column
508
+ # names is derived from +query_constraints_list+ or +primary_key+. This method
509
+ # is for internal use when the primary key is to be treated as an array.
510
+ def composite_query_constraints_list # :nodoc:
511
+ @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
512
+ end
513
+
306
514
  # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
307
515
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
308
516
  # less efficient than #delete but allows cleanup methods and other actions to be run.
@@ -323,7 +531,13 @@ module ActiveRecord
323
531
  # todos = [1,2,3]
324
532
  # Todo.destroy(todos)
325
533
  def destroy(id)
326
- if id.is_a?(Array)
534
+ multiple_ids = if composite_primary_key?
535
+ id.first.is_a?(Array)
536
+ else
537
+ id.is_a?(Array)
538
+ end
539
+
540
+ if multiple_ids
327
541
  find(id).each(&:destroy)
328
542
  else
329
543
  find(id).destroy
@@ -352,50 +566,73 @@ module ActiveRecord
352
566
  delete_by(primary_key => id_or_array)
353
567
  end
354
568
 
355
- def _insert_record(values) # :nodoc:
569
+ def _insert_record(values, returning) # :nodoc:
356
570
  primary_key = self.primary_key
357
571
  primary_key_value = nil
358
572
 
359
- if primary_key && Hash === values
360
- primary_key_value = values[primary_key]
361
-
362
- if !primary_key_value && prefetch_primary_key?
573
+ if prefetch_primary_key? && primary_key
574
+ values[primary_key] ||= begin
363
575
  primary_key_value = next_sequence_value
364
- values[primary_key] = primary_key_value
576
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
365
577
  end
366
578
  end
367
579
 
580
+ im = Arel::InsertManager.new(arel_table)
581
+
368
582
  if values.empty?
369
- im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
370
- im.into arel_table
583
+ im.insert(connection.empty_insert_statement_value(primary_key))
371
584
  else
372
- im = arel_table.compile_insert(_substitute_values(values))
585
+ im.insert(values.transform_keys { |name| arel_table[name] })
373
586
  end
374
587
 
375
- connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
588
+ connection.insert(
589
+ im, "#{self} Create", primary_key || false, primary_key_value,
590
+ returning: returning
591
+ )
376
592
  end
377
593
 
378
594
  def _update_record(values, constraints) # :nodoc:
379
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
595
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
596
+
597
+ default_constraint = build_default_constraint
598
+ constraints << default_constraint if default_constraint
599
+
600
+ if current_scope = self.global_current_scope
601
+ constraints << current_scope.where_clause.ast
602
+ end
380
603
 
381
- um = arel_table.where(
382
- constraints.reduce(&:and)
383
- ).compile_update(_substitute_values(values), primary_key)
604
+ um = Arel::UpdateManager.new(arel_table)
605
+ um.set(values.transform_keys { |name| arel_table[name] })
606
+ um.wheres = constraints
384
607
 
385
608
  connection.update(um, "#{self} Update")
386
609
  end
387
610
 
388
611
  def _delete_record(constraints) # :nodoc:
389
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
612
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
390
613
 
391
- dm = Arel::DeleteManager.new
392
- dm.from(arel_table)
614
+ default_constraint = build_default_constraint
615
+ constraints << default_constraint if default_constraint
616
+
617
+ if current_scope = self.global_current_scope
618
+ constraints << current_scope.where_clause.ast
619
+ end
620
+
621
+ dm = Arel::DeleteManager.new(arel_table)
393
622
  dm.wheres = constraints
394
623
 
395
624
  connection.delete(dm, "#{self} Destroy")
396
625
  end
397
626
 
398
627
  private
628
+ def inherited(subclass)
629
+ super
630
+ subclass.class_eval do
631
+ @_query_constraints_list = nil
632
+ @has_query_constraints = false
633
+ end
634
+ end
635
+
399
636
  # Given a class, an attributes hash, +instantiate_instance_of+ returns a
400
637
  # new instance of the class. Accepts only keys as strings.
401
638
  def instantiate_instance_of(klass, attributes, column_types = {}, &block)
@@ -412,12 +649,14 @@ module ActiveRecord
412
649
  self
413
650
  end
414
651
 
415
- def _substitute_values(values)
416
- values.map do |name, value|
417
- attr = arel_table[name]
418
- bind = predicate_builder.build_bind_attribute(attr.name, value)
419
- [attr, bind]
420
- end
652
+ # Called by +_update_record+ and +_delete_record+
653
+ # to build `where` clause from default scopes.
654
+ # Skips empty scopes.
655
+ def build_default_constraint
656
+ return unless default_scopes?(all_queries: true)
657
+
658
+ default_where_clause = default_scoped(all_queries: true).where_clause
659
+ default_where_clause.ast unless default_where_clause.empty?
421
660
  end
422
661
  end
423
662
 
@@ -428,12 +667,17 @@ module ActiveRecord
428
667
  end
429
668
 
430
669
  # 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
670
+ # update or delete, the object didn't exist in the database and new_record? would have
432
671
  # returned true.
433
672
  def previously_new_record?
434
673
  @previously_new_record
435
674
  end
436
675
 
676
+ # Returns true if this object was previously persisted but now it has been deleted.
677
+ def previously_persisted?
678
+ !new_record? && destroyed?
679
+ end
680
+
437
681
  # Returns true if this object has been destroyed, otherwise returns false.
438
682
  def destroyed?
439
683
  @destroyed
@@ -522,6 +766,7 @@ module ActiveRecord
522
766
  def delete
523
767
  _delete_row if persisted?
524
768
  @destroyed = true
769
+ @previously_new_record = false
525
770
  freeze
526
771
  end
527
772
 
@@ -535,12 +780,9 @@ module ActiveRecord
535
780
  def destroy
536
781
  _raise_readonly_record_error if readonly?
537
782
  destroy_associations
538
- @_trigger_destroy_callback = if persisted?
539
- destroy_row > 0
540
- else
541
- true
542
- end
783
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
543
784
  @destroyed = true
785
+ @previously_new_record = false
544
786
  freeze
545
787
  end
546
788
 
@@ -556,21 +798,24 @@ module ActiveRecord
556
798
  end
557
799
 
558
800
  # Returns an instance of the specified +klass+ with the attributes of the
559
- # current record. This is mostly useful in relation to single-table
560
- # inheritance structures where you want a subclass to appear as the
801
+ # current record. This is mostly useful in relation to single table
802
+ # inheritance (STI) structures where you want a subclass to appear as the
561
803
  # superclass. This can be used along with record identification in
562
804
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
563
805
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
564
806
  # instance using the companies/company partial instead of clients/client.
565
807
  #
566
808
  # Note: The new instance will share a link to the same attributes as the original class.
567
- # Therefore the sti column value will still be the same.
809
+ # Therefore the STI column value will still be the same.
568
810
  # Any change to the attributes on either instance will affect both instances.
569
- # If you want to change the sti column as well, use #becomes! instead.
811
+ # This includes any attribute initialization done by the new instance.
812
+ #
813
+ # If you want to change the STI column as well, use #becomes! instead.
570
814
  def becomes(klass)
571
815
  became = klass.allocate
572
816
 
573
817
  became.send(:initialize) do |becoming|
818
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
574
819
  becoming.instance_variable_set(:@attributes, @attributes)
575
820
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
576
821
  becoming.instance_variable_set(:@new_record, new_record?)
@@ -581,11 +826,11 @@ module ActiveRecord
581
826
  became
582
827
  end
583
828
 
584
- # Wrapper around #becomes that also changes the instance's sti column value.
829
+ # Wrapper around #becomes that also changes the instance's STI column value.
585
830
  # This is especially useful if you want to persist the changed class in your
586
831
  # database.
587
832
  #
588
- # Note: The old instance's sti column value will be changed too, as both objects
833
+ # Note: The old instance's STI column value will be changed too, as both objects
589
834
  # share the same set of attributes.
590
835
  def becomes!(klass)
591
836
  became = becomes(klass)
@@ -605,7 +850,7 @@ module ActiveRecord
605
850
  # * updated_at/updated_on column is updated if that column is available.
606
851
  # * Updates all the attributes that are dirty in this object.
607
852
  #
608
- # This method raises an ActiveRecord::ActiveRecordError if the
853
+ # This method raises an ActiveRecord::ActiveRecordError if the
609
854
  # attribute is marked as readonly.
610
855
  #
611
856
  # Also see #update_column.
@@ -617,6 +862,28 @@ module ActiveRecord
617
862
  save(validate: false)
618
863
  end
619
864
 
865
+ # Updates a single attribute and saves the record.
866
+ # This is especially useful for boolean flags on existing records. Also note that
867
+ #
868
+ # * Validation is skipped.
869
+ # * \Callbacks are invoked.
870
+ # * updated_at/updated_on column is updated if that column is available.
871
+ # * Updates all the attributes that are dirty in this object.
872
+ #
873
+ # This method raises an ActiveRecord::ActiveRecordError if the
874
+ # attribute is marked as readonly.
875
+ #
876
+ # If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
877
+ # and #update_attribute! raises ActiveRecord::RecordNotSaved. See
878
+ # ActiveRecord::Callbacks for further details.
879
+ def update_attribute!(name, value)
880
+ name = name.to_s
881
+ verify_readonly_attribute(name)
882
+ public_send("#{name}=", value)
883
+
884
+ save!(validate: false)
885
+ end
886
+
620
887
  # Updates the attributes of the model from the passed-in hash and saves the
621
888
  # record, all wrapped in a transaction. If the object is invalid, the saving
622
889
  # will fail and false will be returned.
@@ -664,6 +931,7 @@ module ActiveRecord
664
931
  def update_columns(attributes)
665
932
  raise ActiveRecordError, "cannot update a new record" if new_record?
666
933
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
934
+ _raise_readonly_record_error if readonly?
667
935
 
668
936
  attributes = attributes.transform_keys do |key|
669
937
  name = key.to_s
@@ -671,14 +939,15 @@ module ActiveRecord
671
939
  verify_readonly_attribute(name) || name
672
940
  end
673
941
 
674
- id_in_database = self.id_in_database
675
- attributes.each do |k, v|
676
- write_attribute_without_type_cast(k, v)
942
+ update_constraints = _query_constraints_hash
943
+ attributes = attributes.each_with_object({}) do |(k, v), h|
944
+ h[k] = @attributes.write_cast_value(k, v)
945
+ clear_attribute_change(k)
677
946
  end
678
947
 
679
948
  affected_rows = self.class._update_record(
680
949
  attributes,
681
- @primary_key => id_in_database
950
+ update_constraints
682
951
  )
683
952
 
684
953
  affected_rows == 1
@@ -800,13 +1069,14 @@ module ActiveRecord
800
1069
  def reload(options = nil)
801
1070
  self.class.connection.clear_query_cache
802
1071
 
803
- fresh_object =
804
- if options && options[:lock]
805
- self.class.unscoped { self.class.lock(options[:lock]).find(id) }
806
- else
807
- self.class.unscoped { self.class.find(id) }
808
- end
1072
+ fresh_object = if apply_scoping?(options)
1073
+ _find_record((options || {}).merge(all_queries: true))
1074
+ else
1075
+ self.class.unscoped { _find_record(options) }
1076
+ end
809
1077
 
1078
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
1079
+ @association_cache.each_value { |association| association.owner = self }
810
1080
  @attributes = fresh_object.instance_variable_get(:@attributes)
811
1081
  @new_record = false
812
1082
  @previously_new_record = false
@@ -849,12 +1119,15 @@ module ActiveRecord
849
1119
  #
850
1120
  def touch(*names, time: nil)
851
1121
  _raise_record_not_touched_error unless persisted?
1122
+ _raise_readonly_record_error if readonly?
852
1123
 
853
1124
  attribute_names = timestamp_attributes_for_update_in_model
854
- attribute_names |= names.map! do |name|
1125
+ attribute_names = (attribute_names | names).map! do |name|
855
1126
  name = name.to_s
856
- self.class.attribute_aliases[name] || name
857
- end unless names.empty?
1127
+ name = self.class.attribute_aliases[name] || name
1128
+ verify_readonly_attribute(name)
1129
+ name
1130
+ end
858
1131
 
859
1132
  unless attribute_names.empty?
860
1133
  affected_rows = _touch_row(attribute_names, time)
@@ -865,6 +1138,54 @@ module ActiveRecord
865
1138
  end
866
1139
 
867
1140
  private
1141
+ def init_internals
1142
+ super
1143
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
1144
+ @previously_new_record = false
1145
+ end
1146
+
1147
+ def strict_loaded_associations
1148
+ @association_cache.find_all do |_, assoc|
1149
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
1150
+ end.map(&:first)
1151
+ end
1152
+
1153
+ def _find_record(options)
1154
+ all_queries = options ? options[:all_queries] : nil
1155
+ base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
1156
+
1157
+ if options && options[:lock]
1158
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
1159
+ else
1160
+ base.find_by!(_in_memory_query_constraints_hash)
1161
+ end
1162
+ end
1163
+
1164
+ def _in_memory_query_constraints_hash
1165
+ if self.class.query_constraints_list.nil?
1166
+ { @primary_key => id }
1167
+ else
1168
+ self.class.query_constraints_list.index_with do |column_name|
1169
+ attribute(column_name)
1170
+ end
1171
+ end
1172
+ end
1173
+
1174
+ def apply_scoping?(options)
1175
+ !(options && options[:unscoped]) &&
1176
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
1177
+ end
1178
+
1179
+ def _query_constraints_hash
1180
+ if self.class.query_constraints_list.nil?
1181
+ { @primary_key => id_in_database }
1182
+ else
1183
+ self.class.query_constraints_list.index_with do |column_name|
1184
+ attribute_in_database(column_name)
1185
+ end
1186
+ end
1187
+ end
1188
+
868
1189
  # A hook to be overridden by association modules.
869
1190
  def destroy_associations
870
1191
  end
@@ -874,7 +1195,7 @@ module ActiveRecord
874
1195
  end
875
1196
 
876
1197
  def _delete_row
877
- self.class._delete_record(@primary_key => id_in_database)
1198
+ self.class._delete_record(_query_constraints_hash)
878
1199
  end
879
1200
 
880
1201
  def _touch_row(attribute_names, time)
@@ -890,7 +1211,7 @@ module ActiveRecord
890
1211
  def _update_row(attribute_names, attempted_action = "update")
891
1212
  self.class._update_record(
892
1213
  attributes_with_values(attribute_names),
893
- @primary_key => id_in_database
1214
+ _query_constraints_hash
894
1215
  )
895
1216
  end
896
1217
 
@@ -926,11 +1247,16 @@ module ActiveRecord
926
1247
  def _create_record(attribute_names = self.attribute_names)
927
1248
  attribute_names = attributes_for_create(attribute_names)
928
1249
 
929
- new_id = self.class._insert_record(
930
- attributes_with_values(attribute_names)
1250
+ returning_columns = self.class._returning_columns_for_insert
1251
+
1252
+ returning_values = self.class._insert_record(
1253
+ attributes_with_values(attribute_names),
1254
+ returning_columns
931
1255
  )
932
1256
 
933
- self.id ||= new_id if @primary_key
1257
+ returning_columns.zip(returning_values).each do |column, value|
1258
+ _write_attribute(column, value) if !_read_attribute(column)
1259
+ end if returning_values
934
1260
 
935
1261
  @new_record = false
936
1262
  @previously_new_record = true
@@ -946,7 +1272,8 @@ module ActiveRecord
946
1272
 
947
1273
  def _raise_record_not_destroyed
948
1274
  @_association_destroy_exception ||= nil
949
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
1275
+ key = self.class.primary_key
1276
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
950
1277
  ensure
951
1278
  @_association_destroy_exception = nil
952
1279
  end
@@ -961,11 +1288,5 @@ module ActiveRecord
961
1288
  persisted?, new_record?, or destroyed? before touching.
962
1289
  MSG
963
1290
  end
964
-
965
- # The name of the method used to touch a +belongs_to+ association when the
966
- # +:touch+ option is used.
967
- def belongs_to_touch_method
968
- :touch
969
- end
970
1291
  end
971
1292
  end