activerecord 6.1.7 → 7.2.0

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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -57,190 +57,34 @@ module ActiveRecord
57
57
  end
58
58
  end
59
59
 
60
- # Inserts a single record into the database in a single SQL INSERT
61
- # statement. It does not instantiate any models nor does it trigger
62
- # Active Record callbacks or validations. Though passed values
63
- # go through Active Record's type casting and serialization.
60
+ # Builds an object (or multiple objects) and returns either the built object or a list of built
61
+ # objects.
64
62
  #
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)
68
- end
69
-
70
- # Inserts multiple records into the database in a single SQL INSERT
71
- # statement. It does not instantiate any models nor does it trigger
72
- # Active Record callbacks or validations. Though passed values
73
- # go through Active Record's type casting and serialization.
74
- #
75
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
76
- # the attributes for a single row and must have the same keys.
77
- #
78
- # Rows are considered to be unique by every unique index on the table. Any
79
- # duplicate rows are skipped.
80
- # Override with <tt>:unique_by</tt> (see below).
81
- #
82
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
83
- # <tt>:returning</tt> (see below).
84
- #
85
- # ==== Options
86
- #
87
- # [:returning]
88
- # (PostgreSQL only) An array of attributes to return for all successfully
89
- # inserted records, which by default is the primary key.
90
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
91
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
92
- # clause entirely.
93
- #
94
- # [:unique_by]
95
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
96
- # by every unique index on the table. Any duplicate rows are skipped.
97
- #
98
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
99
- #
100
- # Consider a Book model where no duplicate ISBNs make sense, but if any
101
- # row has an existing id, or is not unique by another unique index,
102
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
103
- #
104
- # Unique indexes can be identified by columns or name:
105
- #
106
- # unique_by: :isbn
107
- # unique_by: %i[ author_id name ]
108
- # unique_by: :index_books_on_isbn
109
- #
110
- # Because it relies on the index information from the database
111
- # <tt>:unique_by</tt> is recommended to be paired with
112
- # Active Record's schema_cache.
113
- #
114
- # ==== Example
115
- #
116
- # # Insert records and skip inserting any duplicates.
117
- # # Here "Eloquent Ruby" is skipped because its id is not unique.
118
- #
119
- # Book.insert_all([
120
- # { id: 1, title: "Rework", author: "David" },
121
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
122
- # ])
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
125
- end
126
-
127
- # Inserts a single record into the database in a single SQL INSERT
128
- # statement. It does not instantiate any models nor does it trigger
129
- # Active Record callbacks or validations. Though passed values
130
- # go through Active Record's type casting and serialization.
131
- #
132
- # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
- def insert!(attributes, returning: nil)
134
- insert_all!([ attributes ], returning: returning)
135
- end
136
-
137
- # Inserts multiple records into the database in a single SQL INSERT
138
- # statement. It does not instantiate any models nor does it trigger
139
- # Active Record callbacks or validations. Though passed values
140
- # go through Active Record's type casting and serialization.
141
- #
142
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
143
- # the attributes for a single row and must have the same keys.
144
- #
145
- # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
146
- # unique index on the table. In that case, no rows are inserted.
147
- #
148
- # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
149
- # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
150
- #
151
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
152
- # <tt>:returning</tt> (see below).
153
- #
154
- # ==== Options
155
- #
156
- # [:returning]
157
- # (PostgreSQL only) An array of attributes to return for all successfully
158
- # inserted records, which by default is the primary key.
159
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
160
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
- # clause entirely.
162
- #
163
- # ==== Examples
164
- #
165
- # # Insert multiple records
166
- # Book.insert_all!([
167
- # { title: "Rework", author: "David" },
168
- # { title: "Eloquent Ruby", author: "Russ" }
169
- # ])
170
- #
171
- # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
172
- # # does not have a unique id.
173
- # Book.insert_all!([
174
- # { id: 1, title: "Rework", author: "David" },
175
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
- # ])
177
- def insert_all!(attributes, returning: nil)
178
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
179
- end
180
-
181
- # Updates or inserts (upserts) a single record into the database in a
182
- # single SQL INSERT statement. It does not instantiate any models nor does
183
- # it trigger Active Record callbacks or validations. Though passed values
184
- # go through Active Record's type casting and serialization.
185
- #
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)
189
- end
190
-
191
- # Updates or inserts (upserts) multiple records into the database in a
192
- # single SQL INSERT statement. It does not instantiate any models nor does
193
- # it trigger Active Record callbacks or validations. Though passed values
194
- # go through Active Record's type casting and serialization.
195
- #
196
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
197
- # the attributes for a single row and must have the same keys.
198
- #
199
- # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
200
- # <tt>:returning</tt> (see below).
201
- #
202
- # ==== Options
203
- #
204
- # [:returning]
205
- # (PostgreSQL only) An array of attributes to return for all successfully
206
- # inserted records, which by default is the primary key.
207
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
208
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
- # clause entirely.
210
- #
211
- # [:unique_by]
212
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
- # by every unique index on the table. Any duplicate rows are skipped.
214
- #
215
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
216
- #
217
- # Consider a Book model where no duplicate ISBNs make sense, but if any
218
- # row has an existing id, or is not unique by another unique index,
219
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
220
- #
221
- # Unique indexes can be identified by columns or name:
222
- #
223
- # unique_by: :isbn
224
- # unique_by: %i[ author_id name ]
225
- # unique_by: :index_books_on_isbn
226
- #
227
- # Because it relies on the index information from the database
228
- # <tt>:unique_by</tt> is recommended to be paired with
229
- # Active Record's schema_cache.
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.
230
65
  #
