activerecord 7.0.8.7 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +781 -1777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +30 -30
- 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 +31 -23
- 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 +40 -9
- 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 +35 -21
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
- data/lib/active_record/associations/join_dependency.rb +10 -10
- 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 +1 -3
- 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 +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- 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 +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -33
- data/lib/active_record/attributes.rb +96 -71
- data/lib/active_record/autosave_association.rb +81 -39
- data/lib/active_record/base.rb +11 -7
- data/lib/active_record/callbacks.rb +11 -25
- 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 +123 -131
- 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 +343 -91
- 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 +229 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- 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 +142 -12
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
- 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 +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- 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 +153 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -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 +57 -45
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
- 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 +101 -105
- data/lib/active_record/core.rb +273 -178
- data/lib/active_record/counter_cache.rb +69 -35
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
- 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 +87 -34
- data/lib/active_record/delegated_type.rb +56 -27
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -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 +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 +46 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
- data/lib/active_record/encryption/encryptor.rb +35 -19
- 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/key_provider.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 +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +130 -28
- data/lib/active_record/errors.rb +154 -34
- data/lib/active_record/explain.rb +21 -12
- 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 +48 -10
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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 +236 -118
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +96 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +35 -10
- data/lib/active_record/railtie.rb +131 -87
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +147 -155
- 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 +267 -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 +270 -108
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +97 -21
- 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 +20 -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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +585 -109
- 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 +15 -21
- data/lib/active_record/relation.rb +592 -92
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +90 -23
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- 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/signed_id.rb +33 -11
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- 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 +108 -24
- data/lib/active_record/translation.rb +0 -2
- 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 +1 -3
- 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 +9 -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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- 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/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- 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/delete_statement.rb +4 -2
- 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} +5 -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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +114 -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 +56 -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,57 +11,58 @@ 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
|
#
|
|
54
|
-
# Options
|
|
24
|
+
# ==== Options
|
|
55
25
|
#
|
|
56
26
|
# * <tt>:database</tt> - Path to the database file.
|
|
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,30 @@ 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
|
-
|
|
198
|
+
def connected?
|
|
199
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def active?
|
|
203
|
+
if connected?
|
|
204
|
+
verified!
|
|
205
|
+
true
|
|
206
|
+
end
|
|
168
207
|
end
|
|
169
208
|
|
|
209
|
+
alias :reset! :reconnect!
|
|
210
|
+
|
|
170
211
|
# Disconnects from the database if already connected. Otherwise, this
|
|
171
212
|
# method does nothing.
|
|
172
213
|
def disconnect!
|
|
173
214
|
super
|
|
174
|
-
|
|
215
|
+
|
|
216
|
+
@raw_connection&.close rescue nil
|
|
217
|
+
@raw_connection = nil
|
|
175
218
|
end
|
|
176
219
|
|
|
177
220
|
def supports_index_sort_order?
|
|
@@ -184,7 +227,7 @@ module ActiveRecord
|
|
|
184
227
|
|
|
185
228
|
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
|
186
229
|
def encoding
|
|
187
|
-
|
|
230
|
+
any_raw_connection.encoding.to_s
|
|
188
231
|
end
|
|
189
232
|
|
|
190
233
|
def supports_explain?
|
|
@@ -195,6 +238,10 @@ module ActiveRecord
|
|
|
195
238
|
true
|
|
196
239
|
end
|
|
197
240
|
|
|
241
|
+
def supports_deferrable_constraints?
|
|
242
|
+
true
|
|
243
|
+
end
|
|
244
|
+
|
|
198
245
|
# REFERENTIAL INTEGRITY ====================================
|
|
199
246
|
|
|
200
247
|
def disable_referential_integrity # :nodoc:
|
|
@@ -211,8 +258,14 @@ module ActiveRecord
|
|
|
211
258
|
end
|
|
212
259
|
end
|
|
213
260
|
|
|
214
|
-
def
|
|
215
|
-
|
|
261
|
+
def check_all_foreign_keys_valid! # :nodoc:
|
|
262
|
+
sql = "PRAGMA foreign_key_check"
|
|
263
|
+
result = execute(sql)
|
|
264
|
+
|
|
265
|
+
unless result.blank?
|
|
266
|
+
tables = result.map { |row| row["table"] }
|
|
267
|
+
raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql, connection_pool: @pool)
|
|
268
|
+
end
|
|
216
269
|
end
|
|
217
270
|
|
|
218
271
|
# SCHEMA STATEMENTS ========================================
|
|
@@ -234,14 +287,16 @@ module ActiveRecord
|
|
|
234
287
|
#
|
|
235
288
|
# Example:
|
|
236
289
|
# rename_table('octopuses', 'octopi')
|
|
237
|
-
def rename_table(table_name, new_name)
|
|
290
|
+
def rename_table(table_name, new_name, **options)
|
|
291
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
|
238
292
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
239
293
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
240
294
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
241
|
-
rename_table_indexes(table_name, new_name)
|
|
295
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
242
296
|
end
|
|
243
297
|
|
|
244
298
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
299
|
+
type = type.to_sym
|
|
245
300
|
if invalid_alter_table_type?(type, options)
|
|
246
301
|
alter_table(table_name) do |definition|
|
|
247
302
|
definition.column(column_name, type, **options)
|
|
@@ -277,8 +332,10 @@ module ActiveRecord
|
|
|
277
332
|
end
|
|
278
333
|
|
|
279
334
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
|
335
|
+
validate_change_column_null_argument!(null)
|
|
336
|
+
|
|
280
337
|
unless null || default.nil?
|
|
281
|
-
|
|
338
|
+
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
339
|
end
|
|
283
340
|
alter_table(table_name) do |definition|
|
|
284
341
|
definition[column_name].null = null
|
|
@@ -297,20 +354,58 @@ module ActiveRecord
|
|
|
297
354
|
rename_column_indexes(table_name, column.name, new_column_name)
|
|
298
355
|
end
|
|
299
356
|
|
|
357
|
+
def add_timestamps(table_name, **options)
|
|
358
|
+
options[:null] = false if options[:null].nil?
|
|
359
|
+
|
|
360
|
+
if !options.key?(:precision)
|
|
361
|
+
options[:precision] = 6
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
alter_table(table_name) do |definition|
|
|
365
|
+
definition.column :created_at, :datetime, **options
|
|
366
|
+
definition.column :updated_at, :datetime, **options
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
300
370
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
|
301
371
|
super(table_name, ref_name, type: :integer, **options)
|
|
302
372
|
end
|
|
303
373
|
alias :add_belongs_to :add_reference
|
|
304
374
|
|
|
375
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
376
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
305
377
|
def foreign_keys(table_name)
|
|
306
|
-
|
|
307
|
-
fk_info
|
|
378
|
+
# SQLite returns 1 row for each column of composite foreign keys.
|
|
379
|
+
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
|
380
|
+
# Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
|
|
381
|
+
fk_defs = table_structure_sql(table_name)
|
|
382
|
+
.select do |column_string|
|
|
383
|
+
column_string.start_with?("CONSTRAINT") &&
|
|
384
|
+
column_string.include?("FOREIGN KEY")
|
|
385
|
+
end
|
|
386
|
+
.to_h do |fk_string|
|
|
387
|
+
_, from, table, to = fk_string.match(FK_REGEX).to_a
|
|
388
|
+
_, mode = fk_string.match(DEFERRABLE_REGEX).to_a
|
|
389
|
+
deferred = mode&.downcase&.to_sym || false
|
|
390
|
+
[[table, from, to], deferred]
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
|
394
|
+
grouped_fk.map do |group|
|
|
395
|
+
row = group.first
|
|
308
396
|
options = {
|
|
309
|
-
column: row["from"],
|
|
310
|
-
primary_key: row["to"],
|
|
311
397
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
|
312
|
-
on_update: extract_foreign_key_action(row["on_update"])
|
|
398
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
|
399
|
+
deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
|
|
313
400
|
}
|
|
401
|
+
|
|
402
|
+
if group.one?
|
|
403
|
+
options[:column] = row["from"]
|
|
404
|
+
options[:primary_key] = row["to"]
|
|
405
|
+
else
|
|
406
|
+
options[:column] = group.map { |row| row["from"] }
|
|
407
|
+
options[:primary_key] = group.map { |row| row["to"] }
|
|
408
|
+
end
|
|
314
409
|
ForeignKeyDefinition.new(table_name, row["table"], options)
|
|
315
410
|
end
|
|
316
411
|
end
|
|
@@ -330,6 +425,7 @@ module ActiveRecord
|
|
|
330
425
|
end
|
|
331
426
|
end
|
|
332
427
|
|
|
428
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
333
429
|
sql
|
|
334
430
|
end
|
|
335
431
|
|
|
@@ -337,6 +433,10 @@ module ActiveRecord
|
|
|
337
433
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
|
338
434
|
end
|
|
339
435
|
|
|
436
|
+
def use_insert_returning?
|
|
437
|
+
@use_insert_returning
|
|
438
|
+
end
|
|
439
|
+
|
|
340
440
|
def get_database_version # :nodoc:
|
|
341
441
|
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
|
342
442
|
end
|
|
@@ -367,12 +467,9 @@ module ActiveRecord
|
|
|
367
467
|
end
|
|
368
468
|
|
|
369
469
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
470
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
370
471
|
|
|
371
472
|
private
|
|
372
|
-
def type_map
|
|
373
|
-
TYPE_MAP
|
|
374
|
-
end
|
|
375
|
-
|
|
376
473
|
# See https://www.sqlite.org/limits.html,
|
|
377
474
|
# the default value is 999 when not configured.
|
|
378
475
|
def bind_params_length
|
|
@@ -380,8 +477,8 @@ module ActiveRecord
|
|
|
380
477
|
end
|
|
381
478
|
|
|
382
479
|
def table_structure(table_name)
|
|
383
|
-
structure =
|
|
384
|
-
raise
|
|
480
|
+
structure = table_info(table_name)
|
|
481
|
+
raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
|
|
385
482
|
table_structure_with_collation(table_name, structure)
|
|
386
483
|
end
|
|
387
484
|
alias column_definitions table_structure
|
|
@@ -391,14 +488,17 @@ module ActiveRecord
|
|
|
391
488
|
when /^null$/i
|
|
392
489
|
nil
|
|
393
490
|
# Quoted types
|
|
394
|
-
when /^'(
|
|
491
|
+
when /^'([^|]*)'$/m
|
|
395
492
|
$1.gsub("''", "'")
|
|
396
493
|
# Quoted types
|
|
397
|
-
when /^"(
|
|
494
|
+
when /^"([^|]*)"$/m
|
|
398
495
|
$1.gsub('""', '"')
|
|
399
496
|
# Numeric types
|
|
400
497
|
when /\A-?\d+(\.\d*)?\z/
|
|
401
498
|
$&
|
|
499
|
+
# Binary columns
|
|
500
|
+
when /x'(.*)'/
|
|
501
|
+
[ $1 ].pack("H*")
|
|
402
502
|
else
|
|
403
503
|
# Anything else is blank or some function
|
|
404
504
|
# and we can't know the value of that, so return nil.
|
|
@@ -411,14 +511,15 @@ module ActiveRecord
|
|
|
411
511
|
end
|
|
412
512
|
|
|
413
513
|
def has_default_function?(default_value, default)
|
|
414
|
-
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
|
514
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
|
415
515
|
end
|
|
416
516
|
|
|
417
517
|
# See: https://www.sqlite.org/lang_altertable.html
|
|
418
518
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
|
419
519
|
def invalid_alter_table_type?(type, options)
|
|
420
|
-
type
|
|
421
|
-
options[:null] == false && options[:default].nil?
|
|
520
|
+
type == :primary_key || options[:primary_key] ||
|
|
521
|
+
options[:null] == false && options[:default].nil? ||
|
|
522
|
+
(type == :virtual && options[:stored])
|
|
422
523
|
end
|
|
423
524
|
|
|
424
525
|
def alter_table(
|
|
@@ -446,8 +547,8 @@ module ActiveRecord
|
|
|
446
547
|
yield definition if block_given?
|
|
447
548
|
end
|
|
448
549
|
|
|
449
|
-
|
|
450
|
-
|
|
550
|
+
disable_referential_integrity do
|
|
551
|
+
transaction do
|
|
451
552
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
452
553
|
move_table(altered_table_name, table_name, &caller)
|
|
453
554
|
end
|
|
@@ -474,25 +575,40 @@ module ActiveRecord
|
|
|
474
575
|
options[:rename][column.name.to_sym] ||
|
|
475
576
|
column.name) : column.name
|
|
476
577
|
|
|
477
|
-
|
|
578
|
+
column_options = {
|
|
579
|
+
limit: column.limit,
|
|
580
|
+
precision: column.precision,
|
|
581
|
+
scale: column.scale,
|
|
582
|
+
null: column.null,
|
|
583
|
+
collation: column.collation,
|
|
584
|
+
primary_key: column_name == from_primary_key
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if column.virtual?
|
|
588
|
+
column_options[:as] = column.default_function
|
|
589
|
+
column_options[:stored] = column.virtual_stored?
|
|
590
|
+
column_options[:type] = column.type
|
|
591
|
+
elsif column.has_default?
|
|
478
592
|
type = lookup_cast_type_from_column(column)
|
|
479
593
|
default = type.deserialize(column.default)
|
|
480
594
|
default = -> { column.default_function } if default.nil?
|
|
595
|
+
|
|
596
|
+
unless column.auto_increment?
|
|
597
|
+
column_options[:default] = default
|
|
598
|
+
end
|
|
481
599
|
end
|
|
482
600
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
precision: column.precision, scale: column.scale,
|
|
486
|
-
null: column.null, collation: column.collation,
|
|
487
|
-
primary_key: column_name == from_primary_key
|
|
488
|
-
)
|
|
601
|
+
column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
|
|
602
|
+
@definition.column(column_name, column_type, **column_options)
|
|
489
603
|
end
|
|
490
604
|
|
|
491
605
|
yield @definition if block_given?
|
|
492
606
|
end
|
|
493
607
|
copy_table_indexes(from, to, options[:rename] || {})
|
|
608
|
+
|
|
609
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
|
494
610
|
copy_table_contents(from, to,
|
|
495
|
-
|
|
611
|
+
columns_to_copy,
|
|
496
612
|
options[:rename] || {})
|
|
497
613
|
end
|
|
498
614
|
|
|
@@ -533,7 +649,7 @@ module ActiveRecord
|
|
|
533
649
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
|
534
650
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
|
535
651
|
|
|
536
|
-
|
|
652
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
|
537
653
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
|
538
654
|
end
|
|
539
655
|
|
|
@@ -543,43 +659,36 @@ module ActiveRecord
|
|
|
543
659
|
# Older versions of SQLite return:
|
|
544
660
|
# column *column_name* is not unique
|
|
545
661
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
|
546
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
662
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
547
663
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
|
548
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
664
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
549
665
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
550
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
666
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
551
667
|
elsif exception.message.match?(/called on a closed database/i)
|
|
552
|
-
ConnectionNotEstablished.new(exception)
|
|
668
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
553
669
|
else
|
|
554
670
|
super
|
|
555
671
|
end
|
|
556
672
|
end
|
|
557
673
|
|
|
558
|
-
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
|
674
|
+
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
|
675
|
+
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*"(\w+)".+PRIMARY KEY AUTOINCREMENT/i
|
|
676
|
+
GENERATED_ALWAYS_AS_REGEX = /.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i
|
|
559
677
|
|
|
560
678
|
def table_structure_with_collation(table_name, basic_structure)
|
|
561
679
|
collation_hash = {}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
(SELECT * FROM sqlite_master UNION ALL
|
|
565
|
-
SELECT * FROM sqlite_temp_master)
|
|
566
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
|
567
|
-
SQL
|
|
568
|
-
|
|
569
|
-
# Result will have following sample string
|
|
570
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
571
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
|
572
|
-
result = query_value(sql, "SCHEMA")
|
|
680
|
+
auto_increments = {}
|
|
681
|
+
generated_columns = {}
|
|
573
682
|
|
|
574
|
-
|
|
575
|
-
# Splitting with left parentheses and discarding the first part will return all
|
|
576
|
-
# columns separated with comma(,).
|
|
577
|
-
columns_string = result.split("(", 2).last
|
|
683
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
|
578
684
|
|
|
579
|
-
|
|
685
|
+
if column_strings.any?
|
|
686
|
+
column_strings.each do |column_string|
|
|
580
687
|
# This regex will match the column name and collation type and will save
|
|
581
688
|
# the value in $1 and $2 respectively.
|
|
582
689
|
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
|
690
|
+
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
|
691
|
+
generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
|
|
583
692
|
end
|
|
584
693
|
|
|
585
694
|
basic_structure.map do |column|
|
|
@@ -589,6 +698,14 @@ module ActiveRecord
|
|
|
589
698
|
column["collation"] = collation_hash[column_name]
|
|
590
699
|
end
|
|
591
700
|
|
|
701
|
+
if auto_increments.has_key?(column_name)
|
|
702
|
+
column["auto_increment"] = true
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
if generated_columns.has_key?(column_name)
|
|
706
|
+
column["dflt_value"] = generated_columns[column_name]
|
|
707
|
+
end
|
|
708
|
+
|
|
592
709
|
column
|
|
593
710
|
end
|
|
594
711
|
else
|
|
@@ -596,6 +713,50 @@ module ActiveRecord
|
|
|
596
713
|
end
|
|
597
714
|
end
|
|
598
715
|
|
|
716
|
+
UNQUOTED_OPEN_PARENS_REGEX = /\((?![^'"]*['"][^'"]*$)/
|
|
717
|
+
FINAL_CLOSE_PARENS_REGEX = /\);*\z/
|
|
718
|
+
|
|
719
|
+
def table_structure_sql(table_name, column_names = nil)
|
|
720
|
+
unless column_names
|
|
721
|
+
column_info = table_info(table_name)
|
|
722
|
+
column_names = column_info.map { |column| column["name"] }
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
sql = <<~SQL
|
|
726
|
+
SELECT sql FROM
|
|
727
|
+
(SELECT * FROM sqlite_master UNION ALL
|
|
728
|
+
SELECT * FROM sqlite_temp_master)
|
|
729
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
|
730
|
+
SQL
|
|
731
|
+
|
|
732
|
+
# Result will have following sample string
|
|
733
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
734
|
+
# "password_digest" varchar COLLATE "NOCASE",
|
|
735
|
+
# "o_id" integer,
|
|
736
|
+
# CONSTRAINT "fk_rails_78146ddd2e" FOREIGN KEY ("o_id") REFERENCES "os" ("id"));
|
|
737
|
+
result = query_value(sql, "SCHEMA")
|
|
738
|
+
|
|
739
|
+
return [] unless result
|
|
740
|
+
|
|
741
|
+
# Splitting with left parentheses and discarding the first part will return all
|
|
742
|
+
# columns separated with comma(,).
|
|
743
|
+
result.partition(UNQUOTED_OPEN_PARENS_REGEX)
|
|
744
|
+
.last
|
|
745
|
+
.sub(FINAL_CLOSE_PARENS_REGEX, "")
|
|
746
|
+
# column definitions can have a comma in them, so split on commas followed
|
|
747
|
+
# by a space and a column name in quotes or followed by the keyword CONSTRAINT
|
|
748
|
+
.split(/,(?=\s(?:CONSTRAINT|"(?:#{Regexp.union(column_names).source})"))/i)
|
|
749
|
+
.map(&:strip)
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def table_info(table_name)
|
|
753
|
+
if supports_virtual_columns?
|
|
754
|
+
internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
|
|
755
|
+
else
|
|
756
|
+
internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
|
757
|
+
end
|
|
758
|
+
end
|
|
759
|
+
|
|
599
760
|
def arel_visitor
|
|
600
761
|
Arel::Visitors::SQLite.new(self)
|
|
601
762
|
end
|
|
@@ -605,17 +766,41 @@ module ActiveRecord
|
|
|
605
766
|
end
|
|
606
767
|
|
|
607
768
|
def connect
|
|
608
|
-
@
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
769
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
|
770
|
+
rescue ConnectionNotEstablished => ex
|
|
771
|
+
raise ex.set_pool(@pool)
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
def reconnect
|
|
775
|
+
if active?
|
|
776
|
+
@raw_connection.rollback rescue nil
|
|
777
|
+
else
|
|
778
|
+
connect
|
|
779
|
+
end
|
|
613
780
|
end
|
|
614
781
|
|
|
615
782
|
def configure_connection
|
|
616
|
-
@
|
|
783
|
+
if @config[:timeout] && @config[:retries]
|
|
784
|
+
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
|
785
|
+
elsif @config[:timeout]
|
|
786
|
+
@raw_connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout]))
|
|
787
|
+
elsif @config[:retries]
|
|
788
|
+
retries = self.class.type_cast_config_to_integer(@config[:retries])
|
|
789
|
+
raw_connection.busy_handler do |count|
|
|
790
|
+
count <= retries
|
|
791
|
+
end
|
|
792
|
+
end
|
|
617
793
|
|
|
618
|
-
|
|
794
|
+
super
|
|
795
|
+
|
|
796
|
+
pragmas = @config.fetch(:pragmas, {}).stringify_keys
|
|
797
|
+
DEFAULT_PRAGMAS.merge(pragmas).each do |pragma, value|
|
|
798
|
+
if ::SQLite3::Pragmas.method_defined?("#{pragma}=")
|
|
799
|
+
@raw_connection.public_send("#{pragma}=", value)
|
|
800
|
+
else
|
|
801
|
+
warn "Unknown SQLite pragma: #{pragma}"
|
|
802
|
+
end
|
|
803
|
+
end
|
|
619
804
|
end
|
|
620
805
|
end
|
|
621
806
|
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)
|