activerecord 6.1.7 → 7.2.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 +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- 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 +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- 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 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- 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/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- 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 +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- 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 +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -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 +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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 +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- 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/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- 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 +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- 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 +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- 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/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- 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 +13 -7
- 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 +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- 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/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- 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/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- 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 +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- 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
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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
|
18
|
-
|
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
|
-
)
|
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
|
-
module ConnectionAdapters #:nodoc:
|
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,26 +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
|
-
super
|
88
|
-
configure_connection
|
89
|
-
end
|
99
|
+
def initialize(...)
|
100
|
+
super
|
90
101
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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:/
|
95
109
|
else
|
96
|
-
|
97
|
-
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
|
98
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)
|
99
129
|
end
|
100
130
|
|
101
131
|
def supports_ddl_transactions?
|
@@ -146,6 +176,10 @@ module ActiveRecord
|
|
146
176
|
database_version >= "3.8.3"
|
147
177
|
end
|
148
178
|
|
179
|
+
def supports_insert_returning?
|
180
|
+
database_version >= "3.35.0"
|
181
|
+
end
|
182
|
+
|
149
183
|
def supports_insert_on_conflict?
|
150
184
|
database_version >= "3.24.0"
|
151
185
|
end
|
@@ -153,33 +187,42 @@ module ActiveRecord
|
|
153
187
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
154
188
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
155
189
|
|
156
|
-
def
|
157
|
-
!@
|
190
|
+
def supports_concurrent_connections?
|
191
|
+
!@memory_database
|
158
192
|
end
|
159
193
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
194
|
+
def supports_virtual_columns?
|
195
|
+
database_version >= "3.31.0"
|
196
|
+
end
|
197
|
+
|
198
|
+
def connected?
|
199
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
163
200
|
end
|
164
201
|
|
202
|
+
alias_method :active?, :connected?
|
203
|
+
|
204
|
+
alias :reset! :reconnect!
|
205
|
+
|
165
206
|
# Disconnects from the database if already connected. Otherwise, this
|
166
207
|
# method does nothing.
|
167
208
|
def disconnect!
|
168
209
|
super
|
169
|
-
|
210
|
+
|
211
|
+
@raw_connection&.close rescue nil
|
212
|
+
@raw_connection = nil
|
170
213
|
end
|
171
214
|
|
172
215
|
def supports_index_sort_order?
|
173
216
|
true
|
174
217
|
end
|
175
218
|
|
176
|
-
def native_database_types
|
219
|
+
def native_database_types # :nodoc:
|
177
220
|
NATIVE_DATABASE_TYPES
|
178
221
|
end
|
179
222
|
|
180
|
-
# Returns the current database encoding format as a string,
|
223
|
+
# Returns the current database encoding format as a string, e.g. 'UTF-8'
|
181
224
|
def encoding
|
182
|
-
|
225
|
+
any_raw_connection.encoding.to_s
|
183
226
|
end
|
184
227
|
|
185
228
|
def supports_explain?
|
@@ -190,6 +233,10 @@ module ActiveRecord
|
|
190
233
|
true
|
191
234
|
end
|
192
235
|
|
236
|
+
def supports_deferrable_constraints?
|
237
|
+
true
|
238
|
+
end
|
239
|
+
|
193
240
|
# REFERENTIAL INTEGRITY ====================================
|
194
241
|
|
195
242
|
def disable_referential_integrity # :nodoc:
|
@@ -206,6 +253,16 @@ module ActiveRecord
|
|
206
253
|
end
|
207
254
|
end
|
208
255
|
|
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
|
264
|
+
end
|
265
|
+
|
209
266
|
# SCHEMA STATEMENTS ========================================
|
210
267
|
|
211
268
|
def primary_keys(table_name) # :nodoc:
|
@@ -225,14 +282,16 @@ module ActiveRecord
|
|
225
282
|
#
|
226
283
|
# Example:
|
227
284
|
# rename_table('octopuses', 'octopi')
|
228
|
-
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]
|
229
287
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
230
288
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
231
289
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
232
|
-
rename_table_indexes(table_name, new_name)
|
290
|
+
rename_table_indexes(table_name, new_name, **options)
|
233
291
|
end
|
234
292
|
|
235
|
-
def add_column(table_name, column_name, type, **options)
|
293
|
+
def add_column(table_name, column_name, type, **options) # :nodoc:
|
294
|
+
type = type.to_sym
|
236
295
|
if invalid_alter_table_type?(type, options)
|
237
296
|
alter_table(table_name) do |definition|
|
238
297
|
definition.column(column_name, type, **options)
|
@@ -242,16 +301,24 @@ module ActiveRecord
|
|
242
301
|
end
|
243
302
|
end
|
244
303
|
|
245
|
-
def remove_column(table_name, column_name, type = nil, **options)
|
304
|
+
def remove_column(table_name, column_name, type = nil, **options) # :nodoc:
|
246
305
|
alter_table(table_name) do |definition|
|
247
306
|
definition.remove_column column_name
|
248
|
-
definition.foreign_keys.delete_if
|
249
|
-
|
307
|
+
definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
|
312
|
+
alter_table(table_name) do |definition|
|
313
|
+
column_names.each do |column_name|
|
314
|
+
definition.remove_column column_name
|
250
315
|
end
|
316
|
+
column_names = column_names.map(&:to_s)
|
317
|
+
definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
|
251
318
|
end
|
252
319
|
end
|
253
320
|
|
254
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
321
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
255
322
|
default = extract_new_default_value(default_or_changes)
|
256
323
|
|
257
324
|
alter_table(table_name) do |definition|
|
@@ -259,44 +326,81 @@ module ActiveRecord
|
|
259
326
|
end
|
260
327
|
end
|
261
328
|
|
262
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
329
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
330
|
+
validate_change_column_null_argument!(null)
|
331
|
+
|
263
332
|
unless null || default.nil?
|
264
|
-
|
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")
|
265
334
|
end
|
266
335
|
alter_table(table_name) do |definition|
|
267
336
|
definition[column_name].null = null
|
268
337
|
end
|
269
338
|
end
|
270
339
|
|
271
|
-
def change_column(table_name, column_name, type, **options)
|
340
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
272
341
|
alter_table(table_name) do |definition|
|
273
|
-
definition
|
274
|
-
self.type = aliased_types(type.to_s, type)
|
275
|
-
self.options.merge!(options)
|
276
|
-
end
|
342
|
+
definition.change_column(column_name, type, **options)
|
277
343
|
end
|
278
344
|
end
|
279
345
|
|
280
|
-
def rename_column(table_name, column_name, new_column_name)
|
346
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
281
347
|
column = column_for(table_name, column_name)
|
282
348
|
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
283
349
|
rename_column_indexes(table_name, column.name, new_column_name)
|
284
350
|
end
|
285
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
|
+
|
286
365
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
287
366
|
super(table_name, ref_name, type: :integer, **options)
|
288
367
|
end
|
289
368
|
alias :add_belongs_to :add_reference
|
290
369
|
|
370
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
371
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
291
372
|
def foreign_keys(table_name)
|
292
|
-
|
293
|
-
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
|
294
391
|
options = {
|
295
|
-
column: row["from"],
|
296
|
-
primary_key: row["to"],
|
297
392
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
298
|
-
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"]]]
|
299
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
|
300
404
|
ForeignKeyDefinition.new(table_name, row["table"], options)
|
301
405
|
end
|
302
406
|
end
|
@@ -308,10 +412,15 @@ module ActiveRecord
|
|
308
412
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
309
413
|
elsif insert.update_duplicates?
|
310
414
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
311
|
-
|
312
|
-
|
415
|
+
if insert.raw_update_sql?
|
416
|
+
sql << insert.raw_update_sql
|
417
|
+
else
|
418
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
419
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
420
|
+
end
|
313
421
|
end
|
314
422
|
|
423
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
315
424
|
sql
|
316
425
|
end
|
317
426
|
|
@@ -319,8 +428,12 @@ module ActiveRecord
|
|
319
428
|
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
320
429
|
end
|
321
430
|
|
431
|
+
def use_insert_returning?
|
432
|
+
@use_insert_returning
|
433
|
+
end
|
434
|
+
|
322
435
|
def get_database_version # :nodoc:
|
323
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
436
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
324
437
|
end
|
325
438
|
|
326
439
|
def check_version # :nodoc:
|
@@ -329,6 +442,28 @@ module ActiveRecord
|
|
329
442
|
end
|
330
443
|
end
|
331
444
|
|
445
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
446
|
+
private
|
447
|
+
def _limit
|
448
|
+
# INTEGER storage class can be stored 8 bytes value.
|
449
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
450
|
+
limit || 8
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
455
|
+
|
456
|
+
class << self
|
457
|
+
private
|
458
|
+
def initialize_type_map(m)
|
459
|
+
super
|
460
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
465
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
466
|
+
|
332
467
|
private
|
333
468
|
# See https://www.sqlite.org/limits.html,
|
334
469
|
# the default value is 999 when not configured.
|
@@ -336,23 +471,50 @@ module ActiveRecord
|
|
336
471
|
999
|
337
472
|
end
|
338
473
|
|
339
|
-
def initialize_type_map(m = type_map)
|
340
|
-
super
|
341
|
-
register_class_with_limit m, %r(int)i, SQLite3Integer
|
342
|
-
end
|
343
|
-
|
344
474
|
def table_structure(table_name)
|
345
|
-
structure =
|
346
|
-
raise
|
475
|
+
structure = table_info(table_name)
|
476
|
+
raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
|
347
477
|
table_structure_with_collation(table_name, structure)
|
348
478
|
end
|
349
479
|
alias column_definitions table_structure
|
350
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
|
+
|
351
512
|
# See: https://www.sqlite.org/lang_altertable.html
|
352
513
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
353
514
|
def invalid_alter_table_type?(type, options)
|
354
|
-
type
|
355
|
-
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])
|
356
518
|
end
|
357
519
|
|
358
520
|
def alter_table(
|
@@ -408,19 +570,40 @@ module ActiveRecord
|
|
408
570
|
options[:rename][column.name.to_sym] ||
|
409
571
|
column.name) : column.name
|
410
572
|
|
411
|
-
|
412
|
-
limit: column.limit,
|
413
|
-
precision: column.precision,
|
414
|
-
|
573
|
+
column_options = {
|
574
|
+
limit: column.limit,
|
575
|
+
precision: column.precision,
|
576
|
+
scale: column.scale,
|
577
|
+
null: column.null,
|
578
|
+
collation: column.collation,
|
415
579
|
primary_key: column_name == from_primary_key
|
416
|
-
|
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)
|
417
598
|
end
|
418
599
|
|
419
600
|
yield @definition if block_given?
|
420
601
|
end
|
421
602
|
copy_table_indexes(from, to, options[:rename] || {})
|
603
|
+
|
604
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
422
605
|
copy_table_contents(from, to,
|
423
|
-
|
606
|
+
columns_to_copy,
|
424
607
|
options[:rename] || {})
|
425
608
|
end
|
426
609
|
|
@@ -446,6 +629,7 @@ module ActiveRecord
|
|
446
629
|
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
447
630
|
options[:unique] = true if index.unique
|
448
631
|
options[:where] = index.where if index.where
|
632
|
+
options[:order] = index.orders if index.orders
|
449
633
|
add_index(to, columns, **options)
|
450
634
|
end
|
451
635
|
end
|
@@ -460,7 +644,7 @@ module ActiveRecord
|
|
460
644
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
461
645
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
462
646
|
|
463
|
-
|
647
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
464
648
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
465
649
|
end
|
466
650
|
|
@@ -470,43 +654,36 @@ module ActiveRecord
|
|
470
654
|
# Older versions of SQLite return:
|
471
655
|
# column *column_name* is not unique
|
472
656
|
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
473
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
657
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
474
658
|
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
475
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
659
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
476
660
|
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
477
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
661
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
478
662
|
elsif exception.message.match?(/called on a closed database/i)
|
479
|
-
ConnectionNotEstablished.new(exception)
|
663
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
480
664
|
else
|
481
665
|
super
|
482
666
|
end
|
483
667
|
end
|
484
668
|
|
485
|
-
COLLATE_REGEX =
|
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
|
486
672
|
|
487
673
|
def table_structure_with_collation(table_name, basic_structure)
|
488
674
|
collation_hash = {}
|
489
|
-
|
490
|
-
|
491
|
-
(SELECT * FROM sqlite_master UNION ALL
|
492
|
-
SELECT * FROM sqlite_temp_master)
|
493
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
494
|
-
SQL
|
495
|
-
|
496
|
-
# Result will have following sample string
|
497
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
498
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
499
|
-
result = query_value(sql, "SCHEMA")
|
675
|
+
auto_increments = {}
|
676
|
+
generated_columns = {}
|
500
677
|
|
501
|
-
|
502
|
-
# Splitting with left parentheses and discarding the first part will return all
|
503
|
-
# columns separated with comma(,).
|
504
|
-
columns_string = result.split("(", 2).last
|
678
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
505
679
|
|
506
|
-
|
680
|
+
if column_strings.any?
|
681
|
+
column_strings.each do |column_string|
|
507
682
|
# This regex will match the column name and collation type and will save
|
508
683
|
# the value in $1 and $2 respectively.
|
509
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
|
510
687
|
end
|
511
688
|
|
512
689
|
basic_structure.map do |column|
|
@@ -516,6 +693,14 @@ module ActiveRecord
|
|
516
693
|
column["collation"] = collation_hash[column_name]
|
517
694
|
end
|
518
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
|
+
|
519
704
|
column
|
520
705
|
end
|
521
706
|
else
|
@@ -523,6 +708,50 @@ module ActiveRecord
|
|
523
708
|
end
|
524
709
|
end
|
525
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
|
+
|
526
755
|
def arel_visitor
|
527
756
|
Arel::Visitors::SQLite.new(self)
|
528
757
|
end
|
@@ -532,29 +761,42 @@ module ActiveRecord
|
|
532
761
|
end
|
533
762
|
|
534
763
|
def connect
|
535
|
-
@
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
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
|
540
775
|
end
|
541
776
|
|
542
777
|
def configure_connection
|
543
|
-
@
|
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
|
544
788
|
|
545
|
-
|
546
|
-
end
|
789
|
+
super
|
547
790
|
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
#
|
552
|
-
|
553
|
-
|
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}"
|
554
797
|
end
|
798
|
+
end
|
555
799
|
end
|
556
|
-
|
557
|
-
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
558
800
|
end
|
559
801
|
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
560
802
|
end
|