231
66
  # ==== Examples
67
+ # # Build a single new object
68
+ # User.build(first_name: 'Jamie')
232
69
  #
233
- # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
234
- # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
70
+ # # Build an Array of new objects
71
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
235
72
  #
236
- # Book.upsert_all([
237
- # { title: "Rework", author: "David", isbn: "1" },
238
- # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
239
- # ], unique_by: :isbn)
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
240
77
  #
241
- # 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
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
244
88
  end
245
89
 
246
90
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -264,6 +108,7 @@ module ActiveRecord
264
108
  # ==== Parameters
265
109
  #
266
110
  # * +id+ - This should be the id or an array of ids to be updated.
111
+ # Optional argument, defaults to all records in the relation.
267
112
  # * +attributes+ - This should be a hash of attributes or an array of hashes.
268
113
  #
269
114
  # ==== Examples
@@ -286,6 +131,11 @@ module ActiveRecord
286
131
  # for updating all records in a single query.
287
132
  def update(id = :all, attributes)
288
133
  if id.is_a?(Array)
134
+ if id.any?(ActiveRecord::Base)
135
+ raise ArgumentError,
136
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
137
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
138
+ end
289
139
  id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
290
140
  object.update(attributes[idx])
291
141
  }
@@ -303,99 +153,161 @@ module ActiveRecord
303
153
  end
304
154
  end
305
155
 
306
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
307
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
308
- # less efficient than #delete but allows cleanup methods and other actions to be run.
309
- #
310
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
311
- # from the attributes, and then calls destroy on it.
312
- #
313
- # ==== Parameters
314
- #
315
- # * +id+ - This should be the id or an array of ids to be destroyed.
316
- #
317
- # ==== Examples
318
- #
319
- # # Destroy a single object
320
- # Todo.destroy(1)
321
- #
322
- # # Destroy multiple objects
323
- # todos = [1,2,3]
324
- # Todo.destroy(todos)
325
- def destroy(id)
156
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
157
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
158
+ def update!(id = :all, attributes)
326
159
  if id.is_a?(Array)
327
- find(id).each(&:destroy)
160
+ if id.any?(ActiveRecord::Base)
161
+ raise ArgumentError,
162
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
163
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
164
+ end
165
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
166
+ object.update!(attributes[idx])
167
+ }
168
+ elsif id == :all
169
+ all.each { |record| record.update!(attributes) }
328
170
  else
329
- find(id).destroy
171
+ if ActiveRecord::Base === id
172
+ raise ArgumentError,
173
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
174
+ "Please pass the id of the object by calling `.id`."
175
+ end
176
+ object = find(id)
177
+ object.update!(attributes)
178
+ object
330
179
  end
331
180
  end
332
181
 
