activerecord 7.0.8.7 → 7.1.0.beta1

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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +193 -97
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. data/lib/active_record/null_relation.rb +0 -63
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  ##
11
11
  # :singleton-method:
12
12
  # Indicates the format used to generate the timestamp in the cache key, if
13
- # versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
13
+ # versioning is off. Accepts any of the symbols in +Time::DATE_FORMATS+.
14
14
  #
15
15
  # This is +:usec+, by default.
16
16
  class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  # Indicates whether to use a stable #cache_key method that is accompanied
21
21
  # by a changing version in the #cache_version method.
22
22
  #
23
- # This is +true+, by default on Rails 5.2 and above.
23
+ # This is +true+, by default on \Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
25
 
26
26
  ##
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  # Indicates whether to use a stable #cache_key method that is accompanied
29
29
  # by a changing version in the #cache_version method on collections.
30
30
  #
31
- # This is +false+, by default until Rails 6.1.
31
+ # This is +false+, by default until \Rails 6.1.
32
32
  class_attribute :collection_cache_versioning, instance_writer: false, default: false
33
33
  end
34
34
 
@@ -55,8 +55,8 @@ module ActiveRecord
55
55
  # user = User.find_by(name: 'Phusion')
56
56
  # user_path(user) # => "/users/Phusion"
57
57
  def to_param
58
- # We can't use alias_method here, because method 'id' optimizes itself on the fly.
59
- id && id.to_s # Be sure to stringify the id for routes
58
+ return unless id
59
+ Array(id).join(self.class.param_delimiter)
60
60
  end
61
61
 
62
62
  # Returns a stable cache key that can be used to identify this record.
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  # Product.new.cache_key # => "products/new"
65
65
  # Product.find(5).cache_key # => "products/5"
66
66
  #
67
- # If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
67
+ # If ActiveRecord::Base.cache_versioning is turned off, as it was in \Rails 5.1 and earlier,
68
68
  # the cache key will also include a version.
69
69
  #
70
70
  # Product.cache_versioning = false
@@ -106,7 +106,7 @@ module ActiveRecord
106
106
  timestamp.utc.to_fs(cache_timestamp_format)
107
107
  end
108
108
  elsif self.class.has_attribute?("updated_at")
109
- raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
109
+ raise ActiveModel::MissingAttributeError, "missing attribute 'updated_at' for #{self.class}"
110
110
  end
111
111
  end
112
112
 
@@ -178,7 +178,7 @@ module ActiveRecord
178
178
  def can_use_fast_cache_version?(timestamp)
179
179
  timestamp.is_a?(String) &&
180
180
  cache_timestamp_format == :usec &&
181
- ActiveRecord.default_timezone == :utc &&
181
+ self.class.connection.default_timezone == :utc &&
182
182
  !updated_at_came_from_user?
183
183
  end
184
184
 
@@ -9,52 +9,140 @@ 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
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 primary_key
21
- "key"
22
- end
18
+ def initialize(connection)
19
+ @connection = connection
20
+ @arel_table = Arel::Table.new(table_name)
21
+ end
23
22
 
24
- def table_name
25
- "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
26
- end
23
+ def enabled?
24
+ connection.use_metadata_table?
25
+ end
27
26
 
28
- def []=(key, value)
29
- return unless enabled?
27
+ def primary_key
28
+ "key"
29
+ end
30
+
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
30
38
 
31
- 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]
32
50
  end
51
+ end
33
52
 
34
- def [](key)
35
- return unless enabled?
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
36
65
 
37
- where(key: key).pick(:value)
66
+ def create_table_and_set_flags(environment, schema_sha1 = nil)
67
+ create_table
68
+ update_or_create_entry(:environment, environment)
69
+ update_or_create_entry(:schema_sha1, schema_sha1) if schema_sha1
70
+ end
71
+
72
+ # Creates an internal metadata table with columns +key+ and +value+
73
+ def create_table
74
+ return unless enabled?
75
+
76
+ unless connection.table_exists?(table_name)
77
+ connection.create_table(table_name, id: false) do |t|
78
+ t.string :key, **connection.internal_string_options_for_primary_key
79
+ t.string :value
80
+ t.timestamps
81
+ end
38
82
  end
