activerecord 7.0.8.7 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1944
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +54 -12
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -57,280 +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 #insert_all for documentation.
66
- def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
67
- insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
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 ActiveRecord::Result 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
- # You can also pass an SQL string if you need more control on the return values
95
- # (for example, <tt>returning: "id, name as new_name"</tt>).
96
- #
97
- # [:unique_by]
98
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
99
- # by every unique index on the table. Any duplicate rows are skipped.
100
- #
101
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
102
- #
103
- # Consider a Book model where no duplicate ISBNs make sense, but if any
104
- # row has an existing id, or is not unique by another unique index,
105
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
106
- #
107
- # Unique indexes can be identified by columns or name:
108
- #
109
- # unique_by: :isbn
110
- # unique_by: %i[ author_id name ]
111
- # unique_by: :index_books_on_isbn
112
- #
113
- # [:record_timestamps]
114
- # By default, automatic setting of timestamp columns is controlled by
115
- # the model's <tt>record_timestamps</tt> config, matching typical
116
- # behavior.
117
- #
118
- # To override this and force automatic setting of timestamp columns one
119
- # way or the other, pass <tt>:record_timestamps</tt>:
120
- #
121
- # record_timestamps: true # Always set timestamps automatically
122
- # record_timestamps: false # Never set timestamps automatically
123
- #
124
- # Because it relies on the index information from the database
125
- # <tt>:unique_by</tt> is recommended to be paired with
126
- # Active Record's schema_cache.
127
- #
128
- # ==== Example
129
- #
130
- # # Insert records and skip inserting any duplicates.
131
- # # Here "Eloquent Ruby" is skipped because its id is not unique.
132
- #
133
- # Book.insert_all([
134
- # { id: 1, title: "Rework", author: "David" },
135
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
136
- # ])
137
- #
138
- # # insert_all works on chained scopes, and you can use create_with
139
- # # to set default attributes for all inserted records.
140
- #
141
- # author.books.create_with(created_at: Time.now).insert_all([
142
- # { id: 1, title: "Rework" },
143
- # { id: 2, title: "Eloquent Ruby" }
144
- # ])
145
- def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
146
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
147
- end
148
-
149
- # Inserts a single record into the database in a single SQL INSERT
150
- # statement. It does not instantiate any models nor does it trigger
151
- # Active Record callbacks or validations. Though passed values
152
- # go through Active Record's type casting and serialization.
153
- #
154
- # See #insert_all! for more.
155
- def insert!(attributes, returning: nil, record_timestamps: nil)
156
- insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
157
- end
158
-
159
- # Inserts multiple records into the database in a single SQL INSERT
160
- # statement. It does not instantiate any models nor does it trigger
161
- # Active Record callbacks or validations. Though passed values
162
- # go through Active Record's type casting and serialization.
163
- #
164
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
165
- # the attributes for a single row and must have the same keys.
166
- #
167
- # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
168
- # unique index on the table. In that case, no rows are inserted.
169
- #
170
- # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
171
- #
172
- # Returns an ActiveRecord::Result with its contents based on
173
- # <tt>:returning</tt> (see below).
174
- #
175
- # ==== Options
176
- #
177
- # [:returning]
178
- # (PostgreSQL only) An array of attributes to return for all successfully
179
- # inserted records, which by default is the primary key.
180
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
181
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
182
- # clause entirely.
183
- #
184
- # You can also pass an SQL string if you need more control on the return values
185
- # (for example, <tt>returning: "id, name as new_name"</tt>).
186
- #
187
- # [:record_timestamps]
188
- # By default, automatic setting of timestamp columns is controlled by
189
- # the model's <tt>record_timestamps</tt> config, matching typical
190
- # behavior.
191
- #
192
- # To override this and force automatic setting of timestamp columns one
193
- # way or the other, pass <tt>:record_timestamps</tt>:
194
- #
195
- # record_timestamps: true # Always set timestamps automatically
196
- # record_timestamps: false # Never set timestamps automatically
197
- #
198
- # ==== Examples
199
- #
200
- # # Insert multiple records
201
- # Book.insert_all!([
202
- # { title: "Rework", author: "David" },
203
- # { title: "Eloquent Ruby", author: "Russ" }
204
- # ])
205
- #
206
- # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
207
- # # does not have a unique id.
208
- # Book.insert_all!([
209
- # { id: 1, title: "Rework", author: "David" },
210
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
211
- # ])
212
- def insert_all!(attributes, returning: nil, record_timestamps: nil)
213
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
214
- end
215
-
216
- # Updates or inserts (upserts) a single record into the database in a
217
- # single SQL INSERT statement. It does not instantiate any models nor does
218
- # it trigger Active Record callbacks or validations. Though passed values
219
- # go through Active Record's type casting and serialization.
220
- #
221
- # See #upsert_all for documentation.
222
- def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
223
- upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
224
- end
225
-
226
- # Updates or inserts (upserts) multiple records into the database in a
227
- # single SQL INSERT statement. It does not instantiate any models nor does
228
- # it trigger Active Record callbacks or validations. Though passed values
229
- # go through Active Record's type casting and serialization.
230
- #
231
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
232
- # the attributes for a single row and must have the same keys.
233
- #
234
- # Returns an ActiveRecord::Result with its contents based on
235
- # <tt>:returning</tt> (see below).
236
- #
237
- # By default, +upsert_all+ will update all the columns that can be updated when
238
- # there is a conflict. These are all the columns except primary keys, read-only
239
- # columns, and columns covered by the optional +unique_by+.
240
- #
241
- # ==== Options
242
- #
243
- # [:returning]
244
- # (PostgreSQL only) An array of attributes to return for all successfully
245
- # inserted records, which by default is the primary key.
246
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
247
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
248
- # clause entirely.
249
- #
250
- # You can also pass an SQL string if you need more control on the return values
251
- # (for example, <tt>returning: "id, name as new_name"</tt>).
252
- #
253
- # [:unique_by]
254
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
255
- # by every unique index on the table. Any duplicate rows are skipped.
256
- #
257
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
258
- #
259
- # Consider a Book model where no duplicate ISBNs make sense, but if any
260
- # row has an existing id, or is not unique by another unique index,
261
- # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
262
- #
263
- # Unique indexes can be identified by columns or name:
264
- #
265
- # unique_by: :isbn
266
- # unique_by: %i[ author_id name ]
267
- # unique_by: :index_books_on_isbn
268
- #
269
- # Because it relies on the index information from the database
270
- # <tt>:unique_by</tt> is recommended to be paired with
271
- # Active Record's schema_cache.
272
- #
273
- # [:on_duplicate]
274
- # Configure the SQL update sentence that will be used in case of conflict.
275
- #
276
- # NOTE: If you use this option you must provide all the columns you want to update
277
- # by yourself.
278
- #
279
- # Example:
280
- #
281
- # Commodity.upsert_all(
282
- # [
283
- # { id: 2, name: "Copper", price: 4.84 },
284
- # { id: 4, name: "Gold", price: 1380.87 },
285
- # { id: 6, name: "Aluminium", price: 0.35 }
286
- # ],
287
- # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
288
- # )
289
- #
290
- # See the related +:update_only+ option. Both options can't be used at the same time.
291
- #
292
- # [:update_only]
293
- # Provide a list of column names that will be updated in case of conflict. If not provided,
294
- # +upsert_all+ will update all the columns that can be updated. These are all the columns
295
- # except primary keys, read-only columns, and columns covered by the optional +unique_by+
296
- #
297
- # Example:
298
- #
299
- # Commodity.upsert_all(
300
- # [
301
- # { id: 2, name: "Copper", price: 4.84 },
302
- # { id: 4, name: "Gold", price: 1380.87 },
303
- # { id: 6, name: "Aluminium", price: 0.35 }
304
- # ],
305
- # update_only: [:price] # Only prices will be updated
306
- # )
307
- #
308
- # See the related +:on_duplicate+ option. Both options can't be used at the same time.
309
- #
310
- # [:record_timestamps]
311
- # By default, automatic setting of timestamp columns is controlled by
312
- # the model's <tt>record_timestamps</tt> config, matching typical
313
- # behavior.
314
- #
315
- # To override this and force automatic setting of timestamp columns one
316
- # way or the other, pass <tt>:record_timestamps</tt>:
317
- #
318
- # record_timestamps: true # Always set timestamps automatically
319
- # record_timestamps: false # Never set timestamps automatically
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.
320
65
  #