333
- # Deletes the row with a primary key matching the +id+ argument, using an
334
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
335
- # Record objects are not instantiated, so the object's callbacks are not
336
- # executed, including any <tt>:dependent</tt> association options.
182
+ # Accepts a list of attribute names to be used in the WHERE clause
183
+ # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
337
184
  #
338
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
185
+ # class Developer < ActiveRecord::Base
186
+ # query_constraints :company_id, :id
187
+ # end
339
188
  #
340
- # Note: Although it is often much faster than the alternative, #destroy,
341
- # skipping callbacks might bypass business logic in your application
342
- # that ensures referential integrity or performs other essential jobs.
189
+ # developer = Developer.first
190
+ # # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
191
+ # developer.inspect # => #<Developer id: 1, company_id: 1, ...>
343
192
  #
344
- # ==== Examples
193
+ # developer.update!(name: "Nikita")
194
+ # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
195
+ #
196
+ # # It is possible to update an attribute used in the query_constraints clause:
197
+ # developer.update!(company_id: 2)
198
+ # # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
199
+ #
200
+ # developer.name = "Bob"
201
+ # developer.save!
202
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
345
203
  #
346
- # # Delete a single row
347
- # Todo.delete(1)
204
+ # developer.destroy!
205
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
348
206
  #
349
- # # Delete multiple rows
350
- # Todo.delete([2,3,4])
351
- def delete(id_or_array)
352
- delete_by(primary_key => id_or_array)
207
+ # developer.delete
208
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
209
+ #
210
+ # developer.reload
211
+ # # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
212
+ def query_constraints(*columns_list)
213
+ raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
214
+
215
+ @query_constraints_list = columns_list.map(&:to_s)
216
+ @has_query_constraints = @query_constraints_list
217
+ end
218
+
219
+ def has_query_constraints? # :nodoc:
220
+ @has_query_constraints
221
+ end
222
+
223
+ def query_constraints_list # :nodoc:
224
+ @query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
225
+ primary_key if primary_key.is_a?(Array)
226
+ else
227
+ base_class.query_constraints_list
228
+ end
353
229
  end
354
230
 
355
- def _insert_record(values) # :nodoc:
231
+ # Returns an array of column names to be used in queries. The source of column
232
+ # names is derived from +query_constraints_list+ or +primary_key+. This method
233
+ # is for internal use when the primary key is to be treated as an array.
234
+ def composite_query_constraints_list # :nodoc:
235
+ @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
236
+ end
237
+
238
+ def _insert_record(connection, values, returning) # :nodoc:
356
239
  primary_key = self.primary_key
357
240
  primary_key_value = nil
358
241
 
359
- if primary_key && Hash === values
360
- primary_key_value = values[primary_key]
361
-
362
- if !primary_key_value && prefetch_primary_key?
242
+ if prefetch_primary_key? && primary_key
243
+ values[primary_key] ||= begin
363
244
  primary_key_value = next_sequence_value
364
- values[primary_key] = primary_key_value
245
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
365
246
  end
366
247
  end
367
248
 
368
- if values.empty?
369
- im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
370
- im.into arel_table
371
- else
372
- im = arel_table.compile_insert(_substitute_values(values))
373
- end
249
+ im = Arel::InsertManager.new(arel_table)
374
250
 
375
- connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
251
+ with_connection do |c|
252
+ if values.empty?
253
+ im.insert(connection.empty_insert_statement_value(primary_key))
254
+ else
255
+ im.insert(values.transform_keys { |name| arel_table[name] })
256
+ end
257
+
258
+ connection.insert(
259
+ im, "#{self} Create", primary_key || false, primary_key_value,
260
+ returning: returning
261
+ )
262
+ end
376
263
  end
377
264
 
378
265
  def _update_record(values, constraints) # :nodoc:
379
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
266
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
380
267
 
381
- um = arel_table.where(
382
- constraints.reduce(&:and)
383
- ).compile_update(_substitute_values(values), primary_key)
268
+ default_constraint = build_default_constraint
269
+ constraints << default_constraint if default_constraint
384
270
 
385
- connection.update(um, "#{self} Update")
271
+ if current_scope = self.global_current_scope
272
+ constraints << current_scope.where_clause.ast
273
+ end
274
+
275
+ um = Arel::UpdateManager.new(arel_table)
276
+ um.set(values.transform_keys { |name| arel_table[name] })
277
+ um.wheres = constraints
278
+
279
+ with_connection do |c|
280
+ c.update(um, "#{self} Update")
281
+ end
386
282
  end
