activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  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 +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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
345
195
  #
346
- # # Delete a single row
347
- # Todo.delete(1)
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
348
199
  #
349
- # # Delete multiple rows
350
- # Todo.delete([2,3,4])
351
- def delete(id_or_array)
352
- delete_by(primary_key => id_or_array)
200
+ # developer.name = "Bob"
201
+ # developer.save!
202
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
203
+ #
204
+ # developer.destroy!
205
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
206
+ #
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
229
+ end
230
+
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)
353
236
  end
354
237
 
355
- def _insert_record(values) # :nodoc:
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)
250
+
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
374
257
 
375
- connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
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] }
267
+
268
+ default_constraint = build_default_constraint
269
+ constraints << default_constraint if default_constraint
380
270
 
381
- um = arel_table.where(
382
- constraints.reduce(&:and)
383
- ).compile_update(_substitute_values(values), primary_key)
271
+ if current_scope = self.global_current_scope
272
+ constraints << current_scope.where_clause.ast
273
+ end
384
274
 
385
- connection.update(um, "#{self} Update")
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] }
390
286
 
391
- dm = Arel::DeleteManager.new
392
- dm.from(arel_table)
287
+ default_constraint = build_default_constraint
288
+ constraints << default_constraint if default_constraint
289
+
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,38 +324,49 @@ module ActiveRecord
412
324
  self
413
325
  end
414
326
 
