activerecord 4.2.0 → 6.0.0
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 +612 -971
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -248
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +135 -56
- data/lib/active_record/associations/association_scope.rb +103 -131
- data/lib/active_record/associations/belongs_to_association.rb +67 -54
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +60 -70
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +138 -274
- data/lib/active_record/associations/collection_proxy.rb +252 -151
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -83
- data/lib/active_record/associations/has_many_through_association.rb +62 -80
- data/lib/active_record/associations/has_one_association.rb +62 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -80
- 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 +138 -162
- data/lib/active_record/associations/preloader/association.rb +90 -119
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +92 -94
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +15 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
- data/lib/active_record/attribute_methods/write.rb +33 -55
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +214 -74
- data/lib/active_record/autosave_association.rb +115 -46
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +247 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +366 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +706 -222
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +468 -194
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +535 -597
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +65 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- 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 +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
- 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 +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +474 -286
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -363
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -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 +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +288 -359
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +266 -233
- data/lib/active_record/counter_cache.rb +68 -50
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +164 -88
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +226 -495
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +158 -112
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +91 -98
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +634 -288
- data/lib/active_record/model_schema.rb +314 -112
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +559 -124
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +148 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +338 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +460 -299
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/calculations.rb +269 -248
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +279 -255
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +83 -69
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -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 +53 -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 +116 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +574 -391
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +518 -340
- data/lib/active_record/result.rb +79 -42
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -20
- data/lib/active_record/scoping/default.rb +101 -84
- data/lib/active_record/scoping/named.rb +86 -33
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -88
- data/lib/active_record/tasks/postgresql_database_tasks.rb +82 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +215 -139
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +78 -23
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -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 +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +43 -46
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +43 -21
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -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 +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
- data/lib/rails/generators/active_record/migration.rb +31 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +166 -60
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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/infinity.rb +0 -13
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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]
|
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,25 +48,6 @@ module ActiveRecord
|
|
41
48
|
end
|
42
49
|
|
43
50
|
module ConnectionAdapters #:nodoc:
|
44
|
-
class SQLite3Binary < Type::Binary # :nodoc:
|
45
|
-
def cast_value(value)
|
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
|
-
class SQLite3String < Type::String # :nodoc:
|
54
|
-
def type_cast_for_database(value)
|
55
|
-
if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
|
56
|
-
value.encode(Encoding::UTF_8)
|
57
|
-
else
|
58
|
-
super
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
51
|
# The SQLite3 adapter works SQLite 3.6.16 or newer
|
64
52
|
# with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
|
65
53
|
#
|
@@ -67,11 +55,14 @@ module ActiveRecord
|
|
67
55
|
#
|
68
56
|
# * <tt>:database</tt> - Path to the database file.
|
69
57
|
class SQLite3Adapter < AbstractAdapter
|
70
|
-
ADAPTER_NAME =
|
71
|
-
|
58
|
+
ADAPTER_NAME = "SQLite"
|
59
|
+
|
60
|
+
include SQLite3::Quoting
|
61
|
+
include SQLite3::SchemaStatements
|
62
|
+
include SQLite3::DatabaseStatements
|
72
63
|
|
73
64
|
NATIVE_DATABASE_TYPES = {
|
74
|
-
primary_key:
|
65
|
+
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
|
75
66
|
string: { name: "varchar" },
|
76
67
|
text: { name: "text" },
|
77
68
|
integer: { name: "integer" },
|
@@ -81,70 +72,39 @@ module ActiveRecord
|
|
81
72
|
time: { name: "time" },
|
82
73
|
date: { name: "date" },
|
83
74
|
binary: { name: "blob" },
|
84
|
-
boolean: { name: "boolean" }
|
75
|
+
boolean: { name: "boolean" },
|
76
|
+
json: { name: "json" },
|
85
77
|
}
|
86
78
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def initialize(version_string)
|
91
|
-
@version = version_string.split('.').map { |v| v.to_i }
|
79
|
+
def self.represent_boolean_as_integer=(value) # :nodoc:
|
80
|
+
if value == false
|
81
|
+
raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings."
|
92
82
|
end
|
93
83
|
|
94
|
-
|
95
|
-
|
96
|
-
|
84
|
+
ActiveSupport::Deprecation.warn(
|
85
|
+
"`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1."
|
86
|
+
)
|
97
87
|
end
|
98
88
|
|
99
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
100
|
-
def initialize(connection, max)
|
101
|
-
super
|
102
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
103
|
-
end
|
104
|
-
|
105
|
-
def each(&block); cache.each(&block); end
|
106
|
-
def key?(key); cache.key?(key); end
|
107
|
-
def [](key); cache[key]; end
|
108
|
-
def length; cache.length; end
|
109
|
-
|
110
|
-
def []=(sql, key)
|
111
|
-
while @max <= cache.size
|
112
|
-
dealloc(cache.shift.last[:stmt])
|
113
|
-
end
|
114
|
-
cache[sql] = key
|
115
|
-
end
|
116
|
-
|
117
|
-
def clear
|
118
|
-
cache.each_value do |hash|
|
119
|
-
dealloc hash[:stmt]
|
120
|
-
end
|
121
|
-
cache.clear
|
122
|
-
end
|
123
|
-
|
89
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
124
90
|
private
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
def dealloc(stmt)
|
130
|
-
stmt.close unless stmt.closed?
|
131
|
-
end
|
91
|
+
def dealloc(stmt)
|
92
|
+
stmt.close unless stmt.closed?
|
93
|
+
end
|
132
94
|
end
|
133
95
|
|
134
96
|
def initialize(connection, logger, connection_options, config)
|
135
|
-
super(connection, logger)
|
136
|
-
|
137
|
-
|
138
|
-
@statements = StatementPool.new(@connection,
|
139
|
-
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
140
|
-
@config = config
|
141
|
-
|
142
|
-
@visitor = Arel::Visitors::SQLite.new self
|
97
|
+
super(connection, logger, config)
|
98
|
+
configure_connection
|
99
|
+
end
|
143
100
|
|
144
|
-
|
145
|
-
|
101
|
+
def self.database_exists?(config)
|
102
|
+
config = config.symbolize_keys
|
103
|
+
if config[:database] == ":memory:"
|
104
|
+
return true
|
146
105
|
else
|
147
|
-
|
106
|
+
database_file = defined?(Rails.root) ? File.expand_path(config[:database], Rails.root) : config[:database]
|
107
|
+
File.exist?(database_file)
|
148
108
|
end
|
149
109
|
end
|
150
110
|
|
@@ -157,55 +117,62 @@ module ActiveRecord
|
|
157
117
|
end
|
158
118
|
|
159
119
|
def supports_partial_index?
|
160
|
-
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
def supports_expression_index?
|
124
|
+
database_version >= "3.9.0"
|
161
125
|
end
|
162
126
|
|
163
|
-
|
164
|
-
# caching.
|
165
|
-
def supports_statement_cache?
|
127
|
+
def requires_reloading?
|
166
128
|
true
|
167
129
|
end
|
168
130
|
|
169
|
-
|
170
|
-
def supports_migrations? #:nodoc:
|
131
|
+
def supports_foreign_keys?
|
171
132
|
true
|
172
133
|
end
|
173
134
|
|
174
|
-
def
|
135
|
+
def supports_views?
|
175
136
|
true
|
176
137
|
end
|
177
138
|
|
178
|
-
def
|
139
|
+
def supports_datetime_with_precision?
|
179
140
|
true
|
180
141
|
end
|
181
142
|
|
182
|
-
def
|
143
|
+
def supports_json?
|
183
144
|
true
|
184
145
|
end
|
185
146
|
|
147
|
+
def supports_insert_on_conflict?
|
148
|
+
database_version >= "3.24.0"
|
149
|
+
end
|
150
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
151
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
152
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
153
|
+
|
186
154
|
def active?
|
187
|
-
|
155
|
+
!@connection.closed?
|
156
|
+
end
|
157
|
+
|
158
|
+
def reconnect!
|
159
|
+
super
|
160
|
+
connect if @connection.closed?
|
188
161
|
end
|
189
162
|
|
190
163
|
# Disconnects from the database if already connected. Otherwise, this
|
191
164
|
# method does nothing.
|
192
165
|
def disconnect!
|
193
166
|
super
|
194
|
-
@active = false
|
195
167
|
@connection.close rescue nil
|
196
168
|
end
|
197
169
|
|
198
|
-
# Clears the prepared statements cache.
|
199
|
-
def clear_cache!
|
200
|
-
@statements.clear
|
201
|
-
end
|
202
|
-
|
203
170
|
def supports_index_sort_order?
|
204
171
|
true
|
205
172
|
end
|
206
173
|
|
207
174
|
# Returns 62. SQLite supports index names up to 64
|
208
|
-
# characters. The rest is used by
|
175
|
+
# characters. The rest is used by Rails internally to perform
|
209
176
|
# temporary rename operations
|
210
177
|
def allowed_index_name_length
|
211
178
|
index_name_length - 2
|
@@ -224,217 +191,43 @@ module ActiveRecord
|
|
224
191
|
true
|
225
192
|
end
|
226
193
|
|
227
|
-
|
228
|
-
|
229
|
-
def _quote(value) # :nodoc:
|
230
|
-
case value
|
231
|
-
when Type::Binary::Data
|
232
|
-
"x'#{value.hex}'"
|
233
|
-
else
|
234
|
-
super
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
def _type_cast(value) # :nodoc:
|
239
|
-
case value
|
240
|
-
when BigDecimal
|
241
|
-
value.to_f
|
242
|
-
else
|
243
|
-
super
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def quote_string(s) #:nodoc:
|
248
|
-
@connection.class.quote(s)
|
194
|
+
def supports_lazy_transactions?
|
195
|
+
true
|
249
196
|
end
|
250
197
|
|
251
|
-
|
252
|
-
quote_column_name(attr)
|
253
|
-
end
|
198
|
+
# REFERENTIAL INTEGRITY ====================================
|
254
199
|
|
255
|
-
def
|
256
|
-
|
257
|
-
|
200
|
+
def disable_referential_integrity # :nodoc:
|
201
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
202
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
258
203
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
204
|
+
begin
|
205
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
206
|
+
execute("PRAGMA foreign_keys = OFF")
|
207
|
+
yield
|
208
|
+
ensure
|
209
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
210
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
266
211
|
end
|
267
212
|
end
|
268
213
|
|
269
214
|
#--
|
270
215
|
# DATABASE STATEMENTS ======================================
|
271
216
|
#++
|
272
|
-
|
273
217
|
def explain(arel, binds = [])
|
274
218
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
275
|
-
ExplainPrettyPrinter.new.pp(exec_query(sql,
|
276
|
-
end
|
277
|
-
|
278
|
-
class ExplainPrettyPrinter
|
279
|
-
# Pretty prints the result of a EXPLAIN QUERY PLAN in a way that resembles
|
280
|
-
# the output of the SQLite shell:
|
281
|
-
#
|
282
|
-
# 0|0|0|SEARCH TABLE users USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
|
283
|
-
# 0|1|1|SCAN TABLE posts (~100000 rows)
|
284
|
-
#
|
285
|
-
def pp(result) # :nodoc:
|
286
|
-
result.rows.map do |row|
|
287
|
-
row.join('|')
|
288
|
-
end.join("\n") + "\n"
|
289
|
-
end
|
290
|
-
end
|
291
|
-
|
292
|
-
def exec_query(sql, name = nil, binds = [])
|
293
|
-
type_casted_binds = binds.map { |col, val|
|
294
|
-
[col, type_cast(val, col)]
|
295
|
-
}
|
296
|
-
|
297
|
-
log(sql, name, type_casted_binds) do
|
298
|
-
# Don't cache statements if they are not prepared
|
299
|
-
if without_prepared_statement?(binds)
|
300
|
-
stmt = @connection.prepare(sql)
|
301
|
-
begin
|
302
|
-
cols = stmt.columns
|
303
|
-
records = stmt.to_a
|
304
|
-
ensure
|
305
|
-
stmt.close
|
306
|
-
end
|
307
|
-
stmt = records
|
308
|
-
else
|
309
|
-
cache = @statements[sql] ||= {
|
310
|
-
:stmt => @connection.prepare(sql)
|
311
|
-
}
|
312
|
-
stmt = cache[:stmt]
|
313
|
-
cols = cache[:cols] ||= stmt.columns
|
314
|
-
stmt.reset!
|
315
|
-
stmt.bind_params type_casted_binds.map { |_, val| val }
|
316
|
-
end
|
317
|
-
|
318
|
-
ActiveRecord::Result.new(cols, stmt.to_a)
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
def exec_delete(sql, name = 'SQL', binds = [])
|
323
|
-
exec_query(sql, name, binds)
|
324
|
-
@connection.changes
|
325
|
-
end
|
326
|
-
alias :exec_update :exec_delete
|
327
|
-
|
328
|
-
def last_inserted_id(result)
|
329
|
-
@connection.last_insert_row_id
|
330
|
-
end
|
331
|
-
|
332
|
-
def execute(sql, name = nil) #:nodoc:
|
333
|
-
log(sql, name) { @connection.execute(sql) }
|
334
|
-
end
|
335
|
-
|
336
|
-
def update_sql(sql, name = nil) #:nodoc:
|
337
|
-
super
|
338
|
-
@connection.changes
|
339
|
-
end
|
340
|
-
|
341
|
-
def delete_sql(sql, name = nil) #:nodoc:
|
342
|
-
sql += " WHERE 1=1" unless sql =~ /WHERE/i
|
343
|
-
super sql, name
|
344
|
-
end
|
345
|
-
|
346
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
347
|
-
super
|
348
|
-
id_value || @connection.last_insert_row_id
|
349
|
-
end
|
350
|
-
alias :create :insert_sql
|
351
|
-
|
352
|
-
def select_rows(sql, name = nil, binds = [])
|
353
|
-
exec_query(sql, name, binds).rows
|
354
|
-
end
|
355
|
-
|
356
|
-
def begin_db_transaction #:nodoc:
|
357
|
-
log('begin transaction',nil) { @connection.transaction }
|
358
|
-
end
|
359
|
-
|
360
|
-
def commit_db_transaction #:nodoc:
|
361
|
-
log('commit transaction',nil) { @connection.commit }
|
362
|
-
end
|
363
|
-
|
364
|
-
def rollback_db_transaction #:nodoc:
|
365
|
-
log('rollback transaction',nil) { @connection.rollback }
|
219
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
366
220
|
end
|
367
221
|
|
368
222
|
# SCHEMA STATEMENTS ========================================
|
369
223
|
|
370
|
-
def
|
371
|
-
|
372
|
-
|
373
|
-
FROM sqlite_master
|
374
|
-
WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
|
375
|
-
SQL
|
376
|
-
sql << " AND name = #{quote_table_name(table_name)}" if table_name
|
377
|
-
|
378
|
-
exec_query(sql, 'SCHEMA').map do |row|
|
379
|
-
row['name']
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
def table_exists?(table_name)
|
384
|
-
table_name && tables(nil, table_name).any?
|
385
|
-
end
|
386
|
-
|
387
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
388
|
-
def columns(table_name) #:nodoc:
|
389
|
-
table_structure(table_name).map do |field|
|
390
|
-
case field["dflt_value"]
|
391
|
-
when /^null$/i
|
392
|
-
field["dflt_value"] = nil
|
393
|
-
when /^'(.*)'$/m
|
394
|
-
field["dflt_value"] = $1.gsub("''", "'")
|
395
|
-
when /^"(.*)"$/m
|
396
|
-
field["dflt_value"] = $1.gsub('""', '"')
|
397
|
-
end
|
398
|
-
|
399
|
-
sql_type = field['type']
|
400
|
-
cast_type = lookup_cast_type(sql_type)
|
401
|
-
new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
|
402
|
-
end
|
224
|
+
def primary_keys(table_name) # :nodoc:
|
225
|
+
pks = table_structure(table_name).select { |f| f["pk"] > 0 }
|
226
|
+
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
403
227
|
end
|
404
228
|
|
405
|
-
|
406
|
-
|
407
|
-
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
|
408
|
-
sql = <<-SQL
|
409
|
-
SELECT sql
|
410
|
-
FROM sqlite_master
|
411
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
412
|
-
UNION ALL
|
413
|
-
SELECT sql
|
414
|
-
FROM sqlite_temp_master
|
415
|
-
WHERE name=#{quote(row['name'])} AND type='index'
|
416
|
-
SQL
|
417
|
-
index_sql = exec_query(sql).first['sql']
|
418
|
-
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
419
|
-
where = match[1] if match
|
420
|
-
IndexDefinition.new(
|
421
|
-
table_name,
|
422
|
-
row['name'],
|
423
|
-
row['unique'] != 0,
|
424
|
-
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
|
425
|
-
col['name']
|
426
|
-
}, nil, nil, where)
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
def primary_key(table_name) #:nodoc:
|
431
|
-
column = table_structure(table_name).find { |field|
|
432
|
-
field['pk'] == 1
|
433
|
-
}
|
434
|
-
column && column['name']
|
435
|
-
end
|
436
|
-
|
437
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
229
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
230
|
+
index_name = index_name_for_remove(table_name, options)
|
438
231
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
439
232
|
end
|
440
233
|
|
@@ -447,35 +240,34 @@ module ActiveRecord
|
|
447
240
|
rename_table_indexes(table_name, new_name)
|
448
241
|
end
|
449
242
|
|
450
|
-
# See: http://www.sqlite.org/lang_altertable.html
|
451
|
-
# SQLite has an additional restriction on the ALTER TABLE statement
|
452
|
-
def valid_alter_table_type?(type)
|
453
|
-
type.to_sym != :primary_key
|
454
|
-
end
|
455
|
-
|
456
243
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
457
|
-
if
|
458
|
-
super(table_name, column_name, type, options)
|
459
|
-
else
|
244
|
+
if invalid_alter_table_type?(type, options)
|
460
245
|
alter_table(table_name) do |definition|
|
461
246
|
definition.column(column_name, type, options)
|
462
247
|
end
|
248
|
+
else
|
249
|
+
super
|
463
250
|
end
|
464
251
|
end
|
465
252
|
|
466
253
|
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
|
467
254
|
alter_table(table_name) do |definition|
|
468
255
|
definition.remove_column column_name
|
256
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
257
|
+
fk_options[:column] == column_name.to_s
|
258
|
+
end
|
469
259
|
end
|
470
260
|
end
|
471
261
|
|
472
|
-
def change_column_default(table_name, column_name,
|
262
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
263
|
+
default = extract_new_default_value(default_or_changes)
|
264
|
+
|
473
265
|
alter_table(table_name) do |definition|
|
474
266
|
definition[column_name].default = default
|
475
267
|
end
|
476
268
|
end
|
477
269
|
|
478
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
270
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
479
271
|
unless null || default.nil?
|
480
272
|
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
481
273
|
end
|
@@ -486,81 +278,150 @@ module ActiveRecord
|
|
486
278
|
|
487
279
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
488
280
|
alter_table(table_name) do |definition|
|
489
|
-
include_default = options_include_default?(options)
|
490
281
|
definition[column_name].instance_eval do
|
491
282
|
self.type = type
|
492
283
|
self.limit = options[:limit] if options.include?(:limit)
|
493
|
-
self.default = options[:default] if
|
284
|
+
self.default = options[:default] if options.include?(:default)
|
494
285
|
self.null = options[:null] if options.include?(:null)
|
495
286
|
self.precision = options[:precision] if options.include?(:precision)
|
496
|
-
self.scale
|
287
|
+
self.scale = options[:scale] if options.include?(:scale)
|
288
|
+
self.collation = options[:collation] if options.include?(:collation)
|
497
289
|
end
|
498
290
|
end
|
499
291
|
end
|
500
292
|
|
501
293
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
502
294
|
column = column_for(table_name, column_name)
|
503
|
-
alter_table(table_name, rename: {column.name => new_column_name.to_s})
|
295
|
+
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
504
296
|
rename_column_indexes(table_name, column.name, new_column_name)
|
505
297
|
end
|
506
298
|
|
507
|
-
|
299
|
+
def add_reference(table_name, ref_name, **options) # :nodoc:
|
300
|
+
super(table_name, ref_name, type: :integer, **options)
|
301
|
+
end
|
302
|
+
alias :add_belongs_to :add_reference
|
303
|
+
|
304
|
+
def foreign_keys(table_name)
|
305
|
+
fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
306
|
+
fk_info.map do |row|
|
307
|
+
options = {
|
308
|
+
column: row["from"],
|
309
|
+
primary_key: row["to"],
|
310
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
311
|
+
on_update: extract_foreign_key_action(row["on_update"])
|
312
|
+
}
|
313
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def build_insert_sql(insert) # :nodoc:
|
318
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
319
|
+
|
320
|
+
if insert.skip_duplicates?
|
321
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
322
|
+
elsif insert.update_duplicates?
|
323
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
324
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
325
|
+
end
|
326
|
+
|
327
|
+
sql
|
328
|
+
end
|
329
|
+
|
330
|
+
def get_database_version # :nodoc:
|
331
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
332
|
+
end
|
333
|
+
|
334
|
+
def check_version # :nodoc:
|
335
|
+
if database_version < "3.8.0"
|
336
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
private
|
341
|
+
# See https://www.sqlite.org/limits.html,
|
342
|
+
# the default value is 999 when not configured.
|
343
|
+
def bind_params_length
|
344
|
+
999
|
345
|
+
end
|
508
346
|
|
509
|
-
def initialize_type_map(m)
|
347
|
+
def initialize_type_map(m = type_map)
|
510
348
|
super
|
511
|
-
m
|
512
|
-
register_class_with_limit m, %r(char)i, SQLite3String
|
349
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
513
350
|
end
|
514
351
|
|
515
352
|
def table_structure(table_name)
|
516
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})",
|
353
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
517
354
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
518
|
-
structure
|
355
|
+
table_structure_with_collation(table_name, structure)
|
519
356
|
end
|
357
|
+
alias column_definitions table_structure
|
520
358
|
|
521
|
-
|
359
|
+
# See: https://www.sqlite.org/lang_altertable.html
|
360
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
361
|
+
def invalid_alter_table_type?(type, options)
|
362
|
+
type.to_sym == :primary_key || options[:primary_key]
|
363
|
+
end
|
364
|
+
|
365
|
+
def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
|
522
366
|
altered_table_name = "a#{table_name}"
|
523
|
-
|
367
|
+
|
368
|
+
caller = lambda do |definition|
|
369
|
+
rename = options[:rename] || {}
|
370
|
+
foreign_keys.each do |fk|
|
371
|
+
if column = rename[fk.options[:column]]
|
372
|
+
fk.options[:column] = column
|
373
|
+
end
|
374
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
375
|
+
definition.foreign_key(to_table, fk.options)
|
376
|
+
end
|
377
|
+
|
378
|
+
yield definition if block_given?
|
379
|
+
end
|
524
380
|
|
525
381
|
transaction do
|
526
|
-
|
527
|
-
options.merge(:
|
528
|
-
|
382
|
+
disable_referential_integrity do
|
383
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
384
|
+
move_table(altered_table_name, table_name, &caller)
|
385
|
+
end
|
529
386
|
end
|
530
387
|
end
|
531
388
|
|
532
|
-
def move_table(from, to, options = {}, &block)
|
389
|
+
def move_table(from, to, options = {}, &block)
|
533
390
|
copy_table(from, to, options, &block)
|
534
391
|
drop_table(from)
|
535
392
|
end
|
536
393
|
|
537
|
-
def copy_table(from, to, options = {})
|
394
|
+
def copy_table(from, to, options = {})
|
538
395
|
from_primary_key = primary_key(from)
|
539
396
|
options[:id] = false
|
540
397
|
create_table(to, options) do |definition|
|
541
398
|
@definition = definition
|
542
|
-
|
399
|
+
if from_primary_key.is_a?(Array)
|
400
|
+
@definition.primary_keys from_primary_key
|
401
|
+
end
|
543
402
|
columns(from).each do |column|
|
544
403
|
column_name = options[:rename] ?
|
545
404
|
(options[:rename][column.name] ||
|
546
405
|
options[:rename][column.name.to_sym] ||
|
547
406
|
column.name) : column.name
|
548
|
-
next if column_name == from_primary_key
|
549
407
|
|
550
408
|
@definition.column(column_name, column.type,
|
551
|
-
:
|
552
|
-
:
|
553
|
-
:null
|
409
|
+
limit: column.limit, default: column.default,
|
410
|
+
precision: column.precision, scale: column.scale,
|
411
|
+
null: column.null, collation: column.collation,
|
412
|
+
primary_key: column_name == from_primary_key
|
413
|
+
)
|
554
414
|
end
|
415
|
+
|
555
416
|
yield @definition if block_given?
|
556
417
|
end
|
557
418
|
copy_table_indexes(from, to, options[:rename] || {})
|
558
419
|
copy_table_contents(from, to,
|
559
|
-
@definition.columns.map
|
420
|
+
@definition.columns.map(&:name),
|
560
421
|
options[:rename] || {})
|
561
422
|
end
|
562
423
|
|
563
|
-
def copy_table_indexes(from, to, rename = {})
|
424
|
+
def copy_table_indexes(from, to, rename = {})
|
564
425
|
indexes(from).each do |index|
|
565
426
|
name = index.name
|
566
427
|
if to == "a#{from}"
|
@@ -569,60 +430,128 @@ module ActiveRecord
|
|
569
430
|
name = name[1..-1]
|
570
431
|
end
|
571
432
|
|
572
|
-
|
573
|
-
|
574
|
-
to_column_names.
|
433
|
+
columns = index.columns
|
434
|
+
if columns.is_a?(Array)
|
435
|
+
to_column_names = columns(to).map(&:name)
|
436
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
437
|
+
to_column_names.include?(column)
|
438
|
+
end
|
575
439
|
end
|
576
440
|
|
577
441
|
unless columns.empty?
|
578
442
|
# index name can't be the same
|
579
443
|
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
580
444
|
opts[:unique] = true if index.unique
|
445
|
+
opts[:where] = index.where if index.where
|
581
446
|
add_index(to, columns, opts)
|
582
447
|
end
|
583
448
|
end
|
584
449
|
end
|
585
450
|
|
586
|
-
def copy_table_contents(from, to, columns, rename = {})
|
587
|
-
column_mappings = Hash[columns.map {|name| [name, name]}]
|
451
|
+
def copy_table_contents(from, to, columns, rename = {})
|
452
|
+
column_mappings = Hash[columns.map { |name| [name, name] }]
|
588
453
|
rename.each { |a| column_mappings[a.last] = a.first }
|
589
|
-
from_columns = columns(from).collect
|
590
|
-
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
raw_column_mappings = Hash[columns(from).map { |c| [c.name, c] }]
|
454
|
+
from_columns = columns(from).collect(&:name)
|
455
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
456
|
+
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
457
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
458
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
596
459
|
|
597
|
-
exec_query("
|
598
|
-
|
599
|
-
|
600
|
-
column_values = columns.map do |col|
|
601
|
-
quote(row[column_mappings[col]], raw_column_mappings[col])
|
602
|
-
end
|
603
|
-
|
604
|
-
sql << column_values * ', '
|
605
|
-
sql << ')'
|
606
|
-
exec_query sql
|
607
|
-
end
|
608
|
-
end
|
609
|
-
|
610
|
-
def sqlite_version
|
611
|
-
@sqlite_version ||= SQLite3Adapter::Version.new(select_value('select sqlite_version(*)'))
|
460
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
461
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
612
462
|
end
|
613
463
|
|
614
|
-
def translate_exception(exception, message)
|
464
|
+
def translate_exception(exception, message:, sql:, binds:)
|
615
465
|
case exception.message
|
616
466
|
# SQLite 3.8.2 returns a newly formatted error message:
|
617
467
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
618
468
|
# Older versions of SQLite return:
|
619
469
|
# column *column_name* is not unique
|
620
470
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
621
|
-
RecordNotUnique.new(message,
|
471
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
472
|
+
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
473
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
474
|
+
when /FOREIGN KEY constraint failed/i
|
475
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
622
476
|
else
|
623
477
|
super
|
624
478
|
end
|
625
479
|
end
|
480
|
+
|
481
|
+
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
482
|
+
|
483
|
+
def table_structure_with_collation(table_name, basic_structure)
|
484
|
+
collation_hash = {}
|
485
|
+
sql = <<~SQL
|
486
|
+
SELECT sql FROM
|
487
|
+
(SELECT * FROM sqlite_master UNION ALL
|
488
|
+
SELECT * FROM sqlite_temp_master)
|
489
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
490
|
+
SQL
|
491
|
+
|
492
|
+
# Result will have following sample string
|
493
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
494
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
495
|
+
result = exec_query(sql, "SCHEMA").first
|
496
|
+
|
497
|
+
if result
|
498
|
+
# Splitting with left parentheses and discarding the first part will return all
|
499
|
+
# columns separated with comma(,).
|
500
|
+
columns_string = result["sql"].split("(", 2).last
|
501
|
+
|
502
|
+
columns_string.split(",").each do |column_string|
|
503
|
+
# This regex will match the column name and collation type and will save
|
504
|
+
# the value in $1 and $2 respectively.
|
505
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
506
|
+
end
|
507
|
+
|
508
|
+
basic_structure.map! do |column|
|
509
|
+
column_name = column["name"]
|
510
|
+
|
511
|
+
if collation_hash.has_key? column_name
|
512
|
+
column["collation"] = collation_hash[column_name]
|
513
|
+
end
|
514
|
+
|
515
|
+
column
|
516
|
+
end
|
517
|
+
else
|
518
|
+
basic_structure.to_a
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def arel_visitor
|
523
|
+
Arel::Visitors::SQLite.new(self)
|
524
|
+
end
|
525
|
+
|
526
|
+
def build_statement_pool
|
527
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
528
|
+
end
|
529
|
+
|
530
|
+
def connect
|
531
|
+
@connection = ::SQLite3::Database.new(
|
532
|
+
@config[:database].to_s,
|
533
|
+
@config.merge(results_as_hash: true)
|
534
|
+
)
|
535
|
+
configure_connection
|
536
|
+
end
|
537
|
+
|
538
|
+
def configure_connection
|
539
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
540
|
+
|
541
|
+
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
542
|
+
end
|
543
|
+
|
544
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
545
|
+
private
|
546
|
+
def _limit
|
547
|
+
# INTEGER storage class can be stored 8 bytes value.
|
548
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
549
|
+
limit || 8
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
626
554
|
end
|
555
|
+
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
627
556
|
end
|
628
557
|
end
|