387
283
 
388
284
  def _delete_record(constraints) # :nodoc:
389
- constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
285
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
286
+
287
+ default_constraint = build_default_constraint
288
+ constraints << default_constraint if default_constraint
390
289
 
391
- dm = Arel::DeleteManager.new
392
- dm.from(arel_table)
290
+ if current_scope = self.global_current_scope
291
+ constraints << current_scope.where_clause.ast
292
+ end
293
+
294
+ dm = Arel::DeleteManager.new(arel_table)
393
295
  dm.wheres = constraints
394
296
 
395
- connection.delete(dm, "#{self} Destroy")
297
+ with_connection do |c|
298
+ c.delete(dm, "#{self} Destroy")
299
+ end
396
300
  end
397
301
 
398
302
  private
303
+ def inherited(subclass)
304
+ super
305
+ subclass.class_eval do
306
+ @_query_constraints_list = nil
307
+ @has_query_constraints = false
308
+ end
309
+ end
310
+
399
311
  # Given a class, an attributes hash, +instantiate_instance_of+ returns a
400
312
  # new instance of the class. Accepts only keys as strings.
401
313
  def instantiate_instance_of(klass, attributes, column_types = {}, &block)
@@ -412,12 +324,14 @@ module ActiveRecord
412
324
  self
413
325
  end
414
326
 
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
327
+ # Called by +_update_record+ and +_delete_record+
328
+ # to build `where` clause from default scopes.
329
+ # Skips empty scopes.
330
+ def build_default_constraint
331
+ return unless default_scopes?(all_queries: true)
332
+
333
+ default_where_clause = default_scoped(all_queries: true).where_clause
334
+ default_where_clause.ast unless default_where_clause.empty?
421
335
  end
422
336
  end
423
337
 
@@ -428,12 +342,17 @@ module ActiveRecord
428
342
  end
429
343
 
430
344
  # 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
345
+ # update or delete, the object didn't exist in the database and new_record? would have
432
346
  # returned true.
433
347
  def previously_new_record?
434
348
  @previously_new_record
435
349
  end
436
350
 
351
+ # Returns true if this object was previously persisted but now it has been deleted.
352
+ def previously_persisted?
353
+ !new_record? && destroyed?
354
+ end
355
+
437
356
  # Returns true if this object has been destroyed, otherwise returns false.
438
357
  def destroyed?
439
358
  @destroyed
@@ -522,6 +441,7 @@ module ActiveRecord
522
441
  def delete
523
442
  _delete_row if persisted?
524
443
  @destroyed = true
444
+ @previously_new_record = false
525
445
  freeze
526
446
  end
527
447
 
@@ -535,12 +455,9 @@ module ActiveRecord
535
455
  def destroy
536
456
  _raise_readonly_record_error if readonly?
537
457
  destroy_associations
538
- @_trigger_destroy_callback = if persisted?
539
- destroy_row > 0
540
- else
541
- true
542
- end
458
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
543
459
  @destroyed = true
460
+ @previously_new_record = false
544
461
  freeze
545
462
  end
546
463
 
@@ -556,21 +473,24 @@ module ActiveRecord
556
473
  end
557
474
 
558
475
  # 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
476
+ # current record. This is mostly useful in relation to single table
477
+ # inheritance (STI) structures where you want a subclass to appear as the
561
478
  # superclass. This can be used along with record identification in
562
479
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
563
480
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
564
481
  # instance using the companies/company partial instead of clients/client.
565
482
  #
566
483
  # 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.
484
+ # Therefore the STI column value will still be the same.
568
485
  # 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.
486
+ # This includes any attribute initialization done by the new instance.
487
+ #
488
+ # If you want to change the STI column as well, use #becomes! instead.
570
489
  def becomes(klass)
571
490
  became = klass.allocate
572
491
 
573
492
  became.send(:initialize) do |becoming|
493
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
574
494
  becoming.instance_variable_set(:@attributes, @attributes)
575
495
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
576
496
  becoming.instance_variable_set(:@new_record, new_record?)
@@ -581,11 +501,11 @@ module ActiveRecord
581
501
  became
