activerecord 7.1.5.2 → 8.1.2.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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- 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 +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- 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 +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- 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 +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- 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/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -1,53 +1,55 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
6
|
class DatabaseConfigurations
|
|
5
|
-
#
|
|
7
|
+
# # Active Record Database Hash Config
|
|
6
8
|
#
|
|
7
|
-
# A
|
|
8
|
-
#
|
|
9
|
+
# A `HashConfig` object is created for each database configuration entry that is
|
|
10
|
+
# created from a hash.
|
|
9
11
|
#
|
|
10
12
|
# A hash config:
|
|
11
13
|
#
|
|
12
|
-
#
|
|
14
|
+
# { "development" => { "database" => "db_name" } }
|
|
13
15
|
#
|
|
14
16
|
# Becomes:
|
|
15
17
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
|
|
19
|
+
# @env_name="development", @name="primary", @config={database: "db_name"}>
|
|
18
20
|
#
|
|
19
21
|
# See ActiveRecord::DatabaseConfigurations for more info.
|
|
20
22
|
class HashConfig < DatabaseConfig
|
|
21
23
|
attr_reader :configuration_hash
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
# Initialize a new +HashConfig+ object
|
|
25
|
+
# Initialize a new `HashConfig` object
|
|
25
26
|
#
|
|
26
|
-
#
|
|
27
|
+
# #### Parameters
|
|
28
|
+
#
|
|
29
|
+
# * `env_name` - The Rails environment, i.e. "development".
|
|
30
|
+
# * `name` - The db config name. In a standard two-tier database configuration
|
|
31
|
+
# this will default to "primary". In a multiple database three-tier database
|
|
32
|
+
# configuration this corresponds to the name used in the second tier, for
|
|
33
|
+
# example "primary_readonly".
|
|
34
|
+
# * `configuration_hash` - The config hash. This is the hash that contains the
|
|
35
|
+
# database adapter, name, and other important information for database
|
|
36
|
+
# connections.
|
|
27
37
|
#
|
|
28
|
-
# * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
|
|
29
|
-
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
|
30
|
-
# database configuration this will default to "primary". In a multiple
|
|
31
|
-
# database three-tier database configuration this corresponds to the name
|
|
32
|
-
# used in the second tier, for example "primary_readonly".
|
|
33
|
-
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
|
34
|
-
# database adapter, name, and other important information for database
|
|
35
|
-
# connections.
|
|
36
38
|
def initialize(env_name, name, configuration_hash)
|
|
37
39
|
super(env_name, name)
|
|
38
40
|
@configuration_hash = configuration_hash.symbolize_keys.freeze
|
|
41
|
+
validate_configuration!
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
# Determines whether a database configuration is for a replica / readonly
|
|
42
|
-
# connection. If the
|
|
43
|
-
# return
|
|
45
|
+
# connection. If the `replica` key is present in the config, `replica?` will
|
|
46
|
+
# return `true`.
|
|
44
47
|
def replica?
|
|
45
48
|
configuration_hash[:replica]
|
|
46
49
|
end
|
|
47
50
|
|
|
48
|
-
# The migrations paths for a database configuration. If the
|
|
49
|
-
#
|
|
50
|
-
# will return its value.
|
|
51
|
+
# The migrations paths for a database configuration. If the `migrations_paths`
|
|
52
|
+
# key is present in the config, `migrations_paths` will return its value.
|
|
51
53
|
def migrations_paths
|
|
52
54
|
configuration_hash[:migrations_paths]
|
|
53
55
|
end
|
|
@@ -68,16 +70,35 @@ module ActiveRecord
|
|
|
68
70
|
@configuration_hash = configuration_hash.merge(database: database).freeze
|
|
69
71
|
end
|
|
70
72
|
|
|
71
|
-
def
|
|
72
|
-
(
|
|
73
|
+
def max_connections
|
|
74
|
+
max_connections = configuration_hash.fetch(:max_connections) {
|
|
75
|
+
configuration_hash.fetch(:pool, 5)
|
|
76
|
+
}&.to_i
|
|
77
|
+
max_connections if max_connections && max_connections >= 0
|
|
73
78
|
end
|
|
74
79
|
|
|
80
|
+
def min_connections
|
|
81
|
+
(configuration_hash[:min_connections] || 0).to_i
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
alias :pool :max_connections
|
|
85
|
+
deprecate pool: :max_connections, deprecator: ActiveRecord.deprecator
|
|
86
|
+
|
|
75
87
|
def min_threads
|
|
76
88
|
(configuration_hash[:min_threads] || 0).to_i
|
|
77
89
|
end
|
|
78
90
|
|
|
79
91
|
def max_threads
|
|
80
|
-
(configuration_hash[:max_threads] ||
|
|
92
|
+
(configuration_hash[:max_threads] || (max_connections || 5).clamp(0, 5)).to_i
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def max_age
|
|
96
|
+
v = configuration_hash[:max_age]&.to_i
|
|
97
|
+
if v && v > 0
|
|
98
|
+
v
|
|
99
|
+
else
|
|
100
|
+
Float::INFINITY
|
|
101
|
+
end
|
|
81
102
|
end
|
|
82
103
|
|
|
83
104
|
def query_cache
|
|
@@ -92,10 +113,8 @@ module ActiveRecord
|
|
|
92
113
|
(configuration_hash[:checkout_timeout] || 5).to_f
|
|
93
114
|
end
|
|
94
115
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def reaping_frequency
|
|
98
|
-
configuration_hash.fetch(:reaping_frequency, 60)&.to_f
|
|
116
|
+
def reaping_frequency # :nodoc:
|
|
117
|
+
configuration_hash.fetch(:reaping_frequency, default_reaping_frequency)&.to_f
|
|
99
118
|
end
|
|
100
119
|
|
|
101
120
|
def idle_timeout
|
|
@@ -103,13 +122,17 @@ module ActiveRecord
|
|
|
103
122
|
timeout if timeout > 0
|
|
104
123
|
end
|
|
105
124
|
|
|
125
|
+
def keepalive
|
|
126
|
+
keepalive = (configuration_hash[:keepalive] || 600).to_f
|
|
127
|
+
keepalive if keepalive > 0
|
|
128
|
+
end
|
|
129
|
+
|
|
106
130
|
def adapter
|
|
107
|
-
configuration_hash[:adapter]
|
|
131
|
+
configuration_hash[:adapter]&.to_s
|
|
108
132
|
end
|
|
109
133
|
|
|
110
|
-
# The path to the schema cache dump file for a database.
|
|
111
|
-
#
|
|
112
|
-
# default will be derived.
|
|
134
|
+
# The path to the schema cache dump file for a database. If omitted, the
|
|
135
|
+
# filename will be read from ENV or a default will be derived.
|
|
113
136
|
def schema_cache_path
|
|
114
137
|
configuration_hash[:schema_cache_path]
|
|
115
138
|
end
|
|
@@ -130,15 +153,23 @@ module ActiveRecord
|
|
|
130
153
|
Base.configurations.primary?(name)
|
|
131
154
|
end
|
|
132
155
|
|
|
133
|
-
# Determines whether
|
|
134
|
-
# filename that should be used.
|
|
156
|
+
# Determines whether the db:prepare task should seed the database from db/seeds.rb.
|
|
135
157
|
#
|
|
136
|
-
# If
|
|
137
|
-
# the
|
|
158
|
+
# If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
|
|
159
|
+
# will return `true` for the primary database and `false` for all other configs.
|
|
160
|
+
def seeds?
|
|
161
|
+
configuration_hash.fetch(:seeds, primary?)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Determines whether to dump the schema/structure files and the filename that
|
|
165
|
+
# should be used.
|
|
138
166
|
#
|
|
139
|
-
# If
|
|
140
|
-
# will
|
|
141
|
-
|
|
167
|
+
# If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
|
|
168
|
+
# will not be dumped.
|
|
169
|
+
#
|
|
170
|
+
# If the config option is set that will be used. Otherwise Rails will generate
|
|
171
|
+
# the filename from the database config name.
|
|
172
|
+
def schema_dump(format = schema_format)
|
|
142
173
|
if configuration_hash.key?(:schema_dump)
|
|
143
174
|
if config = configuration_hash[:schema_dump]
|
|
144
175
|
config
|
|
@@ -150,19 +181,50 @@ module ActiveRecord
|
|
|
150
181
|
end
|
|
151
182
|
end
|
|
152
183
|
|
|
184
|
+
def schema_format # :nodoc:
|
|
185
|
+
format = configuration_hash.fetch(:schema_format, ActiveRecord.schema_format).to_sym
|
|
186
|
+
raise "Invalid schema format" unless [:ruby, :sql].include?(format)
|
|
187
|
+
format
|
|
188
|
+
end
|
|
189
|
+
|
|
153
190
|
def database_tasks? # :nodoc:
|
|
154
191
|
!replica? && !!configuration_hash.fetch(:database_tasks, true)
|
|
155
192
|
end
|
|
156
193
|
|
|
194
|
+
def use_metadata_table? # :nodoc:
|
|
195
|
+
configuration_hash.fetch(:use_metadata_table, true)
|
|
196
|
+
end
|
|
197
|
+
|
|
157
198
|
private
|
|
158
199
|
def schema_file_type(format)
|
|
159
|
-
case format
|
|
200
|
+
case format.to_sym
|
|
160
201
|
when :ruby
|
|
161
202
|
"schema.rb"
|
|
162
203
|
when :sql
|
|
163
204
|
"structure.sql"
|
|
164
205
|
end
|
|
165
206
|
end
|
|
207
|
+
|
|
208
|
+
def default_reaping_frequency
|
|
209
|
+
# Reap every 20 seconds by default, but run more often as necessary to
|
|
210
|
+
# meet other configured timeouts.
|
|
211
|
+
[20, idle_timeout, max_age, keepalive].compact.min
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def validate_configuration!
|
|
215
|
+
if configuration_hash[:pool] && configuration_hash[:max_connections]
|
|
216
|
+
pool_val = configuration_hash[:pool].to_i
|
|
217
|
+
max_conn_val = configuration_hash[:max_connections].to_i
|
|
218
|
+
|
|
219
|
+
if pool_val != max_conn_val
|
|
220
|
+
raise "Ambiguous configuration: 'pool' (#{pool_val}) and 'max_connections' (#{max_conn_val}) are set to different values. Prefer just 'max_connections'."
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
if configuration_hash[:pool] && configuration_hash[:min_connections]
|
|
225
|
+
raise "Ambiguous configuration: when setting 'min_connections', use 'max_connections' instead of 'pool'."
|
|
226
|
+
end
|
|
227
|
+
end
|
|
166
228
|
end
|
|
167
229
|
end
|
|
168
230
|
end
|
|
@@ -41,10 +41,39 @@ module ActiveRecord
|
|
|
41
41
|
super(env_name, name, configuration_hash)
|
|
42
42
|
|
|
43
43
|
@url = url
|
|
44
|
-
@configuration_hash = @configuration_hash.merge(build_url_hash)
|
|
44
|
+
@configuration_hash = @configuration_hash.merge(build_url_hash)
|
|
45
|
+
|
|
46
|
+
if @configuration_hash[:schema_dump] == "false"
|
|
47
|
+
@configuration_hash[:schema_dump] = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
query_cache = parse_query_cache
|
|
51
|
+
@configuration_hash[:query_cache] = query_cache unless query_cache.nil?
|
|
52
|
+
|
|
53
|
+
to_boolean!(@configuration_hash, :replica)
|
|
54
|
+
to_boolean!(@configuration_hash, :database_tasks)
|
|
55
|
+
|
|
56
|
+
@configuration_hash.freeze
|
|
45
57
|
end
|
|
46
58
|
|
|
47
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
|
+
|
|
71
|
+
def to_boolean!(configuration_hash, key)
|
|
72
|
+
if configuration_hash[key].is_a?(String)
|
|
73
|
+
configuration_hash[key] = configuration_hash[key] != "false"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
48
77
|
# Return a Hash that can be merged into the main config that represents
|
|
49
78
|
# the passed in url
|
|
50
79
|
def build_url_hash
|
|
@@ -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
|
|
@@ -113,7 +115,7 @@ module ActiveRecord
|
|
|
113
115
|
|
|
114
116
|
if name
|
|
115
117
|
configs.find do |db_config|
|
|
116
|
-
db_config.name == name
|
|
118
|
+
db_config.name == name.to_s
|
|
117
119
|
end
|
|
118
120
|
else
|
|
119
121
|
configs
|
|
@@ -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
|
|
@@ -113,6 +113,26 @@ module ActiveRecord
|
|
|
113
113
|
# end
|
|
114
114
|
# end
|
|
115
115
|
#
|
|
116
|
+
# == Querying across records
|
|
117
|
+
#
|
|
118
|
+
# A consequence of delegated types is that querying attributes spread across multiple classes becomes slightly more
|
|
119
|
+
# tricky, but not impossible.
|
|
120
|
+
#
|
|
121
|
+
# The simplest method is to join the "superclass" to the "subclass" and apply the query parameters (i.e. <tt>#where</tt>)
|
|
122
|
+
# in appropriate places:
|
|
123
|
+
#
|
|
124
|
+
# Comment.joins(:entry).where(comments: { content: 'Hello!' }, entry: { creator: Current.user } )
|
|
125
|
+
#
|
|
126
|
+
# For convenience, add a scope on the concern. Now all classes that implement the concern will automatically include
|
|
127
|
+
# the method:
|
|
128
|
+
#
|
|
129
|
+
# # app/models/concerns/entryable.rb
|
|
130
|
+
# scope :with_entry, ->(attrs) { joins(:entry).where(entry: attrs) }
|
|
131
|
+
#
|
|
132
|
+
# Now the query can be shortened significantly:
|
|
133
|
+
#
|
|
134
|
+
# Comment.where(content: 'Hello!').with_entry(creator: Current.user)
|
|
135
|
+
#
|
|
116
136
|
# == Adding further delegation
|
|
117
137
|
#
|
|
118
138
|
# The delegated type shouldn't just answer the question of what the underlying class is called. In fact, that's
|
|
@@ -161,16 +181,16 @@ module ActiveRecord
|
|
|
161
181
|
# delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
|
|
162
182
|
# end
|
|
163
183
|
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# Entry.messages
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
# Entry.comments
|
|
171
|
-
#
|
|
172
|
-
#
|
|
173
|
-
#
|
|
184
|
+
# @entry.entryable_class # => Message or Comment
|
|
185
|
+
# @entry.entryable_name # => "message" or "comment"
|
|
186
|
+
# Entry.messages # => Entry.where(entryable_type: "Message")
|
|
187
|
+
# @entry.message? # => true when entryable_type == "Message"
|
|
188
|
+
# @entry.message # => returns the message record, when entryable_type == "Message", otherwise nil
|
|
189
|
+
# @entry.message_id # => returns entryable_id, when entryable_type == "Message", otherwise nil
|
|
190
|
+
# Entry.comments # => Entry.where(entryable_type: "Comment")
|
|
191
|
+
# @entry.comment? # => true when entryable_type == "Comment"
|
|
192
|
+
# @entry.comment # => returns the comment record, when entryable_type == "Comment", otherwise nil
|
|
193
|
+
# @entry.comment_id # => returns entryable_id, when entryable_type == "Comment", otherwise nil
|
|
174
194
|
#
|
|
175
195
|
# You can also declare namespaced types:
|
|
176
196
|
#
|
|
@@ -179,25 +199,25 @@ module ActiveRecord
|
|
|
179
199
|
# end
|
|
180
200
|
#
|
|
181
201
|
# Entry.access_notice_messages
|
|
182
|
-
# entry.access_notice_message
|
|
183
|
-
# entry.access_notice_message?
|
|
202
|
+
# @entry.access_notice_message
|
|
203
|
+
# @entry.access_notice_message?
|
|
184
204
|
#
|
|
185
|
-
#
|
|
205
|
+
# ==== Options
|
|
186
206
|
#
|
|
187
207
|
# The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
|
|
188
208
|
# The following options can be included to specialize the behavior of the delegated type convenience methods.
|
|
189
209
|
#
|
|
190
|
-
# [
|
|
210
|
+
# [+:foreign_key+]
|
|
191
211
|
# Specify the foreign key used for the convenience methods. By default this is guessed to be the passed
|
|
192
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
|
193
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
|
194
214
|
# the default <tt>:foreign_key</tt>.
|
|
195
|
-
# [
|
|
215
|
+
# [+:foreign_type+]
|
|
196
216
|
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
|
197
217
|
# +role+ with a "_type" suffix. A class that defines a
|
|
198
218
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
|
199
219
|
# the default <tt>:foreign_type</tt>.
|
|
200
|
-
# [
|
|
220
|
+
# [+:primary_key+]
|
|
201
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
|
202
222
|
# By default this is +id+.
|
|
203
223
|
#
|
|
@@ -206,10 +226,10 @@ module ActiveRecord
|
|
|
206
226
|
# delegated_type :entryable, types: %w[ Message Comment ], primary_key: :uuid, foreign_key: :entryable_uuid
|
|
207
227
|
# end
|
|
208
228
|
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
229
|
+
# @entry.message_uuid # => returns entryable_uuid, when entryable_type == "Message", otherwise nil
|
|
230
|
+
# @entry.comment_uuid # => returns entryable_uuid, when entryable_type == "Comment", otherwise nil
|
|
211
231
|
def delegated_type(role, types:, **options)
|
|
212
|
-
belongs_to role, options.delete(:scope), **options
|
|
232
|
+
belongs_to role, options.delete(:scope), **options, polymorphic: true
|
|
213
233
|
define_delegated_type_methods role, types: types, options: options
|
|
214
234
|
end
|
|
215
235
|
|
|
@@ -219,6 +239,10 @@ module ActiveRecord
|
|
|
219
239
|
role_type = options[:foreign_type] || "#{role}_type"
|
|
220
240
|
role_id = options[:foreign_key] || "#{role}_id"
|
|
221
241
|
|
|
242
|
+
define_singleton_method "#{role}_types" do
|
|
243
|
+
types.map(&:to_s)
|
|
244
|
+
end
|
|
245
|
+
|
|
222
246
|
define_method "#{role}_class" do
|
|
223
247
|
public_send(role_type).constantize
|
|
224
248
|
end
|
|
@@ -7,114 +7,99 @@ 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
|
-
def method_missing(name,
|
|
16
|
-
match = Method.match(
|
|
17
|
+
def method_missing(name, ...)
|
|
18
|
+
match = Method.match(name)
|
|
17
19
|
|
|
18
|
-
if match && match.valid?
|
|
19
|
-
match.define
|
|
20
|
-
send(name,
|
|
20
|
+
if match && match.valid?(self, name)
|
|
21
|
+
match.define(self, name)
|
|
22
|
+
send(name, ...)
|
|
21
23
|
else
|
|
22
24
|
super
|
|
23
25
|
end
|
|
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
|
|
@@ -8,7 +8,8 @@ module ActiveRecord
|
|
|
8
8
|
class Config
|
|
9
9
|
attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
|
|
10
10
|
:support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
|
|
11
|
-
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption
|
|
11
|
+
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption,
|
|
12
|
+
:compressor
|
|
12
13
|
|
|
13
14
|
def initialize
|
|
14
15
|
set_defaults
|
|
@@ -55,6 +56,7 @@ module ActiveRecord
|
|
|
55
56
|
self.previous_schemes = []
|
|
56
57
|
self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
|
|
57
58
|
self.hash_digest_class = OpenSSL::Digest::SHA1
|
|
59
|
+
self.compressor = Zlib
|
|
58
60
|
|
|
59
61
|
# TODO: Setting to false for now as the implementation is a bit experimental
|
|
60
62
|
self.extend_queries = false
|