activerecord 8.0.3 → 8.1.0
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 +538 -512
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -0
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +0 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +405 -72
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -20
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -13
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +17 -15
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +81 -48
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +23 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +14 -9
- data/lib/active_record/core.rb +5 -4
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +53 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/encryptor.rb +12 -0
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +24 -8
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +2 -6
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +26 -16
- data/lib/active_record/model_schema.rb +36 -10
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +3 -7
- data/lib/active_record/railtie.rb +32 -3
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +15 -3
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +35 -0
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +20 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +9 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +40 -29
- data/lib/active_record/relation/where_clause.rb +1 -8
- data/lib/active_record/relation.rb +24 -12
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +41 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +25 -34
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +32 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +65 -3
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +14 -10
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -48,7 +48,7 @@ module ActiveRecord
|
|
|
48
48
|
# way of creating a namespace for tables in a shared database. By default, the prefix is the
|
|
49
49
|
# empty string.
|
|
50
50
|
#
|
|
51
|
-
# If you are
|
|
51
|
+
# If you are organizing your models within modules you can add a prefix to the models within
|
|
52
52
|
# a namespace by defining a singleton method in the parent module called table_name_prefix which
|
|
53
53
|
# returns your chosen prefix.
|
|
54
54
|
|
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
|
65
65
|
# Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
|
|
66
66
|
# "people_basecamp"). By default, the suffix is the empty string.
|
|
67
67
|
#
|
|
68
|
-
# If you are
|
|
68
|
+
# If you are organizing your models within modules, you can add a suffix to the models within
|
|
69
69
|
# a namespace by defining a singleton method in the parent module called table_name_suffix which
|
|
70
70
|
# returns your chosen suffix.
|
|
71
71
|
|
|
@@ -113,17 +113,19 @@ module ActiveRecord
|
|
|
113
113
|
# :singleton-method: implicit_order_column
|
|
114
114
|
# :call-seq: implicit_order_column
|
|
115
115
|
#
|
|
116
|
-
# The name of the column records are ordered by if no explicit order clause
|
|
116
|
+
# The name of the column(s) records are ordered by if no explicit order clause
|
|
117
117
|
# is used during an ordered finder call. If not set the primary key is used.
|
|
118
118
|
|
|
119
119
|
##
|
|
120
120
|
# :singleton-method: implicit_order_column=
|
|
121
121
|
# :call-seq: implicit_order_column=(column_name)
|
|
122
122
|
#
|
|
123
|
-
# Sets the column to sort records by when no explicit order clause is used
|
|
124
|
-
# during an ordered finder call. Useful
|
|
125
|
-
# auto-incrementing integer
|
|
126
|
-
#
|
|
123
|
+
# Sets the column(s) to sort records by when no explicit order clause is used
|
|
124
|
+
# during an ordered finder call. Useful for models where the primary key isn't an
|
|
125
|
+
# auto-incrementing integer (such as UUID).
|
|
126
|
+
#
|
|
127
|
+
# By default, records are subsorted by primary key to ensure deterministic results.
|
|
128
|
+
# To disable this subsort behavior, set `implicit_order_column` to `["column_name", nil]`.
|
|
127
129
|
|
|
128
130
|
##
|
|
129
131
|
# :singleton-method: immutable_strings_by_default=
|
|
@@ -179,6 +181,7 @@ module ActiveRecord
|
|
|
179
181
|
self.protected_environments = ["production"]
|
|
180
182
|
|
|
181
183
|
self.ignored_columns = [].freeze
|
|
184
|
+
self.only_columns = [].freeze
|
|
182
185
|
|
|
183
186
|
delegate :type_for_attribute, :column_for_attribute, to: :class
|
|
184
187
|
|
|
@@ -332,6 +335,12 @@ module ActiveRecord
|
|
|
332
335
|
@ignored_columns || superclass.ignored_columns
|
|
333
336
|
end
|
|
334
337
|
|
|
338
|
+
# The list of columns names the model should allow. Only columns are used to define
|
|
339
|
+
# attribute accessors, and are referenced in SQL queries.
|
|
340
|
+
def only_columns
|
|
341
|
+
@only_columns || superclass.only_columns
|
|
342
|
+
end
|
|
343
|
+
|
|
335
344
|
# Sets the columns names the model should ignore. Ignored columns won't have attribute
|
|
336
345
|
# accessors defined, and won't be referenced in SQL queries.
|
|
337
346
|
#
|
|
@@ -364,10 +373,17 @@ module ActiveRecord
|
|
|
364
373
|
# user = Project.create!(name: "First Project")
|
|
365
374
|
# user.category # => raises NoMethodError
|
|
366
375
|
def ignored_columns=(columns)
|
|
376
|
+
check_model_columns(@only_columns.present?)
|
|
367
377
|
reload_schema_from_cache
|
|
368
378
|
@ignored_columns = columns.map(&:to_s).freeze
|
|
369
379
|
end
|
|
370
380
|
|
|
381
|
+
def only_columns=(columns)
|
|
382
|
+
check_model_columns(@ignored_columns.present?)
|
|
383
|
+
reload_schema_from_cache
|
|
384
|
+
@only_columns = columns.map(&:to_s).freeze
|
|
385
|
+
end
|
|
386
|
+
|
|
371
387
|
def sequence_name
|
|
372
388
|
if base_class?
|
|
373
389
|
@sequence_name ||= reset_sequence_name
|
|
@@ -501,7 +517,7 @@ module ActiveRecord
|
|
|
501
517
|
# when just after creating a table you want to populate it with some default
|
|
502
518
|
# values, e.g.:
|
|
503
519
|
#
|
|
504
|
-
# class CreateJobLevels < ActiveRecord::Migration[8.
|
|
520
|
+
# class CreateJobLevels < ActiveRecord::Migration[8.1]
|
|
505
521
|
# def up
|
|
506
522
|
# create_table :job_levels do |t|
|
|
507
523
|
# t.integer :id
|
|
@@ -577,6 +593,7 @@ module ActiveRecord
|
|
|
577
593
|
child_class.reload_schema_from_cache(false)
|
|
578
594
|
child_class.class_eval do
|
|
579
595
|
@ignored_columns = nil
|
|
596
|
+
@only_columns = nil
|
|
580
597
|
end
|
|
581
598
|
end
|
|
582
599
|
|
|
@@ -590,7 +607,11 @@ module ActiveRecord
|
|
|
590
607
|
end
|
|
591
608
|
|
|
592
609
|
columns_hash = schema_cache.columns_hash(table_name)
|
|
593
|
-
|
|
610
|
+
if only_columns.present?
|
|
611
|
+
columns_hash = columns_hash.slice(*only_columns)
|
|
612
|
+
elsif ignored_columns.present?
|
|
613
|
+
columns_hash = columns_hash.except(*ignored_columns)
|
|
614
|
+
end
|
|
594
615
|
@columns_hash = columns_hash.freeze
|
|
595
616
|
|
|
596
617
|
_default_attributes # Precompute to cache DB-dependent attribute types
|
|
@@ -620,7 +641,8 @@ module ActiveRecord
|
|
|
620
641
|
end
|
|
621
642
|
|
|
622
643
|
def type_for_column(connection, column)
|
|
623
|
-
|
|
644
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
|
645
|
+
type = column.fetch_cast_type(connection)
|
|
624
646
|
|
|
625
647
|
if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
|
|
626
648
|
type = type.to_immutable_string
|
|
@@ -628,6 +650,10 @@ module ActiveRecord
|
|
|
628
650
|
|
|
629
651
|
type
|
|
630
652
|
end
|
|
653
|
+
|
|
654
|
+
def check_model_columns(columns_present)
|
|
655
|
+
raise ArgumentError, "You can not use both only_columns and ignored_columns in the same model." if columns_present
|
|
656
|
+
end
|
|
631
657
|
end
|
|
632
658
|
end
|
|
633
659
|
end
|
|
@@ -387,6 +387,8 @@ module ActiveRecord
|
|
|
387
387
|
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
|
|
388
388
|
silence_redefinition_of_method :#{association_name}_attributes=
|
|
389
389
|
def #{association_name}_attributes=(attributes)
|
|
390
|
+
association = association(:#{association_name})
|
|
391
|
+
deprecated_associations_api_guard(association, __method__)
|
|
390
392
|
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
|
391
393
|
end
|
|
392
394
|
eoruby
|
|
@@ -492,6 +492,7 @@ module ActiveRecord
|
|
|
492
492
|
becoming.instance_variable_set(:@attributes, @attributes)
|
|
493
493
|
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
|
494
494
|
becoming.instance_variable_set(:@new_record, new_record?)
|
|
495
|
+
becoming.instance_variable_set(:@previously_new_record, previously_new_record?)
|
|
495
496
|
becoming.instance_variable_set(:@destroyed, destroyed?)
|
|
496
497
|
becoming.errors.copy!(errors)
|
|
497
498
|
end
|
|
@@ -581,8 +582,8 @@ module ActiveRecord
|
|
|
581
582
|
end
|
|
582
583
|
|
|
583
584
|
# Equivalent to <code>update_columns(name => value)</code>.
|
|
584
|
-
def update_column(name, value)
|
|
585
|
-
update_columns(name => value)
|
|
585
|
+
def update_column(name, value, touch: nil)
|
|
586
|
+
update_columns(name => value, touch: touch)
|
|
586
587
|
end
|
|
587
588
|
|
|
588
589
|
# Updates the attributes directly in the database issuing an UPDATE SQL
|
|
@@ -596,11 +597,25 @@ module ActiveRecord
|
|
|
596
597
|
#
|
|
597
598
|
# * \Validations are skipped.
|
|
598
599
|
# * \Callbacks are skipped.
|
|
599
|
-
# * +updated_at+/+updated_on+ are
|
|
600
|
+
# * +updated_at+/+updated_on+ are updated if the +touch+ option is set to +true+.
|
|
600
601
|
# * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
|
|
601
602
|
#
|
|
602
603
|
# This method raises an ActiveRecord::ActiveRecordError when called on new
|
|
603
604
|
# objects, or when at least one of the attributes is marked as readonly.
|
|
605
|
+
#
|
|
606
|
+
# ==== Parameters
|
|
607
|
+
#
|
|
608
|
+
# * <tt>:touch</tt> option - Touch the timestamp columns when updating.
|
|
609
|
+
# * If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
|
610
|
+
#
|
|
611
|
+
# ==== Examples
|
|
612
|
+
#
|
|
613
|
+
# # Update a single attribute.
|
|
614
|
+
# user.update_columns(last_request_at: Time.current)
|
|
615
|
+
#
|
|
616
|
+
# # Update with touch option.
|
|
617
|
+
# user.update_columns(last_request_at: Time.current, touch: true)
|
|
618
|
+
|
|
604
619
|
def update_columns(attributes)
|
|
605
620
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
|
606
621
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
|
@@ -612,6 +627,15 @@ module ActiveRecord
|
|
|
612
627
|
verify_readonly_attribute(name) || name
|
|
613
628
|
end
|
|
614
629
|
|
|
630
|
+
touch = attributes.delete("touch")
|
|
631
|
+
if touch
|
|
632
|
+
names = touch if touch != true
|
|
633
|
+
names = Array.wrap(names)
|
|
634
|
+
options = names.extract_options!
|
|
635
|
+
touch_updates = self.class.touch_attributes_with_time(*names, **options)
|
|
636
|
+
attributes.with_defaults!(touch_updates) unless touch_updates.empty?
|
|
637
|
+
end
|
|
638
|
+
|
|
615
639
|
update_constraints = _query_constraints_hash
|
|
616
640
|
attributes = attributes.each_with_object({}) do |(k, v), h|
|
|
617
641
|
h[k] = @attributes.write_cast_value(k, v)
|
|
@@ -640,8 +664,15 @@ module ActiveRecord
|
|
|
640
664
|
# This means that any other modified attributes will still be dirty.
|
|
641
665
|
# Validations and callbacks are skipped. Supports the +touch+ option from
|
|
642
666
|
# +update_counters+, see that for more.
|
|
667
|
+
#
|
|
668
|
+
# This method raises an ActiveRecord::ActiveRecordError when called on new
|
|
669
|
+
# objects, or when at least one of the attributes is marked as readonly.
|
|
670
|
+
#
|
|
643
671
|
# Returns +self+.
|
|
644
672
|
def increment!(attribute, by = 1, touch: nil)
|
|
673
|
+
raise ActiveRecordError, "cannot update a new record" if new_record?
|
|
674
|
+
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
|
675
|
+
|
|
645
676
|
increment(attribute, by)
|
|
646
677
|
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
|
|
647
678
|
self.class.update_counters(id, attribute => change, touch: touch)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
# = Active Record Query Cache
|
|
5
5
|
class QueryCache
|
|
6
|
+
# ActiveRecord::Base extends this module, so these methods are available in models.
|
|
6
7
|
module ClassMethods
|
|
7
8
|
# Enable the query cache within the block if Active Record is configured.
|
|
8
9
|
# If it's not, it will execute the given block.
|
|
@@ -20,11 +21,15 @@ module ActiveRecord
|
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
24
|
+
# Runs the block with the query cache disabled.
|
|
25
|
+
#
|
|
26
|
+
# If the query cache was enabled before the block was executed, it is
|
|
27
|
+
# enabled again after it.
|
|
25
28
|
#
|
|
26
|
-
# Set <tt>dirties: false</tt> to prevent query caches on all connections
|
|
27
|
-
# (By default, write operations
|
|
29
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections
|
|
30
|
+
# from being cleared by write operations. (By default, write operations
|
|
31
|
+
# dirty all connections' query caches in case they are replicas whose
|
|
32
|
+
# cache would now be outdated.)
|
|
28
33
|
def uncached(dirties: true, &block)
|
|
29
34
|
if connected? || !configurations.empty?
|
|
30
35
|
connection_pool.disable_query_cache(dirties: dirties, &block)
|
|
@@ -34,22 +39,24 @@ module ActiveRecord
|
|
|
34
39
|
end
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
module ExecutorHooks # :nodoc:
|
|
43
|
+
def self.run
|
|
44
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
|
|
45
|
+
next if pool.db_config&.query_cache == false
|
|
46
|
+
pool.enable_query_cache!
|
|
47
|
+
end
|
|
41
48
|
end
|
|
42
|
-
end
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
def self.complete(pools)
|
|
51
|
+
pools.each do |pool|
|
|
52
|
+
pool.disable_query_cache!
|
|
53
|
+
pool.clear_query_cache
|
|
54
|
+
end
|
|
48
55
|
end
|
|
49
56
|
end
|
|
50
57
|
|
|
51
|
-
def self.install_executor_hooks(executor = ActiveSupport::Executor)
|
|
52
|
-
executor.register_hook(
|
|
58
|
+
def self.install_executor_hooks(executor = ActiveSupport::Executor) # :nodoc:
|
|
59
|
+
executor.register_hook(ExecutorHooks)
|
|
53
60
|
end
|
|
54
61
|
end
|
|
55
62
|
end
|
|
@@ -69,7 +69,7 @@ module ActiveRecord
|
|
|
69
69
|
#
|
|
70
70
|
# Tag comments can be prepended to the query:
|
|
71
71
|
#
|
|
72
|
-
#
|
|
72
|
+
# config.active_record.query_log_tags_prepend_comment = true
|
|
73
73
|
#
|
|
74
74
|
# For applications where the content will not change during the lifetime of
|
|
75
75
|
# the request or job execution, the tags can be cached for reuse in every query:
|
|
@@ -157,11 +157,7 @@ module ActiveRecord
|
|
|
157
157
|
end
|
|
158
158
|
|
|
159
159
|
def query_source_location # :nodoc:
|
|
160
|
-
|
|
161
|
-
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
|
162
|
-
return frame if frame
|
|
163
|
-
end
|
|
164
|
-
nil
|
|
160
|
+
LogSubscriber.backtrace_cleaner.first_clean_frame
|
|
165
161
|
end
|
|
166
162
|
|
|
167
163
|
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
|
@@ -215,7 +211,7 @@ module ActiveRecord
|
|
|
215
211
|
end
|
|
216
212
|
|
|
217
213
|
def escape_sql_comment(content)
|
|
218
|
-
# Sanitize a string to appear within
|
|
214
|
+
# Sanitize a string to appear within an SQL comment
|
|
219
215
|
# For compatibility, this also surrounding "/*+", "/*", and "*/"
|
|
220
216
|
# characters, possibly with single surrounding space.
|
|
221
217
|
# Then follows that by replacing any internal "*/" or "/ *" with
|
|
@@ -35,9 +35,12 @@ module ActiveRecord
|
|
|
35
35
|
config.active_record.query_log_tags = [ :application ]
|
|
36
36
|
config.active_record.query_log_tags_format = :legacy
|
|
37
37
|
config.active_record.cache_query_log_tags = false
|
|
38
|
+
config.active_record.query_log_tags_prepend_comment = false
|
|
38
39
|
config.active_record.raise_on_assign_to_attr_readonly = false
|
|
39
40
|
config.active_record.belongs_to_required_validates_foreign_key = true
|
|
40
41
|
config.active_record.generate_secure_token_on = :create
|
|
42
|
+
config.active_record.use_legacy_signed_id_verifier = :generate_and_verify
|
|
43
|
+
config.active_record.deprecated_associations_options = { mode: :warn, backtrace: false }
|
|
41
44
|
|
|
42
45
|
config.active_record.queues = ActiveSupport::InheritableOptions.new
|
|
43
46
|
|
|
@@ -229,10 +232,12 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
229
232
|
:query_log_tags,
|
|
230
233
|
:query_log_tags_format,
|
|
231
234
|
:cache_query_log_tags,
|
|
235
|
+
:query_log_tags_prepend_comment,
|
|
232
236
|
:sqlite3_adapter_strict_strings_by_default,
|
|
233
237
|
:check_schema_cache_dump_version,
|
|
234
238
|
:use_schema_cache_dump,
|
|
235
239
|
:postgresql_adapter_decode_dates,
|
|
240
|
+
:use_legacy_signed_id_verifier,
|
|
236
241
|
)
|
|
237
242
|
|
|
238
243
|
configs_used_in_other_initializers.each do |k, v|
|
|
@@ -274,6 +279,13 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
274
279
|
end
|
|
275
280
|
end
|
|
276
281
|
|
|
282
|
+
initializer "active_record.job_checkpoints" do
|
|
283
|
+
require "active_record/railties/job_checkpoints"
|
|
284
|
+
ActiveSupport.on_load(:active_job_continuable) do
|
|
285
|
+
prepend ActiveRecord::Railties::JobCheckpoints
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
277
289
|
initializer "active_record.set_reloader_hooks" do
|
|
278
290
|
ActiveSupport.on_load(:active_record) do
|
|
279
291
|
ActiveSupport::Reloader.before_class_unload do
|
|
@@ -319,9 +331,22 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
319
331
|
end
|
|
320
332
|
end
|
|
321
333
|
|
|
322
|
-
initializer "active_record.
|
|
323
|
-
|
|
324
|
-
|
|
334
|
+
initializer "active_record.filter_attributes_as_log_parameters" do |app|
|
|
335
|
+
ActiveRecord::FilterAttributeHandler.new(app).enable
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
initializer "active_record.configure_message_verifiers" do |app|
|
|
339
|
+
ActiveRecord.message_verifiers = app.message_verifiers
|
|
340
|
+
|
|
341
|
+
use_legacy_signed_id_verifier = app.config.active_record.use_legacy_signed_id_verifier
|
|
342
|
+
legacy_options = { digest: "SHA256", serializer: JSON, url_safe: true }
|
|
343
|
+
|
|
344
|
+
if use_legacy_signed_id_verifier == :generate_and_verify
|
|
345
|
+
app.message_verifiers.prepend { |salt| legacy_options if salt == "active_record/signed_id" }
|
|
346
|
+
elsif use_legacy_signed_id_verifier == :verify
|
|
347
|
+
app.message_verifiers.rotate { |salt| legacy_options if salt == "active_record/signed_id" }
|
|
348
|
+
elsif use_legacy_signed_id_verifier
|
|
349
|
+
raise ArgumentError, "Unrecognized value for config.active_record.use_legacy_signed_id_verifier: #{use_legacy_signed_id_verifier.inspect}"
|
|
325
350
|
end
|
|
326
351
|
end
|
|
327
352
|
|
|
@@ -387,6 +412,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
387
412
|
if app.config.active_record.cache_query_log_tags
|
|
388
413
|
ActiveRecord::QueryLogs.cache_query_log_tags = true
|
|
389
414
|
end
|
|
415
|
+
|
|
416
|
+
if app.config.active_record.query_log_tags_prepend_comment
|
|
417
|
+
ActiveRecord::QueryLogs.prepend_comment = true
|
|
418
|
+
end
|
|
390
419
|
end
|
|
391
420
|
end
|
|
392
421
|
end
|
|
@@ -41,11 +41,14 @@ module ActiveRecord
|
|
|
41
41
|
|
|
42
42
|
def cleanup_view_runtime
|
|
43
43
|
if logger && logger.info?
|
|
44
|
-
|
|
44
|
+
runtime_stats = ActiveRecord::RuntimeRegistry.stats
|
|
45
|
+
db_rt_before_render = runtime_stats.reset_runtimes
|
|
45
46
|
self.db_runtime = (db_runtime || 0) + db_rt_before_render
|
|
47
|
+
|
|
46
48
|
runtime = super
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
|
|
50
|
+
queries_rt = runtime_stats.sql_runtime - runtime_stats.async_sql_runtime
|
|
51
|
+
db_rt_after_render = runtime_stats.reset_runtimes
|
|
49
52
|
self.db_runtime += db_rt_after_render
|
|
50
53
|
runtime - queries_rt
|
|
51
54
|
else
|
|
@@ -56,9 +59,11 @@ module ActiveRecord
|
|
|
56
59
|
def append_info_to_payload(payload)
|
|
57
60
|
super
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
payload[:
|
|
61
|
-
payload[:
|
|
62
|
+
runtime_stats = ActiveRecord::RuntimeRegistry.stats
|
|
63
|
+
payload[:db_runtime] = (db_runtime || 0) + runtime_stats.sql_runtime
|
|
64
|
+
payload[:queries_count] = runtime_stats.queries_count
|
|
65
|
+
payload[:cached_queries_count] = runtime_stats.cached_queries_count
|
|
66
|
+
runtime_stats.reset
|
|
62
67
|
end
|
|
63
68
|
end
|
|
64
69
|
end
|
|
@@ -163,6 +163,18 @@ db_namespace = namespace :db do
|
|
|
163
163
|
desc "Resets your database using your migrations for the current environment"
|
|
164
164
|
task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
|
|
165
165
|
|
|
166
|
+
namespace :reset do
|
|
167
|
+
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
|
168
|
+
desc "Drop and recreate the #{name} database using migrations"
|
|
169
|
+
task name => :load_config do
|
|
170
|
+
db_namespace["drop:#{name}"].invoke
|
|
171
|
+
db_namespace["create:#{name}"].invoke
|
|
172
|
+
db_namespace["schema:dump:#{name}"].invoke
|
|
173
|
+
db_namespace["migrate:#{name}"].invoke
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
166
178
|
desc 'Run the "up" for a given migration VERSION.'
|
|
167
179
|
task up: :load_config do
|
|
168
180
|
ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:up")
|
|
@@ -333,7 +345,7 @@ db_namespace = namespace :db do
|
|
|
333
345
|
pending_migrations << pool.migration_context.open.pending_migrations
|
|
334
346
|
end
|
|
335
347
|
|
|
336
|
-
pending_migrations
|
|
348
|
+
pending_migrations.flatten!
|
|
337
349
|
|
|
338
350
|
if pending_migrations.any?
|
|
339
351
|
puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
|
|
@@ -459,7 +471,7 @@ db_namespace = namespace :db do
|
|
|
459
471
|
|
|
460
472
|
namespace :dump do
|
|
461
473
|
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
|
462
|
-
desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on
|
|
474
|
+
desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) for #{name} database"
|
|
463
475
|
task name => :load_config do
|
|
464
476
|
ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
|
|
465
477
|
db_config = pool.db_config
|
|
@@ -474,7 +486,7 @@ db_namespace = namespace :db do
|
|
|
474
486
|
namespace :load do
|
|
475
487
|
ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
|
|
476
488
|
desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) into the #{name} database"
|
|
477
|
-
task name =>
|
|
489
|
+
task name => [:load_config, :check_protected_environments] do
|
|
478
490
|
ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
|
|
479
491
|
db_config = pool.db_config
|
|
480
492
|
ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Railties # :nodoc:
|
|
5
|
+
module JobCheckpoints # :nodoc:
|
|
6
|
+
def checkpoint!
|
|
7
|
+
if ActiveRecord.all_open_transactions.any?
|
|
8
|
+
raise ActiveJob::Continuation::CheckpointError, "Cannot checkpoint job with open transactions"
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -5,19 +5,18 @@ require "active_record/runtime_registry"
|
|
|
5
5
|
module ActiveRecord
|
|
6
6
|
module Railties # :nodoc:
|
|
7
7
|
module JobRuntime # :nodoc:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
result
|
|
16
|
-
end
|
|
17
|
-
else
|
|
18
|
-
super
|
|
8
|
+
def instrument(operation, payload = {}, &block) # :nodoc:
|
|
9
|
+
if operation == :perform && block
|
|
10
|
+
super(operation, payload) do
|
|
11
|
+
db_runtime_before_perform = ActiveRecord::RuntimeRegistry.stats.sql_runtime
|
|
12
|
+
result = block.call
|
|
13
|
+
payload[:db_runtime] = ActiveRecord::RuntimeRegistry.stats.sql_runtime - db_runtime_before_perform
|
|
14
|
+
result
|
|
19
15
|
end
|
|
16
|
+
else
|
|
17
|
+
super
|
|
20
18
|
end
|
|
19
|
+
end
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
end
|
|
@@ -516,6 +516,8 @@ module ActiveRecord
|
|
|
516
516
|
|
|
517
517
|
def initialize(name, scope, options, active_record)
|
|
518
518
|
super
|
|
519
|
+
|
|
520
|
+
@validated = false
|
|
519
521
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
|
520
522
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
|
521
523
|
@join_table = nil
|
|
@@ -534,6 +536,8 @@ module ActiveRecord
|
|
|
534
536
|
options[:query_constraints] = options.delete(:foreign_key)
|
|
535
537
|
end
|
|
536
538
|
|
|
539
|
+
@deprecated = !!options[:deprecated]
|
|
540
|
+
|
|
537
541
|
ensure_option_not_given_as_class!(:class_name)
|
|
538
542
|
end
|
|
539
543
|
|
|
@@ -616,6 +620,8 @@ module ActiveRecord
|
|
|
616
620
|
end
|
|
617
621
|
|
|
618
622
|
def check_validity!
|
|
623
|
+
return if @validated
|
|
624
|
+
|
|
619
625
|
check_validity_of_inverse!
|
|
620
626
|
|
|
621
627
|
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
|
@@ -625,6 +631,8 @@ module ActiveRecord
|
|
|
625
631
|
raise CompositePrimaryKeyMismatchError.new(self)
|
|
626
632
|
end
|
|
627
633
|
end
|
|
634
|
+
|
|
635
|
+
@validated = true
|
|
628
636
|
end
|
|
629
637
|
|
|
630
638
|
def check_eager_loadable!
|
|
@@ -742,6 +750,10 @@ module ActiveRecord
|
|
|
742
750
|
Array(options[:extend])
|
|
743
751
|
end
|
|
744
752
|
|
|
753
|
+
def deprecated?
|
|
754
|
+
@deprecated
|
|
755
|
+
end
|
|
756
|
+
|
|
745
757
|
private
|
|
746
758
|
# Attempts to find the inverse association name automatically.
|
|
747
759
|
# If it cannot find a suitable inverse association name, it returns
|
|
@@ -975,6 +987,8 @@ module ActiveRecord
|
|
|
975
987
|
|
|
976
988
|
def initialize(delegate_reflection)
|
|
977
989
|
super()
|
|
990
|
+
|
|
991
|
+
@validated = false
|
|
978
992
|
@delegate_reflection = delegate_reflection
|
|
979
993
|
@klass = delegate_reflection.options[:anonymous_class]
|
|
980
994
|
@source_reflection_name = delegate_reflection.options[:source]
|
|
@@ -1138,6 +1152,8 @@ module ActiveRecord
|
|
|
1138
1152
|
end
|
|
1139
1153
|
|
|
1140
1154
|
def check_validity!
|
|
1155
|
+
return if @validated
|
|
1156
|
+
|
|
1141
1157
|
if through_reflection.nil?
|
|
1142
1158
|
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
|
1143
1159
|
end
|
|
@@ -1175,6 +1191,8 @@ module ActiveRecord
|
|
|
1175
1191
|
end
|
|
1176
1192
|
|
|
1177
1193
|
check_validity_of_inverse!
|
|
1194
|
+
|
|
1195
|
+
@validated = true
|
|
1178
1196
|
end
|
|
1179
1197
|
|
|
1180
1198
|
def constraints
|
|
@@ -1195,6 +1213,10 @@ module ActiveRecord
|
|
|
1195
1213
|
collect_join_reflections(seed + [self])
|
|
1196
1214
|
end
|
|
1197
1215
|
|
|
1216
|
+
def deprecated_nested_reflections
|
|
1217
|
+
@deprecated_nested_reflections ||= collect_deprecated_nested_reflections
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1198
1220
|
protected
|
|
1199
1221
|
def actual_source_reflection # FIXME: this is a horrible name
|
|
1200
1222
|
source_reflection.actual_source_reflection
|
|
@@ -1219,6 +1241,19 @@ module ActiveRecord
|
|
|
1219
1241
|
options[:source_type] || source_reflection.class_name
|
|
1220
1242
|
end
|
|
1221
1243
|
|
|
1244
|
+
def collect_deprecated_nested_reflections
|
|
1245
|
+
result = []
|
|
1246
|
+
[through_reflection, source_reflection].each do |reflection|
|
|
1247
|
+
result << reflection if reflection.deprecated?
|
|
1248
|
+
# Both the through and the source reflections could be through
|
|
1249
|
+
# themselves. Nesting can go an arbitrary number of levels down.
|
|
1250
|
+
if reflection.through_reflection?
|
|
1251
|
+
result.concat(reflection.deprecated_nested_reflections)
|
|
1252
|
+
end
|
|
1253
|
+
end
|
|
1254
|
+
result
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1222
1257
|
delegate_methods = AssociationReflection.public_instance_methods -
|
|
1223
1258
|
public_instance_methods
|
|
1224
1259
|
|