83
+ end
84
+
85
+ def drop_table
86
+ return unless enabled?
87
+
88
+ connection.drop_table table_name, if_exists: true
89
+ end
90
+
91
+ def table_exists?
92
+ connection.schema_cache.data_source_exists?(table_name)
93
+ end
39
94
 
40
- # Creates an internal metadata table with columns +key+ and +value+
41
- def create_table
42
- return unless enabled?
95
+ private
96
+ def update_or_create_entry(key, value)
97
+ entry = select_entry(key)
43
98
 
44
- unless connection.table_exists?(table_name)
45
- connection.create_table(table_name, id: false) do |t|
46
- t.string :key, **connection.internal_string_options_for_primary_key
47
- t.string :value
48
- t.timestamps
99
+ if entry
100
+ if entry[value_key] != value
101
+ update_entry(key, value)
102
+ else
103
+ entry[value_key]
49
104
  end
105
+ else
106
+ create_entry(key, value)
50
107
  end
51
108
  end
52
109
 
53
- def drop_table
54
- return unless enabled?
110
+ def current_time
111
+ connection.default_timezone == :utc ? Time.now.utc : Time.now
112
+ end
113
+
114
+ def create_entry(key, value)
115
+ im = Arel::InsertManager.new(arel_table)
116
+ im.insert [
117
+ [arel_table[primary_key], key],
118
+ [arel_table[value_key], value],
119
+ [arel_table[:created_at], current_time],
120
+ [arel_table[:updated_at], current_time]
121
+ ]
55
122
 
56
- connection.drop_table table_name, if_exists: true
123
+ connection.insert(im, "#{self.class} Create", primary_key, key)
124
+ end
125
+
126
+ def update_entry(key, new_value)
127
+ um = Arel::UpdateManager.new(arel_table)
128
+ um.set [
129
+ [arel_table[value_key], new_value],
130
+ [arel_table[:updated_at], current_time]
131
+ ]
132
+
133
+ um.where(arel_table[primary_key].eq(key))
134
+
135
+ connection.update(um, "#{self.class} Update")
136
+ end
137
+
138
+ def select_entry(key)
139
+ sm = Arel::SelectManager.new(arel_table)
140
+ sm.project(Arel::Nodes::SqlLiteral.new("*"))
141
+ sm.where(arel_table[primary_key].eq(Arel::Nodes::BindParam.new(key)))
142
+ sm.order(arel_table[primary_key].asc)
143
+ sm.limit = 1
144
+
145
+ connection.select_all(sm, "#{self.class} Load").first
57
146
  end
58
- end
59
147
  end
60
148
  end
@@ -2,6 +2,8 @@
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
  #
@@ -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,8 +82,8 @@ 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
85
+ # Wraps the passed block in a transaction, reloading the object with a
86
+ # lock before yielding. You can pass the SQL locking clause
84
87
  # as an optional argument (see #lock!).
85
88
  #
86
89
  # You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
@@ -7,16 +7,24 @@ 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)
@@ -26,11 +34,9 @@ module ActiveRecord
26
34
  color(reflection.strict_loading_violation_message(owner), RED)
27
35
  end
28
36
  end
37
+ subscribe_log_level :strict_loading_violation, :debug
29
38
 
30
39
  def sql(event)
31
- self.class.runtime += event.duration
32
- return unless logger.debug?
33
-
34
40
  payload = event.payload
35
41
 
36
42
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
@@ -66,10 +72,11 @@ module ActiveRecord
66
72
  end
67
73
 
68
74
  name = colorize_payload_name(name, payload[:name])
69
- sql = color(sql, sql_color(sql), true) if colorize_logging
75
+ sql = color(sql, sql_color(sql), bold: true) if colorize_logging
70
76
 
