activerecord 5.0.7.2 → 6.1.1
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 +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/preloader.rb +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- 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 +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- 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.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -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 +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -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 +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- 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/interval.rb +49 -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/macaddr.rb +25 -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 +52 -30
- 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 +18 -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 +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -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 +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -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 +5 -6
- 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 +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- 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 +20 -22
- data/lib/active_record/fixture_set/model_metadata.rb +32 -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 +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- 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 +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- 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 +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -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 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -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/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -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 +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -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 +15 -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 +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -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 +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -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 +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -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 +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -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 +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- 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 → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- 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_decorators.rb +0 -67
- 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/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- 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/base_handler.rb +0 -17
- 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/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,13 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLite3
|
4
|
-
class SchemaCreation <
|
6
|
+
class SchemaCreation < SchemaCreation # :nodoc:
|
5
7
|
private
|
6
|
-
|
7
|
-
|
8
|
-
options = super
|
9
|
-
options[:null] = false if o.primary_key
|
10
|
-
options
|
8
|
+
def supports_index_using?
|
9
|
+
false
|
11
10
|
end
|
12
11
|
|
13
12
|
def add_column_options!(sql, options)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3
|
6
|
+
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
7
|
+
def references(*args, **options)
|
8
|
+
super(*args, type: :integer, **options)
|
9
|
+
end
|
10
|
+
alias :belongs_to :references
|
11
|
+
|
12
|
+
private
|
13
|
+
def integer_like_primary_key_type(type, options)
|
14
|
+
:primary_key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -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,170 @@
|
|
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"].start_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, :validate)
|
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 check_constraints(table_name)
|
82
|
+
table_sql = query_value(<<-SQL, "SCHEMA")
|
83
|
+
SELECT sql
|
84
|
+
FROM sqlite_master
|
85
|
+
WHERE name = #{quote_table_name(table_name)} AND type = 'table'
|
86
|
+
UNION ALL
|
87
|
+
SELECT sql
|
88
|
+
FROM sqlite_temp_master
|
89
|
+
WHERE name = #{quote_table_name(table_name)} AND type = 'table'
|
90
|
+
SQL
|
91
|
+
|
92
|
+
table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
|
93
|
+
CheckConstraintDefinition.new(table_name, expression, name: name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_check_constraint(table_name, expression, **options)
|
98
|
+
alter_table(table_name) do |definition|
|
99
|
+
definition.check_constraint(expression, **options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_check_constraint(table_name, expression = nil, **options)
|
104
|
+
check_constraints = check_constraints(table_name)
|
105
|
+
chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
|
106
|
+
check_constraints.delete_if { |chk| chk.name == chk_name_to_delete }
|
107
|
+
alter_table(table_name, foreign_keys(table_name), check_constraints)
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_schema_dumper(options)
|
111
|
+
SQLite3::SchemaDumper.create(self, options)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
def schema_creation
|
116
|
+
SQLite3::SchemaCreation.new(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_table_definition(name, **options)
|
120
|
+
SQLite3::TableDefinition.new(self, name, **options)
|
121
|
+
end
|
122
|
+
|
123
|
+
def validate_index_length!(table_name, new_name, internal = false)
|
124
|
+
super unless internal
|
125
|
+
end
|
126
|
+
|
127
|
+
def new_column_from_field(table_name, field)
|
128
|
+
default = \
|
129
|
+
case field["dflt_value"]
|
130
|
+
when /^null$/i
|
131
|
+
nil
|
132
|
+
when /^'(.*)'$/m
|
133
|
+
$1.gsub("''", "'")
|
134
|
+
when /^"(.*)"$/m
|
135
|
+
$1.gsub('""', '"')
|
136
|
+
else
|
137
|
+
field["dflt_value"]
|
138
|
+
end
|
139
|
+
|
140
|
+
type_metadata = fetch_type_metadata(field["type"])
|
141
|
+
Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, collation: field["collation"])
|
142
|
+
end
|
143
|
+
|
144
|
+
def data_source_sql(name = nil, type: nil)
|
145
|
+
scope = quoted_scope(name, type: type)
|
146
|
+
scope[:type] ||= "'table','view'"
|
147
|
+
|
148
|
+
sql = +"SELECT name FROM sqlite_master WHERE name <> 'sqlite_sequence'"
|
149
|
+
sql << " AND name = #{scope[:name]}" if scope[:name]
|
150
|
+
sql << " AND type IN (#{scope[:type]})"
|
151
|
+
sql
|
152
|
+
end
|
153
|
+
|
154
|
+
def quoted_scope(name = nil, type: nil)
|
155
|
+
type = \
|
156
|
+
case type
|
157
|
+
when "BASE TABLE"
|
158
|
+
"'table'"
|
159
|
+
when "VIEW"
|
160
|
+
"'view'"
|
161
|
+
end
|
162
|
+
scope = {}
|
163
|
+
scope[:name] = quote(name) if name
|
164
|
+
scope[:type] = type if type
|
165
|
+
scope
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
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] && !config[:database].to_s.start_with?("file:")
|
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")
|
@@ -42,19 +48,21 @@ module ActiveRecord
|
|
42
48
|
end
|
43
49
|
|
44
50
|
module ConnectionAdapters #:nodoc:
|
45
|
-
# The SQLite3 adapter works
|
46
|
-
#
|
51
|
+
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
52
|
+
# (available as gem from https://rubygems.org/gems/sqlite3).
|
47
53
|
#
|
48
54
|
# Options:
|
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,30 @@ 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
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
79
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
71
80
|
private
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def schema_creation # :nodoc:
|
79
|
-
SQLite3::SchemaCreation.new self
|
80
|
-
end
|
81
|
-
|
82
|
-
def arel_visitor # :nodoc:
|
83
|
-
Arel::Visitors::SQLite.new(self)
|
81
|
+
def dealloc(stmt)
|
82
|
+
stmt.close unless stmt.closed?
|
83
|
+
end
|
84
84
|
end
|
85
85
|
|
86
86
|
def initialize(connection, logger, connection_options, config)
|
87
87
|
super(connection, logger, config)
|
88
|
+
configure_connection
|
89
|
+
end
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
+
def self.database_exists?(config)
|
92
|
+
config = config.symbolize_keys
|
93
|
+
if config[:database] == ":memory:"
|
94
|
+
true
|
95
|
+
else
|
96
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
97
|
+
File.exist?(database_file)
|
98
|
+
end
|
91
99
|
end
|
92
100
|
|
93
101
|
def supports_ddl_transactions?
|
@@ -98,26 +106,27 @@ module ActiveRecord
|
|
98
106
|
true
|
99
107
|
end
|
100
108
|
|
101
|
-
def
|
102
|
-
|
109
|
+
def supports_transaction_isolation?
|
110
|
+
true
|
103
111
|
end
|
104
112
|
|
105
|
-
|
106
|
-
# caching.
|
107
|
-
def supports_statement_cache?
|
113
|
+
def supports_partial_index?
|
108
114
|
true
|
109
115
|
end
|
110
116
|
|
111
|
-
|
112
|
-
|
117
|
+
def supports_expression_index?
|
118
|
+
database_version >= "3.9.0"
|
119
|
+
end
|
120
|
+
|
121
|
+
def requires_reloading?
|
113
122
|
true
|
114
123
|
end
|
115
124
|
|
116
|
-
def
|
125
|
+
def supports_foreign_keys?
|
117
126
|
true
|
118
127
|
end
|
119
128
|
|
120
|
-
def
|
129
|
+
def supports_check_constraints?
|
121
130
|
true
|
122
131
|
end
|
123
132
|
|
@@ -129,42 +138,41 @@ module ActiveRecord
|
|
129
138
|
true
|
130
139
|
end
|
131
140
|
|
132
|
-
def
|
133
|
-
|
141
|
+
def supports_json?
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
def supports_common_table_expressions?
|
146
|
+
database_version >= "3.8.3"
|
134
147
|
end
|
135
148
|
|
149
|
+
def supports_insert_on_conflict?
|
150
|
+
database_version >= "3.24.0"
|
151
|
+
end
|
152
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
153
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
154
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
155
|
+
|
136
156
|
def active?
|
137
|
-
|
157
|
+
!@connection.closed?
|
158
|
+
end
|
159
|
+
|
160
|
+
def reconnect!
|
161
|
+
super
|
162
|
+
connect if @connection.closed?
|
138
163
|
end
|
139
164
|
|
140
165
|
# Disconnects from the database if already connected. Otherwise, this
|
141
166
|
# method does nothing.
|
142
167
|
def disconnect!
|
143
168
|
super
|
144
|
-
@active = false
|
145
169
|
@connection.close rescue nil
|
146
170
|
end
|
147
171
|
|
148
|
-
# Clears the prepared statements cache.
|
149
|
-
def clear_cache!
|
150
|
-
@statements.clear
|
151
|
-
end
|
152
|
-
|
153
172
|
def supports_index_sort_order?
|
154
173
|
true
|
155
174
|
end
|
156
175
|
|
157
|
-
def valid_type?(type)
|
158
|
-
true
|
159
|
-
end
|
160
|
-
|
161
|
-
# Returns 62. SQLite supports index names up to 64
|
162
|
-
# characters. The rest is used by rails internally to perform
|
163
|
-
# temporary rename operations
|
164
|
-
def allowed_index_name_length
|
165
|
-
index_name_length - 2
|
166
|
-
end
|
167
|
-
|
168
176
|
def native_database_types #:nodoc:
|
169
177
|
NATIVE_DATABASE_TYPES
|
170
178
|
end
|
@@ -178,178 +186,38 @@ module ActiveRecord
|
|
178
186
|
true
|
179
187
|
end
|
180
188
|
|
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', []))
|
189
|
+
def supports_lazy_transactions?
|
190
|
+
true
|
188
191
|
end
|
189
192
|
|
190
|
-
|
191
|
-
type_casted_binds = type_casted_binds(binds)
|
193
|
+
# REFERENTIAL INTEGRITY ====================================
|
192
194
|
|
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
|
195
|
+
def disable_referential_integrity # :nodoc:
|
196
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
197
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
216
198
|
|
217
|
-
|
199
|
+
begin
|
200
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
201
|
+
execute("PRAGMA foreign_keys = OFF")
|
202
|
+
yield
|
203
|
+
ensure
|
204
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
205
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
218
206
|
end
|
219
207
|
end
|
220
208
|
|
221
|
-
def exec_delete(sql, name = 'SQL', binds = [])
|
222
|
-
exec_query(sql, name, binds)
|
223
|
-
@connection.changes
|
224
|
-
end
|
225
|
-
alias :exec_update :exec_delete
|
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 }
|
245
|
-
end
|
246
|
-
|
247
209
|
# SCHEMA STATEMENTS ========================================
|
248
210
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
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
|
211
|
+
def primary_keys(table_name) # :nodoc:
|
212
|
+
pks = table_structure(table_name).select { |f| f["pk"] > 0 }
|
213
|
+
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
319
214
|
end
|
320
215
|
|
321
|
-
|
322
|
-
|
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
|
216
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
217
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
345
218
|
|
346
|
-
|
347
|
-
pks = table_structure(table_name).select { |f| f['pk'] > 0 }
|
348
|
-
pks.sort_by { |f| f['pk'] }.map { |f| f['name'] }
|
349
|
-
end
|
219
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
350
220
|
|
351
|
-
def remove_index(table_name, options = {}) #:nodoc:
|
352
|
-
index_name = index_name_for_remove(table_name, options)
|
353
221
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
354
222
|
end
|
355
223
|
|
@@ -358,29 +226,28 @@ module ActiveRecord
|
|
358
226
|
# Example:
|
359
227
|
# rename_table('octopuses', 'octopi')
|
360
228
|
def rename_table(table_name, new_name)
|
229
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
230
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
361
231
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
362
232
|
rename_table_indexes(table_name, new_name)
|
363
233
|
end
|
364
234
|
|
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
|
235
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
236
|
+
if invalid_alter_table_type?(type, options)
|
375
237
|
alter_table(table_name) do |definition|
|
376
|
-
definition.column(column_name, type, options)
|
238
|
+
definition.column(column_name, type, **options)
|
377
239
|
end
|
240
|
+
else
|
241
|
+
super
|
378
242
|
end
|
379
243
|
end
|
380
244
|
|
381
|
-
def remove_column(table_name, column_name, type = nil, options
|
245
|
+
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
382
246
|
alter_table(table_name) do |definition|
|
383
247
|
definition.remove_column column_name
|
248
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
249
|
+
fk_options[:column] == column_name.to_s
|
250
|
+
end
|
384
251
|
end
|
385
252
|
end
|
386
253
|
|
@@ -401,69 +268,154 @@ module ActiveRecord
|
|
401
268
|
end
|
402
269
|
end
|
403
270
|
|
404
|
-
def change_column(table_name, column_name, type, options
|
271
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
405
272
|
alter_table(table_name) do |definition|
|
406
|
-
include_default = options_include_default?(options)
|
407
273
|
definition[column_name].instance_eval do
|
408
|
-
self.type
|
409
|
-
self.
|
410
|
-
self.default = options[:default] if include_default
|
411
|
-
self.null = options[:null] if options.include?(:null)
|
412
|
-
self.precision = options[:precision] if options.include?(:precision)
|
413
|
-
self.scale = options[:scale] if options.include?(:scale)
|
414
|
-
self.collation = options[:collation] if options.include?(:collation)
|
274
|
+
self.type = type
|
275
|
+
self.options.merge!(options)
|
415
276
|
end
|
416
277
|
end
|
417
278
|
end
|
418
279
|
|
419
280
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
420
281
|
column = column_for(table_name, column_name)
|
421
|
-
alter_table(table_name, rename: {column.name => new_column_name.to_s})
|
282
|
+
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
422
283
|
rename_column_indexes(table_name, column.name, new_column_name)
|
423
284
|
end
|
424
285
|
|
425
|
-
|
286
|
+
def add_reference(table_name, ref_name, **options) # :nodoc:
|
287
|
+
super(table_name, ref_name, type: :integer, **options)
|
288
|
+
end
|
289
|
+
alias :add_belongs_to :add_reference
|
290
|
+
|
291
|
+
def foreign_keys(table_name)
|
292
|
+
fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
293
|
+
fk_info.map do |row|
|
294
|
+
options = {
|
295
|
+
column: row["from"],
|
296
|
+
primary_key: row["to"],
|
297
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
298
|
+
on_update: extract_foreign_key_action(row["on_update"])
|
299
|
+
}
|
300
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def build_insert_sql(insert) # :nodoc:
|
305
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
306
|
+
|
307
|
+
if insert.skip_duplicates?
|
308
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
309
|
+
elsif insert.update_duplicates?
|
310
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
311
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
|
312
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
313
|
+
end
|
314
|
+
|
315
|
+
sql
|
316
|
+
end
|
317
|
+
|
318
|
+
def shared_cache? # :nodoc:
|
319
|
+
@config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
|
320
|
+
end
|
321
|
+
|
322
|
+
def get_database_version # :nodoc:
|
323
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
324
|
+
end
|
325
|
+
|
326
|
+
def check_version # :nodoc:
|
327
|
+
if database_version < "3.8.0"
|
328
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
private
|
333
|
+
# See https://www.sqlite.org/limits.html,
|
334
|
+
# the default value is 999 when not configured.
|
335
|
+
def bind_params_length
|
336
|
+
999
|
337
|
+
end
|
338
|
+
|
339
|
+
def initialize_type_map(m = type_map)
|
340
|
+
super
|
341
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
342
|
+
end
|
426
343
|
|
427
344
|
def table_structure(table_name)
|
428
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})",
|
345
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
429
346
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
430
347
|
table_structure_with_collation(table_name, structure)
|
431
348
|
end
|
349
|
+
alias column_definitions table_structure
|
350
|
+
|
351
|
+
# See: https://www.sqlite.org/lang_altertable.html
|
352
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
353
|
+
def invalid_alter_table_type?(type, options)
|
354
|
+
type.to_sym == :primary_key || options[:primary_key] ||
|
355
|
+
options[:null] == false && options[:default].nil?
|
356
|
+
end
|
432
357
|
|
433
|
-
def alter_table(
|
358
|
+
def alter_table(
|
359
|
+
table_name,
|
360
|
+
foreign_keys = foreign_keys(table_name),
|
361
|
+
check_constraints = check_constraints(table_name),
|
362
|
+
**options
|
363
|
+
)
|
434
364
|
altered_table_name = "a#{table_name}"
|
435
|
-
|
365
|
+
|
366
|
+
caller = lambda do |definition|
|
367
|
+
rename = options[:rename] || {}
|
368
|
+
foreign_keys.each do |fk|
|
369
|
+
if column = rename[fk.options[:column]]
|
370
|
+
fk.options[:column] = column
|
371
|
+
end
|
372
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
373
|
+
definition.foreign_key(to_table, **fk.options)
|
374
|
+
end
|
375
|
+
|
376
|
+
check_constraints.each do |chk|
|
377
|
+
definition.check_constraint(chk.expression, **chk.options)
|
378
|
+
end
|
379
|
+
|
380
|
+
yield definition if block_given?
|
381
|
+
end
|
436
382
|
|
437
383
|
transaction do
|
438
|
-
|
439
|
-
options.merge(:
|
440
|
-
|
384
|
+
disable_referential_integrity do
|
385
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
386
|
+
move_table(altered_table_name, table_name, &caller)
|
387
|
+
end
|
441
388
|
end
|
442
389
|
end
|
443
390
|
|
444
|
-
def move_table(from, to, options = {}, &block)
|
391
|
+
def move_table(from, to, options = {}, &block)
|
445
392
|
copy_table(from, to, options, &block)
|
446
393
|
drop_table(from)
|
447
394
|
end
|
448
395
|
|
449
|
-
def copy_table(from, to, options = {})
|
396
|
+
def copy_table(from, to, options = {})
|
450
397
|
from_primary_key = primary_key(from)
|
451
398
|
options[:id] = false
|
452
|
-
create_table(to, options) do |definition|
|
399
|
+
create_table(to, **options) do |definition|
|
453
400
|
@definition = definition
|
454
|
-
|
401
|
+
if from_primary_key.is_a?(Array)
|
402
|
+
@definition.primary_keys from_primary_key
|
403
|
+
end
|
404
|
+
|
455
405
|
columns(from).each do |column|
|
456
406
|
column_name = options[:rename] ?
|
457
407
|
(options[:rename][column.name] ||
|
458
408
|
options[:rename][column.name.to_sym] ||
|
459
409
|
column.name) : column.name
|
460
|
-
next if column_name == from_primary_key
|
461
410
|
|
462
411
|
@definition.column(column_name, column.type,
|
463
|
-
:
|
464
|
-
:
|
465
|
-
:
|
412
|
+
limit: column.limit, default: column.default,
|
413
|
+
precision: column.precision, scale: column.scale,
|
414
|
+
null: column.null, collation: column.collation,
|
415
|
+
primary_key: column_name == from_primary_key
|
416
|
+
)
|
466
417
|
end
|
418
|
+
|
467
419
|
yield @definition if block_given?
|
468
420
|
end
|
469
421
|
copy_table_indexes(from, to, options[:rename] || {})
|
@@ -472,7 +424,7 @@ module ActiveRecord
|
|
472
424
|
options[:rename] || {})
|
473
425
|
end
|
474
426
|
|
475
|
-
def copy_table_indexes(from, to, rename = {})
|
427
|
+
def copy_table_indexes(from, to, rename = {})
|
476
428
|
indexes(from).each do |index|
|
477
429
|
name = index.name
|
478
430
|
if to == "a#{from}"
|
@@ -481,89 +433,129 @@ module ActiveRecord
|
|
481
433
|
name = name[1..-1]
|
482
434
|
end
|
483
435
|
|
484
|
-
|
485
|
-
|
486
|
-
to_column_names.
|
436
|
+
columns = index.columns
|
437
|
+
if columns.is_a?(Array)
|
438
|
+
to_column_names = columns(to).map(&:name)
|
439
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
440
|
+
to_column_names.include?(column)
|
441
|
+
end
|
487
442
|
end
|
488
443
|
|
489
444
|
unless columns.empty?
|
490
445
|
# index name can't be the same
|
491
|
-
|
492
|
-
|
493
|
-
|
446
|
+
options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
447
|
+
options[:unique] = true if index.unique
|
448
|
+
options[:where] = index.where if index.where
|
449
|
+
add_index(to, columns, **options)
|
494
450
|
end
|
495
451
|
end
|
496
452
|
end
|
497
453
|
|
498
|
-
def copy_table_contents(from, to, columns, rename = {})
|
499
|
-
column_mappings = Hash[columns.map {|name| [name, name]}]
|
454
|
+
def copy_table_contents(from, to, columns, rename = {})
|
455
|
+
column_mappings = Hash[columns.map { |name| [name, name] }]
|
500
456
|
rename.each { |a| column_mappings[a.last] = a.first }
|
501
457
|
from_columns = columns(from).collect(&:name)
|
502
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
458
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
503
459
|
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) } *
|
460
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
461
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
506
462
|
|
507
463
|
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
508
464
|
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
509
465
|
end
|
510
466
|
|
511
|
-
def
|
512
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
|
513
|
-
end
|
514
|
-
|
515
|
-
def translate_exception(exception, message)
|
516
|
-
case exception.message
|
467
|
+
def translate_exception(exception, message:, sql:, binds:)
|
517
468
|
# SQLite 3.8.2 returns a newly formatted error message:
|
518
469
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
519
470
|
# Older versions of SQLite return:
|
520
471
|
# column *column_name* is not unique
|
521
|
-
|
522
|
-
RecordNotUnique.new(message)
|
472
|
+
if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
|
473
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
474
|
+
elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
|
475
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
476
|
+
elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
|
477
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
478
|
+
elsif exception.message.match?(/called on a closed database/i)
|
479
|
+
ConnectionNotEstablished.new(exception)
|
523
480
|
else
|
524
481
|
super
|
525
482
|
end
|
526
483
|
end
|
527
484
|
|
528
|
-
private
|
529
485
|
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
530
486
|
|
531
487
|
def table_structure_with_collation(table_name, basic_structure)
|
532
488
|
collation_hash = {}
|
533
|
-
sql
|
534
|
-
|
535
|
-
|
536
|
-
|
489
|
+
sql = <<~SQL
|
490
|
+
SELECT sql FROM
|
491
|
+
(SELECT * FROM sqlite_master UNION ALL
|
492
|
+
SELECT * FROM sqlite_temp_master)
|
493
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
494
|
+
SQL
|
537
495
|
|
538
496
|
# Result will have following sample string
|
539
497
|
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
540
498
|
# "password_digest" varchar COLLATE "NOCASE");
|
541
|
-
result =
|
499
|
+
result = query_value(sql, "SCHEMA")
|
542
500
|
|
543
501
|
if result
|
544
|
-
# Splitting with left parentheses and
|
502
|
+
# Splitting with left parentheses and discarding the first part will return all
|
545
503
|
# columns separated with comma(,).
|
546
|
-
columns_string = result
|
504
|
+
columns_string = result.split("(", 2).last
|
547
505
|
|
548
|
-
columns_string.split(
|
506
|
+
columns_string.split(",").each do |column_string|
|
549
507
|
# This regex will match the column name and collation type and will save
|
550
508
|
# the value in $1 and $2 respectively.
|
551
|
-
collation_hash[$1] = $2 if
|
509
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
552
510
|
end
|
553
511
|
|
554
|
-
basic_structure.map
|
555
|
-
column_name = column[
|
512
|
+
basic_structure.map do |column|
|
513
|
+
column_name = column["name"]
|
556
514
|
|
557
515
|
if collation_hash.has_key? column_name
|
558
|
-
column[
|
516
|
+
column["collation"] = collation_hash[column_name]
|
559
517
|
end
|
560
518
|
|
561
519
|
column
|
562
520
|
end
|
563
521
|
else
|
564
|
-
basic_structure.
|
522
|
+
basic_structure.to_a
|
565
523
|
end
|
566
524
|
end
|
525
|
+
|
526
|
+
def arel_visitor
|
527
|
+
Arel::Visitors::SQLite.new(self)
|
528
|
+
end
|
529
|
+
|
530
|
+
def build_statement_pool
|
531
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
532
|
+
end
|
533
|
+
|
534
|
+
def connect
|
535
|
+
@connection = ::SQLite3::Database.new(
|
536
|
+
@config[:database].to_s,
|
537
|
+
@config.merge(results_as_hash: true)
|
538
|
+
)
|
539
|
+
configure_connection
|
540
|
+
end
|
541
|
+
|
542
|
+
def configure_connection
|
543
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
544
|
+
|
545
|
+
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
546
|
+
end
|
547
|
+
|
548
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
549
|
+
private
|
550
|
+
def _limit
|
551
|
+
# INTEGER storage class can be stored 8 bytes value.
|
552
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
553
|
+
limit || 8
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
567
558
|
end
|
559
|
+
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
568
560
|
end
|
569
561
|
end
|