activerecord 7.2.3 → 8.1.3
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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- 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 +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- 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 +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- 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/quoting.rb +21 -10
- 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 +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +45 -12
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- 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 +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- 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 +22 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +107 -75
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +18 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +24 -20
- 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 +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +37 -16
- 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 +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +84 -49
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -5,12 +5,37 @@ module ActiveRecord
|
|
|
5
5
|
module SQLite3
|
|
6
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
|
7
7
|
private
|
|
8
|
+
def virtual_tables(stream)
|
|
9
|
+
virtual_tables = @connection.virtual_tables.reject { |name, _| ignored?(name) }
|
|
10
|
+
if virtual_tables.any?
|
|
11
|
+
stream.puts
|
|
12
|
+
stream.puts " # Virtual tables defined in this database."
|
|
13
|
+
stream.puts " # Note that virtual tables may not work with other database engines. Be careful if changing database."
|
|
14
|
+
virtual_tables.sort.each do |table_name, options|
|
|
15
|
+
module_name, arguments = options
|
|
16
|
+
stream.puts " create_virtual_table #{table_name.inspect}, #{module_name.inspect}, #{arguments.split(", ").inspect}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
8
21
|
def default_primary_key?(column)
|
|
9
|
-
schema_type(column) == :integer
|
|
22
|
+
schema_type(column) == :integer && primary_key_has_autoincrement?
|
|
10
23
|
end
|
|
11
24
|
|
|
12
25
|
def explicit_primary_key_default?(column)
|
|
13
|
-
column.bigint?
|
|
26
|
+
column.bigint? || (column.type == :integer && !primary_key_has_autoincrement?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def primary_key_has_autoincrement?
|
|
30
|
+
return false unless table_name
|
|
31
|
+
|
|
32
|
+
table_sql = @connection.query_value(<<~SQL, "SCHEMA")
|
|
33
|
+
SELECT sql FROM sqlite_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
34
|
+
UNION ALL
|
|
35
|
+
SELECT sql FROM sqlite_temp_master WHERE name = #{@connection.quote(table_name)} AND type = 'table'
|
|
36
|
+
SQL
|
|
37
|
+
|
|
38
|
+
table_sql.to_s.match?(/\bAUTOINCREMENT\b/i)
|
|
14
39
|
end
|
|
15
40
|
|
|
16
41
|
def prepare_column_options(column)
|
|
@@ -27,6 +27,7 @@ module ActiveRecord
|
|
|
27
27
|
col["name"]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
where = where.sub(/\s*\/\*.*\*\/\z/, "") if where
|
|
30
31
|
orders = {}
|
|
31
32
|
|
|
32
33
|
if columns.any?(&:nil?) # index created with an expression
|
|
@@ -62,27 +63,21 @@ module ActiveRecord
|
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
65
|
-
return if options.delete(:if_exists)
|
|
66
|
+
return if options.delete(:if_exists) && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
|
|
66
67
|
|
|
67
68
|
to_table ||= options[:to_table]
|
|
68
69
|
options = options.except(:name, :to_table, :validate)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
fkey = foreign_keys.detect do |fk|
|
|
72
|
-
table = to_table || begin
|
|
73
|
-
table = options[:column].to_s.delete_suffix("_id")
|
|
74
|
-
Base.pluralize_table_names ? table.pluralize : table
|
|
75
|
-
end
|
|
76
|
-
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
-
options = options.slice(*fk.options.keys)
|
|
78
|
-
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
79
|
-
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
80
|
-
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
70
|
+
fkey = foreign_key_for!(from_table, to_table: to_table, **options)
|
|
81
71
|
|
|
72
|
+
foreign_keys = foreign_keys(from_table)
|
|
82
73
|
foreign_keys.delete(fkey)
|
|
83
74
|
alter_table(from_table, foreign_keys)
|
|
84
75
|
end
|
|
85
76
|
|
|
77
|
+
def virtual_table_exists?(table_name)
|
|
78
|
+
query_values(data_source_sql(table_name, type: "VIRTUAL TABLE"), "SCHEMA").any?
|
|
79
|
+
end
|
|
80
|
+
|
|
86
81
|
def check_constraints(table_name)
|
|
87
82
|
table_sql = query_value(<<-SQL, "SCHEMA")
|
|
88
83
|
SELECT sql
|
|
@@ -152,6 +147,7 @@ module ActiveRecord
|
|
|
152
147
|
|
|
153
148
|
Column.new(
|
|
154
149
|
field["name"],
|
|
150
|
+
lookup_cast_type(field["type"]),
|
|
155
151
|
default_value,
|
|
156
152
|
type_metadata,
|
|
157
153
|
field["notnull"].to_i == 0,
|
|
@@ -177,7 +173,8 @@ module ActiveRecord
|
|
|
177
173
|
scope = quoted_scope(name, type: type)
|
|
178
174
|
scope[:type] ||= "'table','view'"
|
|
179
175
|
|
|
180
|
-
sql = +"SELECT name FROM
|
|
176
|
+
sql = +"SELECT name FROM pragma_table_list WHERE schema <> 'temp'"
|
|
177
|
+
sql << " AND name NOT IN ('sqlite_sequence', 'sqlite_schema')"
|
|
181
178
|
sql << " AND name = #{scope[:name]}" if scope[:name]
|
|
182
179
|
sql << " AND type IN (#{scope[:type]})"
|
|
183
180
|
sql
|
|
@@ -190,6 +187,8 @@ module ActiveRecord
|
|
|
190
187
|
"'table'"
|
|
191
188
|
when "VIEW"
|
|
192
189
|
"'view'"
|
|
190
|
+
when "VIRTUAL TABLE"
|
|
191
|
+
"'virtual'"
|
|
193
192
|
end
|
|
194
193
|
scope = {}
|
|
195
194
|
scope[:name] = quote(name) if name
|
|
@@ -11,19 +11,38 @@ 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
|
-
# = Active Record SQLite3 Adapter
|
|
22
|
+
# = Active Record \SQLite3 Adapter
|
|
20
23
|
#
|
|
21
|
-
# The SQLite3 adapter works with the sqlite3-ruby
|
|
22
|
-
#
|
|
24
|
+
# The \SQLite3 adapter works with the sqlite3[https://sparklemotion.github.io/sqlite3-ruby/]
|
|
25
|
+
# driver.
|
|
23
26
|
#
|
|
24
27
|
# ==== Options
|
|
25
28
|
#
|
|
26
|
-
# *
|
|
29
|
+
# * +:database+ (String): Filesystem path to the database file.
|
|
30
|
+
# * +:statement_limit+ (Integer): Maximum number of prepared statements to cache per database connection. (default: 1000)
|
|
31
|
+
# * +:timeout+ (Integer): Timeout in milliseconds to use when waiting for a lock. (default: no wait)
|
|
32
|
+
# * +:strict+ (Boolean): Enable or disable strict mode. When enabled, this will
|
|
33
|
+
# {disallow double-quoted string literals in SQL
|
|
34
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted].
|
|
35
|
+
# (default: see strict_strings_by_default)
|
|
36
|
+
# * +:extensions+ (Array): (<b>requires sqlite3 v2.4.0</b>) Each entry specifies a sqlite extension
|
|
37
|
+
# to load for this database. The entry may be a filesystem path, or the name of a class that
|
|
38
|
+
# responds to +.to_path+ to provide the filesystem path for the extension. See {sqlite3-ruby
|
|
39
|
+
# documentation}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#class-SQLite3::Database-label-SQLite+Extensions]
|
|
40
|
+
# for more information.
|
|
41
|
+
#
|
|
42
|
+
# There may be other options available specific to the SQLite3 driver. Please read the
|
|
43
|
+
# documentation for
|
|
44
|
+
# {SQLite3::Database.new}[https://sparklemotion.github.io/sqlite3-ruby/SQLite3/Database.html#method-c-new]
|
|
45
|
+
#
|
|
27
46
|
class SQLite3Adapter < AbstractAdapter
|
|
28
47
|
ADAPTER_NAME = "SQLite"
|
|
29
48
|
|
|
@@ -43,9 +62,13 @@ module ActiveRecord
|
|
|
43
62
|
|
|
44
63
|
args << "-#{options[:mode]}" if options[:mode]
|
|
45
64
|
args << "-header" if options[:header]
|
|
46
|
-
args << File.expand_path(config.database, Rails.
|
|
65
|
+
args << File.expand_path(config.database, defined?(Rails.root) ? Rails.root : nil)
|
|
66
|
+
|
|
67
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
|
|
68
|
+
end
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
def native_database_types # :nodoc:
|
|
71
|
+
NATIVE_DATABASE_TYPES
|
|
49
72
|
end
|
|
50
73
|
end
|
|
51
74
|
|
|
@@ -55,12 +78,19 @@ module ActiveRecord
|
|
|
55
78
|
|
|
56
79
|
##
|
|
57
80
|
# :singleton-method:
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
81
|
+
#
|
|
82
|
+
# Configure the SQLite3Adapter to be used in a "strict strings" mode. When enabled, this will
|
|
83
|
+
# {disallow double-quoted string literals in SQL
|
|
84
|
+
# statements}[https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted],
|
|
85
|
+
# which may prevent some typographical errors like creating an index for a non-existent
|
|
86
|
+
# column. The default is +false+.
|
|
87
|
+
#
|
|
61
88
|
# If you wish to enable this mode you can add the following line to your application.rb file:
|
|
62
89
|
#
|
|
63
90
|
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
|
91
|
+
#
|
|
92
|
+
# This can also be configured on individual databases by setting the +strict:+ option.
|
|
93
|
+
#
|
|
64
94
|
class_attribute :strict_strings_by_default, default: false
|
|
65
95
|
|
|
66
96
|
NATIVE_DATABASE_TYPES = {
|
|
@@ -119,9 +149,19 @@ module ActiveRecord
|
|
|
119
149
|
end
|
|
120
150
|
end
|
|
121
151
|
|
|
152
|
+
@previous_read_uncommitted = nil
|
|
122
153
|
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
|
123
|
-
|
|
124
|
-
|
|
154
|
+
|
|
155
|
+
extensions = @config.fetch(:extensions, []).map do |extension|
|
|
156
|
+
extension.safe_constantize || extension
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
@connection_parameters = @config.merge(
|
|
160
|
+
database: @config[:database].to_s,
|
|
161
|
+
results_as_hash: true,
|
|
162
|
+
default_transaction_mode: :immediate,
|
|
163
|
+
extensions: extensions
|
|
164
|
+
)
|
|
125
165
|
end
|
|
126
166
|
|
|
127
167
|
def database_exists?
|
|
@@ -145,7 +185,7 @@ module ActiveRecord
|
|
|
145
185
|
end
|
|
146
186
|
|
|
147
187
|
def supports_expression_index?
|
|
148
|
-
|
|
188
|
+
true
|
|
149
189
|
end
|
|
150
190
|
|
|
151
191
|
def requires_reloading?
|
|
@@ -173,7 +213,7 @@ module ActiveRecord
|
|
|
173
213
|
end
|
|
174
214
|
|
|
175
215
|
def supports_common_table_expressions?
|
|
176
|
-
|
|
216
|
+
true
|
|
177
217
|
end
|
|
178
218
|
|
|
179
219
|
def supports_insert_returning?
|
|
@@ -221,10 +261,6 @@ module ActiveRecord
|
|
|
221
261
|
true
|
|
222
262
|
end
|
|
223
263
|
|
|
224
|
-
def native_database_types # :nodoc:
|
|
225
|
-
NATIVE_DATABASE_TYPES
|
|
226
|
-
end
|
|
227
|
-
|
|
228
264
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
|
229
265
|
def encoding
|
|
230
266
|
any_raw_connection.encoding.to_s
|
|
@@ -283,6 +319,38 @@ module ActiveRecord
|
|
|
283
319
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
|
284
320
|
end
|
|
285
321
|
|
|
322
|
+
VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.*)\)/i
|
|
323
|
+
|
|
324
|
+
# Returns a list of defined virtual tables
|
|
325
|
+
def virtual_tables
|
|
326
|
+
query = <<~SQL
|
|
327
|
+
SELECT name, sql FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL %';
|
|
328
|
+
SQL
|
|
329
|
+
|
|
330
|
+
exec_query(query, "SCHEMA").cast_values.each_with_object({}) do |row, memo|
|
|
331
|
+
table_name, sql = row[0], row[1]
|
|
332
|
+
_, module_name, arguments = sql.match(VIRTUAL_TABLE_REGEX).to_a
|
|
333
|
+
memo[table_name] = [module_name, arguments]
|
|
334
|
+
end.to_a
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Creates a virtual table
|
|
338
|
+
#
|
|
339
|
+
# Example:
|
|
340
|
+
# create_virtual_table :emails, :fts5, ['sender', 'title', 'body']
|
|
341
|
+
def create_virtual_table(table_name, module_name, values)
|
|
342
|
+
exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Drops a virtual table
|
|
346
|
+
#
|
|
347
|
+
# Although this command ignores +module_name+ and +values+,
|
|
348
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
349
|
+
# In that case, +module_name+, +values+ and +options+ will be used by #create_virtual_table.
|
|
350
|
+
def drop_virtual_table(table_name, module_name, values, **options)
|
|
351
|
+
drop_table(table_name)
|
|
352
|
+
end
|
|
353
|
+
|
|
286
354
|
# Renames a table.
|
|
287
355
|
#
|
|
288
356
|
# Example:
|
|
@@ -433,17 +501,13 @@ module ActiveRecord
|
|
|
433
501
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
|
434
502
|
end
|
|
435
503
|
|
|
436
|
-
def use_insert_returning?
|
|
437
|
-
@use_insert_returning
|
|
438
|
-
end
|
|
439
|
-
|
|
440
504
|
def get_database_version # :nodoc:
|
|
441
505
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
|
442
506
|
end
|
|
443
507
|
|
|
444
508
|
def check_version # :nodoc:
|
|
445
|
-
if database_version < "3.
|
|
446
|
-
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.
|
|
509
|
+
if database_version < "3.23.0"
|
|
510
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.23.0."
|
|
447
511
|
end
|
|
448
512
|
end
|
|
449
513
|
|
|
@@ -499,6 +563,8 @@ module ActiveRecord
|
|
|
499
563
|
# Binary columns
|
|
500
564
|
when /x'(.*)'/
|
|
501
565
|
[ $1 ].pack("H*")
|
|
566
|
+
when "TRUE", "FALSE"
|
|
567
|
+
default
|
|
502
568
|
else
|
|
503
569
|
# Anything else is blank or some function
|
|
504
570
|
# and we can't know the value of that, so return nil.
|
|
@@ -589,8 +655,8 @@ module ActiveRecord
|
|
|
589
655
|
column_options[:stored] = column.virtual_stored?
|
|
590
656
|
column_options[:type] = column.type
|
|
591
657
|
elsif column.has_default?
|
|
592
|
-
|
|
593
|
-
default =
|
|
658
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
659
|
+
default = column.fetch_cast_type(self).deserialize(column.default)
|
|
594
660
|
default = -> { column.default_function } if default.nil?
|
|
595
661
|
|
|
596
662
|
unless column.auto_increment?
|
|
@@ -664,8 +730,12 @@ module ActiveRecord
|
|
|
664
730
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
665
731
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
666
732
|
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
733
|
+
elsif exception.message.match?(/CHECK constraint failed: .*/i)
|
|
734
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
667
735
|
elsif exception.message.match?(/called on a closed database/i)
|
|
668
736
|
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
737
|
+
elsif exception.is_a?(::SQLite3::BusyException)
|
|
738
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
669
739
|
else
|
|
670
740
|
super
|
|
671
741
|
end
|
|
@@ -751,9 +821,9 @@ module ActiveRecord
|
|
|
751
821
|
|
|
752
822
|
def table_info(table_name)
|
|
753
823
|
if supports_virtual_columns?
|
|
754
|
-
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
|
824
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
755
825
|
else
|
|
756
|
-
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
|
826
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA", allow_retry: true)
|
|
757
827
|
end
|
|
758
828
|
end
|
|
759
829
|
|
|
@@ -780,15 +850,10 @@ module ActiveRecord
|
|
|
780
850
|
end
|
|
781
851
|
|
|
782
852
|
def configure_connection
|
|
783
|
-
if @config[:timeout]
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
@raw_connection.
|
|
787
|
-
elsif @config[:retries]
|
|
788
|
-
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
789
|
-
raw_connection.busy_handler do |count|
|
|
790
|
-
count <= retries
|
|
791
|
-
end
|
|
853
|
+
if @config[:timeout]
|
|
854
|
+
timeout = self.class.type_cast_config_to_integer(@config[:timeout])
|
|
855
|
+
raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
|
|
856
|
+
@raw_connection.busy_handler_timeout = timeout
|
|
792
857
|
end
|
|
793
858
|
|
|
794
859
|
super
|
|
@@ -4,93 +4,64 @@ 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
|
-
|
|
9
|
+
internal_execute(sql, name)
|
|
23
10
|
end
|
|
24
11
|
|
|
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
|
|
32
|
-
end
|
|
33
|
-
|
|
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
|
+
|
|
33
|
+
notification_payload[:affected_rows] = result.affected_rows
|
|
34
|
+
notification_payload[:row_count] = result.count
|
|
35
|
+
result
|
|
36
|
+
ensure
|
|
37
|
+
if reset_multi_statement && active?
|
|
38
|
+
raw_connection.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
|
|
67
39
|
end
|
|
68
40
|
end
|
|
69
41
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
42
|
+
def cast_result(result)
|
|
43
|
+
if result.fields.empty?
|
|
44
|
+
ActiveRecord::Result.empty(affected_rows: result.affected_rows)
|
|
45
|
+
else
|
|
46
|
+
ActiveRecord::Result.new(result.fields, result.rows, affected_rows: result.affected_rows)
|
|
76
47
|
end
|
|
77
48
|
end
|
|
78
49
|
|
|
79
|
-
def
|
|
80
|
-
|
|
50
|
+
def affected_rows(result)
|
|
51
|
+
result.affected_rows
|
|
81
52
|
end
|
|
82
53
|
|
|
83
|
-
def
|
|
84
|
-
if
|
|
85
|
-
|
|
54
|
+
def last_inserted_id(result)
|
|
55
|
+
if supports_insert_returning?
|
|
56
|
+
super
|
|
57
|
+
else
|
|
58
|
+
result.last_insert_id
|
|
86
59
|
end
|
|
60
|
+
end
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
yield
|
|
92
|
-
ensure
|
|
93
|
-
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF) if active?
|
|
62
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
63
|
+
combine_multi_statements(statements).each do |statement|
|
|
64
|
+
raw_execute(statement, name, batch: true, **kwargs)
|
|
94
65
|
end
|
|
95
66
|
end
|
|
96
67
|
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
|
|
@@ -198,7 +181,7 @@ module ActiveRecord
|
|
|
198
181
|
end
|
|
199
182
|
|
|
200
183
|
case exception
|
|
201
|
-
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError
|
|
184
|
+
when ::Trilogy::ConnectionClosed, ::Trilogy::EOFError, ::Trilogy::SSLError
|
|
202
185
|
return ConnectionFailed.new(message, connection_pool: @pool)
|
|
203
186
|
when ::Trilogy::Error
|
|
204
187
|
if exception.is_a?(SystemCallError) || exception.message.include?("TRILOGY_INVALID_SEQUENCE_ID")
|
|
@@ -31,62 +31,6 @@ module ActiveRecord
|
|
|
31
31
|
class_name, path_to_adapter = @adapters[adapter_name.to_s]
|
|
32
32
|
|
|
33
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
34
|
raise AdapterNotFound, <<~MSG.squish
|
|
91
35
|
Database configuration specifies nonexistent '#{adapter_name}' adapter.
|
|
92
36
|
Available adapters are: #{@adapters.keys.sort.join(", ")}.
|
|
@@ -140,6 +84,7 @@ module ActiveRecord
|
|
|
140
84
|
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
|
141
85
|
autoload :IndexDefinition
|
|
142
86
|
autoload :ColumnDefinition
|
|
87
|
+
autoload :ColumnMethods
|
|
143
88
|
autoload :ChangeColumnDefinition
|
|
144
89
|
autoload :ChangeColumnDefaultDefinition
|
|
145
90
|
autoload :ForeignKeyDefinition
|