71
77
  debug " #{name} #{sql}#{binds}"
72
78
  end
79
+ subscribe_log_level :sql, :debug
73
80
 
74
81
  private
75
82
  def type_casted_binds(casted_binds)
@@ -93,9 +100,9 @@ module ActiveRecord
93
100
 
94
101
  def colorize_payload_name(name, payload_name)
95
102
  if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
96
- color(name, MAGENTA, true)
103
+ color(name, MAGENTA, bold: true)
97
104
  else
98
- color(name, CYAN, true)
105
+ color(name, CYAN, bold: true)
99
106
  end
100
107
  end
101
108
 
@@ -133,15 +140,25 @@ module ActiveRecord
133
140
  end
134
141
 
135
142
  def log_query_source
136
- source = extract_query_source_location(caller)
143
+ source = query_source_location
137
144
 
138
145
  if source
139
146
  logger.debug(" ↳ #{source}")
140
147
  end
141
148
  end
142
149
 
143
- def extract_query_source_location(locations)
144
- 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
145
162
  end
146
163
 
147
164
  def filter(name, value)
@@ -0,0 +1,56 @@
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
+ association_cached?(reflection.name)
29
+ end
30
+
31
+ unless cached_associations.empty?
32
+ payload << cached_associations.map do |reflection|
33
+ [reflection.name, association(reflection.name).target]
34
+ end
35
+ end
36
+
37
+ payload
38
+ end
39
+
40
+ def marshal_load(state)
41
+ attributes_from_database, new_record, associations = state
42
+
43
+ attributes = self.class.attributes_builder.build_from_database(attributes_from_database)
44
+ init_with_attributes(attributes, new_record)
45
+
46
+ if associations
47
+ associations.each do |name, target|
48
+ association(name).target = target
49
+ rescue ActiveRecord::AssociationNotFoundError
50
+ # the association no longer exist, we can just skip it.
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module MessagePack # :nodoc:
5
+ FORMAT_VERSION = 1
6
+
7
+ class << self
8
+ def dump(input)
9
+ encoder = Encoder.new
10
+ [FORMAT_VERSION, encoder.encode(input), encoder.entries]
11
+ end
12
+
13
+ def load(dumped)
14
+ format_version, top_level, entries = dumped
15
+ unless format_version == FORMAT_VERSION
16
+ raise "Invalid format version: #{format_version.inspect}"
17
+ end
18
+ Decoder.new(entries).decode(top_level)
19
+ end
20
+ end
21
+
22
+ module Extensions
23
+ extend self
24
+
25
+ def install(registry)
26
+ registry.register_type 119, ActiveModel::Type::Binary::Data,
27
+ packer: :to_s,
28
+ unpacker: :new
29
+
30
+ registry.register_type 120, ActiveRecord::Base,
31
+ packer: method(:write_record),
32
+ unpacker: method(:read_record),
33
+ recursive: true
34
+ end
35
+
36
+ def write_record(record, packer)
37
+ packer.write(ActiveRecord::MessagePack.dump(record))
38
+ end
39
+
40
+ def read_record(unpacker)
41
+ ActiveRecord::MessagePack.load(unpacker.read)
42
+ end
43
+ end
44
+
45
+ class Encoder
46
+ attr_reader :entries
47
+
48
+ def initialize
49
+ @entries = []
50
+ @refs = {}.compare_by_identity
51
+ end
52
+
53
+ def encode(input)
54
+ if input.is_a?(Array)
55
+ input.map { |record| encode_record(record) }
56
+ elsif input
57
+ encode_record(input)
58
+ end
59
+ end
60
+
61
+ def encode_record(record)
62
+ ref = @refs[record]
63
+
64
+ if !ref
65
+ ref = @refs[record] = @entries.size
66
+ @entries << build_entry(record)
67
+ add_cached_associations(record, @entries.last)
68
+ end
69
+
70
+ ref
71
+ end
72
+
73
+ def build_entry(record)
74
+ [
75
+ ActiveSupport::MessagePack::Extensions.dump_class(record.class),
76
+ record.attributes_for_database,
77
+ record.new_record?
78
+ ]
79
+ end
80
+
81
+ def add_cached_associations(record, entry)
82
+ record.class.reflections.each_value do |reflection|
83
+ if record.association_cached?(reflection.name)
84
+ entry << reflection.name << encode(record.association(reflection.name).target)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ class Decoder
91
+ def initialize(entries)
92
+ @records = entries.map { |entry| build_record(entry) }
93
+ @records.zip(entries) { |record, entry| resolve_cached_associations(record, entry) }
94
+ end
95
+
96
+ def decode(ref)
97
+ if ref.is_a?(Array)
98
+ ref.map { |r| @records[r] }
99
+ elsif ref
100
+ @records[ref]
101
+ end
102
+ end
103
+
104
+ def build_record(entry)
105
+ class_name, attributes_hash, is_new_record, * = entry
106
+ klass = ActiveSupport::MessagePack::Extensions.load_class(class_name)
107
+ attributes = klass.attributes_builder.build_from_database(attributes_hash)
108
+ klass.allocate.init_with_attributes(attributes, is_new_record)
109
+ end
110
+
111
+ def resolve_cached_associations(record, entry)
112
+ i = 3 # entry == [class_name, attributes_hash, is_new_record, *associations]
113
+ while i < entry.length
114
+ begin
115
+ record.association(entry[i]).target = decode(entry[i + 1])
116
+ rescue ActiveRecord::AssociationNotFoundError
117
+ # The association no longer exists, so just skip it.
118
+ end
119
+ i += 2
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -48,6 +48,10 @@ module ActiveRecord
48
48
  context.save(response)
