activerecord 6.0.0 → 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 +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- 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 +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- 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 +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- 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 -25
- 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 +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- 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 +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- 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 +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- 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 +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- 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 +171 -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 +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- 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/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- 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 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -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 +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- 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/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- 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 +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- 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 +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- 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 +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -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
|
|
18
|
-
|
|
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]
|
|
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:
|
|
51
|
-
# The SQLite3 adapter works SQLite 3.6.16 or newer
|
|
52
|
-
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
|
|
18
|
+
module ConnectionAdapters # :nodoc:
|
|
19
|
+
# = Active Record SQLite3 Adapter
|
|
53
20
|
#
|
|
54
|
-
#
|
|
21
|
+
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
|
22
|
+
# (available as gem from https://rubygems.org/gems/sqlite3).
|
|
23
|
+
#
|
|
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,36 +78,54 @@ module ActiveRecord
|
|
|
76
78
|
json: { name: "json" },
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
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
|
+
}
|
|
88
89
|
|
|
89
90
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
|
91
|
+
alias reset clear
|
|
92
|
+
|
|
90
93
|
private
|
|
91
94
|
def dealloc(stmt)
|
|
92
95
|
stmt.close unless stmt.closed?
|
|
93
96
|
end
|
|
94
97
|
end
|
|
95
98
|
|
|
96
|
-
def initialize(
|
|
97
|
-
super
|
|
98
|
-
configure_connection
|
|
99
|
-
end
|
|
99
|
+
def initialize(...)
|
|
100
|
+
super
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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:/
|
|
105
109
|
else
|
|
106
|
-
|
|
107
|
-
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
|
|
108
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)
|
|
109
129
|
end
|
|
110
130
|
|
|
111
131
|
def supports_ddl_transactions?
|
|
@@ -116,6 +136,10 @@ module ActiveRecord
|
|
|
116
136
|
true
|
|
117
137
|
end
|
|
118
138
|
|
|
139
|
+
def supports_transaction_isolation?
|
|
140
|
+
true
|
|
141
|
+
end
|
|
142
|
+
|
|
119
143
|
def supports_partial_index?
|
|
120
144
|
true
|
|
121
145
|
end
|
|
@@ -132,6 +156,10 @@ module ActiveRecord
|
|
|
132
156
|
true
|
|
133
157
|
end
|
|
134
158
|
|
|
159
|
+
def supports_check_constraints?
|
|
160
|
+
true
|
|
161
|
+
end
|
|
162
|
+
|
|
135
163
|
def supports_views?
|
|
136
164
|
true
|
|
137
165
|
end
|
|
@@ -144,6 +172,14 @@ module ActiveRecord
|
|
|
144
172
|
true
|
|
145
173
|
end
|
|
146
174
|
|
|
175
|
+
def supports_common_table_expressions?
|
|
176
|
+
database_version >= "3.8.3"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def supports_insert_returning?
|
|
180
|
+
database_version >= "3.35.0"
|
|
181
|
+
end
|
|
182
|
+
|
|
147
183
|
def supports_insert_on_conflict?
|
|
148
184
|
database_version >= "3.24.0"
|
|
149
185
|
end
|
|
@@ -151,40 +187,47 @@ module ActiveRecord
|
|
|
151
187
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
|
152
188
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
|
153
189
|
|
|
154
|
-
def
|
|
155
|
-
!@
|
|
190
|
+
def supports_concurrent_connections?
|
|
191
|
+
!@memory_database
|
|
156
192
|
end
|
|
157
193
|
|
|
158
|
-
def
|
|
159
|
-
|
|
160
|
-
|
|
194
|
+
def supports_virtual_columns?
|
|
195
|
+
database_version >= "3.31.0"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def connected?
|
|
199
|
+
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
161
200
|
end
|
|
162
201
|
|
|
202
|
+
def active?
|
|
203
|
+
if connected?
|
|
204
|
+
verified!
|
|
205
|
+
true
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
alias :reset! :reconnect!
|
|
210
|
+
|
|
163
211
|
# Disconnects from the database if already connected. Otherwise, this
|
|
164
212
|
# method does nothing.
|
|
165
213
|
def disconnect!
|
|
166
214
|
super
|
|
167
|
-
|
|
215
|
+
|
|
216
|
+
@raw_connection&.close rescue nil
|
|
217
|
+
@raw_connection = nil
|
|
168
218
|
end
|
|
169
219
|
|
|
170
220
|
def supports_index_sort_order?
|
|
171
221
|
true
|
|
172
222
|
end
|
|
173
223
|
|
|
174
|
-
|
|
175
|
-
# characters. The rest is used by Rails internally to perform
|
|
176
|
-
# temporary rename operations
|
|
177
|
-
def allowed_index_name_length
|
|
178
|
-
index_name_length - 2
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def native_database_types #:nodoc:
|
|
224
|
+
def native_database_types # :nodoc:
|
|
182
225
|
NATIVE_DATABASE_TYPES
|
|
183
226
|
end
|
|
184
227
|
|
|
185
|
-
# Returns the current database encoding format as a string,
|
|
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,12 +258,14 @@ module ActiveRecord
|
|
|
211
258
|
end
|
|
212
259
|
end
|
|
213
260
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
220
269
|
end
|
|
221
270
|
|
|
222
271
|
# SCHEMA STATEMENTS ========================================
|
|
@@ -226,8 +275,11 @@ module ActiveRecord
|
|
|
226
275
|
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
|
227
276
|
end
|
|
228
277
|
|
|
229
|
-
def remove_index(table_name,
|
|
230
|
-
|
|
278
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
|
279
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
|
280
|
+
|
|
281
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
|
282
|
+
|
|
231
283
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
|
232
284
|
end
|
|
233
285
|
|
|
@@ -235,31 +287,43 @@ module ActiveRecord
|
|
|
235
287
|
#
|
|
236
288
|
# Example:
|
|
237
289
|
# rename_table('octopuses', 'octopi')
|
|
238
|
-
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]
|
|
292
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
293
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
239
294
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
240
|
-
rename_table_indexes(table_name, new_name)
|
|
295
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
241
296
|
end
|
|
242
297
|
|
|
243
|
-
def add_column(table_name, column_name, type, options
|
|
298
|
+
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
299
|
+
type = type.to_sym
|
|
244
300
|
if invalid_alter_table_type?(type, options)
|
|
245
301
|
alter_table(table_name) do |definition|
|
|
246
|
-
definition.column(column_name, type, options)
|
|
302
|
+
definition.column(column_name, type, **options)
|
|
247
303
|
end
|
|
248
304
|
else
|
|
249
305
|
super
|
|
250
306
|
end
|
|
251
307
|
end
|
|
252
308
|
|
|
253
|
-
def remove_column(table_name, column_name, type = nil, options
|
|
309
|
+
def remove_column(table_name, column_name, type = nil, **options) # :nodoc:
|
|
254
310
|
alter_table(table_name) do |definition|
|
|
255
311
|
definition.remove_column column_name
|
|
256
|
-
definition.foreign_keys.delete_if
|
|
257
|
-
|
|
312
|
+
definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
|
|
317
|
+
alter_table(table_name) do |definition|
|
|
318
|
+
column_names.each do |column_name|
|
|
319
|
+
definition.remove_column column_name
|
|
258
320
|
end
|
|
321
|
+
column_names = column_names.map(&:to_s)
|
|
322
|
+
definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
|
|
259
323
|
end
|
|
260
324
|
end
|
|
261
325
|
|
|
262
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
|
326
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
|
263
327
|
default = extract_new_default_value(default_or_changes)
|
|
264
328
|
|
|
265
329
|
alter_table(table_name) do |definition|
|
|
@@ -267,49 +331,81 @@ module ActiveRecord
|
|
|
267
331
|
end
|
|
268
332
|
end
|
|
269
333
|
|
|
270
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
|
334
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
|
335
|
+
validate_change_column_null_argument!(null)
|
|
336
|
+
|
|
271
337
|
unless null || default.nil?
|
|
272
|
-
|
|
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")
|
|
273
339
|
end
|
|
274
340
|
alter_table(table_name) do |definition|
|
|
275
341
|
definition[column_name].null = null
|
|
276
342
|
end
|
|
277
343
|
end
|
|
278
344
|
|
|
279
|
-
def change_column(table_name, column_name, type, options
|
|
345
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
|
280
346
|
alter_table(table_name) do |definition|
|
|
281
|
-
definition
|
|
282
|
-
self.type = type
|
|
283
|
-
self.limit = options[:limit] if options.include?(:limit)
|
|
284
|
-
self.default = options[:default] if options.include?(:default)
|
|
285
|
-
self.null = options[:null] if options.include?(:null)
|
|
286
|
-
self.precision = options[:precision] if options.include?(:precision)
|
|
287
|
-
self.scale = options[:scale] if options.include?(:scale)
|
|
288
|
-
self.collation = options[:collation] if options.include?(:collation)
|
|
289
|
-
end
|
|
347
|
+
definition.change_column(column_name, type, **options)
|
|
290
348
|
end
|
|
291
349
|
end
|
|
292
350
|
|
|
293
|
-
def rename_column(table_name, column_name, new_column_name)
|
|
351
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
|
294
352
|
column = column_for(table_name, column_name)
|
|
295
353
|
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
|
296
354
|
rename_column_indexes(table_name, column.name, new_column_name)
|
|
297
355
|
end
|
|
298
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
|
+
|
|
299
370
|
def add_reference(table_name, ref_name, **options) # :nodoc:
|
|
300
371
|
super(table_name, ref_name, type: :integer, **options)
|
|
301
372
|
end
|
|
302
373
|
alias :add_belongs_to :add_reference
|
|
303
374
|
|
|
375
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
376
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
304
377
|
def foreign_keys(table_name)
|
|
305
|
-
|
|
306
|
-
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
|
|
307
396
|
options = {
|
|
308
|
-
column: row["from"],
|
|
309
|
-
primary_key: row["to"],
|
|
310
397
|
on_delete: extract_foreign_key_action(row["on_delete"]),
|
|
311
|
-
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"]]]
|
|
312
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
|
|
313
409
|
ForeignKeyDefinition.new(table_name, row["table"], options)
|
|
314
410
|
end
|
|
315
411
|
end
|
|
@@ -321,14 +417,28 @@ module ActiveRecord
|
|
|
321
417
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
|
322
418
|
elsif insert.update_duplicates?
|
|
323
419
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
|
324
|
-
|
|
420
|
+
if insert.raw_update_sql?
|
|
421
|
+
sql << insert.raw_update_sql
|
|
422
|
+
else
|
|
423
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
|
424
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
|
425
|
+
end
|
|
325
426
|
end
|
|
326
427
|
|
|
428
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
327
429
|
sql
|
|
328
430
|
end
|
|
329
431
|
|
|
432
|
+
def shared_cache? # :nodoc:
|
|
433
|
+
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def use_insert_returning?
|
|
437
|
+
@use_insert_returning
|
|
438
|
+
end
|
|
439
|
+
|
|
330
440
|
def get_database_version # :nodoc:
|
|
331
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
|
441
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
|
332
442
|
end
|
|
333
443
|
|
|
334
444
|
def check_version # :nodoc:
|
|
@@ -337,6 +447,28 @@ module ActiveRecord
|
|
|
337
447
|
end
|
|
338
448
|
end
|
|
339
449
|
|
|
450
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
|
451
|
+
private
|
|
452
|
+
def _limit
|
|
453
|
+
# INTEGER storage class can be stored 8 bytes value.
|
|
454
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
|
455
|
+
limit || 8
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
|
460
|
+
|
|
461
|
+
class << self
|
|
462
|
+
private
|
|
463
|
+
def initialize_type_map(m)
|
|
464
|
+
super
|
|
465
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
470
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
471
|
+
|
|
340
472
|
private
|
|
341
473
|
# See https://www.sqlite.org/limits.html,
|
|
342
474
|
# the default value is 999 when not configured.
|
|
@@ -344,25 +476,58 @@ module ActiveRecord
|
|
|
344
476
|
999
|
|
345
477
|
end
|
|
346
478
|
|
|
347
|
-
def initialize_type_map(m = type_map)
|
|
348
|
-
super
|
|
349
|
-
register_class_with_limit m, %r(int)i, SQLite3Integer
|
|
350
|
-
end
|
|
351
|
-
|
|
352
479
|
def table_structure(table_name)
|
|
353
|
-
structure =
|
|
354
|
-
raise
|
|
480
|
+
structure = table_info(table_name)
|
|
481
|
+
raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
|
|
355
482
|
table_structure_with_collation(table_name, structure)
|
|
356
483
|
end
|
|
357
484
|
alias column_definitions table_structure
|
|
358
485
|
|
|
486
|
+
def extract_value_from_default(default)
|
|
487
|
+
case default
|
|
488
|
+
when /^null$/i
|
|
489
|
+
nil
|
|
490
|
+
# Quoted types
|
|
491
|
+
when /^'([^|]*)'$/m
|
|
492
|
+
$1.gsub("''", "'")
|
|
493
|
+
# Quoted types
|
|
494
|
+
when /^"([^|]*)"$/m
|
|
495
|
+
$1.gsub('""', '"')
|
|
496
|
+
# Numeric types
|
|
497
|
+
when /\A-?\d+(\.\d*)?\z/
|
|
498
|
+
$&
|
|
499
|
+
# Binary columns
|
|
500
|
+
when /x'(.*)'/
|
|
501
|
+
[ $1 ].pack("H*")
|
|
502
|
+
else
|
|
503
|
+
# Anything else is blank or some function
|
|
504
|
+
# and we can't know the value of that, so return nil.
|
|
505
|
+
nil
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def extract_default_function(default_value, default)
|
|
510
|
+
default if has_default_function?(default_value, default)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def has_default_function?(default_value, default)
|
|
514
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
|
|
515
|
+
end
|
|
516
|
+
|
|
359
517
|
# See: https://www.sqlite.org/lang_altertable.html
|
|
360
518
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
|
361
519
|
def invalid_alter_table_type?(type, options)
|
|
362
|
-
type
|
|
520
|
+
type == :primary_key || options[:primary_key] ||
|
|
521
|
+
options[:null] == false && options[:default].nil? ||
|
|
522
|
+
(type == :virtual && options[:stored])
|
|
363
523
|
end
|
|
364
524
|
|
|
365
|
-
def alter_table(
|
|
525
|
+
def alter_table(
|
|
526
|
+
table_name,
|
|
527
|
+
foreign_keys = foreign_keys(table_name),
|
|
528
|
+
check_constraints = check_constraints(table_name),
|
|
529
|
+
**options
|
|
530
|
+
)
|
|
366
531
|
altered_table_name = "a#{table_name}"
|
|
367
532
|
|
|
368
533
|
caller = lambda do |definition|
|
|
@@ -372,14 +537,18 @@ module ActiveRecord
|
|
|
372
537
|
fk.options[:column] = column
|
|
373
538
|
end
|
|
374
539
|
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
375
|
-
definition.foreign_key(to_table, fk.options)
|
|
540
|
+
definition.foreign_key(to_table, **fk.options)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
check_constraints.each do |chk|
|
|
544
|
+
definition.check_constraint(chk.expression, **chk.options)
|
|
376
545
|
end
|
|
377
546
|
|
|
378
547
|
yield definition if block_given?
|
|
379
548
|
end
|
|
380
549
|
|
|
381
|
-
|
|
382
|
-
|
|
550
|
+
disable_referential_integrity do
|
|
551
|
+
transaction do
|
|
383
552
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
384
553
|
move_table(altered_table_name, table_name, &caller)
|
|
385
554
|
end
|
|
@@ -394,30 +563,52 @@ module ActiveRecord
|
|
|
394
563
|
def copy_table(from, to, options = {})
|
|
395
564
|
from_primary_key = primary_key(from)
|
|
396
565
|
options[:id] = false
|
|
397
|
-
create_table(to, options) do |definition|
|
|
566
|
+
create_table(to, **options) do |definition|
|
|
398
567
|
@definition = definition
|
|
399
568
|
if from_primary_key.is_a?(Array)
|
|
400
569
|
@definition.primary_keys from_primary_key
|
|
401
570
|
end
|
|
571
|
+
|
|
402
572
|
columns(from).each do |column|
|
|
403
573
|
column_name = options[:rename] ?
|
|
404
574
|
(options[:rename][column.name] ||
|
|
405
575
|
options[:rename][column.name.to_sym] ||
|
|
406
576
|
column.name) : column.name
|
|
407
577
|
|
|
408
|
-
|
|
409
|
-
limit: column.limit,
|
|
410
|
-
precision: column.precision,
|
|
411
|
-
|
|
578
|
+
column_options = {
|
|
579
|
+
limit: column.limit,
|
|
580
|
+
precision: column.precision,
|
|
581
|
+
scale: column.scale,
|
|
582
|
+
null: column.null,
|
|
583
|
+
collation: column.collation,
|
|
412
584
|
primary_key: column_name == from_primary_key
|
|
413
|
-
|
|
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?
|
|
592
|
+
type = lookup_cast_type_from_column(column)
|
|
593
|
+
default = type.deserialize(column.default)
|
|
594
|
+
default = -> { column.default_function } if default.nil?
|
|
595
|
+
|
|
596
|
+
unless column.auto_increment?
|
|
597
|
+
column_options[:default] = default
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
|
|
602
|
+
@definition.column(column_name, column_type, **column_options)
|
|
414
603
|
end
|
|
415
604
|
|
|
416
605
|
yield @definition if block_given?
|
|
417
606
|
end
|
|
418
607
|
copy_table_indexes(from, to, options[:rename] || {})
|
|
608
|
+
|
|
609
|
+
columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
|
|
419
610
|
copy_table_contents(from, to,
|
|
420
|
-
|
|
611
|
+
columns_to_copy,
|
|
421
612
|
options[:rename] || {})
|
|
422
613
|
end
|
|
423
614
|
|
|
@@ -440,10 +631,11 @@ module ActiveRecord
|
|
|
440
631
|
|
|
441
632
|
unless columns.empty?
|
|
442
633
|
# index name can't be the same
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
634
|
+
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
|
635
|
+
options[:unique] = true if index.unique
|
|
636
|
+
options[:where] = index.where if index.where
|
|
637
|
+
options[:order] = index.orders if index.orders
|
|
638
|
+
add_index(to, columns, **options)
|
|
447
639
|
end
|
|
448
640
|
end
|
|
449
641
|
end
|
|
@@ -457,61 +649,63 @@ module ActiveRecord
|
|
|
457
649
|
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
|
458
650
|
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
|
459
651
|
|
|
460
|
-
|
|
652
|
+
internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
|
461
653
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
|
462
654
|
end
|
|
463
655
|
|
|
464
656
|
def translate_exception(exception, message:, sql:, binds:)
|
|
465
|
-
case exception.message
|
|
466
657
|
# SQLite 3.8.2 returns a newly formatted error message:
|
|
467
658
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
|
468
659
|
# Older versions of SQLite return:
|
|
469
660
|
# column *column_name* is not unique
|
|
470
|
-
|
|
471
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
472
|
-
|
|
473
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
474
|
-
|
|
475
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
661
|
+
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
|
662
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
663
|
+
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
|
664
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
665
|
+
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
|
666
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
667
|
+
elsif exception.message.match?(/called on a closed database/i)
|
|
668
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
476
669
|
else
|
|
477
670
|
super
|
|
478
671
|
end
|
|
479
672
|
end
|
|
480
673
|
|
|
481
|
-
COLLATE_REGEX =
|
|
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
|
|
482
677
|
|
|
483
678
|
def table_structure_with_collation(table_name, basic_structure)
|
|
484
679
|
collation_hash = {}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
(SELECT * FROM sqlite_master UNION ALL
|
|
488
|
-
SELECT * FROM sqlite_temp_master)
|
|
489
|
-
WHERE type = 'table' AND name = #{quote(table_name)}
|
|
490
|
-
SQL
|
|
491
|
-
|
|
492
|
-
# Result will have following sample string
|
|
493
|
-
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
494
|
-
# "password_digest" varchar COLLATE "NOCASE");
|
|
495
|
-
result = exec_query(sql, "SCHEMA").first
|
|
680
|
+
auto_increments = {}
|
|
681
|
+
generated_columns = {}
|
|
496
682
|
|
|
497
|
-
|
|
498
|
-
# Splitting with left parentheses and discarding the first part will return all
|
|
499
|
-
# columns separated with comma(,).
|
|
500
|
-
columns_string = result["sql"].split("(", 2).last
|
|
683
|
+
column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
|
|
501
684
|
|
|
502
|
-
|
|
685
|
+
if column_strings.any?
|
|
686
|
+
column_strings.each do |column_string|
|
|
503
687
|
# This regex will match the column name and collation type and will save
|
|
504
688
|
# the value in $1 and $2 respectively.
|
|
505
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
|
|
506
692
|
end
|
|
507
693
|
|
|
508
|
-
basic_structure.map
|
|
694
|
+
basic_structure.map do |column|
|
|
509
695
|
column_name = column["name"]
|
|
510
696
|
|
|
511
697
|
if collation_hash.has_key? column_name
|
|
512
698
|
column["collation"] = collation_hash[column_name]
|
|
513
699
|
end
|
|
514
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
|
+
|
|
515
709
|
column
|
|
516
710
|
end
|
|
517
711
|
else
|
|
@@ -519,6 +713,50 @@ module ActiveRecord
|
|
|
519
713
|
end
|
|
520
714
|
end
|
|
521
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
|
+
|
|
522
760
|
def arel_visitor
|
|
523
761
|
Arel::Visitors::SQLite.new(self)
|
|
524
762
|
end
|
|
@@ -528,29 +766,42 @@ module ActiveRecord
|
|
|
528
766
|
end
|
|
529
767
|
|
|
530
768
|
def connect
|
|
531
|
-
@
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
|
536
780
|
end
|
|
537
781
|
|
|
538
782
|
def configure_connection
|
|
539
|
-
@
|
|
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
|
|
540
793
|
|
|
541
|
-
|
|
542
|
-
end
|
|
794
|
+
super
|
|
543
795
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
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}"
|
|
550
802
|
end
|
|
803
|
+
end
|
|
551
804
|
end
|
|
552
|
-
|
|
553
|
-
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
|
554
805
|
end
|
|
555
806
|
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
|
556
807
|
end
|