activerecord 6.1.7 → 7.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -9,56 +9,142 @@ module ActiveRecord
9
9
  #
10
10
  # This is enabled by default. To disable this functionality set
11
11
  # `use_metadata_table` to false in your database configuration.
12
- class InternalMetadata < ActiveRecord::Base # :nodoc:
13
- self.record_timestamps = true
12
+ class InternalMetadata # :nodoc:
13
+ class NullInternalMetadata # :nodoc:
14
+ end
14
15
 
15
- class << self
16
- def enabled?
17
- ActiveRecord::Base.connection.use_metadata_table?
18
- end
16
+ attr_reader :connection, :arel_table
19
17
 
20
- def _internal?
21
- true
22
- end
18
+ def initialize(connection)
19
+ @connection = connection
20
+ @arel_table = Arel::Table.new(table_name)
21
+ end
23
22
 
24
- def primary_key
25
- "key"
26
- end
23
+ def enabled?
24
+ connection.use_metadata_table?
25
+ end
27
26
 
28
- def table_name
29
- "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
30
- end
27
+ def primary_key
28
+ "key"
29
+ end
31
30
 
32
- def []=(key, value)
33
- return unless enabled?
31
+ def value_key
32
+ "value"
33
+ end
34
+
35
+ def table_name
36
+ "#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{ActiveRecord::Base.table_name_suffix}"
37
+ end
34
38
 
35
- find_or_initialize_by(key: key).update!(value: value)
39
+ def []=(key, value)
40
+ return unless enabled?
41
+
42
+ update_or_create_entry(key, value)
43
+ end
44
+
45
+ def [](key)
46
+ return unless enabled?
47
+
48
+ if entry = select_entry(key)
49
+ entry[value_key]
36
50
  end
51
+ end
52
+
53
+ def delete_all_entries
54
+ dm = Arel::DeleteManager.new(arel_table)
55
+
56
+ connection.delete(dm, "#{self.class} Destroy")
57
+ end
58
+
59
+ def count
60
+ sm = Arel::SelectManager.new(arel_table)
61
+ sm.project(*Arel::Nodes::Count.new([Arel.star]))
62
+
63
+ connection.select_values(sm, "#{self.class} Count").first
64
+ end
65
+
66
+ def create_table_and_set_flags(environment, schema_sha1 = nil)
67
+ return unless enabled?
68
+
69
+ create_table
70
+ update_or_create_entry(:environment, environment)
71
+ update_or_create_entry(:schema_sha1, schema_sha1) if schema_sha1
72
+ end
37
73
 
38
- def [](key)
39
- return unless enabled?
74
+ # Creates an internal metadata table with columns +key+ and +value+
75
+ def create_table
76
+ return unless enabled?
40
77
 
41
- where(key: key).pluck(:value).first
78
+ unless connection.table_exists?(table_name)
79
+ connection.create_table(table_name, id: false) do |t|
80
+ t.string :key, **connection.internal_string_options_for_primary_key
81
+ t.string :value
82
+ t.timestamps
83
+ end
42
84
  end
85
+ end
86
+
87
+ def drop_table
88
+ return unless enabled?
89
+
90
+ connection.drop_table table_name, if_exists: true
91
+ end
92
+
93
+ def table_exists?
94
+ connection.schema_cache.data_source_exists?(table_name)
95
+ end
43
96
 
44
- # Creates an internal metadata table with columns +key+ and +value+
45
- def create_table
46
- return unless enabled?
97
+ private
98
+ def update_or_create_entry(key, value)
99
+ entry = select_entry(key)
47
100
 
48
- unless connection.table_exists?(table_name)
49
- connection.create_table(table_name, id: false) do |t|
50
- t.string :key, **connection.internal_string_options_for_primary_key
51
- t.string :value
52
- t.timestamps
101
+ if entry
102
+ if entry[value_key] != value
103
+ update_entry(key, value)
104
+ else
105
+ entry[value_key]
53
106
  end
107
+ else
108
+ create_entry(key, value)
54
109
  end
55
110
  end
56
111
 