49
49
  end
50
50
 
51
+ def reading_request?(request)
52
+ request.get? || request.head?
53
+ end
54
+
51
55
  private
52
56
  def read_from_primary(&blk)
53
57
  ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: true) do
@@ -4,8 +4,10 @@ require "active_record/middleware/database_selector/resolver"
4
4
 
5
5
  module ActiveRecord
6
6
  module Middleware
7
+ # = Database Selector \Middleware
8
+ #
7
9
  # The DatabaseSelector Middleware provides a framework for automatically
8
- # swapping from the primary to the replica database connection. Rails
10
+ # swapping from the primary to the replica database connection. \Rails
9
11
  # provides a basic framework to determine when to swap and allows for
10
12
  # applications to write custom strategy classes to override the default
11
13
  # behavior.
@@ -15,7 +17,7 @@ module ActiveRecord
15
17
  # resolver context class that sets a value that helps the resolver class
16
18
  # decide when to switch.
17
19
  #
18
- # Rails default middleware uses the request's session to set a timestamp
20
+ # \Rails default middleware uses the request's session to set a timestamp
19
21
  # that informs the application when to read from a primary or read from a
20
22
  # replica.
21
23
  #
@@ -71,7 +73,7 @@ module ActiveRecord
71
73
  context = context_klass.call(request)
72
74
  resolver = resolver_klass.call(context, options)
73
75
 
74
- response = if reading_request?(request)
76
+ response = if resolver.reading_request?(request)
75
77
  resolver.read(&blk)
76
78
  else
77
79
  resolver.write(&blk)
@@ -80,10 +82,6 @@ module ActiveRecord
80
82
  resolver.update_context(response)
81
83
  response
82
84
  end
83
-
84
- def reading_request?(request)
85
- request.get? || request.head?
86
- end
87
85
  end
88
86
  end
89
87
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Middleware
5
+ # = Shard Selector \Middleware
6
+ #
5
7
  # The ShardSelector Middleware provides a framework for automatically
6
- # swapping shards. Rails provides a basic framework to determine which
8
+ # swapping shards. \Rails provides a basic framework to determine which
7
9
  # shard to switch to and allows for applications to write custom strategies
8
10
  # for swapping if needed.
9
11
  #