321
66
  # ==== Examples
67
+ # # Build a single new object
68
+ # User.build(first_name: 'Jamie')
322
69
  #
323
- # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
324
- # # 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' }])
325
72
  #
326
- # Book.upsert_all([
327
- # { title: "Rework", author: "David", isbn: "1" },
328
- # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
329
- # ], 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
330
77
  #
331
- # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
332
- def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
333
- InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).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
334
88
  end
335
89
 
336
90
  # Given an attributes hash, +instantiate+ returns a new instance of
@@ -425,56 +179,63 @@ module ActiveRecord
425
179
  end
426
180
  end
427
181
 
428
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
429
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
430
- # less efficient than #delete but allows cleanup methods and other actions to be run.
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.
431
184
  #
432
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
433
- # from the attributes, and then calls destroy on it.
185
+ # class Developer < ActiveRecord::Base
186
+ # query_constraints :company_id, :id
187
+ # end
434
188
  #
435
- # ==== Parameters
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, ...>
436
192
  #
437
- # * +id+ - This should be the id or an array of ids to be destroyed.
193
+ # developer.update!(name: "Nikita")
194
+ # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
438
195
  #
439
- # ==== Examples
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
440
199
  #
441
- # # Destroy a single object
442
- # Todo.destroy(1)
200
+ # developer.name = "Bob"
201
+ # developer.save!
202
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
443
203
  #