57
- def drop_table
58
- return unless enabled?
112
+ def current_time
113
+ connection.default_timezone == :utc ? Time.now.utc : Time.now
114
+ end
115
+
116
+ def create_entry(key, value)
117
+ im = Arel::InsertManager.new(arel_table)
118
+ im.insert [
119
+ [arel_table[primary_key], key],
120
+ [arel_table[value_key], value],
121
+ [arel_table[:created_at], current_time],
122
+ [arel_table[:updated_at], current_time]
123
+ ]
59
124
 
60
- connection.drop_table table_name, if_exists: true
125
+ connection.insert(im, "#{self.class} Create", primary_key, key)
126
+ end
127
+
128
+ def update_entry(key, new_value)
129
+ um = Arel::UpdateManager.new(arel_table)
130
+ um.set [
131
+ [arel_table[value_key], new_value],
132
+ [arel_table[:updated_at], current_time]
133
+ ]
134
+
135
+ um.where(arel_table[primary_key].eq(key))
136
+
137
+ connection.update(um, "#{self.class} Update")
138
+ end
139
+
140
+ def select_entry(key)
141
+ sm = Arel::SelectManager.new(arel_table)
142
+ sm.project(Arel::Nodes::SqlLiteral.new("*"))
143
+ sm.where(arel_table[primary_key].eq(Arel::Nodes::BindParam.new(key)))
144
+ sm.order(arel_table[primary_key].asc)
145
+ sm.limit = 1
146
+
147
+ connection.select_all(sm, "#{self.class} Load").first
61
148
  end
62
- end
63
149
  end
64
150
  end
@@ -2,50 +2,13 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module LegacyYamlAdapter # :nodoc:
5
- def self.convert(klass, coder)
5
+ def self.convert(coder)
6
6
  return coder unless coder.is_a?(Psych::Coder)
7
7
 
8
8
  case coder["active_record_yaml_version"]
9
9
  when 1, 2 then coder
10
10
  else
11
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
- YAML loading from legacy format older than Rails 5.0 is deprecated
13
- and will be removed in Rails 7.0.
14
- MSG
15
- if coder["attributes"].is_a?(ActiveModel::AttributeSet)
16
- Rails420.convert(klass, coder)
17
- else
18
- Rails41.convert(klass, coder)
19
- end
20
- end
21
- end
22
-
23
- module Rails420 # :nodoc:
24
- def self.convert(klass, coder)
25
- attribute_set = coder["attributes"]
26
-
27
- klass.attribute_names.each do |attr_name|
28
- attribute = attribute_set[attr_name]
29
- if attribute.type.is_a?(Delegator)
30
- type_from_klass = klass.type_for_attribute(attr_name)
31
- attribute_set[attr_name] = attribute.with_type(type_from_klass)
32
- end
33
- end
34
-
35
- coder
36
- end
37
- end
38
-
39
- module Rails41 # :nodoc:
40
- def self.convert(klass, coder)
41
- attributes = klass.attributes_builder
42
- .build_from_database(coder["attributes"])
43
- new_record = coder["attributes"][klass.primary_key].blank?
44
-
45
- {
46
- "attributes" => attributes,
47
- "new_record" => new_record,
48
- }
11
+ raise("Active Record doesn't know how to load YAML with this format.")
49
12
  end
50
13
  end
51
14
  end
@@ -2,19 +2,19 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Locking
5
- # == What is Optimistic Locking
5
+ # == What is \Optimistic \Locking
6
6
  #
7
7
  # Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
8
8
  # conflicts with the data. It does this by checking whether another process has made changes to a record since
9
- # it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
9
+ # it was opened, an ActiveRecord::StaleObjectError exception is thrown if that has occurred
10
10
  # and the update is ignored.
11
11
  #
12
- # Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
12
+ # Check out +ActiveRecord::Locking::Pessimistic+ for an alternative.
13
13
  #
14
14
  # == Usage
15
15
  #
16
16
  # Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
17
- # record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
17
+ # record increments the integer column +lock_version+ and the locking facilities ensure that records instantiated twice
18
18
  # will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
19
19
  #
20
20
  # p1 = Person.find(1)
