activerecord 7.0.4 → 7.1.5.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 +1971 -1243
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +20 -14
- data/lib/active_record/associations/collection_proxy.rb +20 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +333 -222
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +21 -8
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -26
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +59 -10
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -7
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +297 -127
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -103
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +254 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +365 -61
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +213 -85
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +181 -154
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +28 -14
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +15 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +40 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +33 -19
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +9 -11
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +105 -7
- data/lib/active_record/migration/compatibility.rb +163 -58
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +271 -114
- data/lib/active_record/model_schema.rb +69 -44
- data/lib/active_record/nested_attributes.rb +37 -8
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +195 -42
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +14 -9
- data/lib/active_record/railties/databases.rake +144 -150
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +189 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +232 -81
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +408 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +103 -37
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +50 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +9 -9
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +152 -108
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +114 -96
- data/lib/active_record/timestamp.rb +30 -16
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +130 -17
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +83 -18
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +51 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_record/connection_adapters/abstract_adapter"
|
4
4
|
require "active_record/connection_adapters/statement_pool"
|
5
|
+
require "active_record/connection_adapters/sqlite3/column"
|
5
6
|
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
6
7
|
require "active_record/connection_adapters/sqlite3/quoting"
|
7
8
|
require "active_record/connection_adapters/sqlite3/database_statements"
|
@@ -10,44 +11,23 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
10
11
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
11
12
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
12
13
|
|
13
|
-
gem "sqlite3", "
|
14
|
+
gem "sqlite3", ">= 1.4"
|
14
15
|
require "sqlite3"
|
15
16
|
|
16
17
|
module ActiveRecord
|
17
18
|
module ConnectionHandling # :nodoc:
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
# Require database.
|
22
|
-
unless config[:database]
|
23
|
-
raise ArgumentError, "No database file specified. Missing argument: database"
|
24
|
-
end
|
25
|
-
|
26
|
-
# Allow database path relative to Rails.root, but only if the database
|
27
|
-
# path is not the special path that tells sqlite to build a database only
|
28
|
-
# in memory.
|
29
|
-
if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:")
|
30
|
-
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
31
|
-
dirname = File.dirname(config[:database])
|
32
|
-
Dir.mkdir(dirname) unless File.directory?(dirname)
|
33
|
-
end
|
34
|
-
|
35
|
-
db = SQLite3::Database.new(
|
36
|
-
config[:database].to_s,
|
37
|
-
config.merge(results_as_hash: true)
|
38
|
-
)
|
19
|
+
def sqlite3_adapter_class
|
20
|
+
ConnectionAdapters::SQLite3Adapter
|
21
|
+
end
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
if error.message.include?("No such file or directory")
|
43
|
-
raise ActiveRecord::NoDatabaseError
|
44
|
-
else
|
45
|
-
raise
|
46
|
-
end
|
23
|
+
def sqlite3_connection(config)
|
24
|
+
sqlite3_adapter_class.new(config)
|
47
25
|
end
|
48
26
|
end
|
49
27
|
|
50
28
|
module ConnectionAdapters # :nodoc:
|
29
|
+
# = Active Record SQLite3 Adapter
|
30
|
+
#
|
51
31
|
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
52
32
|
# (available as gem from https://rubygems.org/gems/sqlite3).
|
53
33
|
#
|
@@ -57,10 +37,42 @@ module ActiveRecord
|
|
57
37
|
class SQLite3Adapter < AbstractAdapter
|
58
38
|
ADAPTER_NAME = "SQLite"
|
59
39
|
|
40
|
+
class << self
|
41
|
+
def new_client(config)
|
42
|
+
::SQLite3::Database.new(config[:database].to_s, config)
|
43
|
+
rescue Errno::ENOENT => error
|
44
|
+
if error.message.include?("No such file or directory")
|
45
|
+
raise ActiveRecord::NoDatabaseError
|
46
|
+
else
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def dbconsole(config, options = {})
|
52
|
+
args = []
|
53
|
+
|
54
|
+
args << "-#{options[:mode]}" if options[:mode]
|
55
|
+
args << "-header" if options[:header]
|
56
|
+
args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
|
57
|
+
|
58
|
+
find_cmd_and_exec("sqlite3", *args)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
60
62
|
include SQLite3::Quoting
|
61
63
|
include SQLite3::SchemaStatements
|
62
64
|
include SQLite3::DatabaseStatements
|
63
65
|
|
66
|
+
##
|
67
|
+
# :singleton-method:
|
68
|
+
# Configure the SQLite3Adapter to be used in a strict strings mode.
|
69
|
+
# This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
|
70
|
+
# For example, it is possible to create an index for a non existing column.
|
71
|
+
# If you wish to enable this mode you can add the following line to your application.rb file:
|
72
|
+
#
|
73
|
+
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
74
|
+
class_attribute :strict_strings_by_default, default: false
|
75
|
+
|
64
76
|
NATIVE_DATABASE_TYPES = {
|
65
77
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
66
78
|
string: { name: "varchar" },
|
@@ -77,26 +89,48 @@ module ActiveRecord
|
|
77
89
|
}
|
78
90
|
|
79
91
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
92
|
+
alias reset clear
|
93
|
+
|
80
94
|
private
|
81
95
|
def dealloc(stmt)
|
82
96
|
stmt.close unless stmt.closed?
|
83
97
|
end
|
84
98
|
end
|
85
99
|
|
86
|
-
def initialize(
|
87
|
-
|
88
|
-
super(connection, logger, config)
|
89
|
-
configure_connection
|
90
|
-
end
|
100
|
+
def initialize(...)
|
101
|
+
super
|
91
102
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
103
|
+
@memory_database = false
|
104
|
+
case @config[:database].to_s
|
105
|
+
when ""
|
106
|
+
raise ArgumentError, "No database file specified. Missing argument: database"
|
107
|
+
when ":memory:"
|
108
|
+
@memory_database = true
|
109
|
+
when /\Afile:/
|
96
110
|
else
|
97
|
-
|
98
|
-
File.
|
111
|
+
# Otherwise we have a path relative to Rails.root
|
112
|
+
@config[:database] = File.expand_path(@config[:database], Rails.root) if defined?(Rails.root)
|
113
|
+
dirname = File.dirname(@config[:database])
|
114
|
+
unless File.directory?(dirname)
|
115
|
+
begin
|
116
|
+
Dir.mkdir(dirname)
|
117
|
+
rescue Errno::ENOENT => error
|
118
|
+
if error.message.include?("No such file or directory")
|
119
|
+
raise ActiveRecord::NoDatabaseError.new(connection_pool: @pool)
|
120
|
+
else
|
121
|
+
raise
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
99
125
|
end
|
126
|
+
|
127
|
+
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
128
|
+
@connection_parameters = @config.merge(database: @config[:database].to_s, results_as_hash: true)
|
129
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
130
|
+
end
|
131
|
+
|
132
|
+
def database_exists?
|
133
|
+
@config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
|
100
134
|
end
|
101
135
|
|
102
136
|
def supports_ddl_transactions?
|
@@ -147,6 +181,10 @@ module ActiveRecord
|
|
147
181
|
database_version >= "3.8.3"
|
148
182
|
end
|
149
183
|
|
184
|
+
def supports_insert_returning?
|
185
|
+
database_version >= "3.35.0"
|
186
|
+
end
|
187
|
+
|
150
188
|
def supports_insert_on_conflict?
|
151
189
|
database_version >= "3.24.0"
|
152
190
|
end
|
@@ -159,19 +197,22 @@ module ActiveRecord
|
|
159
197
|
end
|
160
198
|
|
161
199
|
def active?
|
162
|
-
!@
|
200
|
+
@raw_connection && !@raw_connection.closed?
|
163
201
|
end
|
164
202
|
|
165
|
-
def
|
166
|
-
|
167
|
-
connect if @connection.closed?
|
203
|
+
def return_value_after_insert?(column) # :nodoc:
|
204
|
+
column.auto_populated?
|
168
205
|
end
|
169
206
|
|
207
|
+
alias :reset! :reconnect!
|
208
|
+
|
170
209
|
# Disconnects from the database if already connected. Otherwise, this
|
171
210
|
# method does nothing.
|
172
211
|
def disconnect!
|
173
212
|
super
|
174
|
-
|
213
|
+
|
214
|
+
@raw_connection&.close rescue nil
|
215
|
+
@raw_connection = nil
|
175
216
|
end
|
176
217
|
|
177
218
|
def supports_index_sort_order?
|
@@ -184,7 +225,7 @@ module ActiveRecord
|
|
184
225
|
|
185
226
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
186
227
|
def encoding
|
187
|
-
|
228
|
+
any_raw_connection.encoding.to_s
|
188
229
|
end
|
189
230
|
|
190
231
|
def supports_explain?
|
@@ -211,8 +252,14 @@ module ActiveRecord
|
|
211
252
|
end
|
212
253
|
end
|
213
254
|
|
214
|
-
def
|
215
|
-
|
255
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
256
|
+
sql = "PRAGMA foreign_key_check"
|
257
|
+
result = execute(sql)
|
258
|
+
|
259
|
+
unless result.blank?
|
260
|
+
tables = result.map { |row| row["table"] }
|
261
|
+
raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql)
|
262
|
+
end
|
216
263
|
end
|
217
264
|
|
218
265
|
# SCHEMA STATEMENTS ========================================
|
@@ -234,11 +281,12 @@ module ActiveRecord
|
|
234
281
|
#
|
235
282
|
# Example:
|
236
283
|
# rename_table('octopuses', 'octopi')
|
237
|
-
def rename_table(table_name, new_name)
|
284
|
+
def rename_table(table_name, new_name, **options)
|
285
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
238
286
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
239
287
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
240
288
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
241
|
-
rename_table_indexes(table_name, new_name)
|
289
|
+
rename_table_indexes(table_name, new_name, **options)
|
242
290
|
end
|
243
291
|
|
244
292
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
@@ -277,8 +325,10 @@ module ActiveRecord
|
|
277
325
|
end
|
278
326
|
|
279
327
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
328
|
+
validate_change_column_null_argument!(null)
|
329
|
+
|
280
330
|
unless null || default.nil?
|
281
|
-
|
331
|
+
internal_exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
282
332
|
end
|
283
333
|
alter_table(table_name) do |definition|
|
284
334
|
definition[column_name].null = null
|
@@ -287,10 +337,7 @@ module ActiveRecord
|
|
287
337
|
|
288
338
|
def change_column(table_name, column_name, type, **options) # :nodoc:
|
289
339
|
alter_table(table_name) do |definition|
|
290
|
-
definition
|
291
|
-
self.type = aliased_types(type.to_s, type)
|
292
|
-
self.options.merge!(options)
|
293
|
-
end
|
340
|
+
definition.change_column(column_name, type, **options)
|
294
341
|
end
|
295
342
|
end
|
296
343
|
|
@@ -300,20 +347,42 @@ module ActiveRecord
|
|
300
347
|
rename_column_indexes(table_name, column.name, new_column_name)
|
301
348
|
end
|
302
349
|
|
350
|
+
def add_timestamps(table_name, **options)
|
351
|
+
options[:null] = false if options[:null].nil?
|
352
|
+
|
353
|
+
if !options.key?(:precision)
|
354
|
+
options[:precision] = 6
|
355
|
+
end
|
356
|
+
|
357
|
+
alter_table(table_name) do |definition|
|
358
|
+
definition.column :created_at, :datetime, **options
|
359
|
+
definition.column :updated_at, :datetime, **options
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
303
363
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
304
364
|
super(table_name, ref_name, type: :integer, **options)
|
305
365
|
end
|
306
366
|
alias :add_belongs_to :add_reference
|
307
367
|
|
308
368
|
def foreign_keys(table_name)
|
309
|
-
|
310
|
-
fk_info
|
369
|
+
# SQLite returns 1 row for each column of composite foreign keys.
|
370
|
+
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
371
|
+
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
372
|
+
grouped_fk.map do |group|
|
373
|
+
row = group.first
|
311
374
|
options = {
|
312
|
-
column: row["from"],
|
313
|
-
primary_key: row["to"],
|
314
375
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
315
376
|
on_update: extract_foreign_key_action(row["on_update"])
|
316
377
|
}
|
378
|
+
|
379
|
+
if group.one?
|
380
|
+
options[:column] = row["from"]
|
381
|
+
options[:primary_key] = row["to"]
|
382
|
+
else
|
383
|
+
options[:column] = group.map { |row| row["from"] }
|
384
|
+
options[:primary_key] = group.map { |row| row["to"] }
|
385
|
+
end
|
317
386
|
ForeignKeyDefinition.new(table_name, row["table"], options)
|
318
387
|
end
|
319
388
|
end
|
@@ -333,6 +402,7 @@ module ActiveRecord
|
|
333
402
|
end
|
334
403
|
end
|
335
404
|
|
405
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
336
406
|
sql
|
337
407
|
end
|
338
408
|
|
@@ -340,6 +410,10 @@ module ActiveRecord
|
|
340
410
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
341
411
|
end
|
342
412
|
|
413
|
+
def use_insert_returning?
|
414
|
+
@use_insert_returning
|
415
|
+
end
|
416
|
+
|
343
417
|
def get_database_version # :nodoc:
|
344
418
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
345
419
|
end
|
@@ -370,12 +444,9 @@ module ActiveRecord
|
|
370
444
|
end
|
371
445
|
|
372
446
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
447
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
373
448
|
|
374
449
|
private
|
375
|
-
def type_map
|
376
|
-
TYPE_MAP
|
377
|
-
end
|
378
|
-
|
379
450
|
# See https://www.sqlite.org/limits.html,
|
380
451
|
# the default value is 999 when not configured.
|
381
452
|
def bind_params_length
|
@@ -383,7 +454,7 @@ module ActiveRecord
|
|
383
454
|
end
|
384
455
|
|
385
456
|
def table_structure(table_name)
|
386
|
-
structure =
|
457
|
+
structure = internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
387
458
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
388
459
|
table_structure_with_collation(table_name, structure)
|
389
460
|
end
|
@@ -394,14 +465,17 @@ module ActiveRecord
|
|
394
465
|
when /^null$/i
|
395
466
|
nil
|
396
467
|
# Quoted types
|
397
|
-
when /^'(
|
468
|
+
when /^'([^|]*)'$/m
|
398
469
|
$1.gsub("''", "'")
|
399
470
|
# Quoted types
|
400
|
-
when /^"(
|
471
|
+
when /^"([^|]*)"$/m
|
401
472
|
$1.gsub('""', '"')
|
402
473
|
# Numeric types
|
403
474
|
when /\A-?\d+(\.\d*)?\z/
|
404
475
|
$&
|
476
|
+
# Binary columns
|
477
|
+
when /x'(.*)'/
|
478
|
+
[ $1 ].pack("H*")
|
405
479
|
else
|
406
480
|
# Anything else is blank or some function
|
407
481
|
# and we can't know the value of that, so return nil.
|
@@ -414,7 +488,7 @@ module ActiveRecord
|
|
414
488
|
end
|
415
489
|
|
416
490
|
def has_default_function?(default_value, default)
|
417
|
-
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
491
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
418
492
|
end
|
419
493
|
|
420
494
|
# See: https://www.sqlite.org/lang_altertable.html
|
@@ -480,14 +554,24 @@ module ActiveRecord
|
|
480
554
|
if column.has_default?
|
481
555
|
type = lookup_cast_type_from_column(column)
|
482
556
|
default = type.deserialize(column.default)
|
557
|
+
default = -> { column.default_function } if default.nil?
|
483
558
|
end
|
484
559
|
|
485
|
-
|
486
|
-
limit: column.limit,
|
487
|
-
precision: column.precision,
|
488
|
-
|
560
|
+
column_options = {
|
561
|
+
limit: column.limit,
|
562
|
+
precision: column.precision,
|
563
|
+
scale: column.scale,
|
564
|
+
null: column.null,
|
565
|
+
collation: column.collation,
|
489
566
|
primary_key: column_name == from_primary_key
|
490
|
-
|
567
|
+
}
|
568
|
+
|
569
|
+
unless column.auto_increment?
|
570
|
+
column_options[:default] = default
|
571
|
+
end
|
572
|
+
|
573
|
+
column_type = column.bigint? ? :bigint : column.type
|
574
|
+
@definition.column(column_name, column_type, **column_options)
|
491
575
|
end
|
492
576
|
|
493
577
|
yield @definition if block_given?
|
@@ -535,7 +619,7 @@ module ActiveRecord
|
|
535
619
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
536
620
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
537
621
|
|
538
|
-
|
622
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
539
623
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
540
624
|
end
|
541
625
|
|
@@ -545,22 +629,24 @@ module ActiveRecord
|
|
545
629
|
# Older versions of SQLite return:
|
546
630
|
# column *column_name* is not unique
|
547
631
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
548
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
632
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
549
633
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
550
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
634
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
551
635
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
552
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
636
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
553
637
|
elsif exception.message.match?(/called on a closed database/i)
|
554
|
-
ConnectionNotEstablished.new(exception)
|
638
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
555
639
|
else
|
556
640
|
super
|
557
641
|
end
|
558
642
|
end
|
559
643
|
|
560
|
-
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
644
|
+
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
645
|
+
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*"(\w+)".+PRIMARY KEY AUTOINCREMENT/i
|
561
646
|
|
562
647
|
def table_structure_with_collation(table_name, basic_structure)
|
563
648
|
collation_hash = {}
|
649
|
+
auto_increments = {}
|
564
650
|
sql = <<~SQL
|
565
651
|
SELECT sql FROM
|
566
652
|
(SELECT * FROM sqlite_master UNION ALL
|
@@ -582,6 +668,7 @@ module ActiveRecord
|
|
582
668
|
# This regex will match the column name and collation type and will save
|
583
669
|
# the value in $1 and $2 respectively.
|
584
670
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
671
|
+
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
585
672
|
end
|
586
673
|
|
587
674
|
basic_structure.map do |column|
|
@@ -591,6 +678,10 @@ module ActiveRecord
|
|
591
678
|
column["collation"] = collation_hash[column_name]
|
592
679
|
end
|
593
680
|
|
681
|
+
if auto_increments.has_key?(column_name)
|
682
|
+
column["auto_increment"] = true
|
683
|
+
end
|
684
|
+
|
594
685
|
column
|
595
686
|
end
|
596
687
|
else
|
@@ -607,17 +698,54 @@ module ActiveRecord
|
|
607
698
|
end
|
608
699
|
|
609
700
|
def connect
|
610
|
-
@
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
701
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
702
|
+
rescue ConnectionNotEstablished => ex
|
703
|
+
raise ex.set_pool(@pool)
|
704
|
+
end
|
705
|
+
|
706
|
+
def reconnect
|
707
|
+
if active?
|
708
|
+
@raw_connection.rollback rescue nil
|
709
|
+
else
|
710
|
+
connect
|
711
|
+
end
|
615
712
|
end
|
616
713
|
|
617
714
|
def configure_connection
|
618
|
-
@
|
715
|
+
if @config[:timeout] && @config[:retries]
|
716
|
+
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
717
|
+
elsif @config[:timeout]
|
718
|
+
@raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
|
719
|
+
elsif @config[:retries]
|
720
|
+
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
721
|
+
raw_connection.busy_handler do |count|
|
722
|
+
count <= retries
|
723
|
+
end
|
724
|
+
end
|
619
725
|
|
620
|
-
|
726
|
+
# Enforce foreign key constraints
|
727
|
+
# https://www.sqlite.org/pragma.html#pragma_foreign_keys
|
728
|
+
# https://www.sqlite.org/foreignkeys.html
|
729
|
+
raw_execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
730
|
+
unless @memory_database
|
731
|
+
# Journal mode WAL allows for greater concurrency (many readers + one writer)
|
732
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_mode
|
733
|
+
raw_execute("PRAGMA journal_mode = WAL", "SCHEMA")
|
734
|
+
# Set more relaxed level of database durability
|
735
|
+
# 2 = "FULL" (sync on every write), 1 = "NORMAL" (sync every 1000 written pages) and 0 = "NONE"
|
736
|
+
# https://www.sqlite.org/pragma.html#pragma_synchronous
|
737
|
+
raw_execute("PRAGMA synchronous = NORMAL", "SCHEMA")
|
738
|
+
# Set the global memory map so all processes can share some data
|
739
|
+
# https://www.sqlite.org/pragma.html#pragma_mmap_size
|
740
|
+
# https://www.sqlite.org/mmap.html
|
741
|
+
raw_execute("PRAGMA mmap_size = #{128.megabytes}", "SCHEMA")
|
742
|
+
end
|
743
|
+
# Impose a limit on the WAL file to prevent unlimited growth
|
744
|
+
# https://www.sqlite.org/pragma.html#pragma_journal_size_limit
|
745
|
+
raw_execute("PRAGMA journal_size_limit = #{64.megabytes}", "SCHEMA")
|
746
|
+
# Set the local connection cache to 2000 pages
|
747
|
+
# https://www.sqlite.org/pragma.html#pragma_cache_size
|
748
|
+
raw_execute("PRAGMA cache_size = 2000", "SCHEMA")
|
621
749
|
end
|
622
750
|
end
|
623
751
|
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
@@ -42,6 +42,13 @@ module ActiveRecord
|
|
42
42
|
cache.clear
|
43
43
|
end
|
44
44
|
|
45
|
+
# Clear the pool without deallocating; this is only safe when we
|
46
|
+
# know the server has independently deallocated all statements
|
47
|
+
# (e.g. due to a reconnect, or a DISCARD ALL)
|
48
|
+
def reset
|
49
|
+
cache.clear
|
50
|
+
end
|
51
|
+
|
45
52
|
def delete(key)
|
46
53
|
dealloc cache[key]
|
47
54
|
cache.delete(key)
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Trilogy
|
6
|
+
module DatabaseStatements
|
7
|
+
def select_all(*, **) # :nodoc:
|
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:
|
16
|
+
sql = transform_query(sql)
|
17
|
+
check_if_write_query(sql)
|
18
|
+
mark_transaction_written_if_write(sql)
|
19
|
+
|
20
|
+
result = raw_execute(sql, name, async: async)
|
21
|
+
ActiveRecord::Result.new(result.fields, result.to_a)
|
22
|
+
end
|
23
|
+
|
24
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
25
|
+
sql = transform_query(sql)
|
26
|
+
check_if_write_query(sql)
|
27
|
+
mark_transaction_written_if_write(sql)
|
28
|
+
|
29
|
+
raw_execute(to_sql(sql, binds), name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
33
|
+
sql = transform_query(sql)
|
34
|
+
check_if_write_query(sql)
|
35
|
+
mark_transaction_written_if_write(sql)
|
36
|
+
|
37
|
+
result = raw_execute(to_sql(sql, binds), name)
|
38
|
+
result.affected_rows
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :exec_update :exec_delete # :nodoc:
|
42
|
+
|
43
|
+
private
|
44
|
+
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
45
|
+
log(sql, name, async: async) do
|
46
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
47
|
+
sync_timezone_changes(conn)
|
48
|
+
result = conn.query(sql)
|
49
|
+
verified!
|
50
|
+
handle_warnings(sql)
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def last_inserted_id(result)
|
57
|
+
result.last_insert_id
|
58
|
+
end
|
59
|
+
|
60
|
+
def sync_timezone_changes(conn)
|
61
|
+
# Sync any changes since connection last established.
|
62
|
+
if default_timezone == :local
|
63
|
+
conn.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
64
|
+
else
|
65
|
+
conn.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_batch(statements, name = nil)
|
70
|
+
statements = statements.map { |sql| transform_query(sql) }
|
71
|
+
combine_multi_statements(statements).each do |statement|
|
72
|
+
with_raw_connection do |conn|
|
73
|
+
raw_execute(statement, name)
|
74
|
+
conn.next_result while conn.more_results_exist?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def multi_statements_enabled?
|
80
|
+
!!@config[:multi_statement]
|
81
|
+
end
|
82
|
+
|
83
|
+
def with_multi_statements
|
84
|
+
if multi_statements_enabled?
|
85
|
+
return yield
|
86
|
+
end
|
87
|
+
|
88
|
+
with_raw_connection do |conn|
|
89
|
+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
|
90
|
+
|
91
|
+
yield
|
92
|
+
ensure
|
93
|
+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|