582
502
  end
583
503
 
584
- # Wrapper around #becomes that also changes the instance's sti column value.
504
+ # Wrapper around #becomes that also changes the instance's STI column value.
585
505
  # This is especially useful if you want to persist the changed class in your
586
506
  # database.
587
507
  #
588
- # Note: The old instance's sti column value will be changed too, as both objects
508
+ # Note: The old instance's STI column value will be changed too, as both objects
589
509
  # share the same set of attributes.
590
510
  def becomes!(klass)
591
511
  became = becomes(klass)
@@ -605,7 +525,7 @@ module ActiveRecord
605
525
  # * updated_at/updated_on column is updated if that column is available.
606
526
  # * Updates all the attributes that are dirty in this object.
607
527
  #
608
- # This method raises an ActiveRecord::ActiveRecordError if the
528
+ # This method raises an ActiveRecord::ActiveRecordError if the
609
529
  # attribute is marked as readonly.
610
530
  #
611
531
  # Also see #update_column.
@@ -617,6 +537,28 @@ module ActiveRecord
617
537
  save(validate: false)
618
538
  end
619
539
 
540
+ # Updates a single attribute and saves the record.
541
+ # This is especially useful for boolean flags on existing records. Also note that
542
+ #
543
+ # * Validation is skipped.
544
+ # * \Callbacks are invoked.
545
+ # * updated_at/updated_on column is updated if that column is available.
546
+ # * Updates all the attributes that are dirty in this object.
547
+ #
548
+ # This method raises an ActiveRecord::ActiveRecordError if the
549
+ # attribute is marked as readonly.
550
+ #
551
+ # If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
552
+ # and #update_attribute! raises ActiveRecord::RecordNotSaved. See
553
+ # ActiveRecord::Callbacks for further details.
554
+ def update_attribute!(name, value)
555
+ name = name.to_s
556
+ verify_readonly_attribute(name)
557
+ public_send("#{name}=", value)
558
+
559
+ save!(validate: false)
560
+ end
561
+
620
562
  # Updates the attributes of the model from the passed-in hash and saves the
621
563
  # record, all wrapped in a transaction. If the object is invalid, the saving
622
564
  # will fail and false will be returned.
@@ -664,6 +606,7 @@ module ActiveRecord
664
606
  def update_columns(attributes)
665
607
  raise ActiveRecordError, "cannot update a new record" if new_record?
666
608
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
609
+ _raise_readonly_record_error if readonly?
667
610
 
668
611
  attributes = attributes.transform_keys do |key|
669
612
  name = key.to_s
@@ -671,14 +614,15 @@ module ActiveRecord
671
614
  verify_readonly_attribute(name) || name
672
615
  end
673
616
 
674
- id_in_database = self.id_in_database
675
- attributes.each do |k, v|
676
- write_attribute_without_type_cast(k, v)
617
+ update_constraints = _query_constraints_hash
618
+ attributes = attributes.each_with_object({}) do |(k, v), h|
619
+ h[k] = @attributes.write_cast_value(k, v)
620
+ clear_attribute_change(k)
677
621
  end
678
622
 
679
623
  affected_rows = self.class._update_record(
680
624
  attributes,
681
- @primary_key => id_in_database
625
+ update_constraints
682
626
  )
683
627
 
684
628
  affected_rows == 1
@@ -798,15 +742,16 @@ module ActiveRecord
798
742
  # end
799
743
  #
800
744
  def reload(options = nil)
801
- self.class.connection.clear_query_cache
745
+ self.class.connection_pool.clear_query_cache
802
746
 
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
747
+ fresh_object = if apply_scoping?(options)
748
+ _find_record((options || {}).merge(all_queries: true))
749
+ else
750
+ self.class.unscoped { _find_record(options) }
751
+ end
809
752
 
753
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
754
+ @association_cache.each_value { |association| association.owner = self }
810
755
  @attributes = fresh_object.instance_variable_get(:@attributes)
811
756
  @new_record = false
812
757
  @previously_new_record = false
@@ -849,12 +794,15 @@ module ActiveRecord
849
794
  #
850
795
  def touch(*names, time: nil)
851
796
  _raise_record_not_touched_error unless persisted?