@@ -56,11 +56,11 @@ module ActiveRecord
56
56
  class_attribute :lock_optimistically, instance_writer: false, default: true
57
57
  end
58
58
 
59
- def locking_enabled? #:nodoc:
59
+ def locking_enabled? # :nodoc:
60
60
  self.class.locking_enabled?
61
61
  end
62
62
 
63
- def increment!(*, **) #:nodoc:
63
+ def increment!(*, **) # :nodoc:
64
64
  super.tap do
65
65
  if locking_enabled?
66
66
  self[self.class.locking_column] += 1
@@ -69,6 +69,11 @@ module ActiveRecord
69
69
  end
70
70
  end
71
71
 
72
+ def initialize_dup(other) # :nodoc:
73
+ super
74
+ _clear_locking_column if locking_enabled?
75
+ end
76
+
72
77
  private
73
78
  def _create_record(attribute_names = self.attribute_names)
74
79
  if locking_enabled?
@@ -90,7 +95,8 @@ module ActiveRecord
90
95
  begin
91
96
  locking_column = self.class.locking_column
92
97
  lock_attribute_was = @attributes[locking_column]
93
- lock_value_for_database = _lock_value_for_database(locking_column)
98
+
99
+ update_constraints = _query_constraints_hash
94
100
 
95
101
  attribute_names = attribute_names.dup if attribute_names.frozen?
96
102
  attribute_names << locking_column
@@ -99,8 +105,7 @@ module ActiveRecord
99
105
 
100
106
  affected_rows = self.class._update_record(
101
107
  attributes_with_values(attribute_names),
102
- @primary_key => id_in_database,
103
- locking_column => lock_value_for_database
108
+ update_constraints
104
109
  )
105
110
 
106
111
  if affected_rows != 1
@@ -117,16 +122,9 @@ module ActiveRecord
117
122
  end
118
123
 
119
124
  def destroy_row
120
- return super unless locking_enabled?
125
+ affected_rows = super
121
126
 
122
- locking_column = self.class.locking_column
123
-
124
- affected_rows = self.class._delete_record(
125
- @primary_key => id_in_database,
126
- locking_column => _lock_value_for_database(locking_column)
127
- )
128
-
129
- if affected_rows != 1
127
+ if locking_enabled? && affected_rows != 1
130
128
  raise ActiveRecord::StaleObjectError.new(self, "destroy")
131
129
  end
132
130
 
@@ -141,6 +139,18 @@ module ActiveRecord
141
139
  end
142
140
  end
143
141
 
142
+ def _clear_locking_column
143
+ self[self.class.locking_column] = nil
144
+ clear_attribute_change(self.class.locking_column)
145
+ end
146
+
147
+ def _query_constraints_hash
148
+ return super unless locking_enabled?
149
+
150
+ locking_column = self.class.locking_column
151
+ super.merge(locking_column => _lock_value_for_database(locking_column))
152
+ end
153
+
144
154
  module ClassMethods
145
155
  DEFAULT_LOCKING_COLUMN = "lock_version"
146
156
 
@@ -158,10 +168,7 @@ module ActiveRecord
158
168
  end
159
169
 
160
170
  # The version column used for optimistic locking. Defaults to +lock_version+.
161
- def locking_column
162
- @locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
163
- @locking_column
164
- end
171
+ attr_reader :locking_column
165
172
 
166
173
  # Reset the column used for optimistic locking back to the +lock_version+ default.
167
174
  def reset_locking_column
@@ -181,6 +188,14 @@ module ActiveRecord
181
188
  end
182
189
  super
183
190
  end
191
+
192
+ private
193
+ def inherited(base)
194
+ super
195
+ base.class_eval do
196
+ @locking_column = DEFAULT_LOCKING_COLUMN
197
+ end
198
+ end
184
199
  end
185
200
  end
186
201
 
@@ -2,10 +2,12 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Locking
5
+ # = \Pessimistic \Locking
6
+ #
5
7
  # Locking::Pessimistic provides support for row-level locking using
6
8
  # SELECT ... FOR UPDATE and other lock types.
7
9
  #
