activerecord 7.0.0 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- 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 +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- 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 +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- 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 +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- 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 +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- 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 +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- 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 +101 -48
- 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/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- 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 +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- 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 +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- 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/dynamic_matchers.rb +2 -2
- 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 +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- 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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- 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 +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- data/lib/active_record/migration/default_strategy.rb +22 -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 +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- 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 +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- 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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -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 +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- 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 +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +109 -27
- data/lib/active_record/translation.rb +1 -3
- 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 +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +12 -6
- 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 +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- 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 +59 -17
- 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,13 @@ 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
|
-
module ConnectionHandling # :nodoc:
|
|
18
|
-
def sqlite3_connection(config)
|
|
19
|
-
config = config.symbolize_keys
|
|
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
|
-
)
|
|
39
|
-
|
|
40
|
-
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
|
41
|
-
rescue Errno::ENOENT => error
|
|
42
|
-
if error.message.include?("No such file or directory")
|
|
43
|
-
raise ActiveRecord::NoDatabaseError
|
|
44
|
-
else
|
|
45
|
-
raise
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
18
|
module ConnectionAdapters # :nodoc:
|
|
19
|
+
# = Active Record SQLite3 Adapter
|
|
20
|
+
#
|
|
51
21
|
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
|
52
22
|
# (available as gem from https://rubygems.org/gems/sqlite3).
|
|
53
23
|
#
|
|
@@ -57,10 +27,42 @@ module ActiveRecord
|
|
|
57
27
|
class SQLite3Adapter < AbstractAdapter
|
|
58
28
|
ADAPTER_NAME = "SQLite"
|
|
59
29
|
|
|
30
|
+
class << self
|
|
31
|
+
def new_client(config)
|
|
32
|
+
::SQLite3::Database.new(config[:database].to_s, config)
|
|
33
|
+
rescue Errno::ENOENT => error
|
|
34
|
+
if error.message.include?("No such file or directory")
|
|
35
|
+
raise ActiveRecord::NoDatabaseError
|
|
36
|
+
else
|
|
37
|
+
raise
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def dbconsole(config, options = {})
|
|
42
|
+
args = []
|
|
43
|
+
|
|
44
|
+
args << "-#{options[:mode]}" if options[:mode]
|
|
45
|
+
args << "-header" if options[:header]
|
|
46
|
+
args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
|
|
47
|
+
|
|
48
|
+
find_cmd_and_exec("sqlite3", *args)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
60
52
|
include SQLite3::Quoting
|
|
61
53
|
include SQLite3::SchemaStatements
|
|
62
54
|
include SQLite3::DatabaseStatements
|
|
63
55
|
|
|
56
|
+
##
|
|
57
|
+
# :singleton-method:
|
|
58
|
+
# Configure the SQLite3Adapter to be used in a strict strings mode.
|
|
59
|
+
# This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
|
|
60
|
+
# For example, it is possible to create an index for a non existing column.
|
|
61
|
+
# If you wish to enable this mode you can add the following line to your application.rb file:
|
|
62
|
+
#
|
|
63
|
+
# config.active_record.sqlite3_adapter_strict_strings_by_default = true
|
|
64
|
+
class_attribute :strict_strings_by_default, default: false
|
|
65
|
+
|
|
64
66
|
NATIVE_DATABASE_TYPES = {
|
|
65
67
|
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
|
66
68
|
string: { name: "varchar" },
|
|
@@ -76,27 +78,54 @@ module ActiveRecord
|
|
|
76
78
|
json: { name: "json" },
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
DEFAULT_PRAGMAS = {
|
|
82
|
+
"foreign_keys" => true,
|
|
83
|
+
"journal_mode" => :wal,
|
|
84
|
+
"synchronous" => :normal,
|
|
85
|
+
"mmap_size" => 134217728, # 128 megabytes
|
|
86
|
+
"journal_size_limit" => 67108864, # 64 megabytes
|
|
87
|
+
"cache_size" => 2000
|
|
88
|
+
}
|
|
89
|
+
|
|
79
90
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
|
91
|
+
alias reset clear
|
|
92
|
+
|
|
80
93
|
private
|
|
81
94
|
def dealloc(stmt)
|
|
82
95
|
stmt.close unless stmt.closed?
|
|
83
96
|
end
|
|
84
97
|
end
|
|
85
98
|
|
|
86
|
-
def initialize(
|
|
87
|
-
|
|
88
|
-
super(connection, logger, config)
|
|
89
|
-
configure_connection
|
|
90
|
-
end
|
|
99
|
+
def initialize(...)
|
|
100
|
+
super
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
@memory_database = false
|
|
103
|
+
case @config[:database].to_s
|
|
104
|
+
when ""
|
|
105
|
+
raise ArgumentError, "No database file specified. Missing argument: database"
|
|
106
|
+
when ":memory:"
|
|
107
|
+
@memory_database = true
|
|
108
|
+
when /\Afile:/
|
|
96
109
|
else
|
|
97
|
-
|
|
98
|
-
File.
|
|
110
|
+
# Otherwise we have a path relative to Rails.root
|
|
111
|
+
@config[:database] = File.expand_path(@config[:database], Rails.root) if defined?(Rails.root)
|
|
112
|
+
dirname = File.dirname(@config[:database])
|
|
113
|
+
unless File.directory?(dirname)
|
|
114
|
+
begin
|
|
115
|
+
FileUtils.mkdir_p(dirname)
|
|
116
|
+
rescue SystemCallError
|
|
117
|
+
raise ActiveRecord::NoDatabaseError.new(connection_pool: @pool)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
99
120
|
end
|
|
121
|
+
|
|
122
|
+
@config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
|
|
123
|
+
@connection_parameters = @config.merge(database: @config[:database].to_s, results_as_hash: true)
|
|
124
|
+
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def database_exists?
|
|
128
|
+
@config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
|
|
100
129
|
end
|
|
101
130
|
|
|
102
131
|
def supports_ddl_transactions?
|
|
@@ -147,6 +176,10 @@ module ActiveRecord
|
|
|
147
176
|
database_version >= "3.8.3"
|
|
148
177
|
end
|
|
149
178
|
|
|
179
|
+
def supports_insert_returning?
|
|
180
|
+
database_version >= "3.35.0"
|
|
181
|
+
end
|
|
182
|
+
|
|
150
183
|
def supports_insert_on_conflict?
|
|
151
184
|
database_version >= "3.24.0"
|
|
152
185
|
end
|
|
@@ -158,20 +191,25 @@ module ActiveRecord
|
|
|
158
191
|
!@memory_database
|
|
159
192
|
end
|
|
160
193
|
|
|
161
|
-
def
|
|
162
|
-
|
|
194
|
+
def supports_virtual_columns?
|
|
195
|
+
database_version >= "3.31.0"
|
|
163
196
|
end
|
|
164
197
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
connect if @connection.closed?
|
|
198
|
+
def connected?
|
|
199
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
168
200
|
end
|
|
169
201
|
|
|
202
|
+
alias_method :active?, :connected?
|
|
203
|
+
|
|
204
|
+
alias :reset! :reconnect!
|
|
205
|
+
|
|
170
206
|
# Disconnects from the database if already connected. Otherwise, this
|
|
171
207
|
# method does nothing.
|
|
172
208
|
def disconnect!
|
|
173
209
|
super
|
|
174
|
-
|
|
210
|
+
|
|
211
|
+
@raw_connection&.close rescue nil
|
|
212
|
+
@raw_connection = nil
|
|
175
213
|
end
|
|
176
214
|
|
|
177
215
|
def supports_index_sort_order?
|
|
@@ -184,7 +222,7 @@ module ActiveRecord
|
|
|
184
222
|
|
|
185
223
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
|
186
224
|
def encoding
|
|
187
|
-
|
|
225
|
+
any_raw_connection.encoding.to_s
|
|
188
226
|
end
|
|
189
227
|
|
|
190
228
|
def supports_explain?
|
|
@@ -195,6 +233,10 @@ module ActiveRecord
|
|
|
195
233
|
true
|
|
196
234
|
end
|
|
197
235
|
|
|
236
|
+
def supports_deferrable_constraints?
|
|
237
|
+
true
|
|
238
|
+
end
|
|
239
|
+
|
|
198
240
|
# REFERENTIAL INTEGRITY ====================================
|
|
199
241
|
|
|
200
242
|
def disable_referential_integrity # :nodoc:
|
|
@@ -211,8 +253,14 @@ module ActiveRecord
|
|
|
211
253
|
end
|
|
212
254
|
end
|
|
213
255
|
|
|
214
|
-
def
|
|
215
|
-
|
|
256
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
|
257
|
+
sql = "PRAGMA foreign_key_check"
|
|
258
|
+
result = execute(sql)
|
|
259
|
+
|
|
260
|
+
unless result.blank?
|
|
261
|
+
tables = result.map { |row| row["table"] }
|
|
262
|
+
raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql, connection_pool: @pool)
|
|
263
|
+
end
|
|
216
264
|
end
|
|
217
265
|
|
|
218
266
|
# SCHEMA STATEMENTS ========================================
|
|
@@ -234,14 +282,16 @@ module ActiveRecord
|
|
|
234
282
|
#
|
|
235
283
|
# Example:
|
|
236
284
|
# rename_table('octopuses', 'octopi')
|
|
237
|
-
def rename_table(table_name, new_name)
|
|
285
|
+
def rename_table(table_name, new_name, **options)
|
|
286
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
|
238
287
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
239
288
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
240
289
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
241
|
-
rename_table_indexes(table_name, new_name)
|
|
290
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
242
291
|
end
|
|
243
292
|
|
|
244
293
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
294
|
+
type = type.to_sym
|
|
245
295
|
if invalid_alter_table_type?(type, options)
|
|
246
296
|
alter_table(table_name) do |definition|
|
|
247
297
|
definition.column(column_name, type, **options)
|
|
@@ -277,8 +327,10 @@ module ActiveRecord
|
|
|
277
327
|
end
|
|
278
328
|
|
|
279
329
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
|
330
|
+
validate_change_column_null_argument!(null)
|
|
331
|
+
|
|
280
332
|
unless null || default.nil?
|
|
281
|
-
|
|
333
|
+
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
334
|
end
|
|
283
335
|
alter_table(table_name) do |definition|
|
|
284
336
|
definition[column_name].null = null
|
|
@@ -287,10 +339,7 @@ module ActiveRecord
|
|
|
287
339
|
|
|
288
340
|
def change_column(table_name, column_name, type, **options) # :nodoc:
|
|
289
341
|
alter_table(table_name) do |definition|
|
|
290
|
-
definition
|
|
291
|
-
self.type = aliased_types(type.to_s, type)
|
|
292
|
-
self.options.merge!(options)
|
|
293
|
-
end
|
|
342
|
+
definition.change_column(column_name, type, **options)
|
|
294
343
|
end
|
|
295
344
|
end
|
|
296
345
|
|
|
@@ -300,20 +349,58 @@ module ActiveRecord
|
|
|
300
349
|
rename_column_indexes(table_name, column.name, new_column_name)
|
|
301
350
|
end
|
|
302
351
|
|
|
352
|
+
def add_timestamps(table_name, **options)
|
|
353
|
+
options[:null] = false if options[:null].nil?
|
|
354
|
+
|
|
355
|
+
if !options.key?(:precision)
|
|
356
|
+
options[:precision] = 6
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
alter_table(table_name) do |definition|
|
|
360
|
+
definition.column :created_at, :datetime, **options
|
|
361
|
+
definition.column :updated_at, :datetime, **options
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
303
365
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
|
304
366
|
super(table_name, ref_name, type: :integer, **options)
|
|
305
367
|
end
|
|
306
368
|
alias :add_belongs_to :add_reference
|
|
307
369
|
|
|
370
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
371
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
308
372
|
def foreign_keys(table_name)
|
|
309
|
-
|
|
310
|
-
fk_info
|
|
373
|
+
# SQLite returns 1 row for each column of composite foreign keys.
|
|
374
|
+
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
|
375
|
+
# Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
|
|
376
|
+
fk_defs = table_structure_sql(table_name)
|
|
377
|
+
.select do |column_string|
|
|
378
|
+
column_string.start_with?("CONSTRAINT") &&
|
|
379
|
+
column_string.include?("FOREIGN KEY")
|
|
380
|
+
end
|
|
381
|
+
.to_h do |fk_string|
|
|
382
|
+
_, from, table, to = fk_string.match(FK_REGEX).to_a
|
|
383
|
+
_, mode = fk_string.match(DEFERRABLE_REGEX).to_a
|
|
384
|
+
deferred = mode&.downcase&.to_sym || false
|
|
385
|
+
[[table, from, to], deferred]
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
|
389
|
+
grouped_fk.map do |group|
|
|
390
|
+
row = group.first
|
|
311
391
|
options = {
|
|
312
|
-
column: row["from"],
|
|
313
|
-
primary_key: row["to"],
|
|
314
392
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
|
315
|
-
on_update: extract_foreign_key_action(row["on_update"])
|
|
393
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
|
394
|
+
deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
|
|
316
395
|
}
|
|
396
|
+
|
|
397
|
+
if group.one?
|
|
398
|
+
options[:column] = row["from"]
|
|
399
|
+
options[:primary_key] = row["to"]
|
|
400
|
+
else
|
|
401
|
+
options[:column] = group.map { |row| row["from"] }
|
|
402
|
+
options[:primary_key] = group.map { |row| row["to"] }
|
|
403
|
+
end
|
|
317
404
|
ForeignKeyDefinition.new(table_name, row["table"], options)
|
|
318
405
|
end
|
|
319
406
|
end
|
|
@@ -333,6 +420,7 @@ module ActiveRecord
|
|
|
333
420
|
end
|
|
334
421
|
end
|
|
335
422
|
|
|
423
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
336
424
|
sql
|
|
337
425
|
end
|
|
338
426
|
|
|
@@ -340,8 +428,12 @@ module ActiveRecord
|
|
|
340
428
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
|
341
429
|
end
|
|
342
430
|
|
|
431
|
+
def use_insert_returning?
|
|
432
|
+
@use_insert_returning
|
|
433
|
+
end
|
|
434
|
+
|
|
343
435
|
def get_database_version # :nodoc:
|
|
344
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
|
436
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
|
345
437
|
end
|
|
346
438
|
|
|
347
439
|
def check_version # :nodoc:
|
|
@@ -370,12 +462,9 @@ module ActiveRecord
|
|
|
370
462
|
end
|
|
371
463
|
|
|
372
464
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
465
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
373
466
|
|
|
374
467
|
private
|
|
375
|
-
def type_map
|
|
376
|
-
TYPE_MAP
|
|
377
|
-
end
|
|
378
|
-
|
|
379
468
|
# See https://www.sqlite.org/limits.html,
|
|
380
469
|
# the default value is 999 when not configured.
|
|
381
470
|
def bind_params_length
|
|
@@ -383,17 +472,49 @@ module ActiveRecord
|
|
|
383
472
|
end
|
|
384
473
|
|
|
385
474
|
def table_structure(table_name)
|
|
386
|
-
structure =
|
|
387
|
-
raise
|
|
475
|
+
structure = table_info(table_name)
|
|
476
|
+
raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
|
|
388
477
|
table_structure_with_collation(table_name, structure)
|
|
389
478
|
end
|
|
390
479
|
alias column_definitions table_structure
|
|
391
480
|
|
|
481
|
+
def extract_value_from_default(default)
|
|
482
|
+
case default
|
|
483
|
+
when /^null$/i
|
|
484
|
+
nil
|
|
485
|
+
# Quoted types
|
|
486
|
+
when /^'([^|]*)'$/m
|
|
487
|
+
$1.gsub("''", "'")
|
|
488
|
+
# Quoted types
|
|
489
|
+
when /^"([^|]*)"$/m
|
|
490
|
+
$1.gsub('""', '"')
|
|
491
|
+
# Numeric types
|
|
492
|
+
when /\A-?\d+(\.\d*)?\z/
|
|
493
|
+
$&
|
|
494
|
+
# Binary columns
|
|
495
|
+
when /x'(.*)'/
|
|
496
|
+
[ $1 ].pack("H*")
|
|
497
|
+
else
|
|
498
|
+
# Anything else is blank or some function
|
|
499
|
+
# and we can't know the value of that, so return nil.
|
|
500
|
+
nil
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def extract_default_function(default_value, default)
|
|
505
|
+
default if has_default_function?(default_value, default)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def has_default_function?(default_value, default)
|
|
509
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
|
510
|
+
end
|
|
511
|
+
|
|
392
512
|
# See: https://www.sqlite.org/lang_altertable.html
|
|
393
513
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
|
394
514
|
def invalid_alter_table_type?(type, options)
|
|
395
|
-
type
|
|
396
|
-
options[:null] == false && options[:default].nil?
|
|
515
|
+
type == :primary_key || options[:primary_key] ||
|
|
516
|
+
options[:null] == false && options[:default].nil? ||
|
|
517
|
+
(type == :virtual && options[:stored])
|
|
397
518
|
end
|
|
398
519
|
|
|
399
520
|
def alter_table(
|
|
@@ -449,19 +570,40 @@ module ActiveRecord
|
|
|
449
570
|
options[:rename][column.name.to_sym] ||
|
|
450
571
|
column.name) : column.name
|
|
451
572
|
|
|
452
|
-
|
|
453
|
-
limit: column.limit,
|
|
454
|
-
precision: column.precision,
|
|
455
|
-
|
|
573
|
+
column_options = {
|
|
574
|
+
limit: column.limit,
|
|
575
|
+
precision: column.precision,
|
|
576
|
+
scale: column.scale,
|
|
577
|
+
null: column.null,
|
|
578
|
+
collation: column.collation,
|
|
456
579
|
primary_key: column_name == from_primary_key
|
|
457
|
-
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if column.virtual?
|
|
583
|
+
column_options[:as] = column.default_function
|
|
584
|
+
column_options[:stored] = column.virtual_stored?
|
|
585
|
+
column_options[:type] = column.type
|
|
586
|
+
elsif column.has_default?
|
|
587
|
+
type = lookup_cast_type_from_column(column)
|
|
588
|
+
default = type.deserialize(column.default)
|
|
589
|
+
default = -> { column.default_function } if default.nil?
|
|
590
|
+
|
|
591
|
+
unless column.auto_increment?
|
|
592
|
+
column_options[:default] = default
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
|
|
597
|
+
@definition.column(column_name, column_type, **column_options)
|
|
458
598
|
end
|
|
459
599
|
|
|
460
600
|
yield @definition if block_given?
|
|
461
601
|
end
|
|
462
602
|
copy_table_indexes(from, to, options[:rename] || {})
|
|
603
|
+
|
|
604
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
|
463
605
|
copy_table_contents(from, to,
|
|
464
|
-
|
|
606
|
+
columns_to_copy,
|
|
465
607
|
options[:rename] || {})
|
|
466
608
|
end
|
|
467
609
|
|
|
@@ -502,7 +644,7 @@ module ActiveRecord
|
|
|
502
644
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
|
503
645
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
|
504
646
|
|
|
505
|
-
|
|
647
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
|
506
648
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
|
507
649
|
end
|
|
508
650
|
|
|
@@ -512,43 +654,36 @@ module ActiveRecord
|
|
|
512
654
|
# Older versions of SQLite return:
|
|
513
655
|
# column *column_name* is not unique
|
|
514
656
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
|
515
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
657
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
516
658
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
|
517
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
659
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
518
660
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
519
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
661
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
520
662
|
elsif exception.message.match?(/called on a closed database/i)
|
|
521
|
-
ConnectionNotEstablished.new(exception)
|
|
663
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
522
664
|
else
|
|
523
665
|
super
|
|
524
666
|
end
|
|
525
667
|
end
|
|
526
668
|
|
|
527
|
-
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
|
669
|
+
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
|
670
|
+
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*"(\w+)".+PRIMARY KEY AUTOINCREMENT/i
|
|
671
|
+
GENERATED_ALWAYS_AS_REGEX = /.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i
|
|
528
672
|
|
|
529
673
|
def table_structure_with_collation(table_name, basic_structure)
|
|
530
674
|
collation_hash = {}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
(SELECT * FROM sqlite_master UNION ALL
|
|
534
|
-
SELECT * FROM sqlite_temp_master)
|
|
535
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
|
536
|
-
SQL
|
|
537
|
-
|
|
538
|
-
# Result will have following sample string
|
|
539
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
540
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
|
541
|
-
result = query_value(sql, "SCHEMA")
|
|
675
|
+
auto_increments = {}
|
|
676
|
+
generated_columns = {}
|
|
542
677
|
|
|
543
|
-
|
|
544
|
-
# Splitting with left parentheses and discarding the first part will return all
|
|
545
|
-
# columns separated with comma(,).
|
|
546
|
-
columns_string = result.split("(", 2).last
|
|
678
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
|
547
679
|
|
|
548
|
-
|
|
680
|
+
if column_strings.any?
|
|
681
|
+
column_strings.each do |column_string|
|
|
549
682
|
# This regex will match the column name and collation type and will save
|
|
550
683
|
# the value in $1 and $2 respectively.
|
|
551
684
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
|
685
|
+
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
|
686
|
+
generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
|
|
552
687
|
end
|
|
553
688
|
|
|
554
689
|
basic_structure.map do |column|
|
|
@@ -558,6 +693,14 @@ module ActiveRecord
|
|
|
558
693
|
column["collation"] = collation_hash[column_name]
|
|
559
694
|
end
|
|
560
695
|
|
|
696
|
+
if auto_increments.has_key?(column_name)
|
|
697
|
+
column["auto_increment"] = true
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
if generated_columns.has_key?(column_name)
|
|
701
|
+
column["dflt_value"] = generated_columns[column_name]
|
|
702
|
+
end
|
|
703
|
+
|
|
561
704
|
column
|
|
562
705
|
end
|
|
563
706
|
else
|
|
@@ -565,6 +708,50 @@ module ActiveRecord
|
|
|
565
708
|
end
|
|
566
709
|
end
|
|
567
710
|
|
|
711
|
+
UNQUOTED_OPEN_PARENS_REGEX = /\((?![^'"]*['"][^'"]*$)/
|
|
712
|
+
FINAL_CLOSE_PARENS_REGEX = /\);*\z/
|
|
713
|
+
|
|
714
|
+
def table_structure_sql(table_name, column_names = nil)
|
|
715
|
+
unless column_names
|
|
716
|
+
column_info = table_info(table_name)
|
|
717
|
+
column_names = column_info.map { |column| column["name"] }
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
sql = <<~SQL
|
|
721
|
+
SELECT sql FROM
|
|
722
|
+
(SELECT * FROM sqlite_master UNION ALL
|
|
723
|
+
SELECT * FROM sqlite_temp_master)
|
|
724
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
|
725
|
+
SQL
|
|
726
|
+
|
|
727
|
+
# Result will have following sample string
|
|
728
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
729
|
+
# "password_digest" varchar COLLATE "NOCASE",
|
|
730
|
+
# "o_id" integer,
|
|
731
|
+
# CONSTRAINT "fk_rails_78146ddd2e" FOREIGN KEY ("o_id") REFERENCES "os" ("id"));
|
|
732
|
+
result = query_value(sql, "SCHEMA")
|
|
733
|
+
|
|
734
|
+
return [] unless result
|
|
735
|
+
|
|
736
|
+
# Splitting with left parentheses and discarding the first part will return all
|
|
737
|
+
# columns separated with comma(,).
|
|
738
|
+
result.partition(UNQUOTED_OPEN_PARENS_REGEX)
|
|
739
|
+
.last
|
|
740
|
+
.sub(FINAL_CLOSE_PARENS_REGEX, "")
|
|
741
|
+
# column definitions can have a comma in them, so split on commas followed
|
|
742
|
+
# by a space and a column name in quotes or followed by the keyword CONSTRAINT
|
|
743
|
+
.split(/,(?=\s(?:CONSTRAINT|"(?:#{Regexp.union(column_names).source})"))/i)
|
|
744
|
+
.map(&:strip)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def table_info(table_name)
|
|
748
|
+
if supports_virtual_columns?
|
|
749
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
|
750
|
+
else
|
|
751
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
568
755
|
def arel_visitor
|
|
569
756
|
Arel::Visitors::SQLite.new(self)
|
|
570
757
|
end
|
|
@@ -574,17 +761,41 @@ module ActiveRecord
|
|
|
574
761
|
end
|
|
575
762
|
|
|
576
763
|
def connect
|
|
577
|
-
@
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
764
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
|
765
|
+
rescue ConnectionNotEstablished => ex
|
|
766
|
+
raise ex.set_pool(@pool)
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
def reconnect
|
|
770
|
+
if active?
|
|
771
|
+
@raw_connection.rollback rescue nil
|
|
772
|
+
else
|
|
773
|
+
connect
|
|
774
|
+
end
|
|
582
775
|
end
|
|
583
776
|
|
|
584
777
|
def configure_connection
|
|
585
|
-
@
|
|
778
|
+
if @config[:timeout] && @config[:retries]
|
|
779
|
+
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
|
780
|
+
elsif @config[:timeout]
|
|
781
|
+
@raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
|
|
782
|
+
elsif @config[:retries]
|
|
783
|
+
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
784
|
+
raw_connection.busy_handler do |count|
|
|
785
|
+
count <= retries
|
|
786
|
+
end
|
|
787
|
+
end
|
|
586
788
|
|
|
587
|
-
|
|
789
|
+
super
|
|
790
|
+
|
|
791
|
+
pragmas = @config.fetch(:pragmas, {}).stringify_keys
|
|
792
|
+
DEFAULT_PRAGMAS.merge(pragmas).each do |pragma, value|
|
|
793
|
+
if ::SQLite3::Pragmas.method_defined?("#{pragma}=")
|
|
794
|
+
@raw_connection.public_send("#{pragma}=", value)
|
|
795
|
+
else
|
|
796
|
+
warn "Unknown SQLite pragma: #{pragma}"
|
|
797
|
+
end
|
|
798
|
+
end
|
|
588
799
|
end
|
|
589
800
|
end
|
|
590
801
|
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)
|