activerecord 7.2.3.1 → 8.0.5.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +475 -946
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/association.rb +34 -10
  6. data/lib/active_record/associations/builder/association.rb +7 -6
  7. data/lib/active_record/associations/collection_association.rb +1 -1
  8. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  9. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +2 -2
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/preloader/batch.rb +7 -1
  13. data/lib/active_record/associations/singular_association.rb +8 -3
  14. data/lib/active_record/associations.rb +34 -4
  15. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  16. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  17. data/lib/active_record/attribute_methods/query.rb +34 -0
  18. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  19. data/lib/active_record/autosave_association.rb +69 -27
  20. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -17
  24. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  25. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  26. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  28. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  29. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -10
  30. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  31. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -74
  32. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
  33. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  34. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  35. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  36. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  37. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  38. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  39. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  40. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  41. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  42. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  43. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  44. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  45. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  46. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  47. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  48. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -96
  49. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  50. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  51. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  52. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  53. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  54. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  56. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  57. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  58. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  59. data/lib/active_record/connection_adapters.rb +0 -56
  60. data/lib/active_record/connection_handling.rb +23 -1
  61. data/lib/active_record/core.rb +29 -14
  62. data/lib/active_record/database_configurations/database_config.rb +4 -0
  63. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  64. data/lib/active_record/encryption/config.rb +3 -1
  65. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  66. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  67. data/lib/active_record/encryption/encryptor.rb +16 -8
  68. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  69. data/lib/active_record/encryption/scheme.rb +8 -1
  70. data/lib/active_record/enum.rb +9 -22
  71. data/lib/active_record/errors.rb +13 -5
  72. data/lib/active_record/fixtures.rb +0 -2
  73. data/lib/active_record/future_result.rb +15 -9
  74. data/lib/active_record/gem_version.rb +3 -3
  75. data/lib/active_record/insert_all.rb +3 -3
  76. data/lib/active_record/locking/optimistic.rb +1 -1
  77. data/lib/active_record/log_subscriber.rb +5 -11
  78. data/lib/active_record/migration/command_recorder.rb +32 -12
  79. data/lib/active_record/migration/compatibility.rb +5 -2
  80. data/lib/active_record/migration.rb +38 -42
  81. data/lib/active_record/model_schema.rb +3 -4
  82. data/lib/active_record/nested_attributes.rb +4 -6
  83. data/lib/active_record/persistence.rb +128 -130
  84. data/lib/active_record/query_logs.rb +102 -50
  85. data/lib/active_record/query_logs_formatter.rb +17 -28
  86. data/lib/active_record/querying.rb +8 -8
  87. data/lib/active_record/railtie.rb +2 -26
  88. data/lib/active_record/railties/databases.rake +11 -35
  89. data/lib/active_record/reflection.rb +18 -21
  90. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  91. data/lib/active_record/relation/batches.rb +132 -72
  92. data/lib/active_record/relation/calculations.rb +40 -39
  93. data/lib/active_record/relation/delegation.rb +25 -14
  94. data/lib/active_record/relation/finder_methods.rb +18 -18
  95. data/lib/active_record/relation/merger.rb +8 -8
  96. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  97. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  98. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  99. data/lib/active_record/relation/predicate_builder.rb +13 -0
  100. data/lib/active_record/relation/query_methods.rb +105 -61
  101. data/lib/active_record/relation/spawn_methods.rb +7 -7
  102. data/lib/active_record/relation/where_clause.rb +1 -1
  103. data/lib/active_record/relation.rb +80 -62
  104. data/lib/active_record/result.rb +66 -4
  105. data/lib/active_record/sanitization.rb +7 -6
  106. data/lib/active_record/schema_dumper.rb +5 -0
  107. data/lib/active_record/schema_migration.rb +2 -1
  108. data/lib/active_record/scoping/named.rb +5 -2
  109. data/lib/active_record/statement_cache.rb +14 -14
  110. data/lib/active_record/store.rb +7 -3
  111. data/lib/active_record/table_metadata.rb +1 -3
  112. data/lib/active_record/tasks/database_tasks.rb +69 -60
  113. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  114. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
  115. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  116. data/lib/active_record/test_databases.rb +1 -1
  117. data/lib/active_record/test_fixtures.rb +12 -0
  118. data/lib/active_record/token_for.rb +1 -1
  119. data/lib/active_record/transactions.rb +5 -6
  120. data/lib/active_record/type/serialized.rb +5 -0
  121. data/lib/active_record/validations/uniqueness.rb +8 -8
  122. data/lib/active_record.rb +21 -48
  123. data/lib/arel/collectors/bind.rb +2 -2
  124. data/lib/arel/collectors/sql_string.rb +1 -1
  125. data/lib/arel/collectors/substitute_binds.rb +2 -2
  126. data/lib/arel/nodes/binary.rb +1 -1
  127. data/lib/arel/nodes/node.rb +1 -1
  128. data/lib/arel/nodes/sql_literal.rb +1 -1
  129. data/lib/arel/predications.rb +1 -3
  130. data/lib/arel/table.rb +3 -7
  131. metadata +10 -11
  132. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -87,6 +87,8 @@ module ActiveRecord
