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