8
- # Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
10
+ # Chain <tt>ActiveRecord::Base#find</tt> to ActiveRecord::QueryMethods#lock to obtain an exclusive
9
11
  # lock on the selected rows:
10
12
  # # select * from accounts where id=1 for update
11
13
  # Account.lock.find(1)
@@ -71,6 +73,7 @@ module ActiveRecord
71
73
  Locking a record with unpersisted changes is not supported. Use
72
74
  `save` to persist the changes, or `reload` to discard them
73
75
  explicitly.
76
+ Changed attributes: #{changed.map(&:inspect).join(', ')}.
74
77
  MSG
75
78
  end
76
79
 
@@ -79,11 +82,17 @@ module ActiveRecord
79
82
  self
80
83
  end
81
84
 
82
- # Wraps the passed block in a transaction, locking the object
83
- # before yielding. You can pass the SQL locking clause
84
- # as argument (see <tt>lock!</tt>).
85
- def with_lock(lock = true)
86
- transaction do
85
+ # Wraps the passed block in a transaction, reloading the object with a
86
+ # lock before yielding. You can pass the SQL locking clause
87
+ # as an optional argument (see #lock!).
88
+ #
89
+ # You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
90
+ # and <tt>joinable:</tt> to the wrapping transaction (see
91
+ # ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction).
92
+ def with_lock(*args)
93
+ transaction_opts = args.extract_options!
94
+ lock = args.present? ? args.first : true
95
+ transaction(**transaction_opts) do
87
96
  lock!(lock)
88
97
  yield
89
98
  end
@@ -7,37 +7,45 @@ module ActiveRecord
7
7
  class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
8
 
9
9
  def self.runtime=(value)
10
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
11
+ ActiveRecord::LogSubscriber.runtime= is deprecated and will be removed in Rails 7.2.
12
+ MSG
10
13
  ActiveRecord::RuntimeRegistry.sql_runtime = value
11
14
  end
12
15
 
13
16
  def self.runtime
14
- ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
17
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
18
+ ActiveRecord::LogSubscriber.runtime is deprecated and will be removed in Rails 7.2.
19
+ MSG
20
+ ActiveRecord::RuntimeRegistry.sql_runtime
15
21
  end
16
22
 
17
23
  def self.reset_runtime
18
- rt, self.runtime = runtime, 0
19
- rt
24
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
25
+ ActiveRecord::LogSubscriber.reset_runtime is deprecated and will be removed in Rails 7.2.
26
+ MSG
27
+ ActiveRecord::RuntimeRegistry.reset
20
28
  end
21
29
 
22
30
  def strict_loading_violation(event)
23
31
  debug do
24
32
  owner = event.payload[:owner]
25
- association = event.payload[:reflection].klass
26
- name = event.payload[:reflection].name
27
-
28
- color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
33
+ reflection = event.payload[:reflection]
34
+ color(reflection.strict_loading_violation_message(owner), RED)
29
35
  end
30
36
  end
37
+ subscribe_log_level :strict_loading_violation, :debug
31
38
 
32
39
  def sql(event)
33
- self.class.runtime += event.duration
34
- return unless logger.debug?
35
-
36
40
  payload = event.payload
37
41
 
38
42
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
39
43
 
40
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
44
+ name = if payload[:async]
45
+ "ASYNC #{payload[:name]} (#{payload[:lock_wait].round(1)}ms) (db time #{event.duration.round(1)}ms)"
46
+ else
47
+ "#{payload[:name]} (#{event.duration.round(1)}ms)"
48
+ end
41
49
  name = "CACHE #{name}" if payload[:cached]
42
50
  sql = payload[:sql]
43
51
  binds = nil
@@ -47,17 +55,28 @@ module ActiveRecord
47
55
 
48
56
  binds = []
49
57
  payload[:binds].each_with_index do |attr, i|
50
- binds << render_bind(attr, casted_params[i])
58
+ attribute_name = if attr.respond_to?(:name)
59
+ attr.name
60
+ elsif attr.respond_to?(:[]) && attr[i].respond_to?(:name)
61
+ attr[i].name
62
+ else
63
+ nil
64
+ end
65
+
66
+ filtered_params = filter(attribute_name, casted_params[i])
67
+
68
+ binds << render_bind(attr, filtered_params)
51
69
  end