87
87
 
88
88
  connections = []
89
89
 
90
+ @shard_keys = shards.keys
91
+
90
92
  if shards.empty?
91
93
  shards[:default] = database
92
94
  end
@@ -177,6 +179,18 @@ module ActiveRecord
177
179
  end
178
180
  end
179
181
 
182
+ # Passes the block to +connected_to+ for every +shard+ the
183
+ # model is configured to connect to (if any), and returns the
184
+ # results in an array.
185
+ #
186
+ # Optionally, +role+ and/or +prevent_writes+ can be passed which
187
+ # will be forwarded to each +connected_to+ call.
188
+ def connected_to_all_shards(role: nil, prevent_writes: false, &blk)
189
+ shard_keys.map do |shard|
190
+ connected_to(shard: shard, role: role, prevent_writes: prevent_writes, &blk)
191
+ end
192
+ end
193
+
180
194
  # Use a specified connection.
181
195
  #
182
196
  # This method is useful for ensuring that a specific connection is
@@ -289,7 +303,7 @@ module ActiveRecord
289
303
 
290
304
  # Checkouts a connection from the pool, yield it and then check it back in.
291
305
  # If a connection was already leased via #lease_connection or a parent call to
292
- # #with_connection, that same connection is yieled.
306
+ # #with_connection, that same connection is yielded.
293
307
  # If #lease_connection is called inside the block, the connection won't be checked
294
308
  # back in.
295
309
  # If #connection is called inside the block, the connection won't be checked back in
@@ -361,6 +375,14 @@ module ActiveRecord
361
375
  connection_pool.schema_cache.clear!
362
376
  end
363
377
 
378
+ def shard_keys
379
+ connection_class_for_self.instance_variable_get(:@shard_keys) || []
380
+ end
381
+
382
+ def sharded?
383
+ shard_keys.any?
384
+ end
385
+
364
386
  private
365
387
  def resolve_config_for_connection(config_or_env)
366
388
  raise "Anonymous class is not allowed." unless name
@@ -89,6 +89,7 @@ module ActiveRecord
89
89
  class_attribute :belongs_to_required_by_default, instance_accessor: false
90
90
 
91
91
  class_attribute :strict_loading_by_default, instance_accessor: false, default: false
92
+ class_attribute :strict_loading_mode, instance_accessor: false, default: :all
92
93
 
93
94
  class_attribute :has_many_inversing, instance_accessor: false, default: false
94
95
 
@@ -201,6 +202,17 @@ module ActiveRecord
201
202
  false
202
203
  end
203
204
 
