activerecord 8.0.2.1 → 8.1.0.beta1
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 +459 -421
- data/README.rdoc +2 -2
- 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 +9 -1
- 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_association.rb +3 -3
- 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/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +384 -49
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -1
- 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 +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -13
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- 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 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -16
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
- 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 +1 -1
- data/lib/active_record/core.rb +12 -9
- 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 +56 -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 +2 -2
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +27 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +3 -3
- 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 +1 -5
- 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 +31 -21
- data/lib/active_record/model_schema.rb +10 -7
- 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 +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +34 -5
- data/lib/active_record/railties/databases.rake +23 -19
- 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 +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +35 -25
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +37 -21
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +43 -33
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +7 -10
- data/lib/active_record/relation.rb +37 -15
- data/lib/active_record/result.rb +44 -21
- 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/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +46 -18
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +24 -35
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +11 -3
- 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 +34 -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 +68 -5
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +5 -21
- data/lib/arel.rb +3 -1
- metadata +13 -9
- data/lib/active_record/normalization.rb +0 -163
@@ -29,7 +29,8 @@ module ActiveRecord
|
|
29
29
|
raw_connection.next_result
|
30
30
|
end
|
31
31
|
verified!
|
32
|
-
|
32
|
+
|
33
|
+
notification_payload[:affected_rows] = result.affected_rows
|
33
34
|
notification_payload[:row_count] = result.count
|
34
35
|
result
|
35
36
|
ensure
|
@@ -40,9 +41,9 @@ module ActiveRecord
|
|
40
41
|
|
41
42
|
def cast_result(result)
|
42
43
|
if result.fields.empty?
|
43
|
-
ActiveRecord::Result.empty
|
44
|
+
ActiveRecord::Result.empty(affected_rows: result.affected_rows)
|
44
45
|
else
|
45
|
-
ActiveRecord::Result.new(result.fields, result.rows)
|
46
|
+
ActiveRecord::Result.new(result.fields, result.rows, affected_rows: result.affected_rows)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
@@ -181,7 +181,7 @@ module ActiveRecord
|
|
181
181
|
end
|
182
182
|
|
183
183
|
case exception
|
184
|
-
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError
|
184
|
+
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError, ::Trilogy::SSLError
|
185
185
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
186
186
|
when ::Trilogy::Error
|
187
187
|
if exception.is_a?(SystemCallError) || exception.message.include?("TRILOGY_INVALID_SEQUENCE_ID")
|
@@ -84,6 +84,7 @@ module ActiveRecord
|
|
84
84
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
85
85
|
autoload :IndexDefinition
|
86
86
|
autoload :ColumnDefinition
|
87
|
+
autoload :ColumnMethods
|
87
88
|
autoload :ChangeColumnDefinition
|
88
89
|
autoload :ChangeColumnDefaultDefinition
|
89
90
|
autoload :ForeignKeyDefinition
|
@@ -301,7 +301,7 @@ module ActiveRecord
|
|
301
301
|
|
302
302
|
# Checkouts a connection from the pool, yield it and then check it back in.
|
303
303
|
# If a connection was already leased via #lease_connection or a parent call to
|
304
|
-
# #with_connection, that same connection is
|
304
|
+
# #with_connection, that same connection is yielded.
|
305
305
|
# If #lease_connection is called inside the block, the connection won't be checked
|
306
306
|
# back in.
|
307
307
|
# If #connection is called inside the block, the connection won't be checked back in
|
data/lib/active_record/core.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
4
|
-
require "active_support/core_ext/module/delegation"
|
5
4
|
require "active_support/parameter_filter"
|
6
5
|
require "concurrent/map"
|
7
6
|
|
@@ -112,7 +111,7 @@ module ActiveRecord
|
|
112
111
|
# Post.attributes_for_inspect = [:id, :title]
|
113
112
|
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
114
113
|
#
|
115
|
-
# When set to
|
114
|
+
# When set to +:all+ inspect will list all the record's attributes:
|
116
115
|
#
|
117
116
|
# Post.attributes_for_inspect = :all
|
118
117
|
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
@@ -358,6 +357,8 @@ module ActiveRecord
|
|
358
357
|
def filter_attributes=(filter_attributes)
|
359
358
|
@inspection_filter = nil
|
360
359
|
@filter_attributes = filter_attributes
|
360
|
+
|
361
|
+
FilterAttributeHandler.sensitive_attribute_was_declared(self, filter_attributes)
|
361
362
|
end
|
362
363
|
|
363
364
|
def inspection_filter # :nodoc:
|
@@ -451,7 +452,7 @@ module ActiveRecord
|
|
451
452
|
where(wheres).limit(1)
|
452
453
|
}
|
453
454
|
|
454
|
-
statement.execute(values.flatten, connection
|
455
|
+
statement.execute(values.flatten, connection).then do |r|
|
455
456
|
r.first
|
456
457
|
rescue TypeError
|
457
458
|
raise ActiveRecord::StatementInvalid
|
@@ -600,7 +601,7 @@ module ActiveRecord
|
|
600
601
|
#
|
601
602
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
602
603
|
# topic.slice(:title, :author_name)
|
603
|
-
# => { "title" => "Budget", "author_name" => "Jason" }
|
604
|
+
# # => { "title" => "Budget", "author_name" => "Jason" }
|
604
605
|
#
|
605
606
|
#--
|
606
607
|
# Implemented by ActiveModel::Access#slice.
|
@@ -614,7 +615,7 @@ module ActiveRecord
|
|
614
615
|
#
|
615
616
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
616
617
|
# topic.values_at(:title, :author_name)
|
617
|
-
# => ["Budget", "Jason"]
|
618
|
+
# # => ["Budget", "Jason"]
|
618
619
|
#
|
619
620
|
#--
|
620
621
|
# Implemented by ActiveModel::Access#values_at.
|
@@ -641,7 +642,7 @@ module ActiveRecord
|
|
641
642
|
def hash
|
642
643
|
id = self.id
|
643
644
|
|
644
|
-
if primary_key_values_present?
|
645
|
+
if self.class.composite_primary_key? ? primary_key_values_present? : id
|
645
646
|
self.class.hash ^ id.hash
|
646
647
|
else
|
647
648
|
super
|
@@ -691,12 +692,14 @@ module ActiveRecord
|
|
691
692
|
# Sets the record to strict_loading mode. This will raise an error
|
692
693
|
# if the record tries to lazily load an association.
|
693
694
|
#
|
695
|
+
# NOTE: Strict loading is disabled during validation in order to let the record validate its association.
|
696
|
+
#
|
694
697
|
# user = User.first
|
695
698
|
# user.strict_loading! # => true
|
696
699
|
# user.address.city
|
697
|
-
# => ActiveRecord::StrictLoadingViolationError
|
700
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
698
701
|
# user.comments.to_a
|
699
|
-
# => ActiveRecord::StrictLoadingViolationError
|
702
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
700
703
|
#
|
701
704
|
# ==== Parameters
|
702
705
|
#
|
@@ -716,7 +719,7 @@ module ActiveRecord
|
|
716
719
|
# user.address.city # => "Tatooine"
|
717
720
|
# user.comments.to_a # => [#<Comment:0x00...]
|
718
721
|
# user.comments.first.ratings.to_a
|
719
|
-
# => ActiveRecord::StrictLoadingViolationError
|
722
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
720
723
|
def strict_loading!(value = true, mode: :all)
|
721
724
|
unless [:all, :n_plus_one_only].include?(mode)
|
722
725
|
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
#
|
18
18
|
# ==== Parameters
|
19
19
|
#
|
20
|
-
# * +id+ - The id of the object you wish to reset a counter on.
|
20
|
+
# * +id+ - The id of the object you wish to reset a counter on or an array of ids.
|
21
21
|
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
22
22
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
23
23
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
@@ -28,13 +28,25 @@ module ActiveRecord
|
|
28
28
|
# # For the Post with id #1, reset the comments_count
|
29
29
|
# Post.reset_counters(1, :comments)
|
30
30
|
#
|
31
|
+
# # For posts with ids #1 and #2, reset the comments_count
|
32
|
+
# Post.reset_counters([1, 2], :comments)
|
33
|
+
#
|
31
34
|
# # Like above, but also touch the updated_at and/or updated_on
|
32
35
|
# # attributes.
|
33
36
|
# Post.reset_counters(1, :comments, touch: true)
|
34
37
|
def reset_counters(id, *counters, touch: nil)
|
35
|
-
|
38
|
+
ids = if composite_primary_key?
|
39
|
+
if id.first.is_a?(Array)
|
40
|
+
id
|
41
|
+
else
|
42
|
+
[id]
|
43
|
+
end
|
44
|
+
else
|
45
|
+
Array(id)
|
46
|
+
end
|
47
|
+
|
48
|
+
updates = Hash.new { |h, k| h[k] = {} }
|
36
49
|
|
37
|
-
updates = {}
|
38
50
|
counters.each do |counter_association|
|
39
51
|
has_many_association = _reflect_on_association(counter_association)
|
40
52
|
unless has_many_association
|
@@ -48,14 +60,22 @@ module ActiveRecord
|
|
48
60
|
has_many_association = has_many_association.through_reflection
|
49
61
|
end
|
50
62
|
|
63
|
+
counter_association = counter_association.to_sym
|
51
64
|
foreign_key = has_many_association.foreign_key.to_s
|
52
65
|
child_class = has_many_association.klass
|
53
66
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
54
67
|
counter_name = reflection.counter_cache_column
|
55
68
|
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
counts =
|
70
|
+
unscoped
|
71
|
+
.joins(counter_association)
|
72
|
+
.where(primary_key => ids)
|
73
|
+
.group(primary_key)
|
74
|
+
.count(:all)
|
75
|
+
|
76
|
+
ids.each do |id|
|
77
|
+
updates[id].merge!(counter_name => counts[id] || 0)
|
78
|
+
end
|
59
79
|
end
|
60
80
|
|
61
81
|
if touch
|
@@ -63,10 +83,15 @@ module ActiveRecord
|
|
63
83
|
names = Array.wrap(names)
|
64
84
|
options = names.extract_options!
|
65
85
|
touch_updates = touch_attributes_with_time(*names, **options)
|
66
|
-
|
86
|
+
|
87
|
+
updates.each_value do |record_updates|
|
88
|
+
record_updates.merge!(touch_updates)
|
89
|
+
end
|
67
90
|
end
|
68
91
|
|
69
|
-
|
92
|
+
updates.each do |id, record_updates|
|
93
|
+
unscoped.where(primary_key => [id]).update_all(record_updates)
|
94
|
+
end
|
70
95
|
|
71
96
|
true
|
72
97
|
end
|
@@ -38,6 +38,7 @@ module ActiveRecord
|
|
38
38
|
def initialize(env_name, name, configuration_hash)
|
39
39
|
super(env_name, name)
|
40
40
|
@configuration_hash = configuration_hash.symbolize_keys.freeze
|
41
|
+
validate_configuration!
|
41
42
|
end
|
42
43
|
|
43
44
|
# Determines whether a database configuration is for a replica / readonly
|
@@ -69,16 +70,32 @@ module ActiveRecord
|
|
69
70
|
@configuration_hash = configuration_hash.merge(database: database).freeze
|
70
71
|
end
|
71
72
|
|
72
|
-
def
|
73
|
-
(configuration_hash[:pool] || 5).to_i
|
73
|
+
def max_connections
|
74
|
+
(configuration_hash[:max_connections] || configuration_hash[:pool] || 5).to_i
|
74
75
|
end
|
75
76
|
|
77
|
+
def min_connections
|
78
|
+
(configuration_hash[:min_connections] || 0).to_i
|
79
|
+
end
|
80
|
+
|
81
|
+
alias :pool :max_connections
|
82
|
+
deprecate pool: :max_connections, deprecator: ActiveRecord.deprecator
|
83
|
+
|
76
84
|
def min_threads
|
77
85
|
(configuration_hash[:min_threads] || 0).to_i
|
78
86
|
end
|
79
87
|
|
80
88
|
def max_threads
|
81
|
-
(configuration_hash[:max_threads] ||
|
89
|
+
(configuration_hash[:max_threads] || max_connections).to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
def max_age
|
93
|
+
v = configuration_hash[:max_age]&.to_i
|
94
|
+
if v && v > 0
|
95
|
+
v
|
96
|
+
else
|
97
|
+
Float::INFINITY
|
98
|
+
end
|
82
99
|
end
|
83
100
|
|
84
101
|
def query_cache
|
@@ -93,10 +110,8 @@ module ActiveRecord
|
|
93
110
|
(configuration_hash[:checkout_timeout] || 5).to_f
|
94
111
|
end
|
95
112
|
|
96
|
-
|
97
|
-
|
98
|
-
def reaping_frequency
|
99
|
-
configuration_hash.fetch(:reaping_frequency, 60)&.to_f
|
113
|
+
def reaping_frequency # :nodoc:
|
114
|
+
configuration_hash.fetch(:reaping_frequency, default_reaping_frequency)&.to_f
|
100
115
|
end
|
101
116
|
|
102
117
|
def idle_timeout
|
@@ -104,6 +119,11 @@ module ActiveRecord
|
|
104
119
|
timeout if timeout > 0
|
105
120
|
end
|
106
121
|
|
122
|
+
def keepalive
|
123
|
+
keepalive = (configuration_hash[:keepalive] || 600).to_f
|
124
|
+
keepalive if keepalive > 0
|
125
|
+
end
|
126
|
+
|
107
127
|
def adapter
|
108
128
|
configuration_hash[:adapter]&.to_s
|
109
129
|
end
|
@@ -146,7 +166,7 @@ module ActiveRecord
|
|
146
166
|
#
|
147
167
|
# If the config option is set that will be used. Otherwise Rails will generate
|
148
168
|
# the filename from the database config name.
|
149
|
-
def schema_dump(format =
|
169
|
+
def schema_dump(format = schema_format)
|
150
170
|
if configuration_hash.key?(:schema_dump)
|
151
171
|
if config = configuration_hash[:schema_dump]
|
152
172
|
config
|
@@ -158,6 +178,12 @@ module ActiveRecord
|
|
158
178
|
end
|
159
179
|
end
|
160
180
|
|
181
|
+
def schema_format # :nodoc:
|
182
|
+
format = configuration_hash.fetch(:schema_format, ActiveRecord.schema_format).to_sym
|
183
|
+
raise "Invalid schema format" unless [:ruby, :sql].include?(format)
|
184
|
+
format
|
185
|
+
end
|
186
|
+
|
161
187
|
def database_tasks? # :nodoc:
|
162
188
|
!replica? && !!configuration_hash.fetch(:database_tasks, true)
|
163
189
|
end
|
@@ -168,13 +194,34 @@ module ActiveRecord
|
|
168
194
|
|
169
195
|
private
|
170
196
|
def schema_file_type(format)
|
171
|
-
case format
|
197
|
+
case format.to_sym
|
172
198
|
when :ruby
|
173
199
|
"schema.rb"
|
174
200
|
when :sql
|
175
201
|
"structure.sql"
|
176
202
|
end
|
177
203
|
end
|
204
|
+
|
205
|
+
def default_reaping_frequency
|
206
|
+
# Reap every 20 seconds by default, but run more often as necessary to
|
207
|
+
# meet other configured timeouts.
|
208
|
+
[20, idle_timeout, max_age, keepalive].compact.min
|
209
|
+
end
|
210
|
+
|
211
|
+
def validate_configuration!
|
212
|
+
if configuration_hash[:pool] && configuration_hash[:max_connections]
|
213
|
+
pool_val = configuration_hash[:pool].to_i
|
214
|
+
max_conn_val = configuration_hash[:max_connections].to_i
|
215
|
+
|
216
|
+
if pool_val != max_conn_val
|
217
|
+
raise "Ambiguous configuration: 'pool' (#{pool_val}) and 'max_connections' (#{max_conn_val}) are set to different values. Prefer just 'max_connections'."
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
if configuration_hash[:pool] && configuration_hash[:min_connections]
|
222
|
+
raise "Ambiguous configuration: when setting 'min_connections', use 'max_connections' instead of 'pool'."
|
223
|
+
end
|
224
|
+
end
|
178
225
|
end
|
179
226
|
end
|
180
227
|
end
|
@@ -47,9 +47,8 @@ module ActiveRecord
|
|
47
47
|
@configuration_hash[:schema_dump] = false
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
end
|
50
|
+
query_cache = parse_query_cache
|
51
|
+
@configuration_hash[:query_cache] = query_cache unless query_cache.nil?
|
53
52
|
|
54
53
|
to_boolean!(@configuration_hash, :replica)
|
55
54
|
to_boolean!(@configuration_hash, :database_tasks)
|
@@ -58,6 +57,17 @@ module ActiveRecord
|
|
58
57
|
end
|
59
58
|
|
60
59
|
private
|
60
|
+
def parse_query_cache
|
61
|
+
case value = @configuration_hash[:query_cache]
|
62
|
+
when /\A\d+\z/
|
63
|
+
value.to_i
|
64
|
+
when "false"
|
65
|
+
false
|
66
|
+
else
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
61
71
|
def to_boolean!(configuration_hash, key)
|
62
72
|
if configuration_hash[key].is_a?(String)
|
63
73
|
configuration_hash[key] = configuration_hash[key] != "false"
|
@@ -36,9 +36,11 @@ module ActiveRecord
|
|
36
36
|
# to respond to `sharded?`. To implement this define the following in an
|
37
37
|
# initializer:
|
38
38
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
39
|
+
# ActiveSupport.on_load(:active_record_database_configurations) do
|
40
|
+
# ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
|
41
|
+
# next unless config.key?(:vitess)
|
42
|
+
# VitessConfig.new(env_name, name, config)
|
43
|
+
# end
|
42
44
|
# end
|
43
45
|
#
|
44
46
|
# Note: applications must handle the condition in which custom config should be
|
@@ -306,4 +308,6 @@ module ActiveRecord
|
|
306
308
|
url
|
307
309
|
end
|
308
310
|
end
|
311
|
+
|
312
|
+
ActiveSupport.run_load_hooks(:active_record_database_configurations, DatabaseConfigurations)
|
309
313
|
end
|
@@ -202,7 +202,7 @@ module ActiveRecord
|
|
202
202
|
# @entry.access_notice_message
|
203
203
|
# @entry.access_notice_message?
|
204
204
|
#
|
205
|
-
#
|
205
|
+
# ==== Options
|
206
206
|
#
|
207
207
|
# The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
|
208
208
|
# The following options can be included to specialize the behavior of the delegated type convenience methods.
|
@@ -229,7 +229,7 @@ module ActiveRecord
|
|
229
229
|
# @entry.message_uuid # => returns entryable_uuid, when entryable_type == "Message", otherwise nil
|
230
230
|
# @entry.comment_uuid # => returns entryable_uuid, when entryable_type == "Comment", otherwise nil
|
231
231
|
def delegated_type(role, types:, **options)
|
232
|
-
belongs_to role, options.delete(:scope), **options
|
232
|
+
belongs_to role, options.delete(:scope), **options, polymorphic: true
|
233
233
|
define_delegated_type_methods role, types: types, options: options
|
234
234
|
end
|
235
235
|
|
@@ -7,16 +7,18 @@ module ActiveRecord
|
|
7
7
|
if self == Base
|
8
8
|
super
|
9
9
|
else
|
10
|
-
|
11
|
-
|
10
|
+
super || begin
|
11
|
+
match = Method.match(name)
|
12
|
+
match && match.valid?(self, name)
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
15
17
|
def method_missing(name, ...)
|
16
|
-
match = Method.match(
|
18
|
+
match = Method.match(name)
|
17
19
|
|
18
|
-
if match && match.valid?
|
19
|
-
match.define
|
20
|
+
if match && match.valid?(self, name)
|
21
|
+
match.define(self, name)
|
20
22
|
send(name, ...)
|
21
23
|
else
|
22
24
|
super
|
@@ -24,97 +26,80 @@ module ActiveRecord
|
|
24
26
|
end
|
25
27
|
|
26
28
|
class Method
|
27
|
-
@matchers = []
|
28
|
-
|
29
29
|
class << self
|
30
|
-
|
31
|
-
|
32
|
-
def match(model, name)
|
33
|
-
klass = matchers.find { |k| k.pattern.match?(name) }
|
34
|
-
klass.new(model, name) if klass
|
30
|
+
def match(name)
|
31
|
+
FindBy.match?(name) || FindByBang.match?(name)
|
35
32
|
end
|
36
33
|
|
37
|
-
def
|
38
|
-
|
34
|
+
def valid?(model, name)
|
35
|
+
attribute_names(model, name.to_s).all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
|
39
36
|
end
|
40
37
|
|
41
|
-
def
|
42
|
-
|
38
|
+
def define(model, name)
|
39
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
40
|
+
def self.#{name}(#{signature(model, name)})
|
41
|
+
#{body(model, name)}
|
42
|
+
end
|
43
|
+
CODE
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
private
|
47
|
+
def make_pattern(prefix, suffix)
|
48
|
+
/\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
+
def attribute_names(model, name)
|
52
|
+
attribute_names = name.match(pattern)[1].split("_and_")
|
53
|
+
attribute_names.map! { |name| model.attribute_aliases[name] || name }
|
54
|
+
end
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@attribute_names = @name.match(self.class.pattern)[1].split("_and_")
|
56
|
-
@attribute_names.map! { |name| @model.attribute_aliases[name] || name }
|
57
|
-
end
|
56
|
+
def body(model, method_name)
|
57
|
+
"#{finder}(#{attributes_hash(model, method_name)})"
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
# The parameters in the signature may have reserved Ruby words, in order
|
61
|
+
# to prevent errors, we start each param name with `_`.
|
62
|
+
def signature(model, method_name)
|
63
|
+
attribute_names(model, method_name.to_s).map { |name| "_#{name}" }.join(", ")
|
64
|
+
end
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
def
|
66
|
-
#{
|
66
|
+
# Given that the parameters starts with `_`, the finder needs to use the
|
67
|
+
# same parameter name.
|
68
|
+
def attributes_hash(model, method_name)
|
69
|
+
"{" + attribute_names(model, method_name).map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
67
70
|
end
|
68
|
-
CODE
|
69
71
|
end
|
72
|
+
end
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
"#{finder}(#{attributes_hash})"
|
74
|
-
end
|
74
|
+
class FindBy < Method
|
75
|
+
@pattern = make_pattern("find_by", "")
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
def signature
|
79
|
-
attribute_names.map { |name| "_#{name}" }.join(", ")
|
80
|
-
end
|
77
|
+
class << self
|
78
|
+
attr_reader :pattern
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
def attributes_hash
|
85
|
-
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
80
|
+
def match?(name)
|
81
|
+
pattern.match?(name) && self
|
86
82
|
end
|
87
83
|
|
88
84
|
def finder
|
89
|
-
|
85
|
+
"find_by"
|
90
86
|
end
|
91
|
-
end
|
92
|
-
|
93
|
-
class FindBy < Method
|
94
|
-
Method.matchers << self
|
95
|
-
|
96
|
-
def self.prefix
|
97
|
-
"find_by"
|
98
|
-
end
|
99
|
-
|
100
|
-
def finder
|
101
|
-
"find_by"
|
102
87
|
end
|
103
88
|
end
|
104
89
|
|
105
90
|
class FindByBang < Method
|
106
|
-
|
91
|
+
@pattern = make_pattern("find_by", "!")
|
107
92
|
|
108
|
-
|
109
|
-
|
110
|
-
end
|
93
|
+
class << self
|
94
|
+
attr_reader :pattern
|
111
95
|
|
112
|
-
|
113
|
-
|
114
|
-
|
96
|
+
def match?(name)
|
97
|
+
pattern.match?(name) && self
|
98
|
+
end
|
115
99
|
|
116
|
-
|
117
|
-
|
100
|
+
def finder
|
101
|
+
"find_by!"
|
102
|
+
end
|
118
103
|
end
|
119
104
|
end
|
120
105
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
class_methods do
|
17
17
|
# Encrypts the +name+ attribute.
|
18
18
|
#
|
19
|
-
#
|
19
|
+
# ==== Options
|
20
20
|
#
|
21
21
|
# * <tt>:key_provider</tt> - A key provider to provide encryption and decryption keys. Defaults to
|
22
22
|
# +ActiveRecord::Encryption.key_provider+.
|
@@ -30,10 +30,10 @@ module ActiveRecord
|
|
30
30
|
# will use the oldest encryption scheme to encrypt new data by default. You can change this by setting
|
31
31
|
# <tt>deterministic: { fixed: false }</tt>. That will make it use the newest encryption scheme for encrypting new
|
32
32
|
# data.
|
33
|
-
# * <tt>:support_unencrypted_data</tt> -
|
34
|
-
#
|
35
|
-
# scenarios where you encrypt one column, and want to disable support for unencrypted data
|
36
|
-
# the global setting.
|
33
|
+
# * <tt>:support_unencrypted_data</tt> - When true, unencrypted data can be read normally. When false, it will raise errors.
|
34
|
+
# Falls back to +config.active_record.encryption.support_unencrypted_data+ if no value is provided.
|
35
|
+
# This is useful for scenarios where you encrypt one column, and want to disable support for unencrypted data
|
36
|
+
# without having to tweak the global setting.
|
37
37
|
# * <tt>:downcase</tt> - When true, it converts the encrypted content to downcase automatically. This allows to
|
38
38
|
# effectively ignore case when querying data. Notice that the case is lost. Use +:ignore_case+ if you are interested
|
39
39
|
# in preserving it.
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
delegate :key_provider, :downcase?, :deterministic?, :previous_schemes, :with_context, :fixed?, to: :scheme
|
16
16
|
delegate :accessor, :type, to: :cast_type
|
17
17
|
|
18
|
-
#
|
18
|
+
# ==== Options
|
19
19
|
#
|
20
20
|
# * <tt>:scheme</tt> - A +Scheme+ with the encryption properties for this attribute.
|
21
21
|
# * <tt>:cast_type</tt> - A type that will be used to serialize (before encrypting) and deserialize
|
@@ -59,7 +59,7 @@ module ActiveRecord
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def support_unencrypted_data?
|
62
|
-
|
62
|
+
scheme.support_unencrypted_data? && !previous_type?
|
63
63
|
end
|
64
64
|
|
65
65
|
private
|