activerecord 7.0.0 → 7.1.0
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 +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -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 +18 -3
- 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 +17 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- 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.rb +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- 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 +345 -219
- 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 +40 -26
- 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 +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- 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 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
- 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 +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
- 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 +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- 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 -12
- 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 +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- 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 +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- 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 +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- 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 +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +73 -96
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
- 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.rb +1 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +114 -27
- data/lib/active_record/errors.rb +108 -15
- 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 +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- data/lib/active_record/marshalling.rb +56 -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 +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +104 -9
- data/lib/active_record/migration/compatibility.rb +158 -64
- 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.rb +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- 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 +16 -3
- data/lib/active_record/railtie.rb +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- 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 +177 -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 +200 -83
- 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 +4 -6
- 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 +429 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- 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/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +9 -7
- data/lib/active_record/store.rb +16 -11
- 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 +138 -107
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +26 -14
- 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/translation.rb +1 -1
- 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/associated.rb +3 -3
- 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 +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- 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 +0 -8
- 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/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- 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 +50 -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"
|
@@ -15,39 +16,18 @@ 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,7 +281,8 @@ 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)}"
|
@@ -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,8 +410,12 @@ 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
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
418
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
345
419
|
end
|
346
420
|
|
347
421
|
def check_version # :nodoc:
|
@@ -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,12 +454,43 @@ 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
|
390
461
|
alias column_definitions table_structure
|
391
462
|
|
463
|
+
def extract_value_from_default(default)
|
464
|
+
case default
|
465
|
+
when /^null$/i
|
466
|
+
nil
|
467
|
+
# Quoted types
|
468
|
+
when /^'([^|]*)'$/m
|
469
|
+
$1.gsub("''", "'")
|
470
|
+
# Quoted types
|
471
|
+
when /^"([^|]*)"$/m
|
472
|
+
$1.gsub('""', '"')
|
473
|
+
# Numeric types
|
474
|
+
when /\A-?\d+(\.\d*)?\z/
|
475
|
+
$&
|
476
|
+
# Binary columns
|
477
|
+
when /x'(.*)'/
|
478
|
+
[ $1 ].pack("H*")
|
479
|
+
else
|
480
|
+
# Anything else is blank or some function
|
481
|
+
# and we can't know the value of that, so return nil.
|
482
|
+
nil
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def extract_default_function(default_value, default)
|
487
|
+
default if has_default_function?(default_value, default)
|
488
|
+
end
|
489
|
+
|
490
|
+
def has_default_function?(default_value, default)
|
491
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
492
|
+
end
|
493
|
+
|
392
494
|
# See: https://www.sqlite.org/lang_altertable.html
|
393
495
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
394
496
|
def invalid_alter_table_type?(type, options)
|
@@ -449,12 +551,27 @@ module ActiveRecord
|
|
449
551
|
options[:rename][column.name.to_sym] ||
|
450
552
|
column.name) : column.name
|
451
553
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
554
|
+
if column.has_default?
|
555
|
+
type = lookup_cast_type_from_column(column)
|
556
|
+
default = type.deserialize(column.default)
|
557
|
+
default = -> { column.default_function } if default.nil?
|
558
|
+
end
|
559
|
+
|
560
|
+
column_options = {
|
561
|
+
limit: column.limit,
|
562
|
+
precision: column.precision,
|
563
|
+
scale: column.scale,
|
564
|
+
null: column.null,
|
565
|
+
collation: column.collation,
|
456
566
|
primary_key: column_name == from_primary_key
|
457
|
-
|
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)
|
458
575
|
end
|
459
576
|
|
460
577
|
yield @definition if block_given?
|
@@ -502,7 +619,7 @@ module ActiveRecord
|
|
502
619
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
503
620
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
504
621
|
|
505
|
-
|
622
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
506
623
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
507
624
|
end
|
508
625
|
|
@@ -512,22 +629,24 @@ module ActiveRecord
|
|
512
629
|
# Older versions of SQLite return:
|
513
630
|
# column *column_name* is not unique
|
514
631
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
515
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
632
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
516
633
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
517
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
634
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
518
635
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
519
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
636
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
520
637
|
elsif exception.message.match?(/called on a closed database/i)
|
521
|
-
ConnectionNotEstablished.new(exception)
|
638
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
522
639
|
else
|
523
640
|
super
|
524
641
|
end
|
525
642
|
end
|
526
643
|
|
527
|
-
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
|
528
646
|
|
529
647
|
def table_structure_with_collation(table_name, basic_structure)
|
530
648
|
collation_hash = {}
|
649
|
+
auto_increments = {}
|
531
650
|
sql = <<~SQL
|
532
651
|
SELECT sql FROM
|
533
652
|
(SELECT * FROM sqlite_master UNION ALL
|
@@ -549,6 +668,7 @@ module ActiveRecord
|
|
549
668
|
# This regex will match the column name and collation type and will save
|
550
669
|
# the value in $1 and $2 respectively.
|
551
670
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
671
|
+
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
552
672
|
end
|
553
673
|
|
554
674
|
basic_structure.map do |column|
|
@@ -558,6 +678,10 @@ module ActiveRecord
|
|
558
678
|
column["collation"] = collation_hash[column_name]
|
559
679
|
end
|
560
680
|
|
681
|
+
if auto_increments.has_key?(column_name)
|
682
|
+
column["auto_increment"] = true
|
683
|
+
end
|
684
|
+
|
561
685
|
column
|
562
686
|
end
|
563
687
|
else
|
@@ -574,17 +698,54 @@ module ActiveRecord
|
|
574
698
|
end
|
575
699
|
|
576
700
|
def connect
|
577
|
-
@
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
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
|
582
712
|
end
|
583
713
|
|
584
714
|
def configure_connection
|
585
|
-
@
|
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
|
586
725
|
|
587
|
-
|
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")
|
588
749
|
end
|
589
750
|
end
|
590
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,98 @@
|
|
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
|
+
handle_warnings(sql)
|
50
|
+
result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def last_inserted_id(result)
|
56
|
+
result.last_insert_id
|
57
|
+
end
|
58
|
+
|
59
|
+
def sync_timezone_changes(conn)
|
60
|
+
# Sync any changes since connection last established.
|
61
|
+
if default_timezone == :local
|
62
|
+
conn.query_flags |= ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
63
|
+
else
|
64
|
+
conn.query_flags &= ~::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def execute_batch(statements, name = nil)
|
69
|
+
statements = statements.map { |sql| transform_query(sql) }
|
70
|
+
combine_multi_statements(statements).each do |statement|
|
71
|
+
with_raw_connection do |conn|
|
72
|
+
raw_execute(statement, name)
|
73
|
+
conn.next_result while conn.more_results_exist?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def multi_statements_enabled?
|
79
|
+
!!@config[:multi_statement]
|
80
|
+
end
|
81
|
+
|
82
|
+
def with_multi_statements
|
83
|
+
if multi_statements_enabled?
|
84
|
+
return yield
|
85
|
+
end
|
86
|
+
|
87
|
+
with_raw_connection do |conn|
|
88
|
+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
|
89
|
+
|
90
|
+
yield
|
91
|
+
ensure
|
92
|
+
conn.set_server_option(::Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|