205
+ # Intended to behave like `.current_preventing_writes` given the class name as input.
206
+ # See PoolConfig and ConnectionHandler::ConnectionDescriptor.
207
+ def self.preventing_writes?(class_name) # :nodoc:
208
+ connected_to_stack.reverse_each do |hash|
209
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
210
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
211
+ end
212
+
213
+ false
214
+ end
215
+
204
216
  def self.connected_to_stack # :nodoc:
205
217
  if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
206
218
  connected_to_stack
@@ -381,7 +393,7 @@ module ActiveRecord
381
393
  end
382
394
 
383
395
  def predicate_builder # :nodoc:
384
- @predicate_builder ||= PredicateBuilder.new(table_metadata)
396
+ @predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
385
397
  end
386
398
 
387
399
  def type_caster # :nodoc:
@@ -426,10 +438,6 @@ module ActiveRecord
426
438
  end
427
439
  end
428
440
 
429
- def table_metadata
430
- TableMetadata.new(self, arel_table)
431
- end
432
-
433
441
  def cached_find_by(keys, values)
434
442
  with_connection do |connection|
435
443
  statement = cached_find_by_statement(connection, keys) { |params|
@@ -443,8 +451,8 @@ module ActiveRecord
443
451
  where(wheres).limit(1)
444
452
  }
445
453
 
446
- begin
447
- statement.execute(values.flatten, connection, allow_retry: true).first
454
+ statement.execute(values.flatten, connection, allow_retry: true).then do |r|
455
+ r.first
448
456
  rescue TypeError
449
457
  raise ActiveRecord::StatementInvalid
450
458
  end
@@ -540,12 +548,7 @@ module ActiveRecord
540
548
 
541
549
  ##
542
550
  def initialize_dup(other) # :nodoc:
543
- @attributes = @attributes.deep_dup
544
- if self.class.composite_primary_key?
545
- @primary_key.each { |key| @attributes.reset(key) }
546
- else
547
- @attributes.reset(@primary_key)
548
- end
551
+ @attributes = init_attributes(other)
549
552
 
550
553
  _run_initialize_callbacks
551
554
 
@@ -557,6 +560,18 @@ module ActiveRecord
557
560
  super
558
561
  end
559
562
 
563
+ def init_attributes(_) # :nodoc:
564
+ attrs = @attributes.deep_dup
565
+
566
+ if self.class.composite_primary_key?
567
+ @primary_key.each { |key| attrs.reset(key) }
568
+ else
569
+ attrs.reset(@primary_key)
570
+ end
571
+
572
+ attrs
573
+ end
574
+
560
575
  # Populate +coder+ with attributes about this record that should be
561
576
  # serialized. The structure of +coder+ defined in this method is
562
577
  # guaranteed to match the structure of +coder+ passed to the #init_with
@@ -830,7 +845,7 @@ module ActiveRecord
830
845
 
831
846
  @primary_key = klass.primary_key
832
847
  @strict_loading = klass.strict_loading_by_default
833
- @strict_loading_mode = :all
848
+ @strict_loading_mode = klass.strict_loading_mode
834
849
 
835
850
  klass.define_attribute_methods
836
851
  end
@@ -99,6 +99,10 @@ module ActiveRecord
99
99
  def use_metadata_table?
100
100
  raise NotImplementedError
101
101
  end
102
+
103
+ def seeds?
104
+ raise NotImplementedError
105
+ end
102
106
  end
103
107
  end
104
108
  end
@@ -130,6 +130,14 @@ module ActiveRecord
130
130
  Base.configurations.primary?(name)
131
131
  end
132
132
 
133
+ # Determines whether the db:prepare task should seed the database from db/seeds.rb.
134
+ #
135
+ # If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
136
+ # will return `true` for the primary database and `false` for all other configs.
137
+ def seeds?
138
+ configuration_hash.fetch(:seeds, primary?)
139
+ end
140
+
133
141
  # Determines whether to dump the schema/structure files and the filename that
134
142
  # should be used.
135
143
  #
@@ -138,7 +146,7 @@ module ActiveRecord
138
146
  #
139
147
  # If the config option is set that will be used. Otherwise Rails will generate
