activerecord 7.0.8 → 7.1.3.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -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 +20 -4
- 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 +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- 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 +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- 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 +313 -217
- 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 +52 -34
- 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 +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- 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 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -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 +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- 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 +52 -39
- 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 +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- 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 +9 -4
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- 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 +112 -28
- data/lib/active_record/errors.rb +112 -18
- 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 +135 -71
- data/lib/active_record/future_result.rb +31 -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 +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- 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 +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- 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 +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- 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/polymorphic_array_value.rb +4 -6
- 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 +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- 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 +1 -9
- 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 +48 -12
- 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,70 @@ 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 inherited(base) # :nodoc:
|
120
|
-
base.defined_enums = defined_enums.deep_dup
|
121
|
-
super
|
122
|
-
end
|
123
|
-
|
124
170
|
class EnumType < Type::Value # :nodoc:
|
125
171
|
delegate :type, to: :subtype
|
126
172
|
|
127
|
-
def initialize(name, mapping, subtype)
|
173
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
128
174
|
@name = name
|
129
175
|
@mapping = mapping
|
130
176
|
@subtype = subtype
|
177
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
131
178
|
end
|
132
179
|
|
133
180
|
def cast(value)
|
@@ -153,6 +200,8 @@ module ActiveRecord
|
|
153
200
|
end
|
154
201
|
|
155
202
|
def assert_valid_value(value)
|
203
|
+
return unless @_raise_on_invalid_values
|
204
|
+
|
156
205
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
157
206
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
158
207
|
end
|
@@ -170,14 +219,19 @@ module ActiveRecord
|
|
170
219
|
return _enum(name, values, **options)
|
171
220
|
end
|
172
221
|
|
173
|
-
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
|
222
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
174
223
|
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
175
224
|
|
176
225
|
definitions.each { |name, values| _enum(name, values, **options) }
|
177
226
|
end
|
178
227
|
|
179
228
|
private
|
180
|
-
def
|
229
|
+
def inherited(base)
|
230
|
+
base.defined_enums = defined_enums.deep_dup
|
231
|
+
super
|
232
|
+
end
|
233
|
+
|
234
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
181
235
|
assert_valid_enum_definition_values(values)
|
182
236
|
# statuses = { }
|
183
237
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
@@ -192,8 +246,14 @@ module ActiveRecord
|
|
192
246
|
detect_enum_conflict!(name, "#{name}=")
|
193
247
|
|
194
248
|
attribute(name, **options) do |subtype|
|
249
|
+
if subtype == ActiveModel::Type.default_value
|
250
|
+
raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
|
251
|
+
" backed by a database column or declared with an explicit type" \
|
252
|
+
" via `attribute`."
|
253
|
+
end
|
254
|
+
|
195
255
|
subtype = subtype.subtype if EnumType === subtype
|
196
|
-
EnumType.new(name, enum_values, subtype)
|
256
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
197
257
|
end
|
198
258
|
|
199
259
|
value_method_names = []
|
@@ -213,18 +273,24 @@ module ActiveRecord
|
|
213
273
|
|
214
274
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
215
275
|
value_method_names << value_method_name
|
216
|
-
define_enum_methods(name, value_method_name, value, scopes)
|
276
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
217
277
|
|
218
278
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
219
279
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
220
280
|
|
221
281
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
222
282
|
value_method_names << value_method_alias
|
223
|
-
define_enum_methods(name, value_method_alias, value, scopes)
|
283
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
224
284
|
end
|
225
285
|
end
|
226
286
|
end
|
227
287
|
detect_negative_enum_conditions!(value_method_names) if scopes
|
288
|
+
|
289
|
+
if validate
|
290
|
+
validate = {} unless Hash === validate
|
291
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
292
|
+
end
|
293
|
+
|
228
294
|
enum_values.freeze
|
229
295
|
end
|
230
296
|
|
@@ -236,21 +302,23 @@ module ActiveRecord
|
|
236
302
|
private
|
237
303
|
attr_reader :klass
|
238
304
|
|
239
|
-
def define_enum_methods(name, value_method_name, value, scopes)
|
240
|
-
|
241
|
-
|
242
|
-
|
305
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
306
|
+
if instance_methods
|
307
|
+
# def active?() status_for_database == 0 end
|
308
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
309
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
243
310
|
|
244
|
-
|
245
|
-
|
246
|
-
|
311
|
+
# def active!() update!(status: 0) end
|
312
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
313
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
314
|
+
end
|
247
315
|
|
248
|
-
# scope :active, -> { where(status: 0) }
|
249
|
-
# scope :not_active, -> { where.not(status: 0) }
|
250
316
|
if scopes
|
317
|
+
# scope :active, -> { where(status: 0) }
|
251
318
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
252
319
|
klass.scope value_method_name, -> { where(name => value) }
|
253
320
|
|
321
|
+
# scope :not_active, -> { where.not(status: 0) }
|
254
322
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
255
323
|
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
256
324
|
end
|
@@ -267,15 +335,29 @@ module ActiveRecord
|
|
267
335
|
end
|
268
336
|
|
269
337
|
def assert_valid_enum_definition_values(values)
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
end
|
338
|
+
case values
|
339
|
+
when Hash
|
340
|
+
if values.empty?
|
341
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
342
|
+
end
|
276
343
|
|
277
|
-
|
278
|
-
|
344
|
+
if values.keys.any?(&:blank?)
|
345
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
346
|
+
end
|
347
|
+
when Array
|
348
|
+
if values.empty?
|
349
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
350
|
+
end
|
351
|
+
|
352
|
+
unless values.all?(Symbol) || values.all?(String)
|
353
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
354
|
+
end
|
355
|
+
|
356
|
+
if values.any?(&:blank?)
|
357
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
358
|
+
end
|
359
|
+
else
|
360
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
279
361
|
end
|
280
362
|
end
|
281
363
|
|
@@ -290,6 +372,8 @@ module ActiveRecord
|
|
290
372
|
raise_conflict_error(enum_name, method_name, type: "class")
|
291
373
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
292
374
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
375
|
+
elsif klass_method && method_name.to_sym == :id
|
376
|
+
raise_conflict_error(enum_name, method_name)
|
293
377
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
294
378
|
raise_conflict_error(enum_name, method_name)
|
295
379
|
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,21 +311,22 @@ 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.
|
253
322
|
|
254
|
-
To resolve this
|
323
|
+
To resolve this error:
|
255
324
|
|
256
|
-
- Did you create the database
|
257
|
-
- Has the database name changed? Check your database.yml config has the correct database name.
|
325
|
+
- Did you not create the database, or did you delete it? To create the database, run:
|
258
326
|
|
259
|
-
|
327
|
+
bin/rails db:create
|
328
|
+
|
329
|
+
- Has the database name changed? Verify that config/database.yml contains the correct database name.
|
260
330
|
MSG
|
261
331
|
end
|
262
332
|
end
|
@@ -407,13 +477,26 @@ module ActiveRecord
|
|
407
477
|
# * You are joining an existing open transaction
|
408
478
|
# * You are creating a nested (savepoint) transaction
|
409
479
|
#
|
410
|
-
# The mysql2 and postgresql adapters support setting the transaction isolation level.
|
480
|
+
# The mysql2, trilogy, and postgresql adapters support setting the transaction isolation level.
|
411
481
|
class TransactionIsolationError < ActiveRecordError
|
412
482
|
end
|
413
483
|
|
414
484
|
# TransactionRollbackError will be raised when a transaction is rolled
|
415
485
|
# back by the database due to a serialization failure or a deadlock.
|
416
486
|
#
|
487
|
+
# These exceptions should not be generally rescued in nested transaction
|
488
|
+
# blocks, because they have side-effects in the actual enclosing transaction
|
489
|
+
# and internal Active Record state. They can be rescued if you are above the
|
490
|
+
# root transaction block, though.
|
491
|
+
#
|
492
|
+
# In that case, beware of transactional tests, however, because they run test
|
493
|
+
# cases in their own umbrella transaction. If you absolutely need to handle
|
494
|
+
# these exceptions in tests please consider disabling transactional tests in
|
495
|
+
# the affected test class (<tt>self.use_transactional_tests = false</tt>).
|
496
|
+
#
|
497
|
+
# Due to the aforementioned side-effects, this exception should not be raised
|
498
|
+
# manually by users.
|
499
|
+
#
|
417
500
|
# See the following:
|
418
501
|
#
|
419
502
|
# * https://www.postgresql.org/docs/current/static/transaction-iso.html
|
@@ -428,11 +511,17 @@ module ActiveRecord
|
|
428
511
|
|
429
512
|
# SerializationFailure will be raised when a transaction is rolled
|
430
513
|
# back by the database due to a serialization failure.
|
514
|
+
#
|
515
|
+
# This is a subclass of TransactionRollbackError, please make sure to check
|
516
|
+
# its documentation to be aware of its caveats.
|
431
517
|
class SerializationFailure < TransactionRollbackError
|
432
518
|
end
|
433
519
|
|
434
520
|
# Deadlocked will be raised when a transaction is rolled
|
435
521
|
# back by the database when a deadlock is encountered.
|
522
|
+
#
|
523
|
+
# This is a subclass of TransactionRollbackError, please make sure to check
|
524
|
+
# its documentation to be aware of its caveats.
|
436
525
|
class Deadlocked < TransactionRollbackError
|
437
526
|
end
|
438
527
|
|
@@ -461,6 +550,11 @@ module ActiveRecord
|
|
461
550
|
class AdapterTimeout < QueryAborted
|
462
551
|
end
|
463
552
|
|
553
|
+
# ConnectionFailed will be raised when the network connection to the
|
554
|
+
# database fails while sending a query or waiting for its result.
|
555
|
+
class ConnectionFailed < QueryAborted
|
556
|
+
end
|
557
|
+
|
464
558
|
# UnknownAttributeReference is raised when an unknown and potentially unsafe
|
465
559
|
# value is passed to a query method. For example, passing a non column name
|
466
560
|
# 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]
|