415
- def _substitute_values(values)
416
- values.map do |name, value|
417
- attr = arel_attribute(name)
418
- bind = predicate_builder.build_bind_attribute(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
 
424
338
  # Returns true if this object hasn't been saved yet -- that is, a record
425
339
  # for the object doesn't exist in the database yet; otherwise, returns false.
426
340
  def new_record?
427
- sync_with_transaction_state if @transaction_state&.finalized?
428
341
  @new_record
429
342
  end
430
343
 
344
+ # Returns true if this object was just created -- that is, prior to the last
345
+ # update or delete, the object didn't exist in the database and new_record? would have
346
+ # returned true.
347
+ def previously_new_record?
348
+ @previously_new_record
349
+ end
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
+
431
356
  # Returns true if this object has been destroyed, otherwise returns false.
432
357
  def destroyed?
433
- sync_with_transaction_state if @transaction_state&.finalized?
434
358
  @destroyed
435
359
  end
436
360
 
437
361
  # Returns true if the record is persisted, i.e. it's not a new record and it was
438
362
  # not destroyed, otherwise returns false.
439
363
  def persisted?
440
- sync_with_transaction_state if @transaction_state&.finalized?
441
364
  !(@new_record || @destroyed)
442
365
  end
443
366
 
444
367
  ##
445
368
  # :call-seq:
446
- # save(*args)
369
+ # save(**options)
447
370
  #
448
371
  # Saves the model.
449
372
  #
@@ -466,15 +389,15 @@ module ActiveRecord
466
389
  #
467
390
  # Attributes marked as readonly are silently ignored if the record is
468
391
  # being updated.
469
- def save(*args, &block)
470
- create_or_update(*args, &block)
392
+ def save(**options, &block)
393
+ create_or_update(**options, &block)
471
394
  rescue ActiveRecord::RecordInvalid
472
395
  false
473
396
  end
474
397
 
475
398
  ##
476
399
  # :call-seq:
477
- # save!(*args)
400
+ # save!(**options)
478
401
  #
479
402
  # Saves the model.
480
403
  #
@@ -499,8 +422,8 @@ module ActiveRecord
499
422
  # being updated.
500
423
  #
501
424
  # Unless an error is raised, returns true.
502
- def save!(*args, &block)
503
- create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
425
+ def save!(**options, &block)
426
+ create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
504
427
  end
505
428
 
506
429
  # Deletes the record in the database and freezes this instance to
@@ -514,10 +437,11 @@ module ActiveRecord
514
437
  #
515
438
  # To enforce the object's +before_destroy+ and +after_destroy+
516
439
  # callbacks or any <tt>:dependent</tt> association
517
- # options, use <tt>#destroy</tt>.
440
+ # options, use #destroy.
518
441
  def delete
519
442
  _delete_row if persisted?
520
443
  @destroyed = true
444
+ @previously_new_record = false
521
445
  freeze
522
446
  end
523
447
 
@@ -531,12 +455,9 @@ module ActiveRecord
531
455
  def destroy
532
456
  _raise_readonly_record_error if readonly?
533
457
  destroy_associations
534
- @_trigger_destroy_callback = if persisted?
535
- destroy_row > 0
536
- else
537
- true
538
- end
458
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
539
459
  @destroyed = true
460
+ @previously_new_record = false
540
461
  freeze
541
462
  end
542
463
 
@@ -552,33 +473,39 @@ module ActiveRecord
552
473
  end
553
474
 
554
475
  # Returns an instance of the specified +klass+ with the attributes of the
555
- # current record. This is mostly useful in relation to single-table
556
- # 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
557
478
  # superclass. This can be used along with record identification in
558
479
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
559
480
  # like render <tt>partial: @client.becomes(Company)</tt> to render that
560
481
  # instance using the companies/company partial instead of clients/client.
561
482
  #
562
483
  # Note: The new instance will share a link to the same attributes as the original class.
563
- # Therefore the sti column value will still be the same.
484
+ # Therefore the STI column value will still be the same.
564
485
  # Any change to the attributes on either instance will affect both instances.
565
- # 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.
566
489
  def becomes(klass)
567
490
  became = klass.allocate
568
- became.send(:initialize)
569
- became.instance_variable_set("@attributes", @attributes)
570
- became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
571
- became.instance_variable_set("@new_record", new_record?)
572
- became.instance_variable_set("@destroyed", destroyed?)
573
- became.errors.copy!(errors)
491
+
492
+ became.send(:initialize) do |becoming|
493
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
494
+ becoming.instance_variable_set(:@attributes, @attributes)
495
+ becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
496
+ becoming.instance_variable_set(:@new_record, new_record?)
497
+ becoming.instance_variable_set(:@destroyed, destroyed?)
498
+ becoming.errors.copy!(errors)
499
+ end
500
+
574
501
  became
575
502
  end
576
503
 
577
- # 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.
578
505
  # This is especially useful if you want to persist the changed class in your
579
506
  # database.
580
507
  #
581
- # 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
582
509
  # share the same set of attributes.
583
510
  def becomes!(klass)
584
511
  became = becomes(klass)
@@ -598,7 +525,7 @@ module ActiveRecord
598
525
  # * updated_at/updated_on column is updated if that column is available.
599
526
  # * Updates all the attributes that are dirty in this object.
600
527
  #
601
- # This method raises an ActiveRecord::ActiveRecordError if the
528
+ # This method raises an ActiveRecord::ActiveRecordError if the
602
529
  # attribute is marked as readonly.
603
530
  #
604
531
  # Also see #update_column.
@@ -610,6 +537,28 @@ module ActiveRecord
610
537
  save(validate: false)
611
538
  end
612
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
+
613
562
  # Updates the attributes of the model from the passed-in hash and saves the
614
563
  # record, all wrapped in a transaction. If the object is invalid, the saving
615
564
  # will fail and false will be returned.
@@ -622,9 +571,6 @@ module ActiveRecord
622
571
  end
623
572
  end
624
573
 
625
- alias update_attributes update
626
- deprecate update_attributes: "please, use update instead"
627
-
628
574
  # Updates its receiver just like #update but calls #save! instead
629
575
  # of +save+, so an exception is raised if the record is invalid and saving will fail.
630
576
  def update!(attributes)
@@ -636,9 +582,6 @@ module ActiveRecord
636
582
  end
637
583
  end
638
584
 
639
- alias update_attributes! update!
640
- deprecate update_attributes!: "please, use update! instead"
641
-
642
585
  # Equivalent to <code>update_columns(name => value)</code>.
643
586
  def update_column(name, value)
644
587
  update_columns(name => value)
@@ -663,24 +606,23 @@ module ActiveRecord
663
606
  def update_columns(attributes)
664
607
  raise ActiveRecordError, "cannot update a new record" if new_record?
665
608
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
609
+ _raise_readonly_record_error if readonly?
666
610
 
667
611
  attributes = attributes.transform_keys do |key|
668
612
  name = key.to_s
669
- self.class.attribute_aliases[name] || name
670
- end
671
-
672
- attributes.each_key do |key|
673
- verify_readonly_attribute(key)
613
+ name = self.class.attribute_aliases[name] || name
614
+ verify_readonly_attribute(name) || name
674
615
  end
675
616
 
676
- id_in_database = self.id_in_database
677
- attributes.each do |k, v|
678
- 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)
679
621
  end
680
622
 
681
623
  affected_rows = self.class._update_record(
682
624
  attributes,
683
- @primary_key => id_in_database
625
+ update_constraints
684
626
  )
685
627
 
686
628
  affected_rows == 1
@@ -703,9 +645,9 @@ module ActiveRecord
703
645
  # Returns +self+.
704
646
  def increment!(attribute, by = 1, touch: nil)
705
647
  increment(attribute, by)
706
- change = public_send(attribute) - (attribute_in_database(attribute.to_s) || 0)
648
+ change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
707
649
  self.class.update_counters(id, attribute => change, touch: touch)
708
- clear_attribute_change(attribute) # eww
650
+ public_send(:"clear_#{attribute}_change")
709
651
  self
710
652
  end
711
653
 
@@ -800,17 +742,19 @@ module ActiveRecord
800
742
  # end
801
743
  #
802
744
  def reload(options = nil)