140
148
  # the filename from the database config name.
141
- def schema_dump(format = ActiveRecord.schema_format)
149
+ def schema_dump(format = schema_format)
142
150
  if configuration_hash.key?(:schema_dump)
143
151
  if config = configuration_hash[:schema_dump]
144
152
  config
@@ -150,6 +158,12 @@ module ActiveRecord
150
158
  end
151
159
  end
152
160
 
161
+ def schema_format # :nodoc:
162
+ format = configuration_hash[:schema_format]&.to_sym || ActiveRecord.schema_format
163
+ raise "Invalid schema format" unless [ :ruby, :sql ].include? format
164
+ format
165
+ end
166
+
153
167
  def database_tasks? # :nodoc:
154
168
  !replica? && !!configuration_hash.fetch(:database_tasks, true)
155
169
  end
@@ -160,7 +174,7 @@ module ActiveRecord
160
174
 
161
175
  private
162
176
  def schema_file_type(format)
163
- case format
177
+ case format.to_sym
164
178
  when :ruby
165
179
  "schema.rb"
166
180
  when :sql
@@ -8,7 +8,8 @@ module ActiveRecord
8
8
  class Config
9
9
  attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
10
10
  :support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
11
- :excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption
11
+ :excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption,
12
+ :compressor
12
13
 
13
14
  def initialize
14
15
  set_defaults
@@ -55,6 +56,7 @@ module ActiveRecord
55
56
  self.previous_schemes = []
56
57
  self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
57
58
  self.hash_digest_class = OpenSSL::Digest::SHA1
59
+ self.compressor = Zlib
58
60
 
59
61
  # TODO: Setting to false for now as the implementation is a bit experimental
60
62
  self.extend_queries = false
@@ -46,11 +46,11 @@ module ActiveRecord
46
46
  # * <tt>:previous</tt> - List of previous encryption schemes. When provided, they will be used in order when trying to read
47
47
  # the attribute. Each entry of the list can contain the properties supported by #encrypts. Also, when deterministic
48
48
  # encryption is used, they will be used to generate additional ciphertexts to check in the queries.
49
- def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
49
+ def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
50
50
  self.encrypted_attributes ||= Set.new # not using :default because the instance would be shared across classes
51
51
 
52
52
  names.each do |name|
53
- encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
53
+ encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
54
54
  end
55
55
  end
56
56
 
@@ -81,12 +81,12 @@ module ActiveRecord
81
81
  end
82
82
  end
83
83
 
84
- def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
84
+ def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
85
85
  encrypted_attributes << name.to_sym
86
86
 
87
87
  decorate_attributes([name]) do |name, cast_type|
88
88
  scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
89
- downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
89
+ downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
90
90
 
91
91
  ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
92
92
  end
@@ -100,7 +100,7 @@ module ActiveRecord
100
100
  end
101
101
 
102
102
  def decrypt(value)
103
- text_to_database_type decrypt_as_text(value)
103
+ text_to_database_type decrypt_as_text(database_type_to_text(value))
104
104
  end
105
105
 
106
106
  def try_to_deserialize_with_previous_encrypted_types(value)
@@ -170,6 +170,15 @@ module ActiveRecord
170
170
  value
171
171
  end
172
172
  end
173
+
174
+ def database_type_to_text(value)
175
+ if value && cast_type.binary?
176
+ binary_cast_type = cast_type.serialized? ? cast_type.subtype : cast_type
177
+ binary_cast_type.deserialize(value)
178
+ else
179
+ value
180
+ end
181
+ end
173
182
  end
174
183
  end
175
184
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openssl"
4
- require "zlib"
5
4
  require "active_support/core_ext/numeric"
6
5
 
7
6
  module ActiveRecord
@@ -12,13 +11,22 @@ module ActiveRecord
12
11
  # It interacts with a KeyProvider for getting the keys, and delegate to
13
12
  # ActiveRecord::Encryption::Cipher the actual encryption algorithm.