444
- # # Destroy multiple objects
445
- # todos = [1,2,3]
446
- # Todo.destroy(todos)
447
- def destroy(id)
448
- if id.is_a?(Array)
449
- find(id).each(&:destroy)
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)
450
226
  else
451
- find(id).destroy
227
+ base_class.query_constraints_list
452
228
  end
453
229
  end
454
230
 
455
- # Deletes the row with a primary key matching the +id+ argument, using an
456
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
457
- # Record objects are not instantiated, so the object's callbacks are not
458
- # executed, including any <tt>:dependent</tt> association options.
459
- #
460
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
461
- #
462
- # Note: Although it is often much faster than the alternative, #destroy,
463
- # skipping callbacks might bypass business logic in your application
464
- # that ensures referential integrity or performs other essential jobs.
465
- #
466
- # ==== Examples
467
- #
468
- # # Delete a single row
469
- # Todo.delete(1)
470
- #
471
- # # Delete multiple rows
472
- # Todo.delete([2,3,4])
473
- def delete(id_or_array)
474
- delete_by(primary_key => id_or_array)
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)
475
236
  end
476
237
 
477
- def _insert_record(values) # :nodoc:
238
+ def _insert_record(connection, values, returning) # :nodoc:
478
239
  primary_key = self.primary_key
479
240
  primary_key_value = nil
480
241
 
@@ -487,13 +248,18 @@ module ActiveRecord
487
248
 
488
249
  im = Arel::InsertManager.new(arel_table)
489
250
 
490
- if values.empty?
491
- im.insert(connection.empty_insert_statement_value(primary_key))
492
- else
493
- im.insert(values.transform_keys { |name| arel_table[name] })
494
- end
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
495
257
 
496
- 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
497
263
  end
498
264
 
499
265
  def _update_record(values, constraints) # :nodoc:
@@ -510,7 +276,9 @@ module ActiveRecord
510
276
  um.set(values.transform_keys { |name| arel_table[name] })
511
277
  um.wheres = constraints
512
278
 
513
- connection.update(um, "#{self} Update")
279
+ with_connection do |c|
280
+ c.update(um, "#{self} Update")
281
+ end
514
282
  end
515
283
 
516
284
  def _delete_record(constraints) # :nodoc:
@@ -526,10 +294,20 @@ module ActiveRecord
526
294
  dm = Arel::DeleteManager.new(arel_table)
527
295
  dm.wheres = constraints
528
296
 
529
- connection.delete(dm, "#{self} Destroy")
297
+ with_connection do |c|
298
+ c.delete(dm, "#{self} Destroy")
299
+ end
530
300
  end
531
301
 
532
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
+
533
311
  # Given a class, an attributes hash, +instantiate_instance_of+ returns a
534
312
  # new instance of the class. Accepts only keys as strings.
535
313
  def instantiate_instance_of(klass, attributes, column_types = {}, &block)
@@ -677,11 +455,7 @@ module ActiveRecord
677
455
  def destroy
678
456
  _raise_readonly_record_error if readonly?
679
457
  destroy_associations
680
- @_trigger_destroy_callback = if persisted?
681
- destroy_row > 0
682
- else
683
- true
684
- end
458
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
685
459
  @destroyed = true
686
460
  @previously_new_record = false
687
461
  freeze
@@ -709,11 +483,14 @@ module ActiveRecord
709
483
  # Note: The new instance will share a link to the same attributes as the original class.
710
484
  # Therefore the STI column value will still be the same.
711
485
  # Any change to the attributes on either instance will affect both instances.
486
+ # This includes any attribute initialization done by the new instance.
487
+ #
712
488
  # If you want to change the STI column as well, use #becomes! instead.
713
489
  def becomes(klass)
714
490
  became = klass.allocate
715
491
 
716
492
  became.send(:initialize) do |becoming|
493
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
717
494
  becoming.instance_variable_set(:@attributes, @attributes)
718
495
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
719
496
  becoming.instance_variable_set(:@new_record, new_record?)
@@ -748,7 +525,7 @@ module ActiveRecord
748
525
  # * updated_at/updated_on column is updated if that column is available.
