activerecord 7.2.3 → 8.0.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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +192 -1261
- data/README.rdoc +2 -2
- data/lib/active_record/associations/alias_tracker.rb +4 -6
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/belongs_to_association.rb +2 -18
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +50 -32
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods.rb +19 -24
- data/lib/active_record/attributes.rb +26 -37
- data/lib/active_record/autosave_association.rb +81 -49
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -75
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +27 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -58
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -15
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +12 -14
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +51 -9
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +44 -101
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -13
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +60 -22
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_handling.rb +29 -11
- data/lib/active_record/core.rb +15 -60
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -3
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +35 -29
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +12 -13
- data/lib/active_record/errors.rb +16 -8
- data/lib/active_record/fixture_set/table_row.rb +2 -19
- data/lib/active_record/fixtures.rb +0 -1
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +36 -35
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +5 -4
- data/lib/active_record/query_logs.rb +98 -44
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +10 -10
- data/lib/active_record/railtie.rb +5 -6
- data/lib/active_record/railties/databases.rake +1 -2
- data/lib/active_record/reflection.rb +9 -7
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +55 -55
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +31 -32
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +0 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +5 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +90 -91
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +2 -8
- data/lib/active_record/relation.rb +77 -76
- data/lib/active_record/result.rb +68 -7
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +16 -29
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +6 -7
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +24 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +1 -3
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +16 -1
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/crud.rb +0 -2
- data/lib/arel/delete_manager.rb +0 -5
- data/lib/arel/nodes/delete_statement.rb +2 -4
- data/lib/arel/nodes/update_statement.rb +2 -4
- data/lib/arel/select_manager.rb +2 -6
- data/lib/arel/update_manager.rb +0 -5
- data/lib/arel/visitors/dot.rb +0 -2
- data/lib/arel/visitors/sqlite.rb +0 -25
- data/lib/arel/visitors/to_sql.rb +1 -3
- metadata +14 -11
|
@@ -11,9 +11,12 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
|
11
11
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
|
12
12
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
|
13
13
|
|
|
14
|
-
gem "sqlite3", ">= 1
|
|
14
|
+
gem "sqlite3", ">= 2.1"
|
|
15
15
|
require "sqlite3"
|
|
16
16
|
|
|
17
|
+
# Suppress the warning that SQLite3 issues when open writable connections are carried across fork()
|
|
18
|
+
SQLite3::ForkSafety.suppress_warnings!
|
|
19
|
+
|
|
17
20
|
module ActiveRecord
|
|
18
21
|
module ConnectionAdapters # :nodoc:
|
|
19
22
|
# = Active Record SQLite3 Adapter
|
|
@@ -21,7 +24,7 @@ module ActiveRecord
|
|
|
21
24
|
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
|
22
25
|
# (available as gem from https://rubygems.org/gems/sqlite3).
|
|
23
26
|
#
|
|
24
|
-
#
|
|
27
|
+
# Options:
|
|
25
28
|
#
|
|
26
29
|
# * <tt>:database</tt> - Path to the database file.
|
|
27
30
|
class SQLite3Adapter < AbstractAdapter
|
|
@@ -45,7 +48,7 @@ module ActiveRecord
|
|
|
45
48
|
args << "-header" if options[:header]
|
|
46
49
|
args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
|
|
47
50
|
|
|
48
|
-
find_cmd_and_exec(
|
|
51
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
|
|
@@ -119,9 +122,14 @@ module ActiveRecord
|
|
|
119
122
|
end
|
|
120
123
|
end
|
|
121
124
|
|
|
125
|
+
@last_affected_rows = nil
|
|
126
|
+
@previous_read_uncommitted = nil
|
|
122
127
|
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
|
123
|
-
@connection_parameters = @config.merge(
|
|
124
|
-
|
|
128
|
+
@connection_parameters = @config.merge(
|
|
129
|
+
database: @config[:database].to_s,
|
|
130
|
+
results_as_hash: true,
|
|
131
|
+
default_transaction_mode: :immediate,
|
|
132
|
+
)
|
|
125
133
|
end
|
|
126
134
|
|
|
127
135
|
def database_exists?
|
|
@@ -199,12 +207,7 @@ module ActiveRecord
|
|
|
199
207
|
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
200
208
|
end
|
|
201
209
|
|
|
202
|
-
|
|
203
|
-
if connected?
|
|
204
|
-
verified!
|
|
205
|
-
true
|
|
206
|
-
end
|
|
207
|
-
end
|
|
210
|
+
alias_method :active?, :connected?
|
|
208
211
|
|
|
209
212
|
alias :reset! :reconnect!
|
|
210
213
|
|
|
@@ -283,6 +286,38 @@ module ActiveRecord
|
|
|
283
286
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
|
284
287
|
end
|
|
285
288
|
|
|
289
|
+
VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.+)\)/i
|
|
290
|
+
|
|
291
|
+
# Returns a list of defined virtual tables
|
|
292
|
+
def virtual_tables
|
|
293
|
+
query = <<~SQL
|
|
294
|
+
SELECT name, sql FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL %';
|
|
295
|
+
SQL
|
|
296
|
+
|
|
297
|
+
exec_query(query, "SCHEMA").cast_values.each_with_object({}) do |row, memo|
|
|
298
|
+
table_name, sql = row[0], row[1]
|
|
299
|
+
_, module_name, arguments = sql.match(VIRTUAL_TABLE_REGEX).to_a
|
|
300
|
+
memo[table_name] = [module_name, arguments]
|
|
301
|
+
end.to_a
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Creates a virtual table
|
|
305
|
+
#
|
|
306
|
+
# Example:
|
|
307
|
+
# create_virtual_table :emails, :fts5, ['sender', 'title',' body']
|
|
308
|
+
def create_virtual_table(table_name, module_name, values)
|
|
309
|
+
exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Drops a virtual table
|
|
313
|
+
#
|
|
314
|
+
# Although this command ignores +module_name+ and +values+,
|
|
315
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
316
|
+
# In that case, +module_name+, +values+ and +options+ will be used by #create_virtual_table.
|
|
317
|
+
def drop_virtual_table(table_name, module_name, values, **options)
|
|
318
|
+
drop_table(table_name)
|
|
319
|
+
end
|
|
320
|
+
|
|
286
321
|
# Renames a table.
|
|
287
322
|
#
|
|
288
323
|
# Example:
|
|
@@ -372,7 +407,7 @@ module ActiveRecord
|
|
|
372
407
|
end
|
|
373
408
|
alias :add_belongs_to :add_reference
|
|
374
409
|
|
|
375
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
|
410
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
376
411
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
377
412
|
def foreign_keys(table_name)
|
|
378
413
|
# SQLite returns 1 row for each column of composite foreign keys.
|
|
@@ -433,10 +468,6 @@ module ActiveRecord
|
|
|
433
468
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
|
434
469
|
end
|
|
435
470
|
|
|
436
|
-
def use_insert_returning?
|
|
437
|
-
@use_insert_returning
|
|
438
|
-
end
|
|
439
|
-
|
|
440
471
|
def get_database_version # :nodoc:
|
|
441
472
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
|
442
473
|
end
|
|
@@ -547,8 +578,8 @@ module ActiveRecord
|
|
|
547
578
|
yield definition if block_given?
|
|
548
579
|
end
|
|
549
580
|
|
|
550
|
-
|
|
551
|
-
|
|
581
|
+
transaction do
|
|
582
|
+
disable_referential_integrity do
|
|
552
583
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
553
584
|
move_table(altered_table_name, table_name, &caller)
|
|
554
585
|
end
|
|
@@ -666,6 +697,8 @@ module ActiveRecord
|
|
|
666
697
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
667
698
|
elsif exception.message.match?(/called on a closed database/i)
|
|
668
699
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
700
|
+
elsif exception.is_a?(::SQLite3::BusyException)
|
|
701
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
669
702
|
else
|
|
670
703
|
super
|
|
671
704
|
end
|
|
@@ -692,6 +725,8 @@ module ActiveRecord
|
|
|
692
725
|
end
|
|
693
726
|
|
|
694
727
|
basic_structure.map do |column|
|
|
728
|
+
column = column.to_h
|
|
729
|
+
|
|
695
730
|
column_name = column["name"]
|
|
696
731
|
|
|
697
732
|
if collation_hash.has_key? column_name
|
|
@@ -783,12 +818,15 @@ module ActiveRecord
|
|
|
783
818
|
if @config[:timeout] && @config[:retries]
|
|
784
819
|
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
|
785
820
|
elsif @config[:timeout]
|
|
786
|
-
|
|
821
|
+
timeout = self.class.type_cast_config_to_integer(@config[:timeout])
|
|
822
|
+
raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
|
|
823
|
+
@raw_connection.busy_handler_timeout = timeout
|
|
787
824
|
elsif @config[:retries]
|
|
825
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
826
|
+
The retries option is deprecated and will be removed in Rails 8.1. Use timeout instead.
|
|
827
|
+
MSG
|
|
788
828
|
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
789
|
-
raw_connection.busy_handler
|
|
790
|
-
count <= retries
|
|
791
|
-
end
|
|
829
|
+
raw_connection.busy_handler { |count| count <= retries }
|
|
792
830
|
end
|
|
793
831
|
|
|
794
832
|
super
|
|
@@ -4,93 +4,63 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module Trilogy
|
|
6
6
|
module DatabaseStatements
|
|
7
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
|
8
|
-
sql = transform_query(sql)
|
|
9
|
-
check_if_write_query(sql)
|
|
10
|
-
mark_transaction_written_if_write(sql)
|
|
11
|
-
|
|
12
|
-
result = raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
|
13
|
-
ActiveRecord::Result.new(result.fields, result.to_a)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
7
|
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
|
17
|
-
sql = transform_query(sql)
|
|
18
|
-
check_if_write_query(sql)
|
|
19
|
-
mark_transaction_written_if_write(sql)
|
|
20
|
-
|
|
21
8
|
sql, _binds = sql_for_insert(sql, pk, binds, returning)
|
|
22
|
-
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
|
26
|
-
sql = transform_query(sql)
|
|
27
|
-
check_if_write_query(sql)
|
|
28
|
-
mark_transaction_written_if_write(sql)
|
|
29
|
-
|
|
30
|
-
result = raw_execute(to_sql(sql, binds), name)
|
|
31
|
-
result.affected_rows
|
|
9
|
+
internal_execute(sql, name)
|
|
32
10
|
end
|
|
33
11
|
|
|
34
|
-
alias :exec_update :exec_delete # :nodoc:
|
|
35
|
-
|
|
36
12
|
private
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
result = conn.query(sql)
|
|
42
|
-
while conn.more_results_exist?
|
|
43
|
-
conn.next_result
|
|
44
|
-
end
|
|
45
|
-
verified!
|
|
46
|
-
handle_warnings(sql)
|
|
47
|
-
notification_payload[:row_count] = result.count
|
|
48
|
-
result
|
|
49
|
-
end
|
|
13
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
14
|
+
reset_multi_statement = if batch && !@config[:multi_statement]
|
|
15
|
+
raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
|
|
16
|
+
true
|
|
50
17
|
end
|
|
51
|
-
end
|
|
52
18
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
19
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
20
|
+
# made since we established the connection
|
|
21
|
+
if default_timezone == :local
|
|
22
|
+
raw_connection.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
|
56
23
|
else
|
|
57
|
-
|
|
24
|
+
raw_connection.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
|
58
25
|
end
|
|
59
|
-
end
|
|
60
26
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
27
|
+
result = raw_connection.query(sql)
|
|
28
|
+
while raw_connection.more_results_exist?
|
|
29
|
+
raw_connection.next_result
|
|
30
|
+
end
|
|
31
|
+
verified!
|
|
32
|
+
handle_warnings(sql)
|
|
33
|
+
notification_payload[:row_count] = result.count
|
|
34
|
+
result
|
|
35
|
+
ensure
|
|
36
|
+
if reset_multi_statement && active?
|
|
37
|
+
raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
|
|
67
38
|
end
|
|
68
39
|
end
|
|
69
40
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
41
|
+
def cast_result(result)
|
|
42
|
+
if result.fields.empty?
|
|
43
|
+
ActiveRecord::Result.empty
|
|
44
|
+
else
|
|
45
|
+
ActiveRecord::Result.new(result.fields, result.rows)
|
|
76
46
|
end
|
|
77
47
|
end
|
|
78
48
|
|
|
79
|
-
def
|
|
80
|
-
|
|
49
|
+
def affected_rows(result)
|
|
50
|
+
result.affected_rows
|
|
81
51
|
end
|
|
82
52
|
|
|
83
|
-
def
|
|
84
|
-
if
|
|
85
|
-
|
|
53
|
+
def last_inserted_id(result)
|
|
54
|
+
if supports_insert_returning?
|
|
55
|
+
super
|
|
56
|
+
else
|
|
57
|
+
result.last_insert_id
|
|
86
58
|
end
|
|
59
|
+
end
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
yield
|
|
92
|
-
ensure
|
|
93
|
-
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF) if active?
|
|
61
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
62
|
+
combine_multi_statements(statements).each do |statement|
|
|
63
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
|
94
64
|
end
|
|
95
65
|
end
|
|
96
66
|
end
|
|
@@ -121,7 +121,7 @@ module ActiveRecord
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def active?
|
|
124
|
-
connected? && @lock.synchronize { @raw_connection&.ping
|
|
124
|
+
connected? && @lock.synchronize { @raw_connection&.ping } || false
|
|
125
125
|
rescue ::Trilogy::Error
|
|
126
126
|
false
|
|
127
127
|
end
|
|
@@ -149,23 +149,6 @@ module ActiveRecord
|
|
|
149
149
|
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
def each_hash(result)
|
|
153
|
-
return to_enum(:each_hash, result) unless block_given?
|
|
154
|
-
|
|
155
|
-
keys = result.fields.map(&:to_sym)
|
|
156
|
-
result.rows.each do |row|
|
|
157
|
-
hash = {}
|
|
158
|
-
idx = 0
|
|
159
|
-
row.each do |value|
|
|
160
|
-
hash[keys[idx]] = value
|
|
161
|
-
idx += 1
|
|
162
|
-
end
|
|
163
|
-
yield hash
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
nil
|
|
167
|
-
end
|
|
168
|
-
|
|
169
152
|
def error_number(exception)
|
|
170
153
|
exception.error_code if exception.respond_to?(:error_code)
|
|
171
154
|
end
|
|
@@ -87,6 +87,8 @@ module ActiveRecord
|
|
|
87
87
|
|
|
88
88
|
connections = []
|
|
89
89
|
|
|
90
|
+
@shard_keys = shards.keys
|
|
91
|
+
|
|
90
92
|
if shards.empty?
|
|
91
93
|
shards[:default] = database
|
|
92
94
|
end
|
|
@@ -170,10 +172,20 @@ module ActiveRecord
|
|
|
170
172
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
171
173
|
|
|
172
174
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
yield
|
|
176
|
+
ensure
|
|
177
|
+
connected_to_stack.pop
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Passes the block to +connected_to+ for every +shard+ the
|
|
181
|
+
# model is configured to connect to (if any), and returns the
|
|
182
|
+
# results in an array.
|
|
183
|
+
#
|
|
184
|
+
# Optionally, +role+ and/or +prevent_writes+ can be passed which
|
|
185
|
+
# will be forwarded to each +connected_to+ call.
|
|
186
|
+
def connected_to_all_shards(role: nil, prevent_writes: false, &blk)
|
|
187
|
+
shard_keys.map do |shard|
|
|
188
|
+
connected_to(shard: shard, role: role, prevent_writes: prevent_writes, &blk)
|
|
177
189
|
end
|
|
178
190
|
end
|
|
179
191
|
|
|
@@ -361,6 +373,14 @@ module ActiveRecord
|
|
|
361
373
|
connection_pool.schema_cache.clear!
|
|
362
374
|
end
|
|
363
375
|
|
|
376
|
+
def shard_keys
|
|
377
|
+
connection_class_for_self.instance_variable_get(:@shard_keys) || []
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def sharded?
|
|
381
|
+
shard_keys.any?
|
|
382
|
+
end
|
|
383
|
+
|
|
364
384
|
private
|
|
365
385
|
def resolve_config_for_connection(config_or_env)
|
|
366
386
|
raise "Anonymous class is not allowed." unless name
|
|
@@ -375,13 +395,11 @@ module ActiveRecord
|
|
|
375
395
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
376
396
|
|
|
377
397
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
self.connected_to_stack.pop
|
|
384
|
-
end
|
|
398
|
+
return_value = yield
|
|
399
|
+
return_value.load if return_value.is_a? ActiveRecord::Relation
|
|
400
|
+
return_value
|
|
401
|
+
ensure
|
|
402
|
+
self.connected_to_stack.pop
|
|
385
403
|
end
|
|
386
404
|
|
|
387
405
|
def append_to_connected_to_stack(entry)
|
data/lib/active_record/core.rb
CHANGED
|
@@ -89,6 +89,7 @@ module ActiveRecord
|
|
|
89
89
|
class_attribute :belongs_to_required_by_default, instance_accessor: false
|
|
90
90
|
|
|
91
91
|
class_attribute :strict_loading_by_default, instance_accessor: false, default: false
|
|
92
|
+
class_attribute :strict_loading_mode, instance_accessor: false, default: :all
|
|
92
93
|
|
|
93
94
|
class_attribute :has_many_inversing, instance_accessor: false, default: false
|
|
94
95
|
|
|
@@ -102,19 +103,7 @@ module ActiveRecord
|
|
|
102
103
|
|
|
103
104
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
|
104
105
|
|
|
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">"
|
|
106
|
+
# Specifies the attributes that will be included in the output of the #inspect method
|
|
118
107
|
class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
|
|
119
108
|
|
|
120
109
|
def self.application_record_class? # :nodoc:
|
|
@@ -265,7 +254,7 @@ module ActiveRecord
|
|
|
265
254
|
return super if StatementCache.unsupported_value?(id)
|
|
266
255
|
|
|
267
256
|
cached_find_by([primary_key], [id]) ||
|
|
268
|
-
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id
|
|
257
|
+
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
|
|
269
258
|
end
|
|
270
259
|
|
|
271
260
|
def find_by(*args) # :nodoc:
|
|
@@ -443,8 +432,8 @@ module ActiveRecord
|
|
|
443
432
|
where(wheres).limit(1)
|
|
444
433
|
}
|
|
445
434
|
|
|
446
|
-
|
|
447
|
-
|
|
435
|
+
statement.execute(values.flatten, connection, allow_retry: true).then do |r|
|
|
436
|
+
r.first
|
|
448
437
|
rescue TypeError
|
|
449
438
|
raise ActiveRecord::StatementInvalid
|
|
450
439
|
end
|
|
@@ -585,7 +574,7 @@ module ActiveRecord
|
|
|
585
574
|
#
|
|
586
575
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
587
576
|
# topic.slice(:title, :author_name)
|
|
588
|
-
#
|
|
577
|
+
# => { "title" => "Budget", "author_name" => "Jason" }
|
|
589
578
|
#
|
|
590
579
|
#--
|
|
591
580
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -599,7 +588,7 @@ module ActiveRecord
|
|
|
599
588
|
#
|
|
600
589
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
601
590
|
# topic.values_at(:title, :author_name)
|
|
602
|
-
#
|
|
591
|
+
# => ["Budget", "Jason"]
|
|
603
592
|
#
|
|
604
593
|
#--
|
|
605
594
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -676,14 +665,12 @@ module ActiveRecord
|
|
|
676
665
|
# Sets the record to strict_loading mode. This will raise an error
|
|
677
666
|
# if the record tries to lazily load an association.
|
|
678
667
|
#
|
|
679
|
-
# NOTE: Strict loading is disabled during validation in order to let the record validate its association.
|
|
680
|
-
#
|
|
681
668
|
# user = User.first
|
|
682
669
|
# user.strict_loading! # => true
|
|
683
670
|
# user.address.city
|
|
684
|
-
#
|
|
671
|
+
# => ActiveRecord::StrictLoadingViolationError
|
|
685
672
|
# user.comments.to_a
|
|
686
|
-
#
|
|
673
|
+
# => ActiveRecord::StrictLoadingViolationError
|
|
687
674
|
#
|
|
688
675
|
# ==== Parameters
|
|
689
676
|
#
|
|
@@ -703,7 +690,7 @@ module ActiveRecord
|
|
|
703
690
|
# user.address.city # => "Tatooine"
|
|
704
691
|
# user.comments.to_a # => [#<Comment:0x00...]
|
|
705
692
|
# user.comments.first.ratings.to_a
|
|
706
|
-
#
|
|
693
|
+
# => ActiveRecord::StrictLoadingViolationError
|
|
707
694
|
def strict_loading!(value = true, mode: :all)
|
|
708
695
|
unless [:all, :n_plus_one_only].include?(mode)
|
|
709
696
|
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
|
@@ -725,29 +712,11 @@ module ActiveRecord
|
|
|
725
712
|
@strict_loading_mode == :all
|
|
726
713
|
end
|
|
727
714
|
|
|
728
|
-
#
|
|
729
|
-
#
|
|
730
|
-
# customer = Customer.new
|
|
731
|
-
# customer.readonly!
|
|
732
|
-
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
|
733
|
-
#
|
|
734
|
-
# customer = Customer.first
|
|
735
|
-
# customer.readonly!
|
|
736
|
-
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
|
737
|
-
#
|
|
738
|
-
# Read-only records cannot be deleted from the database either:
|
|
715
|
+
# Marks this record as read only.
|
|
739
716
|
#
|
|
740
717
|
# customer = Customer.first
|
|
741
718
|
# customer.readonly!
|
|
742
|
-
# customer.
|
|
743
|
-
#
|
|
744
|
-
# Please, note that the objects themselves are still mutable in memory:
|
|
745
|
-
#
|
|
746
|
-
# customer = Customer.new
|
|
747
|
-
# customer.readonly!
|
|
748
|
-
# customer.name = 'New Name' # OK
|
|
749
|
-
#
|
|
750
|
-
# but you won't be able to persist the changes.
|
|
719
|
+
# customer.save # Raises an ActiveRecord::ReadOnlyRecord
|
|
751
720
|
def readonly!
|
|
752
721
|
@readonly = true
|
|
753
722
|
end
|
|
@@ -756,26 +725,12 @@ module ActiveRecord
|
|
|
756
725
|
self.class.connection_handler
|
|
757
726
|
end
|
|
758
727
|
|
|
759
|
-
# Returns the attributes
|
|
760
|
-
#
|
|
761
|
-
# Post.first.inspect
|
|
762
|
-
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
763
|
-
#
|
|
764
|
-
# The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
|
|
765
|
-
#
|
|
766
|
-
# Post.attributes_for_inspect = [:id, :title]
|
|
767
|
-
# Post.first.inspect
|
|
768
|
-
# #=> "#<Post id: 1, title: "Hello, World!">"
|
|
728
|
+
# Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
|
|
769
729
|
def inspect
|
|
770
730
|
inspect_with_attributes(attributes_for_inspect)
|
|
771
731
|
end
|
|
772
732
|
|
|
773
|
-
# Returns
|
|
774
|
-
# ignoring <tt>.attributes_for_inspect</tt>.
|
|
775
|
-
#
|
|
776
|
-
# Post.first.full_inspect
|
|
777
|
-
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
778
|
-
#
|
|
733
|
+
# Returns the full contents of the record as a nicely formatted string.
|
|
779
734
|
def full_inspect
|
|
780
735
|
inspect_with_attributes(all_attributes_for_inspect)
|
|
781
736
|
end
|
|
@@ -830,7 +785,7 @@ module ActiveRecord
|
|
|
830
785
|
|
|
831
786
|
@primary_key = klass.primary_key
|
|
832
787
|
@strict_loading = klass.strict_loading_by_default
|
|
833
|
-
@strict_loading_mode =
|
|
788
|
+
@strict_loading_mode = klass.strict_loading_mode
|
|
834
789
|
|
|
835
790
|
klass.define_attribute_methods
|
|
836
791
|
end
|
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
|
28
28
|
# # For the Post with id #1, reset the comments_count
|
|
29
29
|
# Post.reset_counters(1, :comments)
|
|
30
30
|
#
|
|
31
|
-
# # Like above, but also touch the updated_at and/or updated_on
|
|
31
|
+
# # Like above, but also touch the +updated_at+ and/or +updated_on+
|
|
32
32
|
# # attributes.
|
|
33
33
|
# Post.reset_counters(1, :comments, touch: true)
|
|
34
34
|
def reset_counters(id, *counters, touch: nil)
|
|
@@ -81,9 +81,7 @@ module ActiveRecord
|
|
|
81
81
|
|
|
82
82
|
def resolved_adapter
|
|
83
83
|
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
|
84
|
-
|
|
85
|
-
adapter = ActiveRecord.protocol_adapters[adapter]
|
|
86
|
-
end
|
|
84
|
+
adapter = ActiveRecord.protocol_adapters[adapter] || adapter
|
|
87
85
|
adapter
|
|
88
86
|
end
|
|
89
87
|
|
|
@@ -181,16 +181,16 @@ module ActiveRecord
|
|
|
181
181
|
# delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
|
|
182
182
|
# end
|
|
183
183
|
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# Entry.messages
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# Entry.comments
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
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
|
|
194
194
|
#
|
|
195
195
|
# You can also declare namespaced types:
|
|
196
196
|
#
|
|
@@ -199,25 +199,25 @@ module ActiveRecord
|
|
|
199
199
|
# end
|
|
200
200
|
#
|
|
201
201
|
# Entry.access_notice_messages
|
|
202
|
-
#
|
|
203
|
-
#
|
|
202
|
+
# entry.access_notice_message
|
|
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.
|
|
209
209
|
#
|
|
210
|
-
# [
|
|
210
|
+
# [:foreign_key]
|
|
211
211
|
# Specify the foreign key used for the convenience methods. By default this is guessed to be the passed
|
|
212
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
|
213
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
|
214
214
|
# the default <tt>:foreign_key</tt>.
|
|
215
|
-
# [
|
|
215
|
+
# [:foreign_type]
|
|
216
216
|
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
|
217
217
|
# +role+ with a "_type" suffix. A class that defines a
|
|
218
218
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
|
219
219
|
# the default <tt>:foreign_type</tt>.
|
|
220
|
-
# [
|
|
220
|
+
# [:primary_key]
|
|
221
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
|
222
222
|
# By default this is +id+.
|
|
223
223
|
#
|
|
@@ -226,8 +226,8 @@ module ActiveRecord
|
|
|
226
226
|
# delegated_type :entryable, types: %w[ Message Comment ], primary_key: :uuid, foreign_key: :entryable_uuid
|
|
227
227
|
# end
|
|
228
228
|
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
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
|
|
231
231
|
def delegated_type(role, types:, **options)
|
|
232
232
|
belongs_to role, options.delete(:scope), **options.merge(polymorphic: true)
|
|
233
233
|
define_delegated_type_methods role, types: types, options: options
|
|
@@ -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
|