14
13
  class Encryptor
14
+ # The compressor to use for compressing the payload.
15
+ attr_reader :compressor
16
+
15
17
  # ==== Options
16
18
  #
17
19
  # [+:compress+]
18
20
  # Boolean indicating whether records should be compressed before
19
21
  # encryption. Defaults to +true+.
20
- def initialize(compress: true)
22
+ #
23
+ # [+:compressor+]
24
+ # The compressor to use. It must respond to +deflate+ and +inflate+.
25
+ # If not provided, will default to +ActiveRecord::Encryption.config.compressor+,
26
+ # which itself defaults to +Zlib+.
27
+ def initialize(compress: true, compressor: nil)
21
28
  @compress = compress
29
+ @compressor = compressor || ActiveRecord::Encryption.config.compressor
22
30
  end
23
31
 
24
32
  # Encrypts +clean_text+ and returns the encrypted result.
@@ -79,6 +87,10 @@ module ActiveRecord
79
87
  serializer.binary?
80
88
  end
81
89
 
90
+ def compress? # :nodoc:
91
+ @compress
92
+ end
93
+
82
94
  private
83
95
  DECRYPT_ERRORS = [OpenSSL::Cipher::CipherError, Errors::EncryptedContentIntegrity, Errors::Decryption]
84
96
  ENCODING_ERRORS = [EncodingError, Errors::Encoding]
@@ -131,12 +143,8 @@ module ActiveRecord
131
143
  end
132
144
  end
133
145
 
134
- def compress?
135
- @compress
136
- end
137
-
138
146
  def compress(data)
139
- Zlib::Deflate.deflate(data).tap do |compressed_data|
147
+ @compressor.deflate(data).tap do |compressed_data|
140
148
  compressed_data.force_encoding(data.encoding)
141
149
  end
142
150
  end
@@ -150,7 +158,7 @@ module ActiveRecord
150
158
  end
151
159
 
152
160
  def uncompress(data)
153
- Zlib::Inflate.inflate(data).tap do |uncompressed_data|
161
+ @compressor.inflate(data).tap do |uncompressed_data|
154
162
  uncompressed_data.force_encoding(data.encoding)
155
163
  end
156
164
  end
@@ -41,6 +41,8 @@ module ActiveRecord
41
41
  module EncryptedQuery # :nodoc:
42
42
  class << self
43
43
  def process_arguments(owner, args, check_for_additional_values)
44
+ owner = owner.model if owner.is_a?(Relation)
45
+
44
46
  return args if owner.deterministic_encrypted_attributes&.empty?
45
47
 
46
48
  if args.is_a?(Array) && (options = args.first).is_a?(Hash)
@@ -102,12 +104,12 @@ module ActiveRecord
102
104
  end
103
105
 
104
106
  def scope_for_create
105
- return super unless klass.deterministic_encrypted_attributes&.any?
107
+ return super unless model.deterministic_encrypted_attributes&.any?
106
108
 
107
109
  scope_attributes = super
108
110
  wheres = where_values_hash
109
111
 
110
- klass.deterministic_encrypted_attributes.each do |attribute_name|
112
+ model.deterministic_encrypted_attributes.each do |attribute_name|
111
113
  attribute_name = attribute_name.to_s
112
114
  values = wheres[attribute_name]
113
115
  if values.is_a?(Array) && values[1..].all?(AdditionalValue)
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  attr_accessor :previous_schemes
12
12
 
13
13
  def initialize(key_provider: nil, key: nil, deterministic: nil, support_unencrypted_data: nil, downcase: nil, ignore_case: nil,
14
- previous_schemes: nil, **context_properties)
14
+ previous_schemes: nil, compress: true, compressor: nil, **context_properties)
15
15
  # Initializing all attributes to +nil+ as we want to allow a "not set" semantics so that we
16
16
  # can merge schemes without overriding values with defaults. See +#merge+
17
17
 
@@ -24,8 +24,13 @@ module ActiveRecord
24
24
  @previous_schemes_param = previous_schemes
