activerecord 4.2.9 → 6.1.4.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 +5 -5
- data/CHANGELOG.md +964 -1382
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +266 -251
- data/lib/active_record/association_relation.rb +40 -15
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +162 -69
- data/lib/active_record/associations/association_scope.rb +105 -130
- data/lib/active_record/associations/belongs_to_association.rb +83 -65
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -37
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +148 -287
- data/lib/active_record/associations/collection_proxy.rb +252 -150
- data/lib/active_record/associations/foreign_association.rb +23 -1
- data/lib/active_record/associations/has_many_association.rb +56 -98
- data/lib/active_record/associations/has_many_through_association.rb +68 -89
- data/lib/active_record/associations/has_one_association.rb +73 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
- 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/join_dependency.rb +174 -169
- data/lib/active_record/associations/preloader/association.rb +108 -115
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +97 -94
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +39 -19
- data/lib/active_record/associations.rb +1845 -1598
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
- data/lib/active_record/attribute_methods/dirty.rb +168 -148
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
- data/lib/active_record/attribute_methods/write.rb +24 -55
- data/lib/active_record/attribute_methods.rb +149 -154
- data/lib/active_record/attributes.rb +234 -78
- data/lib/active_record/autosave_association.rb +133 -60
- data/lib/active_record/base.rb +46 -46
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
- data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
- 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 +31 -20
- 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 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- 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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
- data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
- 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 +322 -373
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +458 -241
- data/lib/active_record/counter_cache.rb +70 -49
- 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/database_configurations.rb +272 -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 -106
- data/lib/active_record/enum.rb +211 -92
- data/lib/active_record/errors.rb +224 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +33 -14
- 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 +275 -500
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +62 -0
- data/lib/active_record/legacy_yaml_adapter.rb +27 -5
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +98 -92
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +295 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +673 -325
- data/lib/active_record/model_schema.rb +418 -113
- data/lib/active_record/nested_attributes.rb +263 -224
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +572 -136
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +170 -51
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +523 -199
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +454 -291
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +324 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +316 -242
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +95 -103
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +136 -122
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -413
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -20
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -343
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -23
- data/lib/active_record/scoping/default.rb +96 -83
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +128 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +364 -130
- data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
- data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +182 -163
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +27 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +63 -56
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +42 -29
- 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/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 +76 -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/nodes.rb +70 -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/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/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +172 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- 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 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,14 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
3
|
-
require 'arel/visitors/bind_visitor'
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
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"
|
7
15
|
|
8
16
|
module ActiveRecord
|
9
17
|
module ConnectionHandling # :nodoc:
|
10
|
-
# sqlite3 adapter reuses sqlite_connection.
|
11
18
|
def sqlite3_connection(config)
|
19
|
+
config = config.symbolize_keys
|
20
|
+
|
12
21
|
# Require database.
|
13
22
|
unless config[:database]
|
14
23
|
raise ArgumentError, "No database file specified. Missing argument: database"
|
@@ -17,7 +26,7 @@ module ActiveRecord
|
|
17
26
|
# Allow database path relative to Rails.root, but only if the database
|
18
27
|
# path is not the special path that tells sqlite to build a database only
|
19
28
|
# in memory.
|
20
|
-
if
|
29
|
+
if ":memory:" != config[:database] && !config[:database].to_s.start_with?("file:")
|
21
30
|
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
22
31
|
dirname = File.dirname(config[:database])
|
23
32
|
Dir.mkdir(dirname) unless File.directory?(dirname)
|
@@ -25,15 +34,13 @@ module ActiveRecord
|
|
25
34
|
|
26
35
|
db = SQLite3::Database.new(
|
27
36
|
config[:database].to_s,
|
28
|
-
:
|
37
|
+
config.merge(results_as_hash: true)
|
29
38
|
)
|
30
39
|
|
31
|
-
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
32
|
-
|
33
40
|
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
34
41
|
rescue Errno::ENOENT => error
|
35
42
|
if error.message.include?("No such file or directory")
|
36
|
-
raise ActiveRecord::NoDatabaseError
|
43
|
+
raise ActiveRecord::NoDatabaseError
|
37
44
|
else
|
38
45
|
raise
|
39
46
|
end
|
@@ -41,27 +48,21 @@ module ActiveRecord
|
|
41
48
|
end
|
42
49
|
|
43
50
|
module ConnectionAdapters #:nodoc:
|
44
|
-
|
45
|
-
|
46
|
-
if value.encoding != Encoding::ASCII_8BIT
|
47
|
-
value = value.force_encoding(Encoding::ASCII_8BIT)
|
48
|
-
end
|
49
|
-
value
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# The SQLite3 adapter works SQLite 3.6.16 or newer
|
54
|
-
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
|
51
|
+
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
52
|
+
# (available as gem from https://rubygems.org/gems/sqlite3).
|
55
53
|
#
|
56
54
|
# Options:
|
57
55
|
#
|
58
56
|
# * <tt>:database</tt> - Path to the database file.
|
59
57
|
class SQLite3Adapter < AbstractAdapter
|
60
|
-
ADAPTER_NAME =
|
61
|
-
|
58
|
+
ADAPTER_NAME = "SQLite"
|
59
|
+
|
60
|
+
include SQLite3::Quoting
|
61
|
+
include SQLite3::SchemaStatements
|
62
|
+
include SQLite3::DatabaseStatements
|
62
63
|
|
63
64
|
NATIVE_DATABASE_TYPES = {
|
64
|
-
primary_key:
|
65
|
+
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
65
66
|
string: { name: "varchar" },
|
66
67
|
text: { name: "text" },
|
67
68
|
integer: { name: "integer" },
|
@@ -71,58 +72,29 @@ module ActiveRecord
|
|
71
72
|
time: { name: "time" },
|
72
73
|
date: { name: "date" },
|
73
74
|
binary: { name: "blob" },
|
74
|
-
boolean: { name: "boolean" }
|
75
|
+
boolean: { name: "boolean" },
|
76
|
+
json: { name: "json" },
|
75
77
|
}
|
76
78
|
|
77
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
78
|
-
def initialize(connection, max)
|
79
|
-
super
|
80
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
81
|
-
end
|
82
|
-
|
83
|
-
def each(&block); cache.each(&block); end
|
84
|
-
def key?(key); cache.key?(key); end
|
85
|
-
def [](key); cache[key]; end
|
86
|
-
def length; cache.length; end
|
87
|
-
|
88
|
-
def []=(sql, key)
|
89
|
-
while @max <= cache.size
|
90
|
-
dealloc(cache.shift.last[:stmt])
|
91
|
-
end
|
92
|
-
cache[sql] = key
|
93
|
-
end
|
94
|
-
|
95
|
-
def clear
|
96
|
-
cache.each_value do |hash|
|
97
|
-
dealloc hash[:stmt]
|
98
|
-
end
|
99
|
-
cache.clear
|
100
|
-
end
|
101
|
-
|
79
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
102
80
|
private
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
def dealloc(stmt)
|
108
|
-
stmt.close unless stmt.closed?
|
109
|
-
end
|
81
|
+
def dealloc(stmt)
|
82
|
+
stmt.close unless stmt.closed?
|
83
|
+
end
|
110
84
|
end
|
111
85
|
|
112
86
|
def initialize(connection, logger, connection_options, config)
|
113
|
-
super(connection, logger)
|
114
|
-
|
115
|
-
|
116
|
-
@statements = StatementPool.new(@connection,
|
117
|
-
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
118
|
-
@config = config
|
119
|
-
|
120
|
-
@visitor = Arel::Visitors::SQLite.new self
|
87
|
+
super(connection, logger, config)
|
88
|
+
configure_connection
|
89
|
+
end
|
121
90
|
|
122
|
-
|
123
|
-
|
91
|
+
def self.database_exists?(config)
|
92
|
+
config = config.symbolize_keys
|
93
|
+
if config[:database] == ":memory:"
|
94
|
+
true
|
124
95
|
else
|
125
|
-
|
96
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
97
|
+
File.exist?(database_file)
|
126
98
|
end
|
127
99
|
end
|
128
100
|
|
@@ -134,26 +106,27 @@ module ActiveRecord
|
|
134
106
|
true
|
135
107
|
end
|
136
108
|
|
137
|
-
def
|
138
|
-
|
109
|
+
def supports_transaction_isolation?
|
110
|
+
true
|
139
111
|
end
|
140
112
|
|
141
|
-
|
142
|
-
# caching.
|
143
|
-
def supports_statement_cache?
|
113
|
+
def supports_partial_index?
|
144
114
|
true
|
145
115
|
end
|
146
116
|
|
147
|
-
|
148
|
-
|
117
|
+
def supports_expression_index?
|
118
|
+
database_version >= "3.9.0"
|
119
|
+
end
|
120
|
+
|
121
|
+
def requires_reloading?
|
149
122
|
true
|
150
123
|
end
|
151
124
|
|
152
|
-
def
|
125
|
+
def supports_foreign_keys?
|
153
126
|
true
|
154
127
|
end
|
155
128
|
|
156
|
-
def
|
129
|
+
def supports_check_constraints?
|
157
130
|
true
|
158
131
|
end
|
159
132
|
|
@@ -161,34 +134,45 @@ module ActiveRecord
|
|
161
134
|
true
|
162
135
|
end
|
163
136
|
|
137
|
+
def supports_datetime_with_precision?
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def supports_json?
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
def supports_common_table_expressions?
|
146
|
+
database_version >= "3.8.3"
|
147
|
+
end
|
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
|
+
|
164
156
|
def active?
|
165
|
-
|
157
|
+
!@connection.closed?
|
158
|
+
end
|
159
|
+
|
160
|
+
def reconnect!
|
161
|
+
super
|
162
|
+
connect if @connection.closed?
|
166
163
|
end
|
167
164
|
|
168
165
|
# Disconnects from the database if already connected. Otherwise, this
|
169
166
|
# method does nothing.
|
170
167
|
def disconnect!
|
171
168
|
super
|
172
|
-
@active = false
|
173
169
|
@connection.close rescue nil
|
174
170
|
end
|
175
171
|
|
176
|
-
# Clears the prepared statements cache.
|
177
|
-
def clear_cache!
|
178
|
-
@statements.clear
|
179
|
-
end
|
180
|
-
|
181
172
|
def supports_index_sort_order?
|
182
173
|
true
|
183
174
|
end
|
184
175
|
|
185
|
-
# Returns 62. SQLite supports index names up to 64
|
186
|
-
# characters. The rest is used by rails internally to perform
|
187
|
-
# temporary rename operations
|
188
|
-
def allowed_index_name_length
|
189
|
-
index_name_length - 2
|
190
|
-
end
|
191
|
-
|
192
176
|
def native_database_types #:nodoc:
|
193
177
|
NATIVE_DATABASE_TYPES
|
194
178
|
end
|
@@ -202,224 +186,38 @@ module ActiveRecord
|
|
202
186
|
true
|
203
187
|
end
|
204
188
|
|
205
|
-
|
206
|
-
|
207
|
-
def _quote(value) # :nodoc:
|
208
|
-
case value
|
209
|
-
when Type::Binary::Data
|
210
|
-
"x'#{value.hex}'"
|
211
|
-
else
|
212
|
-
super
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def _type_cast(value) # :nodoc:
|
217
|
-
case value
|
218
|
-
when BigDecimal
|
219
|
-
value.to_f
|
220
|
-
when String
|
221
|
-
if value.encoding == Encoding::ASCII_8BIT
|
222
|
-
super(value.encode(Encoding::UTF_8))
|
223
|
-
else
|
224
|
-
super
|
225
|
-
end
|
226
|
-
else
|
227
|
-
super
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def quote_string(s) #:nodoc:
|
232
|
-
@connection.class.quote(s)
|
233
|
-
end
|
234
|
-
|
235
|
-
def quote_table_name_for_assignment(table, attr)
|
236
|
-
quote_column_name(attr)
|
237
|
-
end
|
238
|
-
|
239
|
-
def quote_column_name(name) #:nodoc:
|
240
|
-
%Q("#{name.to_s.gsub('"', '""')}")
|
241
|
-
end
|
242
|
-
|
243
|
-
# Quote date/time values for use in SQL input. Includes microseconds
|
244
|
-
# if the value is a Time responding to usec.
|
245
|
-
def quoted_date(value) #:nodoc:
|
246
|
-
if value.respond_to?(:usec)
|
247
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
248
|
-
else
|
249
|
-
super
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
#--
|
254
|
-
# DATABASE STATEMENTS ======================================
|
255
|
-
#++
|
256
|
-
|
257
|
-
def explain(arel, binds = [])
|
258
|
-
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
259
|
-
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
|
189
|
+
def supports_lazy_transactions?
|
190
|
+
true
|
260
191
|
end
|
261
192
|
|
262
|
-
|
263
|
-
# Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
|
264
|
-
# the output of the SQLite shell:
|
265
|
-
#
|
266
|
-
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
|
267
|
-
# 0|1|1|SCAN TABLE posts (~100000 rows)
|
268
|
-
#
|
269
|
-
def pp(result) # :nodoc:
|
270
|
-
result.rows.map do |row|
|
271
|
-
row.join('|')
|
272
|
-
end.join("\n") + "\n"
|
273
|
-
end
|
274
|
-
end
|
193
|
+
# REFERENTIAL INTEGRITY ====================================
|
275
194
|
|
276
|
-
def
|
277
|
-
|
278
|
-
|
279
|
-
}
|
280
|
-
|
281
|
-
log(sql, name, type_casted_binds) do
|
282
|
-
# Don't cache statements if they are not prepared
|
283
|
-
if without_prepared_statement?(binds)
|
284
|
-
stmt = @connection.prepare(sql)
|
285
|
-
begin
|
286
|
-
cols = stmt.columns
|
287
|
-
records = stmt.to_a
|
288
|
-
ensure
|
289
|
-
stmt.close
|
290
|
-
end
|
291
|
-
stmt = records
|
292
|
-
else
|
293
|
-
cache = @statements[sql] ||= {
|
294
|
-
:stmt => @connection.prepare(sql)
|
295
|
-
}
|
296
|
-
stmt = cache[:stmt]
|
297
|
-
cols = cache[:cols] ||= stmt.columns
|
298
|
-
stmt.reset!
|
299
|
-
stmt.bind_params type_casted_binds.map { |_, val| val }
|
300
|
-
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")
|
301
198
|
|
302
|
-
|
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}")
|
303
206
|
end
|
304
207
|
end
|
305
208
|
|
306
|
-
def exec_delete(sql, name = 'SQL', binds = [])
|
307
|
-
exec_query(sql, name, binds)
|
308
|
-
@connection.changes
|
309
|
-
end
|
310
|
-
alias :exec_update :exec_delete
|
311
|
-
|
312
|
-
def last_inserted_id(result)
|
313
|
-
@connection.last_insert_row_id
|
314
|
-
end
|
315
|
-
|
316
|
-
def execute(sql, name = nil) #:nodoc:
|
317
|
-
log(sql, name) { @connection.execute(sql) }
|
318
|
-
end
|
319
|
-
|
320
|
-
def update_sql(sql, name = nil) #:nodoc:
|
321
|
-
super
|
322
|
-
@connection.changes
|
323
|
-
end
|
324
|
-
|
325
|
-
def delete_sql(sql, name = nil) #:nodoc:
|
326
|
-
sql += " WHERE 1=1" unless sql =~ /WHERE/i
|
327
|
-
super sql, name
|
328
|
-
end
|
329
|
-
|
330
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
331
|
-
super
|
332
|
-
id_value || @connection.last_insert_row_id
|
333
|
-
end
|
334
|
-
alias :create :insert_sql
|
335
|
-
|
336
|
-
def select_rows(sql, name = nil, binds = [])
|
337
|
-
exec_query(sql, name, binds).rows
|
338
|
-
end
|
339
|
-
|
340
|
-
def begin_db_transaction #:nodoc:
|
341
|
-
log('begin transaction',nil) { @connection.transaction }
|
342
|
-
end
|
343
|
-
|
344
|
-
def commit_db_transaction #:nodoc:
|
345
|
-
log('commit transaction',nil) { @connection.commit }
|
346
|
-
end
|
347
|
-
|
348
|
-
def exec_rollback_db_transaction #:nodoc:
|
349
|
-
log('rollback transaction',nil) { @connection.rollback }
|
350
|
-
end
|
351
|
-
|
352
209
|
# SCHEMA STATEMENTS ========================================
|
353
210
|
|
354
|
-
def
|
355
|
-
|
356
|
-
|
357
|
-
FROM sqlite_master
|
358
|
-
WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
|
359
|
-
SQL
|
360
|
-
sql << " AND name = #{quote_table_name(table_name)}" if table_name
|
361
|
-
|
362
|
-
exec_query(sql, 'SCHEMA').map do |row|
|
363
|
-
row['name']
|
364
|
-
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"] }
|
365
214
|
end
|
366
|
-
alias data_sources tables
|
367
215
|
|
368
|
-
def
|
369
|
-
|
370
|
-
end
|
371
|
-
alias data_source_exists? table_exists?
|
372
|
-
|
373
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
374
|
-
def columns(table_name) #:nodoc:
|
375
|
-
table_structure(table_name).map do |field|
|
376
|
-
case field["dflt_value"]
|
377
|
-
when /^null$/i
|
378
|
-
field["dflt_value"] = nil
|
379
|
-
when /^'(.*)'$/m
|
380
|
-
field["dflt_value"] = $1.gsub("''", "'")
|
381
|
-
when /^"(.*)"$/m
|
382
|
-
field["dflt_value"] = $1.gsub('""', '"')
|
383
|
-
end
|
384
|
-
|
385
|
-
sql_type = field['type']
|
386
|
-
cast_type = lookup_cast_type(sql_type)
|
387
|
-
new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
|
388
|
-
end
|
389
|
-
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)
|
390
218
|
|
391
|
-
|
392
|
-
def indexes(table_name, name = nil) #:nodoc:
|
393
|
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
|
394
|
-
sql = <<-SQL
|
395
|
-
SELECT sql
|
396
|
-
FROM sqlite_master
|
397
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
398
|
-
UNION ALL
|
399
|
-
SELECT sql
|
400
|
-
FROM sqlite_temp_master
|
401
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
402
|
-
SQL
|
403
|
-
index_sql = exec_query(sql).first['sql']
|
404
|
-
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
405
|
-
where = match[1] if match
|
406
|
-
IndexDefinition.new(
|
407
|
-
table_name,
|
408
|
-
row['name'],
|
409
|
-
row['unique'] != 0,
|
410
|
-
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
|
411
|
-
col['name']
|
412
|
-
}, nil, nil, where)
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
def primary_key(table_name) #:nodoc:
|
417
|
-
pks = table_structure(table_name).select { |f| f['pk'] > 0 }
|
418
|
-
return nil unless pks.count == 1
|
419
|
-
pks[0]['name']
|
420
|
-
end
|
219
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
421
220
|
|
422
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
423
221
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
424
222
|
end
|
425
223
|
|
@@ -428,39 +226,40 @@ module ActiveRecord
|
|
428
226
|
# Example:
|
429
227
|
# rename_table('octopuses', 'octopi')
|
430
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)
|
431
231
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
432
232
|
rename_table_indexes(table_name, new_name)
|
433
233
|
end
|
434
234
|
|
435
|
-
|
436
|
-
|
437
|
-
def valid_alter_table_type?(type)
|
438
|
-
type.to_sym != :primary_key
|
439
|
-
end
|
440
|
-
|
441
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
442
|
-
if valid_alter_table_type?(type)
|
443
|
-
super(table_name, column_name, type, options)
|
444
|
-
else
|
235
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
236
|
+
if invalid_alter_table_type?(type, options)
|
445
237
|
alter_table(table_name) do |definition|
|
446
|
-
definition.column(column_name, type, options)
|
238
|
+
definition.column(column_name, type, **options)
|
447
239
|
end
|
240
|
+
else
|
241
|
+
super
|
448
242
|
end
|
449
243
|
end
|
450
244
|
|
451
|
-
def remove_column(table_name, column_name, type = nil, options
|
245
|
+
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
452
246
|
alter_table(table_name) do |definition|
|
453
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
|
454
251
|
end
|
455
252
|
end
|
456
253
|
|
457
|
-
def change_column_default(table_name, column_name,
|
254
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
255
|
+
default = extract_new_default_value(default_or_changes)
|
256
|
+
|
458
257
|
alter_table(table_name) do |definition|
|
459
258
|
definition[column_name].default = default
|
460
259
|
end
|
461
260
|
end
|
462
261
|
|
463
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
262
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
464
263
|
unless null || default.nil?
|
465
264
|
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
466
265
|
end
|
@@ -469,82 +268,163 @@ module ActiveRecord
|
|
469
268
|
end
|
470
269
|
end
|
471
270
|
|
472
|
-
def change_column(table_name, column_name, type, options
|
271
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
473
272
|
alter_table(table_name) do |definition|
|
474
|
-
include_default = options_include_default?(options)
|
475
273
|
definition[column_name].instance_eval do
|
476
|
-
self.type
|
477
|
-
self.
|
478
|
-
self.default = options[:default] if include_default
|
479
|
-
self.null = options[:null] if options.include?(:null)
|
480
|
-
self.precision = options[:precision] if options.include?(:precision)
|
481
|
-
self.scale = options[:scale] if options.include?(:scale)
|
274
|
+
self.type = aliased_types(type.to_s, type)
|
275
|
+
self.options.merge!(options)
|
482
276
|
end
|
483
277
|
end
|
484
278
|
end
|
485
279
|
|
486
280
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
487
281
|
column = column_for(table_name, column_name)
|
488
|
-
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 })
|
489
283
|
rename_column_indexes(table_name, column.name, new_column_name)
|
490
284
|
end
|
491
285
|
|
492
|
-
|
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
|
493
338
|
|
494
|
-
def initialize_type_map(m)
|
339
|
+
def initialize_type_map(m = type_map)
|
495
340
|
super
|
496
|
-
m
|
341
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
497
342
|
end
|
498
343
|
|
499
344
|
def table_structure(table_name)
|
500
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})",
|
345
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
501
346
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
502
|
-
structure
|
347
|
+
table_structure_with_collation(table_name, structure)
|
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?
|
503
356
|
end
|
504
357
|
|
505
|
-
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
|
+
)
|
506
364
|
altered_table_name = "a#{table_name}"
|
507
|
-
|
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
|
508
382
|
|
509
383
|
transaction do
|
510
|
-
|
511
|
-
options.merge(:
|
512
|
-
|
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
|
513
388
|
end
|
514
389
|
end
|
515
390
|
|
516
|
-
def move_table(from, to, options = {}, &block)
|
391
|
+
def move_table(from, to, options = {}, &block)
|
517
392
|
copy_table(from, to, options, &block)
|
518
393
|
drop_table(from)
|
519
394
|
end
|
520
395
|
|
521
|
-
def copy_table(from, to, options = {})
|
396
|
+
def copy_table(from, to, options = {})
|
522
397
|
from_primary_key = primary_key(from)
|
523
398
|
options[:id] = false
|
524
|
-
create_table(to, options) do |definition|
|
399
|
+
create_table(to, **options) do |definition|
|
525
400
|
@definition = definition
|
526
|
-
|
401
|
+
if from_primary_key.is_a?(Array)
|
402
|
+
@definition.primary_keys from_primary_key
|
403
|
+
end
|
404
|
+
|
527
405
|
columns(from).each do |column|
|
528
406
|
column_name = options[:rename] ?
|
529
407
|
(options[:rename][column.name] ||
|
530
408
|
options[:rename][column.name.to_sym] ||
|
531
409
|
column.name) : column.name
|
532
|
-
next if column_name == from_primary_key
|
533
410
|
|
534
411
|
@definition.column(column_name, column.type,
|
535
|
-
:
|
536
|
-
:
|
537
|
-
:null
|
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
|
+
)
|
538
417
|
end
|
418
|
+
|
539
419
|
yield @definition if block_given?
|
540
420
|
end
|
541
421
|
copy_table_indexes(from, to, options[:rename] || {})
|
542
422
|
copy_table_contents(from, to,
|
543
|
-
@definition.columns.map
|
423
|
+
@definition.columns.map(&:name),
|
544
424
|
options[:rename] || {})
|
545
425
|
end
|
546
426
|
|
547
|
-
def copy_table_indexes(from, to, rename = {})
|
427
|
+
def copy_table_indexes(from, to, rename = {})
|
548
428
|
indexes(from).each do |index|
|
549
429
|
name = index.name
|
550
430
|
if to == "a#{from}"
|
@@ -553,60 +433,129 @@ module ActiveRecord
|
|
553
433
|
name = name[1..-1]
|
554
434
|
end
|
555
435
|
|
556
|
-
|
557
|
-
|
558
|
-
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
|
559
442
|
end
|
560
443
|
|
561
444
|
unless columns.empty?
|
562
445
|
# index name can't be the same
|
563
|
-
|
564
|
-
|
565
|
-
|
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)
|
566
450
|
end
|
567
451
|
end
|
568
452
|
end
|
569
453
|
|
570
|
-
def copy_table_contents(from, to, columns, rename = {})
|
571
|
-
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] }]
|
572
456
|
rename.each { |a| column_mappings[a.last] = a.first }
|
573
|
-
from_columns = columns(from).collect
|
574
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
575
|
-
|
457
|
+
from_columns = columns(from).collect(&:name)
|
458
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
459
|
+
from_columns_to_copy = columns.map { |col| column_mappings[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) } * ","
|
462
|
+
|
463
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
464
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
465
|
+
end
|
466
|
+
|
467
|
+
def translate_exception(exception, message:, sql:, binds:)
|
468
|
+
# SQLite 3.8.2 returns a newly formatted error message:
|
469
|
+
# UNIQUE constraint failed: *table_name*.*column_name*
|
470
|
+
# Older versions of SQLite return:
|
471
|
+
# column *column_name* is not unique
|
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)
|
480
|
+
else
|
481
|
+
super
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
576
486
|
|
577
|
-
|
487
|
+
def table_structure_with_collation(table_name, basic_structure)
|
488
|
+
collation_hash = {}
|
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
|
578
495
|
|
579
|
-
|
496
|
+
# Result will have following sample string
|
497
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
498
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
499
|
+
result = query_value(sql, "SCHEMA")
|
580
500
|
|
581
|
-
|
582
|
-
|
501
|
+
if result
|
502
|
+
# Splitting with left parentheses and discarding the first part will return all
|
503
|
+
# columns separated with comma(,).
|
504
|
+
columns_string = result.split("(", 2).last
|
583
505
|
|
584
|
-
|
585
|
-
|
506
|
+
columns_string.split(",").each do |column_string|
|
507
|
+
# This regex will match the column name and collation type and will save
|
508
|
+
# the value in $1 and $2 respectively.
|
509
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
586
510
|
end
|
587
511
|
|
588
|
-
|
589
|
-
|
590
|
-
|
512
|
+
basic_structure.map do |column|
|
513
|
+
column_name = column["name"]
|
514
|
+
|
515
|
+
if collation_hash.has_key? column_name
|
516
|
+
column["collation"] = collation_hash[column_name]
|
517
|
+
end
|
518
|
+
|
519
|
+
column
|
520
|
+
end
|
521
|
+
else
|
522
|
+
basic_structure.to_a
|
591
523
|
end
|
592
524
|
end
|
593
525
|
|
594
|
-
def
|
595
|
-
|
526
|
+
def arel_visitor
|
527
|
+
Arel::Visitors::SQLite.new(self)
|
596
528
|
end
|
597
529
|
|
598
|
-
def
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
end
|
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
|
609
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)
|
610
558
|
end
|
559
|
+
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
611
560
|
end
|
612
561
|
end
|