749
526
  # * Updates all the attributes that are dirty in this object.
750
527
  #
751
- # This method raises an ActiveRecord::ActiveRecordError if the
528
+ # This method raises an ActiveRecord::ActiveRecordError if the
752
529
  # attribute is marked as readonly.
753
530
  #
754
531
  # Also see #update_column.
@@ -760,6 +537,28 @@ module ActiveRecord
760
537
  save(validate: false)
761
538
  end
762
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
+
763
562
  # Updates the attributes of the model from the passed-in hash and saves the
764
563
  # record, all wrapped in a transaction. If the object is invalid, the saving
765
564
  # will fail and false will be returned.
@@ -943,15 +742,16 @@ module ActiveRecord
943
742
  # end
944
743
  #
945
744
  def reload(options = nil)
946
- self.class.connection.clear_query_cache
745
+ self.class.connection_pool.clear_query_cache
947
746
 
948
747
  fresh_object = if apply_scoping?(options)
949
- _find_record(options)
748
+ _find_record((options || {}).merge(all_queries: true))
950
749
  else
951
750
  self.class.unscoped { _find_record(options) }
952
751
  end
953
752
 
954
753
  @association_cache = fresh_object.instance_variable_get(:@association_cache)
754
+ @association_cache.each_value { |association| association.owner = self }
955
755
  @attributes = fresh_object.instance_variable_get(:@attributes)
956
756
  @new_record = false
957
757
  @previously_new_record = false
@@ -997,10 +797,12 @@ module ActiveRecord
997
797
  _raise_readonly_record_error if readonly?
998
798
 
999
799
  attribute_names = timestamp_attributes_for_update_in_model
1000
- attribute_names |= names.map! do |name|
800
+ attribute_names = (attribute_names | names).map! do |name|
1001
801
  name = name.to_s
1002
- self.class.attribute_aliases[name] || name
1003
- end unless names.empty?
802
+ name = self.class.attribute_aliases[name] || name
803
+ verify_readonly_attribute(name)
804
+ name
805
+ end
1004
806
 
1005
807
  unless attribute_names.empty?
1006
808
  affected_rows = _touch_row(attribute_names, time)
@@ -1011,6 +813,12 @@ module ActiveRecord
1011
813
  end
1012
814
 
1013
815
  private
816
+ def init_internals
817
+ super
818
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
819
+ @previously_new_record = false
820
+ end
821
+
1014
822
  def strict_loaded_associations
1015
823
  @association_cache.find_all do |_, assoc|
1016
824
  assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
@@ -1018,10 +826,23 @@ module ActiveRecord
1018
826
  end
1019
827
 
1020
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
+
1021
832
  if options && options[:lock]
1022
- self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
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 }
1023
842
  else
1024
- self.class.preload(strict_loaded_associations).find(id)
843
+ self.class.query_constraints_list.index_with do |column_name|
844
+ attribute(column_name)
845
+ end
1025
846
  end
1026
847
  end
1027
848
 
@@ -1031,7 +852,13 @@ module ActiveRecord
1031
852
  end
1032
853
 
1033
854
  def _query_constraints_hash
1034
- { @primary_key => id_in_database }
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
1035
862
  end
1036
863
 
1037
864
  # A hook to be overridden by association modules.
@@ -1095,11 +922,19 @@ module ActiveRecord
1095
922
  def _create_record(attribute_names = self.attribute_names)
1096
923
  attribute_names = attributes_for_create(attribute_names)
1097
924
 
1098
- new_id = self.class._insert_record(
1099
- attributes_with_values(attribute_names)
1100
- )
925
+ self.class.with_connection do |connection|
926
+ returning_columns = self.class._returning_columns_for_insert(connection)
1101
927
 
1102
- 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
1103
938
 
1104
939
  @new_record = false
1105
940
  @previously_new_record = true
@@ -1116,7 +951,7 @@ module ActiveRecord
1116
951
  def _raise_record_not_destroyed
1117
952
  @_association_destroy_exception ||= nil
1118
953
  key = self.class.primary_key
1119
- raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{send(key)}", self)
954
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
1120
955
  ensure
1121
956
  @_association_destroy_exception = nil
1122
957
  end
@@ -1131,11 +966,5 @@ module ActiveRecord
1131
966
  persisted?, new_record?, or destroyed? before touching.
1132
967
  MSG
1133
968
  end
1134
-
1135
- # The name of the method used to touch a +belongs_to+ association when the
1136
- # +:touch+ option is used.
1137
- def belongs_to_touch_method
1138
- :touch
1139
- end
1140
969
  end
1141
970
  end