activerecord 7.1.5.1 → 7.2.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 +645 -2329
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- 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/collection_association.rb +7 -1
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +59 -292
- 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 +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +54 -63
- data/lib/active_record/attributes.rb +61 -47
- data/lib/active_record/autosave_association.rb +12 -29
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +189 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +40 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +85 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +3 -3
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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/enum.rb +18 -1
- data/lib/active_record/errors.rb +46 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- 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 +85 -76
- data/lib/active_record/model_schema.rb +31 -68
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +37 -55
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +40 -43
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +96 -63
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +224 -58
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/signed_id.rb +20 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +69 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +70 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +16 -10
@@ -4,20 +4,12 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module Trilogy
|
6
6
|
module DatabaseStatements
|
7
|
-
def
|
8
|
-
result = super
|
9
|
-
with_raw_connection do |conn|
|
10
|
-
conn.next_result while conn.more_results_exist?
|
11
|
-
end
|
12
|
-
result
|
13
|
-
end
|
14
|
-
|
15
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
7
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
16
8
|
sql = transform_query(sql)
|
17
9
|
check_if_write_query(sql)
|
18
10
|
mark_transaction_written_if_write(sql)
|
19
11
|
|
20
|
-
result = raw_execute(sql, name, async: async)
|
12
|
+
result = raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
21
13
|
ActiveRecord::Result.new(result.fields, result.to_a)
|
22
14
|
end
|
23
15
|
|
@@ -26,7 +18,8 @@ module ActiveRecord
|
|
26
18
|
check_if_write_query(sql)
|
27
19
|
mark_transaction_written_if_write(sql)
|
28
20
|
|
29
|
-
|
21
|
+
sql, _binds = sql_for_insert(sql, pk, binds, returning)
|
22
|
+
raw_execute(sql, name)
|
30
23
|
end
|
31
24
|
|
32
25
|
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
@@ -42,19 +35,27 @@ module ActiveRecord
|
|
42
35
|
|
43
36
|
private
|
44
37
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
45
|
-
log(sql, name, async: async) do
|
38
|
+
log(sql, name, async: async) do |notification_payload|
|
46
39
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
47
40
|
sync_timezone_changes(conn)
|
48
41
|
result = conn.query(sql)
|
42
|
+
while conn.more_results_exist?
|
43
|
+
conn.next_result
|
44
|
+
end
|
49
45
|
verified!
|
50
46
|
handle_warnings(sql)
|
47
|
+
notification_payload[:row_count] = result.count
|
51
48
|
result
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
53
|
def last_inserted_id(result)
|
57
|
-
|
54
|
+
if supports_insert_returning?
|
55
|
+
super
|
56
|
+
else
|
57
|
+
result.last_insert_id
|
58
|
+
end
|
58
59
|
end
|
59
60
|
|
60
61
|
def sync_timezone_changes(conn)
|
@@ -71,7 +72,6 @@ module ActiveRecord
|
|
71
72
|
combine_multi_statements(statements).each do |statement|
|
72
73
|
with_raw_connection do |conn|
|
73
74
|
raw_execute(statement, name)
|
74
|
-
conn.next_result while conn.more_results_exist?
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -90,7 +90,7 @@ module ActiveRecord
|
|
90
90
|
|
91
91
|
yield
|
92
92
|
ensure
|
93
|
-
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
|
93
|
+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF) if active?
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
@@ -2,44 +2,17 @@
|
|
2
2
|
|
3
3
|
require "active_record/connection_adapters/abstract_mysql_adapter"
|
4
4
|
|
5
|
-
gem "trilogy", "~> 2.
|
5
|
+
gem "trilogy", "~> 2.7"
|
6
6
|
require "trilogy"
|
7
7
|
|
8
8
|
require "active_record/connection_adapters/trilogy/database_statements"
|
9
9
|
|
10
10
|
module ActiveRecord
|
11
|
-
module ConnectionHandling # :nodoc:
|
12
|
-
def trilogy_adapter_class
|
13
|
-
ConnectionAdapters::TrilogyAdapter
|
14
|
-
end
|
15
|
-
|
16
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
17
|
-
def trilogy_connection(config)
|
18
|
-
configuration = config.dup
|
19
|
-
|
20
|
-
# Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
|
21
|
-
# matched rather than number of rows updated.
|
22
|
-
configuration[:found_rows] = true
|
23
|
-
|
24
|
-
options = [
|
25
|
-
configuration[:host],
|
26
|
-
configuration[:port],
|
27
|
-
configuration[:database],
|
28
|
-
configuration[:username],
|
29
|
-
configuration[:password],
|
30
|
-
configuration[:socket],
|
31
|
-
0
|
32
|
-
]
|
33
|
-
|
34
|
-
trilogy_adapter_class.new nil, logger, options, configuration
|
35
|
-
end
|
36
|
-
end
|
37
11
|
module ConnectionAdapters
|
38
12
|
class TrilogyAdapter < AbstractMysqlAdapter
|
39
13
|
ER_BAD_DB_ERROR = 1049
|
40
14
|
ER_DBACCESS_DENIED_ERROR = 1044
|
41
15
|
ER_ACCESS_DENIED_ERROR = 1045
|
42
|
-
ER_SERVER_SHUTDOWN = 1053
|
43
16
|
|
44
17
|
ADAPTER_NAME = "Trilogy"
|
45
18
|
|
@@ -99,16 +72,22 @@ module ActiveRecord
|
|
99
72
|
end
|
100
73
|
end
|
101
74
|
|
102
|
-
def initialize(
|
103
|
-
|
75
|
+
def initialize(config, *)
|
76
|
+
config = config.dup
|
77
|
+
|
78
|
+
# Trilogy ignores `socket` if `host is set. We want the opposite to allow
|
79
|
+
# configuring UNIX domain sockets via `DATABASE_URL`.
|
80
|
+
config.delete(:host) if config[:socket]
|
104
81
|
|
105
|
-
|
82
|
+
# Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
|
83
|
+
# matched rather than number of rows updated.
|
84
|
+
config[:found_rows] = true
|
85
|
+
|
86
|
+
if config[:prepared_statements]
|
106
87
|
raise ArgumentError, "Trilogy currently doesn't support prepared statements. Remove `prepared_statements: true` from your database configuration."
|
107
88
|
end
|
108
89
|
|
109
|
-
|
110
|
-
# configuring UNIX domain sockets via `DATABASE_URL`.
|
111
|
-
@config.delete(:host) if @config[:socket]
|
90
|
+
super
|
112
91
|
end
|
113
92
|
|
114
93
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
@@ -137,14 +116,12 @@ module ActiveRecord
|
|
137
116
|
true
|
138
117
|
end
|
139
118
|
|
140
|
-
def
|
141
|
-
|
142
|
-
conn.escape(string)
|
143
|
-
end
|
119
|
+
def connected?
|
120
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
144
121
|
end
|
145
122
|
|
146
123
|
def active?
|
147
|
-
|
124
|
+
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
148
125
|
rescue ::Trilogy::Error
|
149
126
|
false
|
150
127
|
end
|
@@ -206,7 +183,7 @@ module ActiveRecord
|
|
206
183
|
end
|
207
184
|
|
208
185
|
def full_version
|
209
|
-
|
186
|
+
database_version.full_version_string
|
210
187
|
end
|
211
188
|
|
212
189
|
def get_full_version
|
@@ -219,18 +196,12 @@ module ActiveRecord
|
|
219
196
|
if exception.is_a?(::Trilogy::TimeoutError) && !exception.error_code
|
220
197
|
return ActiveRecord::AdapterTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
221
198
|
end
|
222
|
-
error_code = exception.error_code if exception.respond_to?(:error_code)
|
223
|
-
|
224
|
-
case error_code
|
225
|
-
when ER_SERVER_SHUTDOWN
|
226
|
-
return ConnectionFailed.new(message, connection_pool: @pool)
|
227
|
-
end
|
228
199
|
|
229
200
|
case exception
|
230
|
-
when
|
201
|
+
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError
|
231
202
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
232
203
|
when ::Trilogy::Error
|
233
|
-
if
|
204
|
+
if exception.is_a?(SystemCallError) || exception.message.include?("TRILOGY_INVALID_SEQUENCE_ID")
|
234
205
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
235
206
|
end
|
236
207
|
end
|
@@ -1,9 +1,130 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
extend ActiveSupport::Autoload
|
6
8
|
|
9
|
+
@adapters = {}
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Registers a custom database adapter.
|
13
|
+
#
|
14
|
+
# Can also be used to define aliases.
|
15
|
+
#
|
16
|
+
# == Example
|
17
|
+
#
|
18
|
+
# ActiveRecord::ConnectionAdapters.register("megadb", "MegaDB::ActiveRecordAdapter", "mega_db/active_record_adapter")
|
19
|
+
#
|
20
|
+
# ActiveRecord::ConnectionAdapters.register("mysql", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter")
|
21
|
+
#
|
22
|
+
def register(name, class_name, path = class_name.underscore)
|
23
|
+
@adapters[name.to_s] = [class_name, path]
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve(adapter_name) # :nodoc:
|
27
|
+
# Require the adapter itself and give useful feedback about
|
28
|
+
# 1. Missing adapter gems.
|
29
|
+
# 2. Incorrectly registered adapters.
|
30
|
+
# 3. Adapter gems' missing dependencies.
|
31
|
+
class_name, path_to_adapter = @adapters[adapter_name.to_s]
|
32
|
+
|
33
|
+
unless class_name
|
34
|
+
# To provide better error messages for adapters expecting the pre-7.2 adapter registration API, we attempt
|
35
|
+
# to load the adapter file from the old location which was required by convention, and then raise an error
|
36
|
+
# describing how to upgrade the adapter to the new API.
|
37
|
+
legacy_adapter_path = "active_record/connection_adapters/#{adapter_name}_adapter"
|
38
|
+
legacy_adapter_connection_method_name = "#{adapter_name}_connection".to_sym
|
39
|
+
|
40
|
+
begin
|
41
|
+
require legacy_adapter_path
|
42
|
+
# If we reach here it means we found the found a file that may be the legacy adapter and should raise.
|
43
|
+
if ActiveRecord::ConnectionHandling.method_defined?(legacy_adapter_connection_method_name)
|
44
|
+
# If we find the connection method then we care certain it is a legacy adapter.
|
45
|
+
deprecation_message = <<~MSG.squish
|
46
|
+
Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
|
47
|
+
Rails 7.2 has changed the way Active Record database adapters are loaded. The adapter needs to be
|
48
|
+
updated to register itself rather than being loaded by convention.
|
49
|
+
Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
|
50
|
+
be modified.
|
51
|
+
See:
|
52
|
+
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
|
53
|
+
MSG
|
54
|
+
|
55
|
+
exception_message = <<~MSG.squish
|
56
|
+
Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
|
57
|
+
Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
|
58
|
+
be modified.
|
59
|
+
MSG
|
60
|
+
else
|
61
|
+
# If we do not find the connection method we are much less certain it is a legacy adapter. Even though the
|
62
|
+
# file exists in the location defined by convenntion, it does not necessarily mean that file is supposed
|
63
|
+
# to define the adapter the legacy way. So raise an error that explains both possibilities.
|
64
|
+
deprecation_message = <<~MSG.squish
|
65
|
+
Database configuration specifies nonexistent '#{adapter_name}' adapter.
|
66
|
+
Available adapters are: #{@adapters.keys.sort.join(", ")}.
|
67
|
+
Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
|
68
|
+
adapter gem to your Gemfile if it's not in the list of available adapters.
|
69
|
+
Rails 7.2 has changed the way Active Record database adapters are loaded. Ensure that the adapter in
|
70
|
+
the Gemfile is at the latest version. If it is up to date, the adapter may need to be modified.
|
71
|
+
See:
|
72
|
+
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
|
73
|
+
MSG
|
74
|
+
|
75
|
+
exception_message = <<~MSG.squish
|
76
|
+
Database configuration specifies nonexistent '#{adapter_name}' adapter.
|
77
|
+
Available adapters are: #{@adapters.keys.sort.join(", ")}.
|
78
|
+
Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
|
79
|
+
adapter gem to your Gemfile and that it is at its latest version. If it is up to date, the adapter may
|
80
|
+
need to be modified.
|
81
|
+
MSG
|
82
|
+
end
|
83
|
+
|
84
|
+
ActiveRecord.deprecator.warn(deprecation_message)
|
85
|
+
raise AdapterNotFound, exception_message
|
86
|
+
rescue LoadError => error
|
87
|
+
# The adapter was not found in the legacy location so fall through to the error handling for a missing adapter.
|
88
|
+
end
|
89
|
+
|
90
|
+
raise AdapterNotFound, <<~MSG.squish
|
91
|
+
Database configuration specifies nonexistent '#{adapter_name}' adapter.
|
92
|
+
Available adapters are: #{@adapters.keys.sort.join(", ")}.
|
93
|
+
Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
|
94
|
+
adapter gem to your Gemfile if it's not in the list of available adapters.
|
95
|
+
MSG
|
96
|
+
end
|
97
|
+
|
98
|
+
unless Object.const_defined?(class_name)
|
99
|
+
begin
|
100
|
+
require path_to_adapter
|
101
|
+
rescue LoadError => error
|
102
|
+
# We couldn't require the adapter itself.
|
103
|
+
if error.path == path_to_adapter
|
104
|
+
# We can assume here that a non-builtin adapter was specified and the path
|
105
|
+
# registered by the adapter gem is incorrect.
|
106
|
+
raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Ensure that the path registered by the adapter gem is correct. #{error.message}", error.backtrace
|
107
|
+
else
|
108
|
+
# Bubbled up from the adapter require. Prefix the exception message
|
109
|
+
# with some guidance about how to address it and reraise.
|
110
|
+
raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Missing a gem it depends on? #{error.message}", error.backtrace
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
begin
|
116
|
+
Object.const_get(class_name)
|
117
|
+
rescue NameError => error
|
118
|
+
raise AdapterNotFound, "Could not load the #{class_name} Active Record adapter (#{error.message})."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
register "sqlite3", "ActiveRecord::ConnectionAdapters::SQLite3Adapter", "active_record/connection_adapters/sqlite3_adapter"
|
124
|
+
register "mysql2", "ActiveRecord::ConnectionAdapters::Mysql2Adapter", "active_record/connection_adapters/mysql2_adapter"
|
125
|
+
register "trilogy", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter"
|
126
|
+
register "postgresql", "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter", "active_record/connection_adapters/postgresql_adapter"
|
127
|
+
|
7
128
|
eager_autoload do
|
8
129
|
autoload :AbstractAdapter
|
9
130
|
end
|
@@ -243,22 +243,64 @@ module ActiveRecord
|
|
243
243
|
# Clears the query cache for all connections associated with the current thread.
|
244
244
|
def clear_query_caches_for_current_thread
|
245
245
|
connection_handler.each_connection_pool do |pool|
|
246
|
-
pool.
|
246
|
+
pool.clear_query_cache
|
247
247
|
end
|
248
248
|
end
|
249
249
|
|
250
250
|
# Returns the connection currently associated with the class. This can
|
251
251
|
# also be used to "borrow" the connection to do database work unrelated
|
252
252
|
# to any of the specific Active Records.
|
253
|
+
# The connection will remain leased for the entire duration of the request
|
254
|
+
# or job, or until +#release_connection+ is called.
|
255
|
+
def lease_connection
|
256
|
+
connection_pool.lease_connection
|
257
|
+
end
|
258
|
+
|
259
|
+
# Soft deprecated. Use +#with_connection+ or +#lease_connection+ instead.
|
253
260
|
def connection
|
254
|
-
|
261
|
+
pool = connection_pool
|
262
|
+
if pool.permanent_lease?
|
263
|
+
case ActiveRecord.permanent_connection_checkout
|
264
|
+
when :deprecated
|
265
|
+
ActiveRecord.deprecator.warn <<~MESSAGE
|
266
|
+
Called deprecated `ActiveRecord::Base.connection` method.
|
267
|
+
|
268
|
+
Either use `with_connection` or `lease_connection`.
|
269
|
+
MESSAGE
|
270
|
+
when :disallowed
|
271
|
+
raise ActiveRecordError, <<~MESSAGE
|
272
|
+
Called deprecated `ActiveRecord::Base.connection` method.
|
273
|
+
|
274
|
+
Either use `with_connection` or `lease_connection`.
|
275
|
+
MESSAGE
|
276
|
+
end
|
277
|
+
pool.lease_connection
|
278
|
+
else
|
279
|
+
pool.active_connection
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Return the currently leased connection into the pool
|
284
|
+
def release_connection
|
285
|
+
connection_pool.release_connection
|
286
|
+
end
|
287
|
+
|
288
|
+
# Checkouts a connection from the pool, yield it and then check it back in.
|
289
|
+
# If a connection was already leased via #lease_connection or a parent call to
|
290
|
+
# #with_connection, that same connection is yieled.
|
291
|
+
# If #lease_connection is called inside the block, the connection won't be checked
|
292
|
+
# back in.
|
293
|
+
# If #connection is called inside the block, the connection won't be checked back in
|
294
|
+
# unless the +prevent_permanent_checkout+ argument is set to +true+.
|
295
|
+
def with_connection(prevent_permanent_checkout: false, &block)
|
296
|
+
connection_pool.with_connection(prevent_permanent_checkout: prevent_permanent_checkout, &block)
|
255
297
|
end
|
256
298
|
|
257
299
|
attr_writer :connection_specification_name
|
258
300
|
|
259
301
|
# Returns the connection specification name from the current class or its parent.
|
260
302
|
def connection_specification_name
|
261
|
-
if
|
303
|
+
if @connection_specification_name.nil?
|
262
304
|
return self == Base ? Base.name : superclass.connection_specification_name
|
263
305
|
end
|
264
306
|
@connection_specification_name
|
@@ -279,8 +321,12 @@ module ActiveRecord
|
|
279
321
|
connection_pool.db_config
|
280
322
|
end
|
281
323
|
|
324
|
+
def adapter_class # :nodoc:
|
325
|
+
connection_pool.db_config.adapter_class
|
326
|
+
end
|
327
|
+
|
282
328
|
def connection_pool
|
283
|
-
connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard
|
329
|
+
connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard, strict: true)
|
284
330
|
end
|
285
331
|
|
286
332
|
def retrieve_connection
|
@@ -292,16 +338,9 @@ module ActiveRecord
|
|
292
338
|
connection_handler.connected?(connection_specification_name, role: current_role, shard: current_shard)
|
293
339
|
end
|
294
340
|
|
295
|
-
def remove_connection
|
296
|
-
if
|
297
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
298
|
-
The name argument for `#remove_connection` is deprecated without replacement
|
299
|
-
and will be removed in Rails 7.2. `#remove_connection` should always be called
|
300
|
-
on the connection class directly, which makes the name argument obsolete.
|
301
|
-
MSG
|
302
|
-
end
|
341
|
+
def remove_connection
|
342
|
+
name = @connection_specification_name if defined?(@connection_specification_name)
|
303
343
|
|
304
|
-
name ||= @connection_specification_name if defined?(@connection_specification_name)
|
305
344
|
# if removing a connection that has a pool, we reset the
|
306
345
|
# connection_specification_name so it will use the parent
|
307
346
|
# pool.
|
@@ -312,39 +351,15 @@ module ActiveRecord
|
|
312
351
|
connection_handler.remove_connection_pool(name, role: current_role, shard: current_shard)
|
313
352
|
end
|
314
353
|
|
315
|
-
def
|
316
|
-
|
317
|
-
end
|
318
|
-
|
319
|
-
def clear_active_connections!(role = nil)
|
320
|
-
deprecation_for_delegation(__method__)
|
321
|
-
connection_handler.clear_active_connections!(role)
|
322
|
-
end
|
323
|
-
|
324
|
-
def clear_reloadable_connections!(role = nil)
|
325
|
-
deprecation_for_delegation(__method__)
|
326
|
-
connection_handler.clear_reloadable_connections!(role)
|
354
|
+
def schema_cache # :nodoc:
|
355
|
+
connection_pool.schema_cache
|
327
356
|
end
|
328
357
|
|
329
|
-
def
|
330
|
-
|
331
|
-
connection_handler.clear_all_connections!(role)
|
332
|
-
end
|
333
|
-
|
334
|
-
def flush_idle_connections!(role = nil)
|
335
|
-
deprecation_for_delegation(__method__)
|
336
|
-
connection_handler.flush_idle_connections!(role)
|
358
|
+
def clear_cache! # :nodoc:
|
359
|
+
connection_pool.schema_cache.clear!
|
337
360
|
end
|
338
361
|
|
339
362
|
private
|
340
|
-
def deprecation_for_delegation(method)
|
341
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
342
|
-
Calling `ActiveRecord::Base.#{method} is deprecated. Please
|
343
|
-
call the method directly on the connection handler; for
|
344
|
-
example: `ActiveRecord::Base.connection_handler.#{method}`.
|
345
|
-
MSG
|
346
|
-
end
|
347
|
-
|
348
363
|
def resolve_config_for_connection(config_or_env)
|
349
364
|
raise "Anonymous class is not allowed." unless name
|
350
365
|
|
data/lib/active_record/core.rb
CHANGED
@@ -73,7 +73,7 @@ module ActiveRecord
|
|
73
73
|
end
|
74
74
|
self.configurations = {}
|
75
75
|
|
76
|
-
# Returns fully resolved ActiveRecord::DatabaseConfigurations object
|
76
|
+
# Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
|
77
77
|
def self.configurations
|
78
78
|
@@configurations
|
79
79
|
end
|
@@ -102,6 +102,21 @@ module ActiveRecord
|
|
102
102
|
|
103
103
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
104
104
|
|
105
|
+
##
|
106
|
+
# :singleton-method:
|
107
|
+
#
|
108
|
+
# Specifies the attributes that will be included in the output of the
|
109
|
+
# #inspect method:
|
110
|
+
#
|
111
|
+
# Post.attributes_for_inspect = [:id, :title]
|
112
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
113
|
+
#
|
114
|
+
# When set to `:all` inspect will list all the record's attributes:
|
115
|
+
#
|
116
|
+
# Post.attributes_for_inspect = :all
|
117
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
118
|
+
class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
|
119
|
+
|
105
120
|
def self.application_record_class? # :nodoc:
|
106
121
|
if ActiveRecord.application_record_class
|
107
122
|
self == ActiveRecord.application_record_class
|
@@ -350,8 +365,8 @@ module ActiveRecord
|
|
350
365
|
super
|
351
366
|
elsif abstract_class?
|
352
367
|
"#{super}(abstract)"
|
353
|
-
elsif !connected?
|
354
|
-
"#{super} (call '#{super}.
|
368
|
+
elsif !schema_loaded? && !connected?
|
369
|
+
"#{super} (call '#{super}.load_schema' to load schema informations)"
|
355
370
|
elsif table_exists?
|
356
371
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
357
372
|
"#{super}(#{attr_list})"
|
@@ -373,7 +388,7 @@ module ActiveRecord
|
|
373
388
|
TypeCaster::Map.new(self)
|
374
389
|
end
|
375
390
|
|
376
|
-
def cached_find_by_statement(key, &block) # :nodoc:
|
391
|
+
def cached_find_by_statement(connection, key, &block) # :nodoc:
|
377
392
|
cache = @find_by_statement_cache[connection.prepared_statements]
|
378
393
|
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
379
394
|
end
|
@@ -416,21 +431,23 @@ module ActiveRecord
|
|
416
431
|
end
|
417
432
|
|
418
433
|
def cached_find_by(keys, values)
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
434
|
+
with_connection do |connection|
|
435
|
+
statement = cached_find_by_statement(connection, keys) { |params|
|
436
|
+
wheres = keys.index_with do |key|
|
437
|
+
if key.is_a?(Array)
|
438
|
+
[key.map { params.bind }]
|
439
|
+
else
|
440
|
+
params.bind
|
441
|
+
end
|
425
442
|
end
|
426
|
-
|
427
|
-
|
428
|
-
}
|
443
|
+
where(wheres).limit(1)
|
444
|
+
}
|
429
445
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
446
|
+
begin
|
447
|
+
statement.execute(values.flatten, connection, allow_retry: true).first
|
448
|
+
rescue TypeError
|
449
|
+
raise ActiveRecord::StatementInvalid
|
450
|
+
end
|
434
451
|
end
|
435
452
|
end
|
436
453
|
end
|
@@ -450,7 +467,7 @@ module ActiveRecord
|
|
450
467
|
init_internals
|
451
468
|
initialize_internals_callback
|
452
469
|
|
453
|
-
|
470
|
+
super
|
454
471
|
|
455
472
|
yield self if block_given?
|
456
473
|
_run_initialize_callbacks
|
@@ -719,21 +736,28 @@ module ActiveRecord
|
|
719
736
|
self.class.connection_handler
|
720
737
|
end
|
721
738
|
|
722
|
-
# Returns the
|
739
|
+
# Returns the attributes of the record as a nicely formatted string.
|
740
|
+
#
|
741
|
+
# Post.first.inspect
|
742
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
743
|
+
#
|
744
|
+
# The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
|
745
|
+
#
|
746
|
+
# Post.attributes_for_inspect = [:id, :title]
|
747
|
+
# Post.first.inspect
|
748
|
+
# #=> "#<Post id: 1, title: "Hello, World!">"
|
723
749
|
def inspect
|
724
|
-
|
725
|
-
|
726
|
-
inspection = if defined?(@attributes) && @attributes
|
727
|
-
attribute_names.filter_map do |name|
|
728
|
-
if _has_attribute?(name)
|
729
|
-
"#{name}: #{attribute_for_inspect(name)}"
|
730
|
-
end
|
731
|
-
end.join(", ")
|
732
|
-
else
|
733
|
-
"not initialized"
|
734
|
-
end
|
750
|
+
inspect_with_attributes(attributes_for_inspect)
|
751
|
+
end
|
735
752
|
|
736
|
-
|
753
|
+
# Returns all attributes of the record as a nicely formatted string,
|
754
|
+
# ignoring <tt>.attributes_for_inspect</tt>.
|
755
|
+
#
|
756
|
+
# Post.first.full_inspect
|
757
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
758
|
+
#
|
759
|
+
def full_inspect
|
760
|
+
inspect_with_attributes(all_attributes_for_inspect)
|
737
761
|
end
|
738
762
|
|
739
763
|
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
@@ -741,17 +765,17 @@ module ActiveRecord
|
|
741
765
|
def pretty_print(pp)
|
742
766
|
return super if custom_inspect_method_defined?
|
743
767
|
pp.object_address_group(self) do
|
744
|
-
if
|
745
|
-
attr_names =
|
768
|
+
if @attributes
|
769
|
+
attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
|
746
770
|
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
771
|
+
attr_name = attr_name.to_s
|
747
772
|
pp.breakable " "
|
748
773
|
pp.group(1) do
|
749
774
|
pp.text attr_name
|
750
775
|
pp.text ":"
|
751
776
|
pp.breakable
|
752
|
-
value =
|
753
|
-
|
754
|
-
pp.pp value
|
777
|
+
value = attribute_for_inspect(attr_name)
|
778
|
+
pp.text value
|
755
779
|
end
|
756
780
|
end
|
757
781
|
else
|
@@ -789,7 +813,6 @@ module ActiveRecord
|
|
789
813
|
@strict_loading_mode = :all
|
790
814
|
|
791
815
|
klass.define_attribute_methods
|
792
|
-
klass.generate_alias_attributes
|
793
816
|
end
|
794
817
|
|
795
818
|
def initialize_internals_callback
|
@@ -809,5 +832,30 @@ module ActiveRecord
|
|
809
832
|
def inspection_filter
|
810
833
|
self.class.inspection_filter
|
811
834
|
end
|
835
|
+
|
836
|
+
def inspect_with_attributes(attributes_to_list)
|
837
|
+
inspection = if @attributes
|
838
|
+
attributes_to_list.filter_map do |name|
|
839
|
+
name = name.to_s
|
840
|
+
if _has_attribute?(name)
|
841
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
842
|
+
end
|
843
|
+
end.join(", ")
|
844
|
+
else
|
845
|
+
"not initialized"
|
846
|
+
end
|
847
|
+
|
848
|
+
"#<#{self.class} #{inspection}>"
|
849
|
+
end
|
850
|
+
|
851
|
+
def attributes_for_inspect
|
852
|
+
self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
|
853
|
+
end
|
854
|
+
|
855
|
+
def all_attributes_for_inspect
|
856
|
+
return [] unless @attributes
|
857
|
+
|
858
|
+
attribute_names
|
859
|
+
end
|
812
860
|
end
|
813
861
|
end
|