25
25
  @previous_schemes = Array.wrap(previous_schemes)
26
26
  @context_properties = context_properties
27
+ @compress = compress
28
+ @compressor = compressor
27
29
 
28
30
  validate_config!
31
+
32
+ @context_properties[:encryptor] = Encryptor.new(compress: @compress) unless @compress
33
+ @context_properties[:encryptor] = Encryptor.new(compressor: compressor) if compressor
29
34
  end
30
35
 
31
36
  def ignore_case?
@@ -78,6 +83,8 @@ module ActiveRecord
78
83
  def validate_config!
79
84
  raise Errors::Configuration, "ignore_case: can only be used with deterministic encryption" if @ignore_case && !@deterministic
80
85
  raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
86
+ raise Errors::Configuration, "compressor: can't be used with compress: false" if !@compress && @compressor
87
+ raise Errors::Configuration, "compressor: can't be used with encryptor" if @compressor && @context_properties[:encryptor]
81
88
  end
82
89
 
83
90
  def key_provider_from_key
@@ -214,34 +214,16 @@ module ActiveRecord
214
214
  attr_reader :name, :mapping
215
215
  end
216
216
 
217
- def enum(name = nil, values = nil, **options)
218
- if name
219
- values, options = options, {} unless values
220
- return _enum(name, values, **options)
221
- end
222
-
223
- definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
224
- options.transform_keys! { |key| :"#{key[1..-1]}" }
225
-
226
- definitions.each { |name, values| _enum(name, values, **options) }
227
-
228
- ActiveRecord.deprecator.warn(<<~MSG)
229
- Defining enums with keyword arguments is deprecated and will be removed
230
- in Rails 8.0. Positional arguments should be used instead:
231
-
232
- #{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
233
- MSG
217
+ def enum(name, values = nil, **options)
218
+ values, options = options, {} unless values
219
+ _enum(name, values, **options)
234
220
  end
235
221
 
236
222
  private
237
- def inherited(base)
238
- base.defined_enums = defined_enums.deep_dup
239
- super
240
- end
241
-
242
223
  def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
243
224
  assert_valid_enum_definition_values(values)
244
225
  assert_valid_enum_options(options)
226
+
245
227
  # statuses = { }
246
228
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
247
229
  name = name.to_s
@@ -305,6 +287,11 @@ module ActiveRecord
305
287
  enum_values.freeze
306
288
  end
307
289
 
290
+ def inherited(base)
291
+ base.defined_enums = defined_enums.deep_dup
292
+ super
293
+ end
294
+
308
295
  class EnumMethods < Module # :nodoc:
309
296
  def initialize(klass)
310
297
  @klass = klass
@@ -84,6 +84,19 @@ module ActiveRecord
84
84
  class ConnectionTimeoutError < ConnectionNotEstablished
85
85
  end
86
86
 
87
+ # Raised when a database connection pool is requested but
88
+ # has not been defined.
89
+ class ConnectionNotDefined < ConnectionNotEstablished
90
+ def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
91
+ super(message)
92
+ @connection_name = connection_name
93
+ @role = role
94
+ @shard = shard
95
+ end
96
+
97
+ attr_reader :connection_name, :role, :shard
98
+ end
99
+
87
100
  # Raised when connection to the database could not been established because it was not
88
101
  # able to connect to the host or when the authorization failed.
89
102
  class DatabaseConnectionError < ConnectionNotEstablished
@@ -484,11 +497,6 @@ module ActiveRecord
484
497
  # relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
485
498
  class UnmodifiableRelation < ActiveRecordError
486
499
  end
487
- deprecate_constant(
488
- :ImmutableRelation,
489
- "ActiveRecord::UnmodifiableRelation",
490
- deprecator: ActiveRecord.deprecator
491
- )
492
500
 
493
501
  # TransactionIsolationError will be raised under the following conditions:
494
502
  #
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "yaml"
5
- require "zlib"
6
- require "set"
7
5
  require "active_support/dependencies"
