activerecord 5.0.7.2 → 6.0.3.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +708 -2040
- data/MIT-LICENSE +3 -1
- data/README.rdoc +9 -7
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -22
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +18 -14
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/associations/alias_tracker.rb +24 -34
- data/lib/active_record/associations/association.rb +114 -55
- data/lib/active_record/associations/association_scope.rb +94 -94
- data/lib/active_record/associations/belongs_to_association.rb +58 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +18 -25
- data/lib/active_record/associations/builder/belongs_to.rb +43 -54
- data/lib/active_record/associations/builder/collection_association.rb +7 -18
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
- data/lib/active_record/associations/builder/has_many.rb +4 -0
- data/lib/active_record/associations/builder/has_one.rb +37 -1
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +86 -254
- data/lib/active_record/associations/collection_proxy.rb +158 -122
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -30
- data/lib/active_record/associations/has_many_through_association.rb +58 -44
- data/lib/active_record/associations/has_one_association.rb +59 -54
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +143 -176
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/preloader.rb +90 -103
- data/lib/active_record/associations/preloader/association.rb +86 -100
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +26 -14
- data/lib/active_record/attribute_assignment.rb +54 -61
- data/lib/active_record/attribute_decorators.rb +38 -17
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +85 -92
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +20 -49
- data/lib/active_record/attribute_methods/serialization.rb +29 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
- data/lib/active_record/attribute_methods/write.rb +34 -33
- data/lib/active_record/attributes.rb +38 -25
- data/lib/active_record/autosave_association.rb +54 -35
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +64 -35
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +552 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +228 -147
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +396 -562
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -31
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
- data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +259 -266
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +143 -40
- data/lib/active_record/core.rb +201 -163
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +60 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -4
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +13 -8
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +194 -504
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +150 -99
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +16 -19
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +77 -87
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +48 -29
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +369 -302
- data/lib/active_record/migration/command_recorder.rb +134 -100
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +131 -127
- data/lib/active_record/nested_attributes.rb +213 -202
- data/lib/active_record/no_touching.rb +12 -3
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +446 -77
- data/lib/active_record/query_cache.rb +13 -12
- data/lib/active_record/querying.rb +37 -24
- data/lib/active_record/railtie.rb +128 -36
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +312 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +214 -252
- data/lib/active_record/relation.rb +440 -318
- data/lib/active_record/relation/batches.rb +98 -52
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/calculations.rb +212 -173
- data/lib/active_record/relation/delegation.rb +72 -69
- data/lib/active_record/relation/finder_methods.rb +207 -247
- data/lib/active_record/relation/from_clause.rb +6 -8
- data/lib/active_record/relation/merger.rb +78 -62
- data/lib/active_record/relation/predicate_builder.rb +83 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +476 -334
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -96
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/result.rb +69 -40
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +83 -99
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +71 -69
- data/lib/active_record/schema_migration.rb +16 -6
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +47 -27
- data/lib/active_record/secure_token.rb +4 -2
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +63 -28
- data/lib/active_record/store.rb +121 -41
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +39 -18
- data/lib/active_record/tasks/database_tasks.rb +271 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
- data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +70 -36
- data/lib/active_record/touch_later.rb +8 -6
- data/lib/active_record/transactions.rb +141 -157
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +23 -18
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +16 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +2 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -12
- data/lib/active_record/type_caster/map.rb +5 -4
- data/lib/active_record/validations.rb +7 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +29 -42
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +62 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +256 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +17 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +137 -52
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3
|
6
|
+
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
7
|
+
private
|
8
|
+
def default_primary_key?(column)
|
9
|
+
schema_type(column) == :integer
|
10
|
+
end
|
11
|
+
|
12
|
+
def explicit_primary_key_default?(column)
|
13
|
+
column.bigint?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3
|
6
|
+
module SchemaStatements # :nodoc:
|
7
|
+
# Returns an array of indexes for the given table.
|
8
|
+
def indexes(table_name)
|
9
|
+
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
|
10
|
+
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
11
|
+
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
|
+
next if row["name"].starts_with?("sqlite_")
|
13
|
+
|
14
|
+
index_sql = query_value(<<~SQL, "SCHEMA")
|
15
|
+
SELECT sql
|
16
|
+
FROM sqlite_master
|
17
|
+
WHERE name = #{quote(row['name'])} AND type = 'index'
|
18
|
+
UNION ALL
|
19
|
+
SELECT sql
|
20
|
+
FROM sqlite_temp_master
|
21
|
+
WHERE name = #{quote(row['name'])} AND type = 'index'
|
22
|
+
SQL
|
23
|
+
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
|
25
|
+
|
26
|
+
columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
27
|
+
col["name"]
|
28
|
+
end
|
29
|
+
|
30
|
+
orders = {}
|
31
|
+
|
32
|
+
if columns.any?(&:nil?) # index created with an expression
|
33
|
+
columns = expressions
|
34
|
+
else
|
35
|
+
# Add info on sort order for columns (only desc order is explicitly specified,
|
36
|
+
# asc is the default)
|
37
|
+
if index_sql # index_sql can be null in case of primary key indexes
|
38
|
+
index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column|
|
39
|
+
orders[order_column] = :desc
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
IndexDefinition.new(
|
45
|
+
table_name,
|
46
|
+
row["name"],
|
47
|
+
row["unique"] != 0,
|
48
|
+
columns,
|
49
|
+
where: where,
|
50
|
+
orders: orders
|
51
|
+
)
|
52
|
+
end.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_foreign_key(from_table, to_table, **options)
|
56
|
+
alter_table(from_table) do |definition|
|
57
|
+
to_table = strip_table_name_prefix_and_suffix(to_table)
|
58
|
+
definition.foreign_key(to_table, **options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
63
|
+
to_table ||= options[:to_table]
|
64
|
+
options = options.except(:name, :to_table)
|
65
|
+
foreign_keys = foreign_keys(from_table)
|
66
|
+
|
67
|
+
fkey = foreign_keys.detect do |fk|
|
68
|
+
table = to_table || begin
|
69
|
+
table = options[:column].to_s.delete_suffix("_id")
|
70
|
+
Base.pluralize_table_names ? table.pluralize : table
|
71
|
+
end
|
72
|
+
table = strip_table_name_prefix_and_suffix(table)
|
73
|
+
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
74
|
+
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
75
|
+
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
76
|
+
|
77
|
+
foreign_keys.delete(fkey)
|
78
|
+
alter_table(from_table, foreign_keys)
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_schema_dumper(options)
|
82
|
+
SQLite3::SchemaDumper.create(self, options)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def schema_creation
|
87
|
+
SQLite3::SchemaCreation.new(self)
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_table_definition(*args, **options)
|
91
|
+
SQLite3::TableDefinition.new(self, *args, **options)
|
92
|
+
end
|
93
|
+
|
94
|
+
def new_column_from_field(table_name, field)
|
95
|
+
default = \
|
96
|
+
case field["dflt_value"]
|
97
|
+
when /^null$/i
|
98
|
+
nil
|
99
|
+
when /^'(.*)'$/m
|
100
|
+
$1.gsub("''", "'")
|
101
|
+
when /^"(.*)"$/m
|
102
|
+
$1.gsub('""', '"')
|
103
|
+
else
|
104
|
+
field["dflt_value"]
|
105
|
+
end
|
106
|
+
|
107
|
+
type_metadata = fetch_type_metadata(field["type"])
|
108
|
+
Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
|
109
|
+
end
|
110
|
+
|
111
|
+
def data_source_sql(name = nil, type: nil)
|
112
|
+
scope = quoted_scope(name, type: type)
|
113
|
+
scope[:type] ||= "'table','view'"
|
114
|
+
|
115
|
+
sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
116
|
+
sql << " AND name = #{scope[:name]}" if scope[:name]
|
117
|
+
sql << " AND type IN (#{scope[:type]})"
|
118
|
+
sql
|
119
|
+
end
|
120
|
+
|
121
|
+
def quoted_scope(name = nil, type: nil)
|
122
|
+
type = \
|
123
|
+
case type
|
124
|
+
when "BASE TABLE"
|
125
|
+
"'table'"
|
126
|
+
when "VIEW"
|
127
|
+
"'view'"
|
128
|
+
end
|
129
|
+
scope = {}
|
130
|
+
scope[:name] = quote(name) if name
|
131
|
+
scope[:type] = type if type
|
132
|
+
scope
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -1,15 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
3
|
-
require 'active_record/connection_adapters/sqlite3/explain_pretty_printer'
|
4
|
-
require 'active_record/connection_adapters/sqlite3/quoting'
|
5
|
-
require 'active_record/connection_adapters/sqlite3/schema_creation'
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
require
|
3
|
+
require "active_record/connection_adapters/abstract_adapter"
|
4
|
+
require "active_record/connection_adapters/statement_pool"
|
5
|
+
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
6
|
+
require "active_record/connection_adapters/sqlite3/quoting"
|
7
|
+
require "active_record/connection_adapters/sqlite3/database_statements"
|
8
|
+
require "active_record/connection_adapters/sqlite3/schema_creation"
|
9
|
+
require "active_record/connection_adapters/sqlite3/schema_definitions"
|
10
|
+
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
11
|
+
require "active_record/connection_adapters/sqlite3/schema_statements"
|
12
|
+
|
13
|
+
gem "sqlite3", "~> 1.4"
|
14
|
+
require "sqlite3"
|
9
15
|
|
10
16
|
module ActiveRecord
|
11
17
|
module ConnectionHandling # :nodoc:
|
12
18
|
def sqlite3_connection(config)
|
19
|
+
config = config.symbolize_keys
|
20
|
+
|
13
21
|
# Require database.
|
14
22
|
unless config[:database]
|
15
23
|
raise ArgumentError, "No database file specified. Missing argument: database"
|
@@ -18,7 +26,7 @@ module ActiveRecord
|
|
18
26
|
# Allow database path relative to Rails.root, but only if the database
|
19
27
|
# path is not the special path that tells sqlite to build a database only
|
20
28
|
# in memory.
|
21
|
-
if
|
29
|
+
if ":memory:" != config[:database]
|
22
30
|
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
23
31
|
dirname = File.dirname(config[:database])
|
24
32
|
Dir.mkdir(dirname) unless File.directory?(dirname)
|
@@ -26,11 +34,9 @@ module ActiveRecord
|
|
26
34
|
|
27
35
|
db = SQLite3::Database.new(
|
28
36
|
config[:database].to_s,
|
29
|
-
:
|
37
|
+
config.merge(results_as_hash: true)
|
30
38
|
)
|
31
39
|
|
32
|
-
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
33
|
-
|
34
40
|
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
35
41
|
rescue Errno::ENOENT => error
|
36
42
|
if error.message.include?("No such file or directory")
|
@@ -49,12 +55,14 @@ module ActiveRecord
|
|
49
55
|
#
|
50
56
|
# * <tt>:database</tt> - Path to the database file.
|
51
57
|
class SQLite3Adapter < AbstractAdapter
|
52
|
-
ADAPTER_NAME =
|
58
|
+
ADAPTER_NAME = "SQLite"
|
53
59
|
|
54
60
|
include SQLite3::Quoting
|
61
|
+
include SQLite3::SchemaStatements
|
62
|
+
include SQLite3::DatabaseStatements
|
55
63
|
|
56
64
|
NATIVE_DATABASE_TYPES = {
|
57
|
-
primary_key:
|
65
|
+
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
58
66
|
string: { name: "varchar" },
|
59
67
|
text: { name: "text" },
|
60
68
|
integer: { name: "integer" },
|
@@ -64,30 +72,40 @@ module ActiveRecord
|
|
64
72
|
time: { name: "time" },
|
65
73
|
date: { name: "date" },
|
66
74
|
binary: { name: "blob" },
|
67
|
-
boolean: { name: "boolean" }
|
75
|
+
boolean: { name: "boolean" },
|
76
|
+
json: { name: "json" },
|
68
77
|
}
|
69
78
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def dealloc(stmt)
|
74
|
-
stmt[:stmt].close unless stmt[:stmt].closed?
|
79
|
+
def self.represent_boolean_as_integer=(value) # :nodoc:
|
80
|
+
if value == false
|
81
|
+
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
|
75
82
|
end
|
76
|
-
end
|
77
83
|
|
78
|
-
|
79
|
-
|
84
|
+
ActiveSupport::Deprecation.warn(
|
85
|
+
"`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
|
86
|
+
)
|
80
87
|
end
|
81
88
|
|
82
|
-
|
83
|
-
|
89
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
90
|
+
private
|
91
|
+
def dealloc(stmt)
|
92
|
+
stmt.close unless stmt.closed?
|
93
|
+
end
|
84
94
|
end
|
85
95
|
|
86
96
|
def initialize(connection, logger, connection_options, config)
|
87
97
|
super(connection, logger, config)
|
98
|
+
configure_connection
|
99
|
+
end
|
88
100
|
|
89
|
-
|
90
|
-
|
101
|
+
def self.database_exists?(config)
|
102
|
+
config = config.symbolize_keys
|
103
|
+
if config[:database] == ":memory:"
|
104
|
+
true
|
105
|
+
else
|
106
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
107
|
+
File.exist?(database_file)
|
108
|
+
end
|
91
109
|
end
|
92
110
|
|
93
111
|
def supports_ddl_transactions?
|
@@ -99,25 +117,18 @@ module ActiveRecord
|
|
99
117
|
end
|
100
118
|
|
101
119
|
def supports_partial_index?
|
102
|
-
sqlite_version >= '3.8.0'
|
103
|
-
end
|
104
|
-
|
105
|
-
# Returns true, since this connection adapter supports prepared statement
|
106
|
-
# caching.
|
107
|
-
def supports_statement_cache?
|
108
120
|
true
|
109
121
|
end
|
110
122
|
|
111
|
-
|
112
|
-
|
113
|
-
true
|
123
|
+
def supports_expression_index?
|
124
|
+
database_version >= "3.9.0"
|
114
125
|
end
|
115
126
|
|
116
|
-
def
|
127
|
+
def requires_reloading?
|
117
128
|
true
|
118
129
|
end
|
119
130
|
|
120
|
-
def
|
131
|
+
def supports_foreign_keys?
|
121
132
|
true
|
122
133
|
end
|
123
134
|
|
@@ -129,37 +140,43 @@ module ActiveRecord
|
|
129
140
|
true
|
130
141
|
end
|
131
142
|
|
132
|
-
def
|
133
|
-
|
143
|
+
def supports_json?
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
def supports_common_table_expressions?
|
148
|
+
database_version >= "3.8.3"
|
134
149
|
end
|
135
150
|
|
151
|
+
def supports_insert_on_conflict?
|
152
|
+
database_version >= "3.24.0"
|
153
|
+
end
|
154
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
155
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
156
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
157
|
+
|
136
158
|
def active?
|
137
|
-
|
159
|
+
!@connection.closed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def reconnect!
|
163
|
+
super
|
164
|
+
connect if @connection.closed?
|
138
165
|
end
|
139
166
|
|
140
167
|
# Disconnects from the database if already connected. Otherwise, this
|
141
168
|
# method does nothing.
|
142
169
|
def disconnect!
|
143
170
|
super
|
144
|
-
@active = false
|
145
171
|
@connection.close rescue nil
|
146
172
|
end
|
147
173
|
|
148
|
-
# Clears the prepared statements cache.
|
149
|
-
def clear_cache!
|
150
|
-
@statements.clear
|
151
|
-
end
|
152
|
-
|
153
174
|
def supports_index_sort_order?
|
154
175
|
true
|
155
176
|
end
|
156
177
|
|
157
|
-
def valid_type?(type)
|
158
|
-
true
|
159
|
-
end
|
160
|
-
|
161
178
|
# Returns 62. SQLite supports index names up to 64
|
162
|
-
# characters. The rest is used by
|
179
|
+
# characters. The rest is used by Rails internally to perform
|
163
180
|
# temporary rename operations
|
164
181
|
def allowed_index_name_length
|
165
182
|
index_name_length - 2
|
@@ -178,174 +195,39 @@ module ActiveRecord
|
|
178
195
|
true
|
179
196
|
end
|
180
197
|
|
181
|
-
|
182
|
-
|
183
|
-
#++
|
184
|
-
|
185
|
-
def explain(arel, binds = [])
|
186
|
-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
187
|
-
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
|
198
|
+
def supports_lazy_transactions?
|
199
|
+
true
|
188
200
|
end
|
189
201
|
|
190
|
-
|
191
|
-
type_casted_binds = type_casted_binds(binds)
|
202
|
+
# REFERENTIAL INTEGRITY ====================================
|
192
203
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
stmt = @connection.prepare(sql)
|
197
|
-
begin
|
198
|
-
cols = stmt.columns
|
199
|
-
unless without_prepared_statement?(binds)
|
200
|
-
stmt.bind_params(type_casted_binds)
|
201
|
-
end
|
202
|
-
records = stmt.to_a
|
203
|
-
ensure
|
204
|
-
stmt.close
|
205
|
-
end
|
206
|
-
else
|
207
|
-
cache = @statements[sql] ||= {
|
208
|
-
:stmt => @connection.prepare(sql)
|
209
|
-
}
|
210
|
-
stmt = cache[:stmt]
|
211
|
-
cols = cache[:cols] ||= stmt.columns
|
212
|
-
stmt.reset!
|
213
|
-
stmt.bind_params(type_casted_binds)
|
214
|
-
records = stmt.to_a
|
215
|
-
end
|
204
|
+
def disable_referential_integrity # :nodoc:
|
205
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
206
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
216
207
|
|
217
|
-
|
208
|
+
begin
|
209
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
210
|
+
execute("PRAGMA foreign_keys = OFF")
|
211
|
+
yield
|
212
|
+
ensure
|
213
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
214
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
218
215
|
end
|
219
216
|
end
|
220
217
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
def last_inserted_id(result)
|
228
|
-
@connection.last_insert_row_id
|
229
|
-
end
|
230
|
-
|
231
|
-
def execute(sql, name = nil) #:nodoc:
|
232
|
-
log(sql, name) { @connection.execute(sql) }
|
233
|
-
end
|
234
|
-
|
235
|
-
def begin_db_transaction #:nodoc:
|
236
|
-
log('begin transaction',nil) { @connection.transaction }
|
237
|
-
end
|
238
|
-
|
239
|
-
def commit_db_transaction #:nodoc:
|
240
|
-
log('commit transaction',nil) { @connection.commit }
|
241
|
-
end
|
242
|
-
|
243
|
-
def exec_rollback_db_transaction #:nodoc:
|
244
|
-
log('rollback transaction',nil) { @connection.rollback }
|
218
|
+
#--
|
219
|
+
# DATABASE STATEMENTS ======================================
|
220
|
+
#++
|
221
|
+
def explain(arel, binds = [])
|
222
|
+
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
223
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
245
224
|
end
|
246
225
|
|
247
226
|
# SCHEMA STATEMENTS ========================================
|
248
227
|
|
249
|
-
def tables(name = nil) # :nodoc:
|
250
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
251
|
-
#tables currently returns both tables and views.
|
252
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
|
253
|
-
Use #data_sources instead.
|
254
|
-
MSG
|
255
|
-
|
256
|
-
if name
|
257
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
258
|
-
Passing arguments to #tables is deprecated without replacement.
|
259
|
-
MSG
|
260
|
-
end
|
261
|
-
|
262
|
-
data_sources
|
263
|
-
end
|
264
|
-
|
265
|
-
def data_sources
|
266
|
-
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
|
267
|
-
end
|
268
|
-
|
269
|
-
def table_exists?(table_name)
|
270
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
271
|
-
#table_exists? currently checks both tables and views.
|
272
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
273
|
-
Use #data_source_exists? instead.
|
274
|
-
MSG
|
275
|
-
|
276
|
-
data_source_exists?(table_name)
|
277
|
-
end
|
278
|
-
|
279
|
-
def data_source_exists?(table_name)
|
280
|
-
return false unless table_name.present?
|
281
|
-
|
282
|
-
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
|
283
|
-
sql << " AND name = #{quote(table_name)}"
|
284
|
-
|
285
|
-
select_values(sql, 'SCHEMA').any?
|
286
|
-
end
|
287
|
-
|
288
|
-
def views # :nodoc:
|
289
|
-
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
|
290
|
-
end
|
291
|
-
|
292
|
-
def view_exists?(view_name) # :nodoc:
|
293
|
-
return false unless view_name.present?
|
294
|
-
|
295
|
-
sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
|
296
|
-
sql << " AND name = #{quote(view_name)}"
|
297
|
-
|
298
|
-
select_values(sql, 'SCHEMA').any?
|
299
|
-
end
|
300
|
-
|
301
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
302
|
-
def columns(table_name) # :nodoc:
|
303
|
-
table_name = table_name.to_s
|
304
|
-
table_structure(table_name).map do |field|
|
305
|
-
case field["dflt_value"]
|
306
|
-
when /^null$/i
|
307
|
-
field["dflt_value"] = nil
|
308
|
-
when /^'(.*)'$/m
|
309
|
-
field["dflt_value"] = $1.gsub("''", "'")
|
310
|
-
when /^"(.*)"$/m
|
311
|
-
field["dflt_value"] = $1.gsub('""', '"')
|
312
|
-
end
|
313
|
-
|
314
|
-
collation = field['collation']
|
315
|
-
sql_type = field['type']
|
316
|
-
type_metadata = fetch_type_metadata(sql_type)
|
317
|
-
new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, table_name, nil, collation)
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
# Returns an array of indexes for the given table.
|
322
|
-
def indexes(table_name, name = nil) #:nodoc:
|
323
|
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
|
324
|
-
sql = <<-SQL
|
325
|
-
SELECT sql
|
326
|
-
FROM sqlite_master
|
327
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
328
|
-
UNION ALL
|
329
|
-
SELECT sql
|
330
|
-
FROM sqlite_temp_master
|
331
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
332
|
-
SQL
|
333
|
-
index_sql = exec_query(sql).first['sql']
|
334
|
-
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
335
|
-
where = match[1] if match
|
336
|
-
IndexDefinition.new(
|
337
|
-
table_name,
|
338
|
-
row['name'],
|
339
|
-
row['unique'] != 0,
|
340
|
-
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
|
341
|
-
col['name']
|
342
|
-
}, nil, nil, where)
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
228
|
def primary_keys(table_name) # :nodoc:
|
347
|
-
pks = table_structure(table_name).select { |f| f[
|
348
|
-
pks.sort_by { |f| f[
|
229
|
+
pks = table_structure(table_name).select { |f| f["pk"] > 0 }
|
230
|
+
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
349
231
|
end
|
350
232
|
|
351
233
|
def remove_index(table_name, options = {}) #:nodoc:
|
@@ -362,25 +244,22 @@ module ActiveRecord
|
|
362
244
|
rename_table_indexes(table_name, new_name)
|
363
245
|
end
|
364
246
|
|
365
|
-
|
366
|
-
|
367
|
-
def valid_alter_table_type?(type)
|
368
|
-
type.to_sym != :primary_key
|
369
|
-
end
|
370
|
-
|
371
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
372
|
-
if valid_alter_table_type?(type)
|
373
|
-
super(table_name, column_name, type, options)
|
374
|
-
else
|
247
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
248
|
+
if invalid_alter_table_type?(type, options)
|
375
249
|
alter_table(table_name) do |definition|
|
376
|
-
definition.column(column_name, type, options)
|
250
|
+
definition.column(column_name, type, **options)
|
377
251
|
end
|
252
|
+
else
|
253
|
+
super
|
378
254
|
end
|
379
255
|
end
|
380
256
|
|
381
|
-
def remove_column(table_name, column_name, type = nil, options
|
257
|
+
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
382
258
|
alter_table(table_name) do |definition|
|
383
259
|
definition.remove_column column_name
|
260
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
261
|
+
fk_options[:column] == column_name.to_s
|
262
|
+
end
|
384
263
|
end
|
385
264
|
end
|
386
265
|
|
@@ -403,14 +282,13 @@ module ActiveRecord
|
|
403
282
|
|
404
283
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
405
284
|
alter_table(table_name) do |definition|
|
406
|
-
include_default = options_include_default?(options)
|
407
285
|
definition[column_name].instance_eval do
|
408
286
|
self.type = type
|
409
287
|
self.limit = options[:limit] if options.include?(:limit)
|
410
|
-
self.default = options[:default] if
|
288
|
+
self.default = options[:default] if options.include?(:default)
|
411
289
|
self.null = options[:null] if options.include?(:null)
|
412
290
|
self.precision = options[:precision] if options.include?(:precision)
|
413
|
-
self.scale
|
291
|
+
self.scale = options[:scale] if options.include?(:scale)
|
414
292
|
self.collation = options[:collation] if options.include?(:collation)
|
415
293
|
end
|
416
294
|
end
|
@@ -418,52 +296,128 @@ module ActiveRecord
|
|
418
296
|
|
419
297
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
420
298
|
column = column_for(table_name, column_name)
|
421
|
-
alter_table(table_name, rename: {column.name => new_column_name.to_s})
|
299
|
+
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
422
300
|
rename_column_indexes(table_name, column.name, new_column_name)
|
423
301
|
end
|
424
302
|
|
425
|
-
|
303
|
+
def add_reference(table_name, ref_name, **options) # :nodoc:
|
304
|
+
super(table_name, ref_name, type: :integer, **options)
|
305
|
+
end
|
306
|
+
alias :add_belongs_to :add_reference
|
307
|
+
|
308
|
+
def foreign_keys(table_name)
|
309
|
+
fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
310
|
+
fk_info.map do |row|
|
311
|
+
options = {
|
312
|
+
column: row["from"],
|
313
|
+
primary_key: row["to"],
|
314
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
315
|
+
on_update: extract_foreign_key_action(row["on_update"])
|
316
|
+
}
|
317
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def build_insert_sql(insert) # :nodoc:
|
322
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
323
|
+
|
324
|
+
if insert.skip_duplicates?
|
325
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
326
|
+
elsif insert.update_duplicates?
|
327
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
328
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
329
|
+
end
|
330
|
+
|
331
|
+
sql
|
332
|
+
end
|
333
|
+
|
334
|
+
def get_database_version # :nodoc:
|
335
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
336
|
+
end
|
337
|
+
|
338
|
+
def check_version # :nodoc:
|
339
|
+
if database_version < "3.8.0"
|
340
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
private
|
345
|
+
# See https://www.sqlite.org/limits.html,
|
346
|
+
# the default value is 999 when not configured.
|
347
|
+
def bind_params_length
|
348
|
+
999
|
349
|
+
end
|
350
|
+
|
351
|
+
def initialize_type_map(m = type_map)
|
352
|
+
super
|
353
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
354
|
+
end
|
426
355
|
|
427
356
|
def table_structure(table_name)
|
428
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})",
|
357
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
429
358
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
430
359
|
table_structure_with_collation(table_name, structure)
|
431
360
|
end
|
361
|
+
alias column_definitions table_structure
|
432
362
|
|
433
|
-
|
363
|
+
# See: https://www.sqlite.org/lang_altertable.html
|
364
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
365
|
+
def invalid_alter_table_type?(type, options)
|
366
|
+
type.to_sym == :primary_key || options[:primary_key] ||
|
367
|
+
options[:null] == false && options[:default].nil?
|
368
|
+
end
|
369
|
+
|
370
|
+
def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
|
434
371
|
altered_table_name = "a#{table_name}"
|
435
|
-
|
372
|
+
|
373
|
+
caller = lambda do |definition|
|
374
|
+
rename = options[:rename] || {}
|
375
|
+
foreign_keys.each do |fk|
|
376
|
+
if column = rename[fk.options[:column]]
|
377
|
+
fk.options[:column] = column
|
378
|
+
end
|
379
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
380
|
+
definition.foreign_key(to_table, **fk.options)
|
381
|
+
end
|
382
|
+
|
383
|
+
yield definition if block_given?
|
384
|
+
end
|
436
385
|
|
437
386
|
transaction do
|
438
|
-
|
439
|
-
options.merge(:
|
440
|
-
|
387
|
+
disable_referential_integrity do
|
388
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
389
|
+
move_table(altered_table_name, table_name, &caller)
|
390
|
+
end
|
441
391
|
end
|
442
392
|
end
|
443
393
|
|
444
|
-
def move_table(from, to, options = {}, &block)
|
394
|
+
def move_table(from, to, options = {}, &block)
|
445
395
|
copy_table(from, to, options, &block)
|
446
396
|
drop_table(from)
|
447
397
|
end
|
448
398
|
|
449
|
-
def copy_table(from, to, options = {})
|
399
|
+
def copy_table(from, to, options = {})
|
450
400
|
from_primary_key = primary_key(from)
|
451
401
|
options[:id] = false
|
452
|
-
create_table(to, options) do |definition|
|
402
|
+
create_table(to, **options) do |definition|
|
453
403
|
@definition = definition
|
454
|
-
|
404
|
+
if from_primary_key.is_a?(Array)
|
405
|
+
@definition.primary_keys from_primary_key
|
406
|
+
end
|
455
407
|
columns(from).each do |column|
|
456
408
|
column_name = options[:rename] ?
|
457
409
|
(options[:rename][column.name] ||
|
458
410
|
options[:rename][column.name.to_sym] ||
|
459
411
|
column.name) : column.name
|
460
|
-
next if column_name == from_primary_key
|
461
412
|
|
462
413
|
@definition.column(column_name, column.type,
|
463
|
-
:
|
464
|
-
:
|
465
|
-
:
|
414
|
+
limit: column.limit, default: column.default,
|
415
|
+
precision: column.precision, scale: column.scale,
|
416
|
+
null: column.null, collation: column.collation,
|
417
|
+
primary_key: column_name == from_primary_key
|
418
|
+
)
|
466
419
|
end
|
420
|
+
|
467
421
|
yield @definition if block_given?
|
468
422
|
end
|
469
423
|
copy_table_indexes(from, to, options[:rename] || {})
|
@@ -472,7 +426,7 @@ module ActiveRecord
|
|
472
426
|
options[:rename] || {})
|
473
427
|
end
|
474
428
|
|
475
|
-
def copy_table_indexes(from, to, rename = {})
|
429
|
+
def copy_table_indexes(from, to, rename = {})
|
476
430
|
indexes(from).each do |index|
|
477
431
|
name = index.name
|
478
432
|
if to == "a#{from}"
|
@@ -481,89 +435,128 @@ module ActiveRecord
|
|
481
435
|
name = name[1..-1]
|
482
436
|
end
|
483
437
|
|
484
|
-
|
485
|
-
|
486
|
-
to_column_names.
|
438
|
+
columns = index.columns
|
439
|
+
if columns.is_a?(Array)
|
440
|
+
to_column_names = columns(to).map(&:name)
|
441
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
442
|
+
to_column_names.include?(column)
|
443
|
+
end
|
487
444
|
end
|
488
445
|
|
489
446
|
unless columns.empty?
|
490
447
|
# index name can't be the same
|
491
448
|
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
492
449
|
opts[:unique] = true if index.unique
|
450
|
+
opts[:where] = index.where if index.where
|
493
451
|
add_index(to, columns, opts)
|
494
452
|
end
|
495
453
|
end
|
496
454
|
end
|
497
455
|
|
498
|
-
def copy_table_contents(from, to, columns, rename = {})
|
499
|
-
column_mappings = Hash[columns.map {|name| [name, name]}]
|
456
|
+
def copy_table_contents(from, to, columns, rename = {})
|
457
|
+
column_mappings = Hash[columns.map { |name| [name, name] }]
|
500
458
|
rename.each { |a| column_mappings[a.last] = a.first }
|
501
459
|
from_columns = columns(from).collect(&:name)
|
502
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
460
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
503
461
|
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
504
|
-
quoted_columns = columns.map { |col| quote_column_name(col) } *
|
505
|
-
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } *
|
462
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
463
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
506
464
|
|
507
465
|
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
508
466
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
509
467
|
end
|
510
468
|
|
511
|
-
def
|
512
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
|
513
|
-
end
|
514
|
-
|
515
|
-
def translate_exception(exception, message)
|
469
|
+
def translate_exception(exception, message:, sql:, binds:)
|
516
470
|
case exception.message
|
517
471
|
# SQLite 3.8.2 returns a newly formatted error message:
|
518
472
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
519
473
|
# Older versions of SQLite return:
|
520
474
|
# column *column_name* is not unique
|
521
475
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
522
|
-
RecordNotUnique.new(message)
|
476
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
477
|
+
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
478
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
479
|
+
when /FOREIGN KEY constraint failed/i
|
480
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
523
481
|
else
|
524
482
|
super
|
525
483
|
end
|
526
484
|
end
|
527
485
|
|
528
|
-
private
|
529
486
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
530
487
|
|
531
488
|
def table_structure_with_collation(table_name, basic_structure)
|
532
489
|
collation_hash = {}
|
533
|
-
sql
|
534
|
-
|
535
|
-
|
536
|
-
|
490
|
+
sql = <<~SQL
|
491
|
+
SELECT sql FROM
|
492
|
+
(SELECT * FROM sqlite_master UNION ALL
|
493
|
+
SELECT * FROM sqlite_temp_master)
|
494
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
495
|
+
SQL
|
537
496
|
|
538
497
|
# Result will have following sample string
|
539
498
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
540
499
|
# "password_digest" varchar COLLATE "NOCASE");
|
541
|
-
result = exec_query(sql,
|
500
|
+
result = exec_query(sql, "SCHEMA").first
|
542
501
|
|
543
502
|
if result
|
544
|
-
# Splitting with left parentheses and
|
503
|
+
# Splitting with left parentheses and discarding the first part will return all
|
545
504
|
# columns separated with comma(,).
|
546
|
-
columns_string = result["sql"].split(
|
505
|
+
columns_string = result["sql"].split("(", 2).last
|
547
506
|
|
548
|
-
columns_string.split(
|
507
|
+
columns_string.split(",").each do |column_string|
|
549
508
|
# This regex will match the column name and collation type and will save
|
550
509
|
# the value in $1 and $2 respectively.
|
551
|
-
collation_hash[$1] = $2 if
|
510
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
552
511
|
end
|
553
512
|
|
554
513
|
basic_structure.map! do |column|
|
555
|
-
column_name = column[
|
514
|
+
column_name = column["name"]
|
556
515
|
|
557
516
|
if collation_hash.has_key? column_name
|
558
|
-
column[
|
517
|
+
column["collation"] = collation_hash[column_name]
|
559
518
|
end
|
560
519
|
|
561
520
|
column
|
562
521
|
end
|
563
522
|
else
|
564
|
-
basic_structure.
|
523
|
+
basic_structure.to_a
|
565
524
|
end
|
566
525
|
end
|
526
|
+
|
527
|
+
def arel_visitor
|
528
|
+
Arel::Visitors::SQLite.new(self)
|
529
|
+
end
|
530
|
+
|
531
|
+
def build_statement_pool
|
532
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
533
|
+
end
|
534
|
+
|
535
|
+
def connect
|
536
|
+
@connection = ::SQLite3::Database.new(
|
537
|
+
@config[:database].to_s,
|
538
|
+
@config.merge(results_as_hash: true)
|
539
|
+
)
|
540
|
+
configure_connection
|
541
|
+
end
|
542
|
+
|
543
|
+
def configure_connection
|
544
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
545
|
+
|
546
|
+
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
547
|
+
end
|
548
|
+
|
549
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
550
|
+
private
|
551
|
+
def _limit
|
552
|
+
# INTEGER storage class can be stored 8 bytes value.
|
553
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
554
|
+
limit || 8
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
567
559
|
end
|
560
|
+
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
568
561
|
end
|
569
562
|
end
|