52
70
  binds = binds.inspect
53
71
  binds.prepend(" ")
54
72
  end
55
73
 
56
74
  name = colorize_payload_name(name, payload[:name])
57
- sql = color(sql, sql_color(sql), true) if colorize_logging
75
+ sql = color(sql, sql_color(sql), bold: true) if colorize_logging
58
76
 
59
77
  debug " #{name} #{sql}#{binds}"
60
78
  end
79
+ subscribe_log_level :sql, :debug
61
80
 
62
81
  private
63
82
  def type_casted_binds(casted_binds)
@@ -81,9 +100,9 @@ module ActiveRecord
81
100
 
82
101
  def colorize_payload_name(name, payload_name)
83
102
  if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
84
- color(name, MAGENTA, true)
103
+ color(name, MAGENTA, bold: true)
85
104
  else
86
- color(name, CYAN, true)
105
+ color(name, CYAN, bold: true)
87
106
  end
88
107
  end
89
108
 
@@ -115,21 +134,35 @@ module ActiveRecord
115
134
  def debug(progname = nil, &block)
116
135
  return unless super
117
136
 
118
- if ActiveRecord::Base.verbose_query_logs
137
+ if ActiveRecord.verbose_query_logs
119
138
  log_query_source
120
139
  end
121
140
  end
122
141
 
123
142
  def log_query_source
124
- source = extract_query_source_location(caller)
143
+ source = query_source_location
125
144
 
126
145
  if source
127
146
  logger.debug(" ↳ #{source}")
128
147
  end
129
148
  end
130
149
 
131
- def extract_query_source_location(locations)
132
- backtrace_cleaner.clean(locations.lazy).first
150
+ if Thread.respond_to?(:each_caller_location)
151
+ def query_source_location
152
+ Thread.each_caller_location do |location|
153
+ frame = backtrace_cleaner.clean_frame(location)
154
+ return frame if frame
155
+ end
156
+ nil
157
+ end
158
+ else
159
+ def query_source_location
160
+ backtrace_cleaner.clean(caller(1).lazy).first
161
+ end
162
+ end
163
+
164
+ def filter(name, value)
165
+ ActiveRecord::Base.inspection_filter.filter_param(name, value)
133
166
  end
134
167
  end
135
168
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Marshalling
5
+ @format_version = 6.1
6
+
7
+ class << self
8
+ attr_reader :format_version
9
+
10
+ def format_version=(version)
11
+ case version
12
+ when 6.1
13
+ Methods.remove_method(:marshal_dump) if Methods.method_defined?(:marshal_dump)
14
+ when 7.1
15
+ Methods.alias_method(:marshal_dump, :_marshal_dump_7_1)
16
+ else
17
+ raise ArgumentError, "Unknown marshalling format: #{version.inspect}"
18
+ end
19
+ @format_version = version
20
+ end
21
+ end
22
+
23
+ module Methods
24
+ def _marshal_dump_7_1
25
+ payload = [attributes_for_database, new_record?]
26
+
27
+ cached_associations = self.class.reflect_on_all_associations.select do |reflection|
28
+ if association_cached?(reflection.name)
29
+ association = association(reflection.name)
30
+ association.loaded? || association.target.present?
31
+ end
32
+ end
33
+
34
+ unless cached_associations.empty?
35
+ payload << cached_associations.map do |reflection|
36
+ [reflection.name, association(reflection.name).target]
37
+ end
38
+ end
39
+
40
+ payload
41
+ end
42
+
43
+ def marshal_load(state)
44
+ attributes_from_database, new_record, associations = state
45
+
46
+ attributes = self.class.attributes_builder.build_from_database(attributes_from_database)
47
+ init_with_attributes(attributes, new_record)
48
+
49
+ if associations
50
+ associations.each do |name, target|
51
+ association(name).target = target
52
+ rescue ActiveRecord::AssociationNotFoundError
53
+ # the association no longer exist, we can just skip it.
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end