8
6
  require "active_support/core_ext/digest/uuid"
9
7
  require "active_record/test_fixtures"
@@ -100,17 +100,23 @@ module ActiveRecord
100
100
  def execute_or_skip
101
101
  return unless pending?
102
102
 
103
- @pool.with_connection do |connection|
104
- return unless @mutex.try_lock
105
- begin
106
- if pending?
107
- @event_buffer = EventBuffer.new(self, @instrumenter)
108
- connection.with_instrumenter(@event_buffer) do
103
+ @session.synchronize do
104
+ return unless pending?
105
+
106
+ @pool.with_connection do |connection|
107
+ return unless @mutex.try_lock
108
+ previous_instrumenter = ActiveSupport::IsolatedExecutionState[:active_record_instrumenter]
109
+ begin
110
+ if pending?
111
+ @event_buffer = EventBuffer.new(self, @instrumenter)
112
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer
113
+
109
114
  execute_query(connection, async: true)
110
115
  end
116
+ ensure
117
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = previous_instrumenter
118
+ @mutex.unlock
111
119
  end
112
- ensure
113
- @mutex.unlock
114
120
  end
115
121
  end
116
122
  end
@@ -163,7 +169,7 @@ module ActiveRecord
163
169
  end
164
170
 
165
171
  def exec_query(connection, *args, **kwargs)
166
- connection.internal_exec_query(*args, **kwargs)
172
+ connection.raw_exec_query(*args, **kwargs)
167
173
  end
168
174
 
169
175
  class SelectAll < FutureResult # :nodoc:
@@ -7,9 +7,9 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 2
12
- TINY = 3
10
+ MAJOR = 8
11
+ MINOR = 0
12
+ TINY = 5
13
13
  PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
  @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
40
40
  @returning = false if @returning == []
41
41
 
42
- @unique_by = find_unique_index_for(@unique_by)
42
+ @unique_by = find_unique_index_for(@unique_by) if @on_duplicate != :raise
43
43
 
44
44
  configure_on_duplicate_update_logic
45
45
  ensure_valid_options_for_connection!
@@ -48,7 +48,7 @@ module ActiveRecord
48
48
  def execute
49
49
  return ActiveRecord::Result.empty if inserts.empty?
50
50
 
51
- message = +"#{model} "
51
+ message = +"#{model.name} "
52
52
  message << "Bulk " if inserts.many?
53
53
  message << (on_duplicate == :update ? "Upsert" : "Insert")
54
54
  connection.exec_insert_all to_sql, message
@@ -240,7 +240,7 @@ module ActiveRecord
240
240
 
241
241
  values_list = insert_all.map_key_with_value do |key, value|
242
242
  next value if Arel::Nodes::SqlLiteral === value
243
- connection.with_yaml_fallback(types[key].serialize(value))
243
+ ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
244
244
  end
245
245
 
246
246
  connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
@@ -9,7 +9,7 @@ module ActiveRecord
9
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 +ActiveRecord::Locking::Pessimistic+ for an alternative.
12
+ # Check out ActiveRecord::Locking::Pessimistic for an alternative.
13
13
  #
14
14
  # == Usage
15
15
  #
@@ -126,18 +126,12 @@ module ActiveRecord
126
126
  end
127
127
  end
128
128
 
129
- if Thread.respond_to?(:each_caller_location)
130
- def query_source_location
131
- Thread.each_caller_location do |location|
132
- frame = backtrace_cleaner.clean_frame(location)
133
- return frame if frame
134
- end
135
- nil
136
- end
137
- else
138
- def query_source_location
139
- backtrace_cleaner.clean(caller(1).lazy).first
129
+ def query_source_location
130
+ Thread.each_caller_location do |location|
131
+ frame = backtrace_cleaner.clean_frame(location)
132
+ return frame if frame
140
133
  end
134
+ nil
141
135
  end
142
136
 
143
137
  def filter(name, value)