activerecord 4.2.0 → 6.0.5.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 +852 -801
- data/MIT-LICENSE +4 -2
- data/README.rdoc +14 -13
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +26 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -36
- data/lib/active_record/associations/association.rb +137 -55
- data/lib/active_record/associations/association_scope.rb +110 -132
- 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 +58 -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 +150 -275
- data/lib/active_record/associations/collection_proxy.rb +253 -152
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -84
- 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 +43 -78
- 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 +159 -162
- data/lib/active_record/associations/preloader/association.rb +102 -113
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +96 -95
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +57 -185
- data/lib/active_record/attribute_decorators.rb +39 -17
- data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +90 -84
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -77
- data/lib/active_record/attribute_methods/serialization.rb +40 -21
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
- data/lib/active_record/attribute_methods/write.rb +33 -56
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +213 -74
- data/lib/active_record/autosave_association.rb +125 -54
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +101 -76
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +36 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
- 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 -47
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
- data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
- 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 +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -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 +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -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 +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- 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/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -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 -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 +9 -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 +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -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 +119 -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 +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 +299 -364
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_handling.rb +167 -41
- data/lib/active_record/core.rb +277 -233
- data/lib/active_record/counter_cache.rb +71 -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 +78 -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 -106
- data/lib/active_record/enum.rb +172 -89
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +22 -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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +225 -497
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +158 -115
- 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 +99 -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 +87 -0
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/migration/command_recorder.rb +166 -91
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +636 -290
- data/lib/active_record/model_schema.rb +344 -112
- data/lib/active_record/nested_attributes.rb +265 -215
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +559 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +44 -30
- data/lib/active_record/railtie.rb +166 -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 +341 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +461 -302
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +206 -55
- data/lib/active_record/relation/calculations.rb +270 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +287 -255
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +86 -68
- 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 +112 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +612 -392
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -17
- data/lib/active_record/relation/where_clause.rb +189 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +533 -340
- data/lib/active_record/result.rb +79 -43
- 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 +98 -82
- data/lib/active_record/scoping/named.rb +91 -33
- data/lib/active_record/scoping.rb +45 -27
- 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 +90 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +243 -0
- data/lib/active_record/timestamp.rb +86 -41
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +222 -146
- 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 -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 +12 -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 +29 -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 +77 -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 +12 -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 +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -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 +256 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +62 -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 +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 +30 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +174 -63
- 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/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 -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
|
+
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,66 @@ module ActiveRecord
|
|
157
117
|
end
|
158
118
|
|
159
119
|
def supports_partial_index?
|
160
|
-
|
120
|
+
true
|
161
121
|
end
|
162
122
|
|
163
|
-
|
164
|
-
|
165
|
-
|
123
|
+
def supports_expression_index?
|
124
|
+
database_version >= "3.9.0"
|
125
|
+
end
|
126
|
+
|
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_common_table_expressions?
|
148
|
+
database_version >= "3.8.3"
|
149
|
+
end
|
150
|
+
|
151
|
+
def supports_insert_on_conflict?
|
152
|
+
database_version >= "3.24.0"
|
153
|
+
end
|
154
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
155
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
156
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
157
|
+
|
186
158
|
def active?
|
187
|
-
|
159
|
+
!@connection.closed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def reconnect!
|
163
|
+
super
|
164
|
+
connect if @connection.closed?
|
188
165
|
end
|
189
166
|
|
190
167
|
# Disconnects from the database if already connected. Otherwise, this
|
191
168
|
# method does nothing.
|
192
169
|
def disconnect!
|
193
170
|
super
|
194
|
-
@active = false
|
195
171
|
@connection.close rescue nil
|
196
172
|
end
|
197
173
|
|
198
|
-
# Clears the prepared statements cache.
|
199
|
-
def clear_cache!
|
200
|
-
@statements.clear
|
201
|
-
end
|
202
|
-
|
203
174
|
def supports_index_sort_order?
|
204
175
|
true
|
205
176
|
end
|
206
177
|
|
207
178
|
# Returns 62. SQLite supports index names up to 64
|
208
|
-
# characters. The rest is used by
|
179
|
+
# characters. The rest is used by Rails internally to perform
|
209
180
|
# temporary rename operations
|
210
181
|
def allowed_index_name_length
|
211
182
|
index_name_length - 2
|
@@ -224,217 +195,43 @@ module ActiveRecord
|
|
224
195
|
true
|
225
196
|
end
|
226
197
|
|
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)
|
198
|
+
def supports_lazy_transactions?
|
199
|
+
true
|
249
200
|
end
|
250
201
|
|
251
|
-
|
252
|
-
quote_column_name(attr)
|
253
|
-
end
|
202
|
+
# REFERENTIAL INTEGRITY ====================================
|
254
203
|
|
255
|
-
def
|
256
|
-
|
257
|
-
|
204
|
+
def disable_referential_integrity # :nodoc:
|
205
|
+
old_foreign_keys = query_value("PRAGMA foreign_keys")
|
206
|
+
old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
|
258
207
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
208
|
+
begin
|
209
|
+
execute("PRAGMA defer_foreign_keys = ON")
|
210
|
+
execute("PRAGMA foreign_keys = OFF")
|
211
|
+
yield
|
212
|
+
ensure
|
213
|
+
execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
|
214
|
+
execute("PRAGMA foreign_keys = #{old_foreign_keys}")
|
266
215
|
end
|
267
216
|
end
|
268
217
|
|
269
218
|
#--
|
270
219
|
# DATABASE STATEMENTS ======================================
|
271
220
|
#++
|
272
|
-
|
273
221
|
def explain(arel, binds = [])
|
274
222
|
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 }
|
223
|
+
SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
366
224
|
end
|
367
225
|
|
368
226
|
# SCHEMA STATEMENTS ========================================
|
369
227
|
|
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?
|
228
|
+
def primary_keys(table_name) # :nodoc:
|
229
|
+
pks = table_structure(table_name).select { |f| f["pk"] > 0 }
|
230
|
+
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
385
231
|
end
|
386
232
|
|
387
|
-
|
388
|
-
|
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
|
403
|
-
end
|
404
|
-
|
405
|
-
# Returns an array of indexes for the given table.
|
406
|
-
def indexes(table_name, name = nil) #:nodoc:
|
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:
|
233
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
234
|
+
index_name = index_name_for_remove(table_name, options)
|
438
235
|
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
439
236
|
end
|
440
237
|
|
@@ -447,35 +244,34 @@ module ActiveRecord
|
|
447
244
|
rename_table_indexes(table_name, new_name)
|
448
245
|
end
|
449
246
|
|
450
|
-
|
451
|
-
|
452
|
-
def valid_alter_table_type?(type)
|
453
|
-
type.to_sym != :primary_key
|
454
|
-
end
|
455
|
-
|
456
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
457
|
-
if valid_alter_table_type?(type)
|
458
|
-
super(table_name, column_name, type, options)
|
459
|
-
else
|
247
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
248
|
+
if invalid_alter_table_type?(type, options)
|
460
249
|
alter_table(table_name) do |definition|
|
461
|
-
definition.column(column_name, type, options)
|
250
|
+
definition.column(column_name, type, **options)
|
462
251
|
end
|
252
|
+
else
|
253
|
+
super
|
463
254
|
end
|
464
255
|
end
|
465
256
|
|
466
|
-
def remove_column(table_name, column_name, type = nil, options
|
257
|
+
def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
|
467
258
|
alter_table(table_name) do |definition|
|
468
259
|
definition.remove_column column_name
|
260
|
+
definition.foreign_keys.delete_if do |_, fk_options|
|
261
|
+
fk_options[:column] == column_name.to_s
|
262
|
+
end
|
469
263
|
end
|
470
264
|
end
|
471
265
|
|
472
|
-
def change_column_default(table_name, column_name,
|
266
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
267
|
+
default = extract_new_default_value(default_or_changes)
|
268
|
+
|
473
269
|
alter_table(table_name) do |definition|
|
474
270
|
definition[column_name].default = default
|
475
271
|
end
|
476
272
|
end
|
477
273
|
|
478
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
274
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
479
275
|
unless null || default.nil?
|
480
276
|
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
277
|
end
|
@@ -486,81 +282,152 @@ module ActiveRecord
|
|
486
282
|
|
487
283
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
488
284
|
alter_table(table_name) do |definition|
|
489
|
-
include_default = options_include_default?(options)
|
490
285
|
definition[column_name].instance_eval do
|
491
|
-
self.type = type
|
286
|
+
self.type = aliased_types(type.to_s, type)
|
492
287
|
self.limit = options[:limit] if options.include?(:limit)
|
493
|
-
self.default = options[:default] if
|
288
|
+
self.default = options[:default] if options.include?(:default)
|
494
289
|
self.null = options[:null] if options.include?(:null)
|
495
290
|
self.precision = options[:precision] if options.include?(:precision)
|
496
|
-
self.scale
|
291
|
+
self.scale = options[:scale] if options.include?(:scale)
|
292
|
+
self.collation = options[:collation] if options.include?(:collation)
|
293
|
+
self.options.merge!(options)
|
497
294
|
end
|
498
295
|
end
|
499
296
|
end
|
500
297
|
|
501
298
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
502
299
|
column = column_for(table_name, column_name)
|
503
|
-
alter_table(table_name, rename: {column.name => new_column_name.to_s})
|
300
|
+
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
504
301
|
rename_column_indexes(table_name, column.name, new_column_name)
|
505
302
|
end
|
506
303
|
|
507
|
-
|
304
|
+
def add_reference(table_name, ref_name, **options) # :nodoc:
|
305
|
+
super(table_name, ref_name, type: :integer, **options)
|
306
|
+
end
|
307
|
+
alias :add_belongs_to :add_reference
|
308
|
+
|
309
|
+
def foreign_keys(table_name)
|
310
|
+
fk_info = exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
311
|
+
fk_info.map do |row|
|
312
|
+
options = {
|
313
|
+
column: row["from"],
|
314
|
+
primary_key: row["to"],
|
315
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
316
|
+
on_update: extract_foreign_key_action(row["on_update"])
|
317
|
+
}
|
318
|
+
ForeignKeyDefinition.new(table_name, row["table"], options)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def build_insert_sql(insert) # :nodoc:
|
323
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
324
|
+
|
325
|
+
if insert.skip_duplicates?
|
326
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
327
|
+
elsif insert.update_duplicates?
|
328
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
329
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
330
|
+
end
|
331
|
+
|
332
|
+
sql
|
333
|
+
end
|
334
|
+
|
335
|
+
def get_database_version # :nodoc:
|
336
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
337
|
+
end
|
338
|
+
|
339
|
+
def check_version # :nodoc:
|
340
|
+
if database_version < "3.8.0"
|
341
|
+
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
private
|
346
|
+
# See https://www.sqlite.org/limits.html,
|
347
|
+
# the default value is 999 when not configured.
|
348
|
+
def bind_params_length
|
349
|
+
999
|
350
|
+
end
|
508
351
|
|
509
|
-
def initialize_type_map(m)
|
352
|
+
def initialize_type_map(m = type_map)
|
510
353
|
super
|
511
|
-
m
|
512
|
-
register_class_with_limit m, %r(char)i, SQLite3String
|
354
|
+
register_class_with_limit m, %r(int)i, SQLite3Integer
|
513
355
|
end
|
514
356
|
|
515
357
|
def table_structure(table_name)
|
516
|
-
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})",
|
358
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
517
359
|
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
518
|
-
structure
|
360
|
+
table_structure_with_collation(table_name, structure)
|
519
361
|
end
|
362
|
+
alias column_definitions table_structure
|
520
363
|
|
521
|
-
|
364
|
+
# See: https://www.sqlite.org/lang_altertable.html
|
365
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
366
|
+
def invalid_alter_table_type?(type, options)
|
367
|
+
type.to_sym == :primary_key || options[:primary_key] ||
|
368
|
+
options[:null] == false && options[:default].nil?
|
369
|
+
end
|
370
|
+
|
371
|
+
def alter_table(table_name, foreign_keys = foreign_keys(table_name), **options)
|
522
372
|
altered_table_name = "a#{table_name}"
|
523
|
-
|
373
|
+
|
374
|
+
caller = lambda do |definition|
|
375
|
+
rename = options[:rename] || {}
|
376
|
+
foreign_keys.each do |fk|
|
377
|
+
if column = rename[fk.options[:column]]
|
378
|
+
fk.options[:column] = column
|
379
|
+
end
|
380
|
+
to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
381
|
+
definition.foreign_key(to_table, **fk.options)
|
382
|
+
end
|
383
|
+
|
384
|
+
yield definition if block_given?
|
385
|
+
end
|
524
386
|
|
525
387
|
transaction do
|
526
|
-
|
527
|
-
options.merge(:
|
528
|
-
|
388
|
+
disable_referential_integrity do
|
389
|
+
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
390
|
+
move_table(altered_table_name, table_name, &caller)
|
391
|
+
end
|
529
392
|
end
|
530
393
|
end
|
531
394
|
|
532
|
-
def move_table(from, to, options = {}, &block)
|
395
|
+
def move_table(from, to, options = {}, &block)
|
533
396
|
copy_table(from, to, options, &block)
|
534
397
|
drop_table(from)
|
535
398
|
end
|
536
399
|
|
537
|
-
def copy_table(from, to, options = {})
|
400
|
+
def copy_table(from, to, options = {})
|
538
401
|
from_primary_key = primary_key(from)
|
539
402
|
options[:id] = false
|
540
|
-
create_table(to, options) do |definition|
|
403
|
+
create_table(to, **options) do |definition|
|
541
404
|
@definition = definition
|
542
|
-
|
405
|
+
if from_primary_key.is_a?(Array)
|
406
|
+
@definition.primary_keys from_primary_key
|
407
|
+
end
|
543
408
|
columns(from).each do |column|
|
544
409
|
column_name = options[:rename] ?
|
545
410
|
(options[:rename][column.name] ||
|
546
411
|
options[:rename][column.name.to_sym] ||
|
547
412
|
column.name) : column.name
|
548
|
-
next if column_name == from_primary_key
|
549
413
|
|
550
414
|
@definition.column(column_name, column.type,
|
551
|
-
:
|
552
|
-
:
|
553
|
-
:null
|
415
|
+
limit: column.limit, default: column.default,
|
416
|
+
precision: column.precision, scale: column.scale,
|
417
|
+
null: column.null, collation: column.collation,
|
418
|
+
primary_key: column_name == from_primary_key
|
419
|
+
)
|
554
420
|
end
|
421
|
+
|
555
422
|
yield @definition if block_given?
|
556
423
|
end
|
557
424
|
copy_table_indexes(from, to, options[:rename] || {})
|
558
425
|
copy_table_contents(from, to,
|
559
|
-
@definition.columns.map
|
426
|
+
@definition.columns.map(&:name),
|
560
427
|
options[:rename] || {})
|
561
428
|
end
|
562
429
|
|
563
|
-
def copy_table_indexes(from, to, rename = {})
|
430
|
+
def copy_table_indexes(from, to, rename = {})
|
564
431
|
indexes(from).each do |index|
|
565
432
|
name = index.name
|
566
433
|
if to == "a#{from}"
|
@@ -569,60 +436,128 @@ module ActiveRecord
|
|
569
436
|
name = name[1..-1]
|
570
437
|
end
|
571
438
|
|
572
|
-
|
573
|
-
|
574
|
-
to_column_names.
|
439
|
+
columns = index.columns
|
440
|
+
if columns.is_a?(Array)
|
441
|
+
to_column_names = columns(to).map(&:name)
|
442
|
+
columns = columns.map { |c| rename[c] || c }.select do |column|
|
443
|
+
to_column_names.include?(column)
|
444
|
+
end
|
575
445
|
end
|
576
446
|
|
577
447
|
unless columns.empty?
|
578
448
|
# index name can't be the same
|
579
449
|
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
580
450
|
opts[:unique] = true if index.unique
|
451
|
+
opts[:where] = index.where if index.where
|
581
452
|
add_index(to, columns, opts)
|
582
453
|
end
|
583
454
|
end
|
584
455
|
end
|
585
456
|
|
586
|
-
def copy_table_contents(from, to, columns, rename = {})
|
587
|
-
column_mappings = Hash[columns.map {|name| [name, name]}]
|
457
|
+
def copy_table_contents(from, to, columns, rename = {})
|
458
|
+
column_mappings = Hash[columns.map { |name| [name, name] }]
|
588
459
|
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] }]
|
460
|
+
from_columns = columns(from).collect(&:name)
|
461
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
462
|
+
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
463
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
464
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
596
465
|
|
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(*)'))
|
466
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
467
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
612
468
|
end
|
613
469
|
|
614
|
-
def translate_exception(exception, message)
|
470
|
+
def translate_exception(exception, message:, sql:, binds:)
|
615
471
|
case exception.message
|
616
472
|
# SQLite 3.8.2 returns a newly formatted error message:
|
617
473
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
618
474
|
# Older versions of SQLite return:
|
619
475
|
# column *column_name* is not unique
|
620
476
|
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
621
|
-
RecordNotUnique.new(message,
|
477
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
478
|
+
when /.* may not be NULL/, /NOT NULL constraint failed: .*/
|
479
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
480
|
+
when /FOREIGN KEY constraint failed/i
|
481
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
622
482
|
else
|
623
483
|
super
|
624
484
|
end
|
625
485
|
end
|
486
|
+
|
487
|
+
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
488
|
+
|
489
|
+
def table_structure_with_collation(table_name, basic_structure)
|
490
|
+
collation_hash = {}
|
491
|
+
sql = <<~SQL
|
492
|
+
SELECT sql FROM
|
493
|
+
(SELECT * FROM sqlite_master UNION ALL
|
494
|
+
SELECT * FROM sqlite_temp_master)
|
495
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
496
|
+
SQL
|
497
|
+
|
498
|
+
# Result will have following sample string
|
499
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
500
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
501
|
+
result = exec_query(sql, "SCHEMA").first
|
502
|
+
|
503
|
+
if result
|
504
|
+
# Splitting with left parentheses and discarding the first part will return all
|
505
|
+
# columns separated with comma(,).
|
506
|
+
columns_string = result["sql"].split("(", 2).last
|
507
|
+
|
508
|
+
columns_string.split(",").each do |column_string|
|
509
|
+
# This regex will match the column name and collation type and will save
|
510
|
+
# the value in $1 and $2 respectively.
|
511
|
+
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
512
|
+
end
|
513
|
+
|
514
|
+
basic_structure.map! do |column|
|
515
|
+
column_name = column["name"]
|
516
|
+
|
517
|
+
if collation_hash.has_key? column_name
|
518
|
+
column["collation"] = collation_hash[column_name]
|
519
|
+
end
|
520
|
+
|
521
|
+
column
|
522
|
+
end
|
523
|
+
else
|
524
|
+
basic_structure.to_a
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def arel_visitor
|
529
|
+
Arel::Visitors::SQLite.new(self)
|
530
|
+
end
|
531
|
+
|
532
|
+
def build_statement_pool
|
533
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
534
|
+
end
|
535
|
+
|
536
|
+
def connect
|
537
|
+
@connection = ::SQLite3::Database.new(
|
538
|
+
@config[:database].to_s,
|
539
|
+
@config.merge(results_as_hash: true)
|
540
|
+
)
|
541
|
+
configure_connection
|
542
|
+
end
|
543
|
+
|
544
|
+
def configure_connection
|
545
|
+
@connection.busy_timeout(self.class.type_cast_config_to_integer(@config[:timeout])) if @config[:timeout]
|
546
|
+
|
547
|
+
execute("PRAGMA foreign_keys = ON", "SCHEMA")
|
548
|
+
end
|
549
|
+
|
550
|
+
class SQLite3Integer < Type::Integer # :nodoc:
|
551
|
+
private
|
552
|
+
def _limit
|
553
|
+
# INTEGER storage class can be stored 8 bytes value.
|
554
|
+
# See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
555
|
+
limit || 8
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
|
626
560
|
end
|
561
|
+
ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
|
627
562
|
end
|
628
563
|
end
|