activerecord 7.0.8.6 → 7.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1340 -1568
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
data/lib/active_record/enum.rb
CHANGED
@@ -111,23 +111,79 @@ module ActiveRecord
|
|
111
111
|
#
|
112
112
|
# conversation.comments_inactive!
|
113
113
|
# conversation.comments_active? # => false
|
114
|
+
#
|
115
|
+
# If you want to disable the auto-generated methods on the model, you can do
|
116
|
+
# so by setting the +:instance_methods+ option to false:
|
117
|
+
#
|
118
|
+
# class Conversation < ActiveRecord::Base
|
119
|
+
# enum :status, [ :active, :archived ], instance_methods: false
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# If you want the enum value to be validated before saving, use the option +:validate+:
|
123
|
+
#
|
124
|
+
# class Conversation < ActiveRecord::Base
|
125
|
+
# enum :status, [ :active, :archived ], validate: true
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# conversation = Conversation.new
|
129
|
+
#
|
130
|
+
# conversation.status = :unknown
|
131
|
+
# conversation.valid? # => false
|
132
|
+
#
|
133
|
+
# conversation.status = nil
|
134
|
+
# conversation.valid? # => false
|
135
|
+
#
|
136
|
+
# conversation.status = :active
|
137
|
+
# conversation.valid? # => true
|
138
|
+
#
|
139
|
+
# It is also possible to pass additional validation options:
|
140
|
+
#
|
141
|
+
# class Conversation < ActiveRecord::Base
|
142
|
+
# enum :status, [ :active, :archived ], validate: { allow_nil: true }
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# conversation = Conversation.new
|
146
|
+
#
|
147
|
+
# conversation.status = :unknown
|
148
|
+
# conversation.valid? # => false
|
149
|
+
#
|
150
|
+
# conversation.status = nil
|
151
|
+
# conversation.valid? # => true
|
152
|
+
#
|
153
|
+
# conversation.status = :active
|
154
|
+
# conversation.valid? # => true
|
155
|
+
#
|
156
|
+
# Otherwise +ArgumentError+ will raise:
|
157
|
+
#
|
158
|
+
# class Conversation < ActiveRecord::Base
|
159
|
+
# enum :status, [ :active, :archived ]
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# conversation = Conversation.new
|
163
|
+
#
|
164
|
+
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
114
165
|
module Enum
|
115
166
|
def self.extended(base) # :nodoc:
|
116
167
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
117
168
|
end
|
118
169
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
170
|
+
def load_schema! # :nodoc:
|
171
|
+
attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
|
172
|
+
unless columns_hash.key?(name)
|
173
|
+
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
174
|
+
raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
|
175
|
+
end
|
176
|
+
end
|
122
177
|
end
|
123
178
|
|
124
179
|
class EnumType < Type::Value # :nodoc:
|
125
180
|
delegate :type, to: :subtype
|
126
181
|
|
127
|
-
def initialize(name, mapping, subtype)
|
182
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
128
183
|
@name = name
|
129
184
|
@mapping = mapping
|
130
185
|
@subtype = subtype
|
186
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
131
187
|
end
|
132
188
|
|
133
189
|
def cast(value)
|
@@ -153,6 +209,8 @@ module ActiveRecord
|
|
153
209
|
end
|
154
210
|
|
155
211
|
def assert_valid_value(value)
|
212
|
+
return unless @_raise_on_invalid_values
|
213
|
+
|
156
214
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
157
215
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
158
216
|
end
|
@@ -170,14 +228,19 @@ module ActiveRecord
|
|
170
228
|
return _enum(name, values, **options)
|
171
229
|
end
|
172
230
|
|
173
|
-
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
|
231
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
174
232
|
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
175
233
|
|
176
234
|
definitions.each { |name, values| _enum(name, values, **options) }
|
177
235
|
end
|
178
236
|
|
179
237
|
private
|
180
|
-
def
|
238
|
+
def inherited(base)
|
239
|
+
base.defined_enums = defined_enums.deep_dup
|
240
|
+
super
|
241
|
+
end
|
242
|
+
|
243
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
181
244
|
assert_valid_enum_definition_values(values)
|
182
245
|
# statuses = { }
|
183
246
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
@@ -193,7 +256,7 @@ module ActiveRecord
|
|
193
256
|
|
194
257
|
attribute(name, **options) do |subtype|
|
195
258
|
subtype = subtype.subtype if EnumType === subtype
|
196
|
-
EnumType.new(name, enum_values, subtype)
|
259
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
197
260
|
end
|
198
261
|
|
199
262
|
value_method_names = []
|
@@ -213,18 +276,24 @@ module ActiveRecord
|
|
213
276
|
|
214
277
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
215
278
|
value_method_names << value_method_name
|
216
|
-
define_enum_methods(name, value_method_name, value, scopes)
|
279
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
217
280
|
|
218
281
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
219
282
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
220
283
|
|
221
284
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
222
285
|
value_method_names << value_method_alias
|
223
|
-
define_enum_methods(name, value_method_alias, value, scopes)
|
286
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
224
287
|
end
|
225
288
|
end
|
226
289
|
end
|
227
290
|
detect_negative_enum_conditions!(value_method_names) if scopes
|
291
|
+
|
292
|
+
if validate
|
293
|
+
validate = {} unless Hash === validate
|
294
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
295
|
+
end
|
296
|
+
|
228
297
|
enum_values.freeze
|
229
298
|
end
|
230
299
|
|
@@ -236,21 +305,23 @@ module ActiveRecord
|
|
236
305
|
private
|
237
306
|
attr_reader :klass
|
238
307
|
|
239
|
-
def define_enum_methods(name, value_method_name, value, scopes)
|
240
|
-
|
241
|
-
|
242
|
-
|
308
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
309
|
+
if instance_methods
|
310
|
+
# def active?() status_for_database == 0 end
|
311
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
312
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
243
313
|
|
244
|
-
|
245
|
-
|
246
|
-
|
314
|
+
# def active!() update!(status: 0) end
|
315
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
316
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
317
|
+
end
|
247
318
|
|
248
|
-
# scope :active, -> { where(status: 0) }
|
249
|
-
# scope :not_active, -> { where.not(status: 0) }
|
250
319
|
if scopes
|
320
|
+
# scope :active, -> { where(status: 0) }
|
251
321
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
252
322
|
klass.scope value_method_name, -> { where(name => value) }
|
253
323
|
|
324
|
+
# scope :not_active, -> { where.not(status: 0) }
|
254
325
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
255
326
|
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
256
327
|
end
|
@@ -267,15 +338,29 @@ module ActiveRecord
|
|
267
338
|
end
|
268
339
|
|
269
340
|
def assert_valid_enum_definition_values(values)
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
341
|
+
case values
|
342
|
+
when Hash
|
343
|
+
if values.empty?
|
344
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
345
|
+
end
|
346
|
+
|
347
|
+
if values.keys.any?(&:blank?)
|
348
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
349
|
+
end
|
350
|
+
when Array
|
351
|
+
if values.empty?
|
352
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
353
|
+
end
|
354
|
+
|
355
|
+
unless values.all?(Symbol) || values.all?(String)
|
356
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
357
|
+
end
|
276
358
|
|
277
|
-
|
278
|
-
|
359
|
+
if values.any?(&:blank?)
|
360
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
361
|
+
end
|
362
|
+
else
|
363
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
279
364
|
end
|
280
365
|
end
|
281
366
|
|
@@ -290,6 +375,8 @@ module ActiveRecord
|
|
290
375
|
raise_conflict_error(enum_name, method_name, type: "class")
|
291
376
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
292
377
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
378
|
+
elsif klass_method && method_name.to_sym == :id
|
379
|
+
raise_conflict_error(enum_name, method_name)
|
293
380
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
294
381
|
raise_conflict_error(enum_name, method_name)
|
295
382
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
data/lib/active_record/errors.rb
CHANGED
@@ -7,9 +7,13 @@ module ActiveRecord
|
|
7
7
|
class ActiveRecordError < StandardError
|
8
8
|
end
|
9
9
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
10
|
+
# DEPRECATED: Previously raised when trying to use a feature in Active Record which
|
11
|
+
# requires Active Job but the gem is not present. Now raises a NameError.
|
12
|
+
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
13
|
+
DeprecatedActiveJobRequiredError = Class.new(ActiveRecordError) # :nodoc:
|
14
|
+
deprecate_constant "ActiveJobRequiredError", "ActiveRecord::DeprecatedActiveJobRequiredError",
|
15
|
+
message: "ActiveRecord::ActiveJobRequiredError has been deprecated. If Active Job is not present, a NameError will be raised instead.",
|
16
|
+
deprecator: ActiveRecord.deprecator
|
13
17
|
|
14
18
|
# Raised when the single-table inheritance mechanism fails to locate the subclass
|
15
19
|
# (for example due to improper usage of column that
|
@@ -51,10 +55,31 @@ module ActiveRecord
|
|
51
55
|
class AdapterNotFound < ActiveRecordError
|
52
56
|
end
|
53
57
|
|
58
|
+
# Superclass for all errors raised from an Active Record adapter.
|
59
|
+
class AdapterError < ActiveRecordError
|
60
|
+
def initialize(message = nil, connection_pool: nil)
|
61
|
+
@connection_pool = connection_pool
|
62
|
+
super(message)
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :connection_pool
|
66
|
+
end
|
67
|
+
|
54
68
|
# Raised when connection to the database could not been established (for example when
|
55
69
|
# {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
|
56
70
|
# is given a +nil+ object).
|
57
|
-
class ConnectionNotEstablished <
|
71
|
+
class ConnectionNotEstablished < AdapterError
|
72
|
+
def initialize(message = nil, connection_pool: nil)
|
73
|
+
super(message, connection_pool: connection_pool)
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_pool(connection_pool)
|
77
|
+
unless @connection_pool
|
78
|
+
@connection_pool = connection_pool
|
79
|
+
end
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
58
83
|
end
|
59
84
|
|
60
85
|
# Raised when a connection could not be obtained within the connection
|
@@ -90,7 +115,7 @@ module ActiveRecord
|
|
90
115
|
# Raised when a pool was unable to get ahold of all its connections
|
91
116
|
# to perform a "group" action such as
|
92
117
|
# {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
|
93
|
-
# or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
|
118
|
+
# or {ActiveRecord::Base.connection_handler.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
|
94
119
|
class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
|
95
120
|
end
|
96
121
|
|
@@ -155,14 +180,23 @@ module ActiveRecord
|
|
155
180
|
# Superclass for all database execution errors.
|
156
181
|
#
|
157
182
|
# Wraps the underlying database error as +cause+.
|
158
|
-
class StatementInvalid <
|
159
|
-
def initialize(message = nil, sql: nil, binds: nil)
|
160
|
-
super(message || $!&.message)
|
183
|
+
class StatementInvalid < AdapterError
|
184
|
+
def initialize(message = nil, sql: nil, binds: nil, connection_pool: nil)
|
185
|
+
super(message || $!&.message, connection_pool: connection_pool)
|
161
186
|
@sql = sql
|
162
187
|
@binds = binds
|
163
188
|
end
|
164
189
|
|
165
190
|
attr_reader :sql, :binds
|
191
|
+
|
192
|
+
def set_query(sql, binds)
|
193
|
+
unless @sql
|
194
|
+
@sql = sql
|
195
|
+
@binds = binds
|
196
|
+
end
|
197
|
+
|
198
|
+
self
|
199
|
+
end
|
166
200
|
end
|
167
201
|
|
168
202
|
# Defunct wrapper class kept for compatibility.
|
@@ -189,8 +223,13 @@ module ActiveRecord
|
|
189
223
|
foreign_key: nil,
|
190
224
|
target_table: nil,
|
191
225
|
primary_key: nil,
|
192
|
-
primary_key_column: nil
|
226
|
+
primary_key_column: nil,
|
227
|
+
query_parser: nil,
|
228
|
+
connection_pool: nil
|
193
229
|
)
|
230
|
+
@original_message = message
|
231
|
+
@query_parser = query_parser
|
232
|
+
|
194
233
|
if table
|
195
234
|
type = primary_key_column.bigint? ? :bigint : primary_key_column.type
|
196
235
|
msg = <<~EOM.squish
|
@@ -208,7 +247,24 @@ module ActiveRecord
|
|
208
247
|
if message
|
209
248
|
msg << "\nOriginal message: #{message}"
|
210
249
|
end
|
211
|
-
|
250
|
+
|
251
|
+
super(msg, sql: sql, binds: binds, connection_pool: connection_pool)
|
252
|
+
end
|
253
|
+
|
254
|
+
def set_query(sql, binds)
|
255
|
+
if @query_parser && !@sql
|
256
|
+
self.class.new(
|
257
|
+
message: @original_message,
|
258
|
+
sql: sql,
|
259
|
+
binds: binds,
|
260
|
+
connection_pool: @connection_pool,
|
261
|
+
**@query_parser.call(sql)
|
262
|
+
).tap do |exception|
|
263
|
+
exception.set_backtrace backtrace
|
264
|
+
end
|
265
|
+
else
|
266
|
+
super
|
267
|
+
end
|
212
268
|
end
|
213
269
|
end
|
214
270
|
|
@@ -224,6 +280,19 @@ module ActiveRecord
|
|
224
280
|
class RangeError < StatementInvalid
|
225
281
|
end
|
226
282
|
|
283
|
+
# Raised when a statement produces an SQL warning.
|
284
|
+
class SQLWarning < AdapterError
|
285
|
+
attr_reader :code, :level
|
286
|
+
attr_accessor :sql
|
287
|
+
|
288
|
+
def initialize(message = nil, code = nil, level = nil, sql = nil, connection_pool = nil)
|
289
|
+
super(message, connection_pool: connection_pool)
|
290
|
+
@code = code
|
291
|
+
@level = level
|
292
|
+
@sql = sql
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
227
296
|
# Raised when the number of placeholders in an SQL fragment passed to
|
228
297
|
# {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]
|
229
298
|
# does not match the number of values supplied.
|
@@ -242,16 +311,16 @@ module ActiveRecord
|
|
242
311
|
ActiveRecord::Tasks::DatabaseTasks.create_current
|
243
312
|
end
|
244
313
|
|
245
|
-
def initialize(message = nil)
|
246
|
-
super(message || "Database not found")
|
314
|
+
def initialize(message = nil, connection_pool: nil)
|
315
|
+
super(message || "Database not found", connection_pool: connection_pool)
|
247
316
|
end
|
248
317
|
|
249
318
|
class << self
|
250
319
|
def db_error(db_name)
|
251
320
|
NoDatabaseError.new(<<~MSG)
|
252
|
-
We could not find your database: #{db_name}.
|
321
|
+
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml file.
|
253
322
|
|
254
|
-
To resolve this
|
323
|
+
To resolve this error:
|
255
324
|
|
256
325
|
- Did you create the database for this app, or delete it? You may need to create your database.
|
257
326
|
- Has the database name changed? Check your database.yml config has the correct database name.
|
@@ -407,7 +476,7 @@ module ActiveRecord
|
|
407
476
|
# * You are joining an existing open transaction
|
408
477
|
# * You are creating a nested (savepoint) transaction
|
409
478
|
#
|
410
|
-
# The mysql2 and postgresql adapters support setting the transaction isolation level.
|
479
|
+
# The mysql2, trilogy, and postgresql adapters support setting the transaction isolation level.
|
411
480
|
class TransactionIsolationError < ActiveRecordError
|
412
481
|
end
|
413
482
|
|
@@ -461,6 +530,11 @@ module ActiveRecord
|
|
461
530
|
class AdapterTimeout < QueryAborted
|
462
531
|
end
|
463
532
|
|
533
|
+
# ConnectionFailed will be raised when the network connection to the
|
534
|
+
# database fails while sending a query or waiting for its result.
|
535
|
+
class ConnectionFailed < QueryAborted
|
536
|
+
end
|
537
|
+
|
464
538
|
# UnknownAttributeReference is raised when an unknown and potentially unsafe
|
465
539
|
# value is passed to a query method. For example, passing a non column name
|
466
540
|
# value to a relation's #order method might cause this exception.
|
@@ -16,15 +16,15 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
# Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
|
18
18
|
# Returns a formatted string ready to be logged.
|
19
|
-
def exec_explain(queries) # :nodoc:
|
19
|
+
def exec_explain(queries, options = []) # :nodoc:
|
20
20
|
str = queries.map do |sql, binds|
|
21
|
-
msg = +"
|
21
|
+
msg = +"#{build_explain_clause(options)} #{sql}"
|
22
22
|
unless binds.empty?
|
23
23
|
msg << " "
|
24
24
|
msg << binds.map { |attr| render_bind(attr) }.inspect
|
25
25
|
end
|
26
26
|
msg << "\n"
|
27
|
-
msg <<
|
27
|
+
msg << connection_explain(sql, binds, options)
|
28
28
|
end.join("\n")
|
29
29
|
|
30
30
|
# Overriding inspect to be more human readable, especially in the console.
|
@@ -50,5 +50,25 @@ module ActiveRecord
|
|
50
50
|
|
51
51
|
[attr&.name, value]
|
52
52
|
end
|
53
|
+
|
54
|
+
def build_explain_clause(options = [])
|
55
|
+
if connection.respond_to?(:build_explain_clause, true)
|
56
|
+
connection.build_explain_clause(options)
|
57
|
+
else
|
58
|
+
"EXPLAIN for:"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection_explain(sql, binds, options)
|
63
|
+
if connection.method(:explain).parameters.size == 2
|
64
|
+
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
65
|
+
The current database adapter, #{connection.adapter_name}, does not support explain options.
|
66
|
+
To remove this warning, the adapter must implement `build_explain_clause(options = [])`.
|
67
|
+
MSG
|
68
|
+
connection.explain(sql, binds)
|
69
|
+
else
|
70
|
+
connection.explain(sql, binds, options)
|
71
|
+
end
|
72
|
+
end
|
53
73
|
end
|
54
74
|
end
|
@@ -12,12 +12,22 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def primary_key_type
|
15
|
-
@primary_key_type ||= @model_class &&
|
15
|
+
@primary_key_type ||= @model_class && column_type(@model_class.primary_key)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
@
|
20
|
-
|
18
|
+
def column_type(column_name)
|
19
|
+
@column_type ||= {}
|
20
|
+
return @column_type[column_name] if @column_type.key?(column_name)
|
21
|
+
|
22
|
+
@column_type[column_name] = @model_class && @model_class.type_for_attribute(column_name).type
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_column?(column_name)
|
26
|
+
column_names.include?(column_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def column_names
|
30
|
+
@column_names ||= @model_class ? @model_class.columns.map(&:name).to_set : Set.new
|
21
31
|
end
|
22
32
|
|
23
33
|
def timestamp_column_names
|
@@ -87,7 +87,7 @@ module ActiveRecord
|
|
87
87
|
return unless model_class
|
88
88
|
fill_timestamps
|
89
89
|
interpolate_label
|
90
|
-
generate_primary_key
|
90
|
+
model_class.composite_primary_key? ? generate_composite_primary_key : generate_primary_key
|
91
91
|
resolve_enums
|
92
92
|
resolve_sti_reflections
|
93
93
|
end
|
@@ -117,14 +117,26 @@ module ActiveRecord
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def generate_primary_key
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
120
|
+
pk = model_metadata.primary_key_name
|
121
|
+
|
122
|
+
unless column_defined?(pk)
|
123
|
+
@row[pk] = ActiveRecord::FixtureSet.identify(@label, model_metadata.column_type(pk))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def generate_composite_primary_key
|
128
|
+
composite_key = ActiveRecord::FixtureSet.composite_identify(@label, model_metadata.primary_key_name)
|
129
|
+
composite_key.each do |column, value|
|
130
|
+
next if column_defined?(column)
|
131
|
+
|
132
|
+
@row[column] = value
|
125
133
|
end
|
126
134
|
end
|
127
135
|
|
136
|
+
def column_defined?(col)
|
137
|
+
!model_metadata.has_column?(col) || @row.include?(col)
|
138
|
+
end
|
139
|
+
|
128
140
|
def resolve_enums
|
129
141
|
reflection_class.defined_enums.each do |name, values|
|
130
142
|
if @row.include?(name)
|
@@ -151,8 +163,17 @@ module ActiveRecord
|
|
151
163
|
raise PrimaryKeyError.new(@label, association, value)
|
152
164
|
end
|
153
165
|
|
154
|
-
|
155
|
-
|
166
|
+
if fk_name.is_a?(Array)
|
167
|
+
composite_key = ActiveRecord::FixtureSet.composite_identify(value, fk_name)
|
168
|
+
composite_key.each do |column, value|
|
169
|
+
next if column_defined?(column)
|
170
|
+
|
171
|
+
@row[column] = value
|
172
|
+
end
|
173
|
+
else
|
174
|
+
fk_type = reflection_class.type_for_attribute(fk_name).type
|
175
|
+
@row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
|
176
|
+
end
|
156
177
|
end
|
157
178
|
when :has_many
|
158
179
|
if association.options[:through]
|