803
- self.class.connection.clear_query_cache
745
+ self.class.connection_pool.clear_query_cache
804
746
 
805
- fresh_object =
806
- if options && options[:lock]
807
- self.class.unscoped { self.class.lock(options[:lock]).find(id) }
808
- else
809
- self.class.unscoped { self.class.find(id) }
810
- 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
811
752
 
812
- @attributes = fresh_object.instance_variable_get("@attributes")
753
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
754
+ @association_cache.each_value { |association| association.owner = self }
755
+ @attributes = fresh_object.instance_variable_get(:@attributes)
813
756
  @new_record = false
757
+ @previously_new_record = false
814
758
  self
815
759
  end
816
760
 
@@ -849,17 +793,16 @@ module ActiveRecord
849
793
  # ball.touch(:updated_at) # => raises ActiveRecordError
850
794
  #
851
795
  def touch(*names, time: nil)
852
- unless persisted?
853
- raise ActiveRecordError, <<-MSG.squish
854
- cannot touch on a new or destroyed record object. Consider using
855
- persisted?, new_record?, or destroyed? before touching
856
- MSG
857
- end
796
+ _raise_record_not_touched_error unless persisted?
797
+ _raise_readonly_record_error if readonly?
858
798
 
859
799
  attribute_names = timestamp_attributes_for_update_in_model
860
- attribute_names |= names.map!(&:to_s).map! { |name|
861
- self.class.attribute_aliases[name] || name
862
- }
800
+ attribute_names = (attribute_names | names).map! do |name|
801
+ name = name.to_s
802
+ name = self.class.attribute_aliases[name] || name
803
+ verify_readonly_attribute(name)
804
+ name
805
+ end
863
806
 
864
807
  unless attribute_names.empty?
865
808
  affected_rows = _touch_row(attribute_names, time)
@@ -870,6 +813,53 @@ module ActiveRecord
870
813
  end
871
814
 
872
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
873
863
 
874
864
  # A hook to be overridden by association modules.
875
865
  def destroy_associations
@@ -880,7 +870,7 @@ module ActiveRecord
880
870
  end
881
871
 
882
872
  def _delete_row
883
- self.class._delete_record(@primary_key => id_in_database)
873
+ self.class._delete_record(_query_constraints_hash)
884
874
  end
885
875
 
886
876
  def _touch_row(attribute_names, time)
@@ -896,7 +886,7 @@ module ActiveRecord
896
886
  def _update_row(attribute_names, attempted_action = "update")
897
887
  self.class._update_record(
898
888
  attributes_with_values(attribute_names),
899
- @primary_key => id_in_database
889
+ _query_constraints_hash
900
890
  )
901
891
  end
902
892
 
@@ -920,6 +910,8 @@ module ActiveRecord
920
910
  @_trigger_update_callback = affected_rows == 1
921
911
  end
922
912
 
913
+ @previously_new_record = false
914
+
923
915
  yield(self) if block_given?
924
916
 
925
917
  affected_rows
@@ -930,13 +922,22 @@ module ActiveRecord
930
922
  def _create_record(attribute_names = self.attribute_names)
931
923
  attribute_names = attributes_for_create(attribute_names)
932
924
 
933
- new_id = self.class._insert_record(
934
- attributes_with_values(attribute_names)
935
- )
925
+ self.class.with_connection do |connection|
926
+ returning_columns = self.class._returning_columns_for_insert(connection)
936
927
 
937
- self.id ||= new_id if @primary_key
928
+ returning_values = self.class._insert_record(
929
+ connection,
930
+ attributes_with_values(attribute_names),
931
+ returning_columns
932
+ )
933
+
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
938
938
 
939
939
  @new_record = false
940
+ @previously_new_record = true
940
941
 
941
942
  yield(self) if block_given?
942
943
 
@@ -944,24 +945,26 @@ module ActiveRecord
944
945
  end
945
946
 
946
947
  def verify_readonly_attribute(name)
947
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
948
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
948
949
  end
949
950
 
950
951
  def _raise_record_not_destroyed
951
952
  @_association_destroy_exception ||= nil
952
- 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)
953
955
  ensure
954
956
  @_association_destroy_exception = nil
955
957
  end
956
958
 
957
- # The name of the method used to touch a +belongs_to+ association when the
958
- # +:touch+ option is used.
959
- def belongs_to_touch_method
960
- :touch
961
- end
962
-
963
959
  def _raise_readonly_record_error
964
960
  raise ReadOnlyRecord, "#{self.class} is marked as readonly"
965
961
  end
962
+
963
+ def _raise_record_not_touched_error
964
+ raise ActiveRecordError, <<~MSG.squish
965
+ Cannot touch on a new or destroyed record object. Consider using
966
+ persisted?, new_record?, or destroyed? before touching.
967
+ MSG
968
+ end
966
969
  end
967
970
  end