797
+ _raise_readonly_record_error if readonly?
852
798
 
853
799
  attribute_names = timestamp_attributes_for_update_in_model
854
- attribute_names |= names.map! do |name|
800
+ attribute_names = (attribute_names | names).map! do |name|
855
801
  name = name.to_s
856
- self.class.attribute_aliases[name] || name
857
- end unless names.empty?
802
+ name = self.class.attribute_aliases[name] || name
803
+ verify_readonly_attribute(name)
804
+ name
805
+ end
858
806
 
859
807
  unless attribute_names.empty?
860
808
  affected_rows = _touch_row(attribute_names, time)
@@ -865,6 +813,54 @@ module ActiveRecord
865
813
  end
866
814
 
867
815
  private
816
+ def init_internals
817
+ super
818
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
819
+ @previously_new_record = false
820
+ end
821
+
822
+ def strict_loaded_associations
823
+ @association_cache.find_all do |_, assoc|
824
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
825
+ end.map(&:first)
826
+ end
827
+
828
+ def _find_record(options)
829
+ all_queries = options ? options[:all_queries] : nil
830
+ base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
831
+
832
+ if options && options[:lock]
833
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
834
+ else
835
+ base.find_by!(_in_memory_query_constraints_hash)
836
+ end
837
+ end
838
+
839
+ def _in_memory_query_constraints_hash
840
+ if self.class.query_constraints_list.nil?
841
+ { @primary_key => id }
842
+ else
843
+ self.class.query_constraints_list.index_with do |column_name|
844
+ attribute(column_name)
845
+ end
846
+ end
847
+ end
848
+
849
+ def apply_scoping?(options)
850
+ !(options && options[:unscoped]) &&
851
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
852
+ end
853
+
854
+ def _query_constraints_hash
855
+ if self.class.query_constraints_list.nil?
856
+ { @primary_key => id_in_database }
857
+ else
858
+ self.class.query_constraints_list.index_with do |column_name|
859
+ attribute_in_database(column_name)
860
+ end
861
+ end
862
+ end
863
+
868
864
  # A hook to be overridden by association modules.
869
865
  def destroy_associations
870
866
  end
@@ -874,7 +870,7 @@ module ActiveRecord
874
870
  end
875
871
 
876
872
  def _delete_row
877
- self.class._delete_record(@primary_key => id_in_database)
873
+ self.class._delete_record(_query_constraints_hash)
878
874
  end
879
875
 
880
876
  def _touch_row(attribute_names, time)
@@ -890,7 +886,7 @@ module ActiveRecord
890
886
  def _update_row(attribute_names, attempted_action = "update")
891
887
  self.class._update_record(
892
888
  attributes_with_values(attribute_names),
893
- @primary_key => id_in_database
889
+ _query_constraints_hash
894
890
  )
895
891
  end
896
892
 
@@ -926,11 +922,19 @@ module ActiveRecord
926
922
  def _create_record(attribute_names = self.attribute_names)
927
923
  attribute_names = attributes_for_create(attribute_names)
928
924
 
929
- new_id = self.class._insert_record(
930
- attributes_with_values(attribute_names)
931
- )
925
+ self.class.with_connection do |connection|
926
+ returning_columns = self.class._returning_columns_for_insert(connection)
927
+
928
+ returning_values = self.class._insert_record(
929
+ connection,
930
+ attributes_with_values(attribute_names),
931
+ returning_columns
932
+ )
932
933
 
933
- self.id ||= new_id if @primary_key
934
+ returning_columns.zip(returning_values).each do |column, value|
935
+ _write_attribute(column, value) if !_read_attribute(column)
936
+ end if returning_values
937
+ end
934
938
 
935
939
  @new_record = false
936
940
  @previously_new_record = true
@@ -946,7 +950,8 @@ module ActiveRecord
946
950
 
947
951
  def _raise_record_not_destroyed
948
952
  @_association_destroy_exception ||= nil
949
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
953
+ key = self.class.primary_key
954
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
950
955
  ensure
951
956
  @_association_destroy_exception = nil
952
957
  end
@@ -961,11 +966,5 @@ module ActiveRecord
961
966
  persisted?, new_record?, or destroyed? before touching.
962
967
  MSG
963
968
  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
969
  end
971
970
  end