activerecord 7.2.3.1 → 8.0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +475 -946
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -96
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +23 -1
- data/lib/active_record/core.rb +29 -14
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +16 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +16 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +9 -22
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +32 -12
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +38 -42
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +2 -26
- data/lib/active_record/railties/databases.rake +11 -35
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +40 -39
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +105 -61
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +1 -1
- data/lib/active_record/relation.rb +80 -62
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +14 -14
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +69 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +5 -6
- data/lib/active_record/type/serialized.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +21 -48
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/table.rb +3 -7
- metadata +10 -11
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -248,18 +248,16 @@ module ActiveRecord
|
|
|
248
248
|
|
|
249
249
|
im = Arel::InsertManager.new(arel_table)
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
im.insert(values.transform_keys { |name| arel_table[name] })
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
connection.insert(
|
|
259
|
-
im, "#{self} Create", primary_key || false, primary_key_value,
|
|
260
|
-
returning: returning
|
|
261
|
-
)
|
|
251
|
+
if values.empty?
|
|
252
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
|
253
|
+
else
|
|
254
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
|
262
255
|
end
|
|
256
|
+
|
|
257
|
+
connection.insert(
|
|
258
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
|
259
|
+
returning: returning
|
|
260
|
+
)
|
|
263
261
|
end
|
|
264
262
|
|
|
265
263
|
def _update_record(values, constraints) # :nodoc:
|
|
@@ -812,159 +810,159 @@ module ActiveRecord
|
|
|
812
810
|
end
|
|
813
811
|
end
|
|
814
812
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
813
|
+
private
|
|
814
|
+
def init_internals
|
|
815
|
+
super
|
|
816
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
|
817
|
+
@previously_new_record = false
|
|
818
|
+
end
|
|
821
819
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
820
|
+
def strict_loaded_associations
|
|
821
|
+
@association_cache.find_all do |_, assoc|
|
|
822
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
|
823
|
+
end.map(&:first)
|
|
824
|
+
end
|
|
827
825
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
826
|
+
def _find_record(options)
|
|
827
|
+
all_queries = options ? options[:all_queries] : nil
|
|
828
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
|
831
829
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
830
|
+
if options && options[:lock]
|
|
831
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
|
832
|
+
else
|
|
833
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
|
834
|
+
end
|
|
836
835
|
end
|
|
837
|
-
end
|
|
838
836
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
837
|
+
def _in_memory_query_constraints_hash
|
|
838
|
+
if self.class.query_constraints_list.nil?
|
|
839
|
+
{ @primary_key => id }
|
|
840
|
+
else
|
|
841
|
+
self.class.query_constraints_list.index_with do |column_name|
|
|
842
|
+
attribute(column_name)
|
|
843
|
+
end
|
|
845
844
|
end
|
|
846
845
|
end
|
|
847
|
-
end
|
|
848
846
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
847
|
+
def apply_scoping?(options)
|
|
848
|
+
!(options && options[:unscoped]) &&
|
|
849
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
|
850
|
+
end
|
|
853
851
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
852
|
+
def _query_constraints_hash
|
|
853
|
+
if self.class.query_constraints_list.nil?
|
|
854
|
+
{ @primary_key => id_in_database }
|
|
855
|
+
else
|
|
856
|
+
self.class.query_constraints_list.index_with do |column_name|
|
|
857
|
+
attribute_in_database(column_name)
|
|
858
|
+
end
|
|
860
859
|
end
|
|
861
860
|
end
|
|
862
|
-
end
|
|
863
861
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
def destroy_row
|
|
869
|
-
_delete_row
|
|
870
|
-
end
|
|
871
|
-
|
|
872
|
-
def _delete_row
|
|
873
|
-
self.class._delete_record(_query_constraints_hash)
|
|
874
|
-
end
|
|
862
|
+
# A hook to be overridden by association modules.
|
|
863
|
+
def destroy_associations
|
|
864
|
+
end
|
|
875
865
|
|
|
876
|
-
|
|
877
|
-
|
|
866
|
+
def destroy_row
|
|
867
|
+
_delete_row
|
|
868
|
+
end
|
|
878
869
|
|
|
879
|
-
|
|
880
|
-
|
|
870
|
+
def _delete_row
|
|
871
|
+
self.class._delete_record(_query_constraints_hash)
|
|
881
872
|
end
|
|
882
873
|
|
|
883
|
-
|
|
884
|
-
|
|
874
|
+
def _touch_row(attribute_names, time)
|
|
875
|
+
time ||= current_time_from_proper_timezone
|
|
885
876
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
_query_constraints_hash
|
|
890
|
-
)
|
|
891
|
-
end
|
|
877
|
+
attribute_names.each do |attr_name|
|
|
878
|
+
_write_attribute(attr_name, time)
|
|
879
|
+
end
|
|
892
880
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
return false if destroyed?
|
|
896
|
-
result = new_record? ? _create_record(&block) : _update_record(&block)
|
|
897
|
-
result != false
|
|
898
|
-
end
|
|
881
|
+
_update_row(attribute_names, "touch")
|
|
882
|
+
end
|
|
899
883
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
884
|
+
def _update_row(attribute_names, attempted_action = "update")
|
|
885
|
+
self.class._update_record(
|
|
886
|
+
attributes_with_values(attribute_names),
|
|
887
|
+
_query_constraints_hash
|
|
888
|
+
)
|
|
889
|
+
end
|
|
904
890
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
@_trigger_update_callback = affected_rows == 1
|
|
891
|
+
def create_or_update(**, &block)
|
|
892
|
+
_raise_readonly_record_error if readonly?
|
|
893
|
+
return false if destroyed?
|
|
894
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
|
895
|
+
result != false
|
|
911
896
|
end
|
|
912
897
|
|
|
913
|
-
|
|
898
|
+
# Updates the associated record with values matching those of the instance attributes.
|
|
899
|
+
# Returns the number of affected rows.
|
|
900
|
+
def _update_record(attribute_names = self.attribute_names)
|
|
901
|
+
attribute_names = attributes_for_update(attribute_names)
|
|
914
902
|
|
|
915
|
-
|
|
903
|
+
if attribute_names.empty?
|
|
904
|
+
affected_rows = 0
|
|
905
|
+
@_trigger_update_callback = true
|
|
906
|
+
else
|
|
907
|
+
affected_rows = _update_row(attribute_names)
|
|
908
|
+
@_trigger_update_callback = affected_rows == 1
|
|
909
|
+
end
|
|
916
910
|
|
|
917
|
-
|
|
918
|
-
end
|
|
911
|
+
@previously_new_record = false
|
|
919
912
|
|
|
920
|
-
|
|
921
|
-
# and returns its id.
|
|
922
|
-
def _create_record(attribute_names = self.attribute_names)
|
|
923
|
-
attribute_names = attributes_for_create(attribute_names)
|
|
913
|
+
yield(self) if block_given?
|
|
924
914
|
|
|
925
|
-
|
|
926
|
-
|
|
915
|
+
affected_rows
|
|
916
|
+
end
|
|
927
917
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
)
|
|
918
|
+
# Creates a record with values matching those of the instance attributes
|
|
919
|
+
# and returns its id.
|
|
920
|
+
def _create_record(attribute_names = self.attribute_names)
|
|
921
|
+
attribute_names = attributes_for_create(attribute_names)
|
|
933
922
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
end if returning_values
|
|
937
|
-
end
|
|
923
|
+
self.class.with_connection do |connection|
|
|
924
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
|
938
925
|
|
|
939
|
-
|
|
940
|
-
|
|
926
|
+
returning_values = self.class._insert_record(
|
|
927
|
+
connection,
|
|
928
|
+
attributes_with_values(attribute_names),
|
|
929
|
+
returning_columns
|
|
930
|
+
)
|
|
941
931
|
|
|
942
|
-
|
|
932
|
+
returning_columns.zip(returning_values).each do |column, value|
|
|
933
|
+
_write_attribute(column, type_for_attribute(column).deserialize(value)) if !_read_attribute(column)
|
|
934
|
+
end if returning_values
|
|
935
|
+
end
|
|
943
936
|
|
|
944
|
-
|
|
945
|
-
|
|
937
|
+
@new_record = false
|
|
938
|
+
@previously_new_record = true
|
|
946
939
|
|
|
947
|
-
|
|
948
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
|
949
|
-
end
|
|
940
|
+
yield(self) if block_given?
|
|
950
941
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
key = self.class.primary_key
|
|
954
|
-
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
|
955
|
-
ensure
|
|
956
|
-
@_association_destroy_exception = nil
|
|
957
|
-
end
|
|
942
|
+
id
|
|
943
|
+
end
|
|
958
944
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
945
|
+
def verify_readonly_attribute(name)
|
|
946
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
|
947
|
+
end
|
|
962
948
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
949
|
+
def _raise_record_not_destroyed
|
|
950
|
+
@_association_destroy_exception ||= nil
|
|
951
|
+
key = self.class.primary_key
|
|
952
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
|
953
|
+
ensure
|
|
954
|
+
@_association_destroy_exception = nil
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
def _raise_readonly_record_error
|
|
958
|
+
raise ReadOnlyRecord, "#{self.class} is marked as readonly"
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
def _raise_record_not_touched_error
|
|
962
|
+
raise ActiveRecordError, <<~MSG.squish
|
|
963
|
+
Cannot touch on a new or destroyed record object. Consider using
|
|
964
|
+
persisted?, new_record?, or destroyed? before touching.
|
|
965
|
+
MSG
|
|
966
|
+
end
|
|
969
967
|
end
|
|
970
968
|
end
|
|
@@ -76,14 +76,70 @@ module ActiveRecord
|
|
|
76
76
|
#
|
|
77
77
|
# config.active_record.cache_query_log_tags = true
|
|
78
78
|
module QueryLogs
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
class GetKeyHandler # :nodoc:
|
|
80
|
+
def initialize(name)
|
|
81
|
+
@name = name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def call(context)
|
|
85
|
+
context[@name]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class IdentityHandler # :nodoc:
|
|
90
|
+
def initialize(value)
|
|
91
|
+
@value = value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def call(_context)
|
|
95
|
+
@value
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class ZeroArityHandler # :nodoc:
|
|
100
|
+
def initialize(proc)
|
|
101
|
+
@proc = proc
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def call(_context)
|
|
105
|
+
@proc.call
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
@taggings = {}.freeze
|
|
110
|
+
@tags = [ :application ].freeze
|
|
111
|
+
@prepend_comment = false
|
|
112
|
+
@cache_query_log_tags = false
|
|
113
|
+
@tags_formatter = false
|
|
114
|
+
|
|
84
115
|
thread_mattr_accessor :cached_comment, instance_accessor: false
|
|
85
116
|
|
|
86
117
|
class << self
|
|
118
|
+
attr_reader :tags, :taggings, :tags_formatter # :nodoc:
|
|
119
|
+
attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
|
|
120
|
+
|
|
121
|
+
def taggings=(taggings) # :nodoc:
|
|
122
|
+
@taggings = taggings.freeze
|
|
123
|
+
@handlers = rebuild_handlers
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def tags=(tags) # :nodoc:
|
|
127
|
+
@tags = tags.freeze
|
|
128
|
+
@handlers = rebuild_handlers
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def tags_formatter=(format) # :nodoc:
|
|
132
|
+
@formatter = case format
|
|
133
|
+
when :legacy
|
|
134
|
+
LegacyFormatter
|
|
135
|
+
when :sqlcommenter
|
|
136
|
+
SQLCommenter
|
|
137
|
+
else
|
|
138
|
+
raise ArgumentError, "Formatter is unsupported: #{format}"
|
|
139
|
+
end
|
|
140
|
+
@tags_formatter = format
|
|
141
|
+
end
|
|
142
|
+
|
|
87
143
|
def call(sql, connection) # :nodoc:
|
|
88
144
|
comment = self.comment(connection)
|
|
89
145
|
|
|
@@ -100,36 +156,46 @@ module ActiveRecord
|
|
|
100
156
|
self.cached_comment = nil
|
|
101
157
|
end
|
|
102
158
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
when :legacy
|
|
108
|
-
LegacyFormatter.new
|
|
109
|
-
when :sqlcommenter
|
|
110
|
-
SQLCommenter.new
|
|
111
|
-
else
|
|
112
|
-
raise ArgumentError, "Formatter is unsupported: #{formatter}"
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
if Thread.respond_to?(:each_caller_location)
|
|
117
|
-
def query_source_location # :nodoc:
|
|
118
|
-
Thread.each_caller_location do |location|
|
|
119
|
-
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
|
120
|
-
return frame if frame
|
|
121
|
-
end
|
|
122
|
-
nil
|
|
123
|
-
end
|
|
124
|
-
else
|
|
125
|
-
def query_source_location # :nodoc:
|
|
126
|
-
LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
|
|
159
|
+
def query_source_location # :nodoc:
|
|
160
|
+
Thread.each_caller_location do |location|
|
|
161
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
|
|
162
|
+
return frame if frame
|
|
127
163
|
end
|
|
164
|
+
nil
|
|
128
165
|
end
|
|
129
166
|
|
|
130
167
|
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
|
131
168
|
|
|
132
169
|
private
|
|
170
|
+
def rebuild_handlers
|
|
171
|
+
handlers = []
|
|
172
|
+
@tags.each do |i|
|
|
173
|
+
if i.is_a?(Hash)
|
|
174
|
+
i.each do |k, v|
|
|
175
|
+
handlers << [k, build_handler(k, v)]
|
|
176
|
+
end
|
|
177
|
+
else
|
|
178
|
+
handlers << [i, build_handler(i)]
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
handlers.sort_by! { |(key, _)| key.to_s }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def build_handler(name, handler = nil)
|
|
185
|
+
handler ||= @taggings[name]
|
|
186
|
+
if handler.nil?
|
|
187
|
+
GetKeyHandler.new(name)
|
|
188
|
+
elsif handler.respond_to?(:call)
|
|
189
|
+
if handler.arity == 0
|
|
190
|
+
ZeroArityHandler.new(handler)
|
|
191
|
+
else
|
|
192
|
+
handler
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
IdentityHandler.new(handler)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
133
199
|
# Returns an SQL comment +String+ containing the query log tags.
|
|
134
200
|
# Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
|
|
135
201
|
def comment(connection)
|
|
@@ -140,10 +206,6 @@ module ActiveRecord
|
|
|
140
206
|
end
|
|
141
207
|
end
|
|
142
208
|
|
|
143
|
-
def formatter
|
|
144
|
-
self.tags_formatter || self.update_formatter(:legacy)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
209
|
def uncached_comment(connection)
|
|
148
210
|
content = tag_content(connection)
|
|
149
211
|
|
|
@@ -169,25 +231,15 @@ module ActiveRecord
|
|
|
169
231
|
context = ActiveSupport::ExecutionContext.to_h
|
|
170
232
|
context[:connection] ||= connection
|
|
171
233
|
|
|
172
|
-
pairs =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
val = if handler.nil?
|
|
177
|
-
context[key]
|
|
178
|
-
elsif handler.respond_to?(:call)
|
|
179
|
-
if handler.arity == 0
|
|
180
|
-
handler.call
|
|
181
|
-
else
|
|
182
|
-
handler.call(context)
|
|
183
|
-
end
|
|
184
|
-
else
|
|
185
|
-
handler
|
|
186
|
-
end
|
|
187
|
-
[key, val] unless val.nil?
|
|
234
|
+
pairs = @handlers.filter_map do |(key, handler)|
|
|
235
|
+
val = handler.call(context)
|
|
236
|
+
@formatter.format(key, val) unless val.nil?
|
|
188
237
|
end
|
|
189
|
-
|
|
238
|
+
@formatter.join(pairs)
|
|
190
239
|
end
|
|
191
240
|
end
|
|
241
|
+
|
|
242
|
+
@handlers = rebuild_handlers
|
|
243
|
+
self.tags_formatter = :legacy
|
|
192
244
|
end
|
|
193
245
|
end
|
|
@@ -2,40 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module QueryLogs
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# Formats the key value pairs into a string.
|
|
11
|
-
def format(pairs)
|
|
12
|
-
pairs.map! do |key, value|
|
|
13
|
-
"#{key}#{key_value_separator}#{format_value(value)}"
|
|
14
|
-
end.join(",")
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
attr_reader :key_value_separator
|
|
19
|
-
|
|
20
|
-
def format_value(value)
|
|
21
|
-
value
|
|
5
|
+
module LegacyFormatter # :nodoc:
|
|
6
|
+
class << self
|
|
7
|
+
# Formats the key value pairs into a string.
|
|
8
|
+
def format(key, value)
|
|
9
|
+
"#{key}:#{value}"
|
|
22
10
|
end
|
|
23
|
-
end
|
|
24
11
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
def join(pairs)
|
|
13
|
+
pairs.join(",")
|
|
14
|
+
end
|
|
28
15
|
end
|
|
16
|
+
end
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
class SQLCommenter # :nodoc:
|
|
19
|
+
class << self
|
|
20
|
+
def format(key, value)
|
|
21
|
+
"#{key}='#{ERB::Util.url_encode(value)}'"
|
|
22
|
+
end
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"'#{ERB::Util.url_encode(value)}'"
|
|
24
|
+
def join(pairs)
|
|
25
|
+
pairs.join(",")
|
|
38
26
|
end
|
|
27
|
+
end
|
|
39
28
|
end
|
|
40
29
|
end
|
|
41
30
|
end
|
|
@@ -56,12 +56,10 @@ module ActiveRecord
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Same as #find_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
|
|
59
|
-
def async_find_by_sql(sql, binds = [], preparable: nil, &block)
|
|
60
|
-
|
|
61
|
-
_query_by_sql(c, sql, binds, preparable: preparable, async: true)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
result.then do |result|
|
|
59
|
+
def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
|
60
|
+
with_connection do |c|
|
|
61
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
|
|
62
|
+
end.then do |result|
|
|
65
63
|
_load_from_sql(result, &block)
|
|
66
64
|
end
|
|
67
65
|
end
|
|
@@ -71,6 +69,8 @@ module ActiveRecord
|
|
|
71
69
|
end
|
|
72
70
|
|
|
73
71
|
def _load_from_sql(result_set, &block) # :nodoc:
|
|
72
|
+
return [] if result_set.empty?
|
|
73
|
+
|
|
74
74
|
column_types = result_set.column_types
|
|
75
75
|
|
|
76
76
|
unless column_types.empty?
|
|
@@ -86,10 +86,10 @@ module ActiveRecord
|
|
|
86
86
|
|
|
87
87
|
message_bus.instrument("instantiation.active_record", payload) do
|
|
88
88
|
if result_set.includes_column?(inheritance_column)
|
|
89
|
-
result_set.map { |record| instantiate(record, column_types, &block) }
|
|
89
|
+
result_set.indexed_rows.map { |record| instantiate(record, column_types, &block) }
|
|
90
90
|
else
|
|
91
91
|
# Instantiate a homogeneous set
|
|
92
|
-
result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
|
|
92
|
+
result_set.indexed_rows.map { |record| instantiate_instance_of(self, record, column_types, &block) }
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
end
|
|
@@ -184,30 +184,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
184
184
|
end
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
-
initializer "active_record.warn_on_records_fetched_greater_than" do
|
|
188
|
-
if config.active_record.warn_on_records_fetched_greater_than
|
|
189
|
-
ActiveRecord.deprecator.warn <<~MSG.squish
|
|
190
|
-
`config.active_record.warn_on_records_fetched_greater_than` is deprecated and will be
|
|
191
|
-
removed in Rails 8.0.
|
|
192
|
-
Please subscribe to `sql.active_record` notifications and access the row count field to
|
|
193
|
-
detect large result set sizes.
|
|
194
|
-
MSG
|
|
195
|
-
ActiveSupport.on_load(:active_record) do
|
|
196
|
-
require "active_record/relation/record_fetch_warning"
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
initializer "active_record.sqlite3_deprecated_warning" do
|
|
202
|
-
if config.active_record.key?(:sqlite3_production_warning)
|
|
203
|
-
config.active_record.delete(:sqlite3_production_warning)
|
|
204
|
-
ActiveRecord.deprecator.warn <<~MSG.squish
|
|
205
|
-
The `config.active_record.sqlite3_production_warning` configuration no longer has any effect
|
|
206
|
-
and can be safely removed.
|
|
207
|
-
MSG
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
187
|
initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
|
|
212
188
|
config.after_initialize do
|
|
213
189
|
if config.active_record.sqlite3_adapter_strict_strings_by_default
|
|
@@ -390,7 +366,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
390
366
|
config.after_initialize do
|
|
391
367
|
if app.config.active_record.query_log_tags_enabled
|
|
392
368
|
ActiveRecord.query_transformers << ActiveRecord::QueryLogs
|
|
393
|
-
ActiveRecord::QueryLogs.taggings.merge
|
|
369
|
+
ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
|
|
394
370
|
application: Rails.application.class.name.split("::").first,
|
|
395
371
|
pid: -> { Process.pid.to_s },
|
|
396
372
|
socket: ->(context) { context[:connection].pool.db_config.socket },
|
|
@@ -405,7 +381,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
|
|
|
405
381
|
end
|
|
406
382
|
|
|
407
383
|
if app.config.active_record.query_log_tags_format
|
|
408
|
-
ActiveRecord::QueryLogs.
|
|
384
|
+
ActiveRecord::QueryLogs.tags_formatter = app.config.active_record.query_log_tags_format
|
|
409
385
|
end
|
|
410
386
|
|
|
411
387
|
if app.config.active_record.cache_query_log_tags
|