activerecord 4.2.11.1 → 6.0.3.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +721 -1522
- 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 +266 -251
- data/lib/active_record/association_relation.rb +20 -13
- data/lib/active_record/associations/alias_tracker.rb +29 -36
- data/lib/active_record/associations/association.rb +128 -57
- data/lib/active_record/associations/association_scope.rb +103 -132
- data/lib/active_record/associations/belongs_to_association.rb +65 -60
- 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 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
- 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 +136 -288
- data/lib/active_record/associations/collection_proxy.rb +241 -147
- data/lib/active_record/associations/foreign_association.rb +10 -1
- data/lib/active_record/associations/has_many_association.rb +34 -98
- data/lib/active_record/associations/has_many_through_association.rb +60 -87
- data/lib/active_record/associations/has_one_association.rb +61 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
- 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 +149 -166
- data/lib/active_record/associations/preloader/association.rb +90 -123
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +90 -93
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1737 -1597
- 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 +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +174 -144
- 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 +57 -37
- data/lib/active_record/attribute_methods/write.rb +32 -55
- data/lib/active_record/attribute_methods.rb +120 -135
- data/lib/active_record/attributes.rb +213 -82
- data/lib/active_record/autosave_association.rb +97 -41
- data/lib/active_record/base.rb +57 -45
- 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 +23 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
- data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
- data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
- 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 +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -181
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/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 +51 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +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 +462 -296
- 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 +558 -356
- 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 -349
- 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 +252 -230
- data/lib/active_record/counter_cache.rb +70 -49
- 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 +163 -86
- data/lib/active_record/errors.rb +188 -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 +10 -5
- 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 +227 -501
- 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 +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +86 -96
- 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 +623 -305
- data/lib/active_record/model_schema.rb +313 -112
- data/lib/active_record/nested_attributes.rb +263 -223
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +557 -126
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +44 -30
- data/lib/active_record/railtie.rb +143 -44
- 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 +331 -185
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +430 -281
- 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 +268 -254
- data/lib/active_record/relation/delegation.rb +75 -84
- data/lib/active_record/relation/finder_methods.rb +285 -241
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +78 -88
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
- 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 +110 -119
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +603 -397
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -14
- 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 +530 -341
- 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 -17
- data/lib/active_record/scoping/default.rb +98 -83
- data/lib/active_record/scoping/named.rb +86 -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 +307 -100
- data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
- 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 +225 -0
- data/lib/active_record/timestamp.rb +86 -41
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +223 -157
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +23 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +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 +42 -55
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +42 -22
- 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 -2
- 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/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +168 -59
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,209 +1,87 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/connection_adapters/abstract_adapter"
|
4
|
+
require "active_record/connection_adapters/statement_pool"
|
5
|
+
require "active_record/connection_adapters/mysql/column"
|
6
|
+
require "active_record/connection_adapters/mysql/explain_pretty_printer"
|
7
|
+
require "active_record/connection_adapters/mysql/quoting"
|
8
|
+
require "active_record/connection_adapters/mysql/schema_creation"
|
9
|
+
require "active_record/connection_adapters/mysql/schema_definitions"
|
10
|
+
require "active_record/connection_adapters/mysql/schema_dumper"
|
11
|
+
require "active_record/connection_adapters/mysql/schema_statements"
|
12
|
+
require "active_record/connection_adapters/mysql/type_metadata"
|
3
13
|
|
4
14
|
module ActiveRecord
|
5
15
|
module ConnectionAdapters
|
6
16
|
class AbstractMysqlAdapter < AbstractAdapter
|
7
|
-
include
|
8
|
-
|
9
|
-
class SchemaCreation < AbstractAdapter::SchemaCreation
|
10
|
-
def visit_AddColumn(o)
|
11
|
-
add_column_position!(super, column_options(o))
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def visit_DropForeignKey(name)
|
17
|
-
"DROP FOREIGN KEY #{name}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def visit_TableDefinition(o)
|
21
|
-
name = o.name
|
22
|
-
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
|
23
|
-
|
24
|
-
statements = o.columns.map { |c| accept c }
|
25
|
-
statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
|
26
|
-
|
27
|
-
create_sql << "(#{statements.join(', ')}) " if statements.present?
|
28
|
-
create_sql << "#{o.options}"
|
29
|
-
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
30
|
-
create_sql
|
31
|
-
end
|
32
|
-
|
33
|
-
def visit_ChangeColumnDefinition(o)
|
34
|
-
column = o.column
|
35
|
-
options = o.options
|
36
|
-
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
|
37
|
-
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
|
38
|
-
add_column_options!(change_column_sql, options.merge(column: column))
|
39
|
-
add_column_position!(change_column_sql, options)
|
40
|
-
end
|
41
|
-
|
42
|
-
def add_column_position!(sql, options)
|
43
|
-
if options[:first]
|
44
|
-
sql << " FIRST"
|
45
|
-
elsif options[:after]
|
46
|
-
sql << " AFTER #{quote_column_name(options[:after])}"
|
47
|
-
end
|
48
|
-
sql
|
49
|
-
end
|
50
|
-
|
51
|
-
def index_in_create(table_name, column_name, options)
|
52
|
-
index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
|
53
|
-
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def schema_creation
|
58
|
-
SchemaCreation.new self
|
59
|
-
end
|
60
|
-
|
61
|
-
def prepare_column_options(column, types) # :nodoc:
|
62
|
-
spec = super
|
63
|
-
spec.delete(:limit) if :boolean === column.type
|
64
|
-
spec
|
65
|
-
end
|
66
|
-
|
67
|
-
class Column < ConnectionAdapters::Column # :nodoc:
|
68
|
-
attr_reader :collation, :strict, :extra
|
69
|
-
|
70
|
-
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
71
|
-
@strict = strict
|
72
|
-
@collation = collation
|
73
|
-
@extra = extra
|
74
|
-
super(name, default, cast_type, sql_type, null)
|
75
|
-
assert_valid_default(default)
|
76
|
-
extract_default
|
77
|
-
end
|
78
|
-
|
79
|
-
def extract_default
|
80
|
-
if blob_or_text_column?
|
81
|
-
@default = null || strict ? nil : ''
|
82
|
-
elsif missing_default_forged_as_empty_string?(@default)
|
83
|
-
@default = nil
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def has_default?
|
88
|
-
return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
|
89
|
-
super
|
90
|
-
end
|
91
|
-
|
92
|
-
def blob_or_text_column?
|
93
|
-
sql_type =~ /blob/i || type == :text
|
94
|
-
end
|
95
|
-
|
96
|
-
def case_sensitive?
|
97
|
-
collation && !collation.match(/_ci$/)
|
98
|
-
end
|
99
|
-
|
100
|
-
def ==(other)
|
101
|
-
super &&
|
102
|
-
collation == other.collation &&
|
103
|
-
strict == other.strict &&
|
104
|
-
extra == other.extra
|
105
|
-
end
|
106
|
-
|
107
|
-
private
|
108
|
-
|
109
|
-
# MySQL misreports NOT NULL column default when none is given.
|
110
|
-
# We can't detect this for columns which may have a legitimate ''
|
111
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
112
|
-
# and the rest).
|
113
|
-
#
|
114
|
-
# Test whether the column has default '', is not null, and is not
|
115
|
-
# a type allowing default ''.
|
116
|
-
def missing_default_forged_as_empty_string?(default)
|
117
|
-
type != :string && !null && default == ''
|
118
|
-
end
|
119
|
-
|
120
|
-
def assert_valid_default(default)
|
121
|
-
if blob_or_text_column? && default.present?
|
122
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def attributes_for_hash
|
127
|
-
super + [collation, strict, extra]
|
128
|
-
end
|
129
|
-
end
|
17
|
+
include MySQL::Quoting
|
18
|
+
include MySQL::SchemaStatements
|
130
19
|
|
131
20
|
##
|
132
21
|
# :singleton-method:
|
133
|
-
# By default, the
|
134
|
-
# as boolean. If you wish to disable this emulation
|
135
|
-
# behavior in versions 0.13.1 and earlier) you can add the following line
|
22
|
+
# By default, the Mysql2Adapter will consider all columns of type <tt>tinyint(1)</tt>
|
23
|
+
# as boolean. If you wish to disable this emulation you can add the following line
|
136
24
|
# to your application.rb file:
|
137
25
|
#
|
138
|
-
# ActiveRecord::ConnectionAdapters::
|
139
|
-
class_attribute :emulate_booleans
|
140
|
-
self.emulate_booleans = true
|
141
|
-
|
142
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
143
|
-
"Server shutdown in progress",
|
144
|
-
"Broken pipe",
|
145
|
-
"Lost connection to MySQL server during query",
|
146
|
-
"MySQL server has gone away" ]
|
147
|
-
|
148
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
26
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
27
|
+
class_attribute :emulate_booleans, default: true
|
149
28
|
|
150
29
|
NATIVE_DATABASE_TYPES = {
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
156
|
-
:
|
157
|
-
:
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
30
|
+
primary_key: "bigint auto_increment PRIMARY KEY",
|
31
|
+
string: { name: "varchar", limit: 255 },
|
32
|
+
text: { name: "text" },
|
33
|
+
integer: { name: "int", limit: 4 },
|
34
|
+
float: { name: "float", limit: 24 },
|
35
|
+
decimal: { name: "decimal" },
|
36
|
+
datetime: { name: "datetime" },
|
37
|
+
timestamp: { name: "timestamp" },
|
38
|
+
time: { name: "time" },
|
39
|
+
date: { name: "date" },
|
40
|
+
binary: { name: "blob" },
|
41
|
+
blob: { name: "blob" },
|
42
|
+
boolean: { name: "tinyint", limit: 1 },
|
43
|
+
json: { name: "json" },
|
162
44
|
}
|
163
45
|
|
164
|
-
|
165
|
-
|
46
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
47
|
+
private
|
48
|
+
def dealloc(stmt)
|
49
|
+
stmt.close
|
50
|
+
end
|
51
|
+
end
|
166
52
|
|
167
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
168
53
|
def initialize(connection, logger, connection_options, config)
|
169
|
-
super(connection, logger)
|
170
|
-
|
171
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
54
|
+
super(connection, logger, config)
|
55
|
+
end
|
172
56
|
|
173
|
-
|
57
|
+
def get_database_version #:nodoc:
|
58
|
+
full_version_string = get_full_version
|
59
|
+
version_string = version_string(full_version_string)
|
60
|
+
Version.new(version_string, full_version_string)
|
61
|
+
end
|
174
62
|
|
175
|
-
|
176
|
-
|
177
|
-
else
|
178
|
-
@prepared_statements = false
|
179
|
-
end
|
63
|
+
def mariadb? # :nodoc:
|
64
|
+
/mariadb/i.match?(full_version)
|
180
65
|
end
|
181
66
|
|
182
|
-
|
183
|
-
def supports_migrations?
|
67
|
+
def supports_bulk_alter?
|
184
68
|
true
|
185
69
|
end
|
186
70
|
|
187
|
-
def
|
188
|
-
|
71
|
+
def supports_index_sort_order?
|
72
|
+
!mariadb? && database_version >= "8.0.1"
|
189
73
|
end
|
190
74
|
|
191
|
-
def
|
192
|
-
|
75
|
+
def supports_expression_index?
|
76
|
+
!mariadb? && database_version >= "8.0.13"
|
193
77
|
end
|
194
78
|
|
195
|
-
|
196
|
-
# but at the moment (5.5) it doesn't yet implement them
|
197
|
-
def supports_index_sort_order?
|
79
|
+
def supports_transaction_isolation?
|
198
80
|
true
|
199
81
|
end
|
200
82
|
|
201
|
-
|
202
|
-
|
203
|
-
#
|
204
|
-
# http://bugs.mysql.com/bug.php?id=39170
|
205
|
-
def supports_transaction_isolation?
|
206
|
-
version >= '5.0.0'
|
83
|
+
def supports_explain?
|
84
|
+
true
|
207
85
|
end
|
208
86
|
|
209
87
|
def supports_indexes_in_create?
|
@@ -215,85 +93,76 @@ module ActiveRecord
|
|
215
93
|
end
|
216
94
|
|
217
95
|
def supports_views?
|
218
|
-
|
96
|
+
true
|
219
97
|
end
|
220
98
|
|
221
99
|
def supports_datetime_with_precision?
|
222
|
-
|
100
|
+
mariadb? || database_version >= "5.6.4"
|
223
101
|
end
|
224
102
|
|
225
|
-
def
|
226
|
-
|
103
|
+
def supports_virtual_columns?
|
104
|
+
mariadb? || database_version >= "5.7.5"
|
227
105
|
end
|
228
106
|
|
229
|
-
|
230
|
-
|
107
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
108
|
+
def supports_optimizer_hints?
|
109
|
+
!mariadb? && database_version >= "5.7.7"
|
231
110
|
end
|
232
111
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
112
|
+
def supports_common_table_expressions?
|
113
|
+
if mariadb?
|
114
|
+
database_version >= "10.2.1"
|
115
|
+
else
|
116
|
+
database_version >= "8.0.1"
|
117
|
+
end
|
239
118
|
end
|
240
119
|
|
241
|
-
def
|
242
|
-
|
120
|
+
def supports_advisory_locks?
|
121
|
+
true
|
243
122
|
end
|
244
123
|
|
245
|
-
|
246
|
-
|
247
|
-
def error_number(exception) # :nodoc:
|
248
|
-
raise NotImplementedError
|
124
|
+
def supports_insert_on_duplicate_skip?
|
125
|
+
true
|
249
126
|
end
|
250
127
|
|
251
|
-
|
252
|
-
|
253
|
-
def _quote(value) # :nodoc:
|
254
|
-
if value.is_a?(Type::Binary::Data)
|
255
|
-
"x'#{value.hex}'"
|
256
|
-
else
|
257
|
-
super
|
258
|
-
end
|
128
|
+
def supports_insert_on_duplicate_update?
|
129
|
+
true
|
259
130
|
end
|
260
131
|
|
261
|
-
def
|
262
|
-
|
132
|
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
133
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
263
134
|
end
|
264
135
|
|
265
|
-
def
|
266
|
-
|
136
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
137
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
267
138
|
end
|
268
139
|
|
269
|
-
def
|
270
|
-
|
140
|
+
def native_database_types
|
141
|
+
NATIVE_DATABASE_TYPES
|
271
142
|
end
|
272
143
|
|
273
|
-
def
|
274
|
-
|
144
|
+
def index_algorithms
|
145
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
275
146
|
end
|
276
147
|
|
277
|
-
|
278
|
-
QUOTED_FALSE
|
279
|
-
end
|
148
|
+
# HELPER METHODS ===========================================
|
280
149
|
|
281
|
-
|
282
|
-
|
150
|
+
# The two drivers have slightly different ways of yielding hashes of results, so
|
151
|
+
# this method must be implemented to provide a uniform interface.
|
152
|
+
def each_hash(result) # :nodoc:
|
153
|
+
raise NotImplementedError
|
283
154
|
end
|
284
155
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
super
|
290
|
-
end
|
156
|
+
# Must return the MySQL error number from the exception, if the exception has an
|
157
|
+
# error number.
|
158
|
+
def error_number(exception) # :nodoc:
|
159
|
+
raise NotImplementedError
|
291
160
|
end
|
292
161
|
|
293
162
|
# REFERENTIAL INTEGRITY ====================================
|
294
163
|
|
295
164
|
def disable_referential_integrity #:nodoc:
|
296
|
-
old =
|
165
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
297
166
|
|
298
167
|
begin
|
299
168
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -303,30 +172,42 @@ module ActiveRecord
|
|
303
172
|
end
|
304
173
|
end
|
305
174
|
|
175
|
+
# CONNECTION MANAGEMENT ====================================
|
176
|
+
|
177
|
+
def clear_cache! # :nodoc:
|
178
|
+
reload_type_map
|
179
|
+
super
|
180
|
+
end
|
181
|
+
|
306
182
|
#--
|
307
183
|
# DATABASE STATEMENTS ======================================
|
308
184
|
#++
|
309
185
|
|
310
|
-
def
|
311
|
-
|
312
|
-
|
186
|
+
def explain(arel, binds = [])
|
187
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
188
|
+
start = Concurrent.monotonic_time
|
189
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
190
|
+
elapsed = Concurrent.monotonic_time - start
|
191
|
+
|
192
|
+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
313
193
|
end
|
314
194
|
|
315
195
|
# Executes the SQL statement in the context of this connection.
|
316
196
|
def execute(sql, name = nil)
|
317
|
-
|
318
|
-
end
|
197
|
+
materialize_transactions
|
319
198
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
199
|
+
log(sql, name) do
|
200
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
201
|
+
@connection.query(sql)
|
202
|
+
end
|
203
|
+
end
|
325
204
|
end
|
326
205
|
|
327
|
-
|
328
|
-
|
329
|
-
|
206
|
+
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
207
|
+
# to write stuff in an abstract way without concerning ourselves about whether it
|
208
|
+
# needs to be explicitly freed or not.
|
209
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
210
|
+
yield execute(sql, name)
|
330
211
|
end
|
331
212
|
|
332
213
|
def begin_db_transaction
|
@@ -346,19 +227,7 @@ module ActiveRecord
|
|
346
227
|
execute "ROLLBACK"
|
347
228
|
end
|
348
229
|
|
349
|
-
|
350
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
351
|
-
# these, we must use a subquery.
|
352
|
-
def join_to_update(update, select) #:nodoc:
|
353
|
-
if select.limit || select.offset || select.orders.any?
|
354
|
-
super
|
355
|
-
else
|
356
|
-
update.table select.source
|
357
|
-
update.wheres = select.constraints
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
def empty_insert_statement_value
|
230
|
+
def empty_insert_statement_value(primary_key = nil)
|
362
231
|
"VALUES ()"
|
363
232
|
end
|
364
233
|
|
@@ -374,7 +243,7 @@ module ActiveRecord
|
|
374
243
|
end
|
375
244
|
|
376
245
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
377
|
-
# Charset defaults to
|
246
|
+
# Charset defaults to utf8mb4.
|
378
247
|
#
|
379
248
|
# Example:
|
380
249
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -382,9 +251,13 @@ module ActiveRecord
|
|
382
251
|
# create_database 'matt_development', charset: :big5
|
383
252
|
def create_database(name, options = {})
|
384
253
|
if options[:collation]
|
385
|
-
execute "CREATE DATABASE
|
254
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
255
|
+
elsif options[:charset]
|
256
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
257
|
+
elsif row_format_dynamic_by_default?
|
258
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
386
259
|
else
|
387
|
-
|
260
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
388
261
|
end
|
389
262
|
end
|
390
263
|
|
@@ -393,108 +266,38 @@ module ActiveRecord
|
|
393
266
|
# Example:
|
394
267
|
# drop_database('sebastian_development')
|
395
268
|
def drop_database(name) #:nodoc:
|
396
|
-
execute "DROP DATABASE IF EXISTS
|
269
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
397
270
|
end
|
398
271
|
|
399
272
|
def current_database
|
400
|
-
|
273
|
+
query_value("SELECT database()", "SCHEMA")
|
401
274
|
end
|
402
275
|
|
403
276
|
# Returns the database character set.
|
404
277
|
def charset
|
405
|
-
show_variable
|
278
|
+
show_variable "character_set_database"
|
406
279
|
end
|
407
280
|
|
408
281
|
# Returns the database collation strategy.
|
409
282
|
def collation
|
410
|
-
show_variable
|
283
|
+
show_variable "collation_database"
|
411
284
|
end
|
412
285
|
|
413
|
-
def
|
414
|
-
|
415
|
-
sql << "IN #{quote_table_name(database)} " if database
|
416
|
-
sql << "LIKE #{quote(like)}" if like
|
286
|
+
def table_comment(table_name) # :nodoc:
|
287
|
+
scope = quoted_scope(table_name)
|
417
288
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
def truncate(table_name, name = nil)
|
425
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
426
|
-
end
|
427
|
-
|
428
|
-
def table_exists?(name)
|
429
|
-
return false unless name.present?
|
430
|
-
return true if tables(nil, nil, name).any?
|
431
|
-
|
432
|
-
name = name.to_s
|
433
|
-
schema, table = name.split('.', 2)
|
434
|
-
|
435
|
-
unless table # A table was provided without a schema
|
436
|
-
table = schema
|
437
|
-
schema = nil
|
438
|
-
end
|
439
|
-
|
440
|
-
tables(nil, schema, table).any?
|
441
|
-
end
|
442
|
-
alias data_source_exists? table_exists?
|
443
|
-
|
444
|
-
# Returns an array of indexes for the given table.
|
445
|
-
def indexes(table_name, name = nil) #:nodoc:
|
446
|
-
indexes = []
|
447
|
-
current_index = nil
|
448
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
449
|
-
each_hash(result) do |row|
|
450
|
-
if current_index != row[:Key_name]
|
451
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
452
|
-
current_index = row[:Key_name]
|
453
|
-
|
454
|
-
mysql_index_type = row[:Index_type].downcase.to_sym
|
455
|
-
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
456
|
-
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
457
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
|
458
|
-
end
|
459
|
-
|
460
|
-
indexes.last.columns << row[:Column_name]
|
461
|
-
indexes.last.lengths << row[:Sub_part]
|
462
|
-
end
|
463
|
-
end
|
464
|
-
|
465
|
-
indexes
|
466
|
-
end
|
467
|
-
|
468
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
469
|
-
def columns(table_name)#:nodoc:
|
470
|
-
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
471
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
472
|
-
each_hash(result).map do |field|
|
473
|
-
field_name = set_field_encoding(field[:Field])
|
474
|
-
sql_type = field[:Type]
|
475
|
-
cast_type = lookup_cast_type(sql_type)
|
476
|
-
new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
|
477
|
-
end
|
478
|
-
end
|
289
|
+
query_value(<<~SQL, "SCHEMA").presence
|
290
|
+
SELECT table_comment
|
291
|
+
FROM information_schema.tables
|
292
|
+
WHERE table_schema = #{scope[:schema]}
|
293
|
+
AND table_name = #{scope[:name]}
|
294
|
+
SQL
|
479
295
|
end
|
480
296
|
|
481
|
-
def
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
def bulk_change_table(table_name, operations) #:nodoc:
|
486
|
-
sqls = operations.flat_map do |command, args|
|
487
|
-
table, arguments = args.shift, args
|
488
|
-
method = :"#{command}_sql"
|
489
|
-
|
490
|
-
if respond_to?(method, true)
|
491
|
-
send(method, table, *arguments)
|
492
|
-
else
|
493
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
494
|
-
end
|
495
|
-
end.join(", ")
|
496
|
-
|
497
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
297
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
298
|
+
comment = extract_new_comment_value(comment_or_changes)
|
299
|
+
comment = "" if comment.nil?
|
300
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
498
301
|
end
|
499
302
|
|
500
303
|
# Renames a table.
|
@@ -506,8 +309,23 @@ module ActiveRecord
|
|
506
309
|
rename_table_indexes(table_name, new_name)
|
507
310
|
end
|
508
311
|
|
312
|
+
# Drops a table from the database.
|
313
|
+
#
|
314
|
+
# [<tt>:force</tt>]
|
315
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
316
|
+
# Defaults to false.
|
317
|
+
# [<tt>:if_exists</tt>]
|
318
|
+
# Set to +true+ to only drop the table if it exists.
|
319
|
+
# Defaults to false.
|
320
|
+
# [<tt>:temporary</tt>]
|
321
|
+
# Set to +true+ to drop temporary table.
|
322
|
+
# Defaults to false.
|
323
|
+
#
|
324
|
+
# Although this command ignores most +options+ and the block if one is given,
|
325
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
326
|
+
# In that case, +options+ and the block will be used by create_table.
|
509
327
|
def drop_table(table_name, options = {})
|
510
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
328
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
511
329
|
end
|
512
330
|
|
513
331
|
def rename_index(table_name, old_name, new_name)
|
@@ -520,150 +338,154 @@ module ActiveRecord
|
|
520
338
|
end
|
521
339
|
end
|
522
340
|
|
523
|
-
def change_column_default(table_name, column_name,
|
524
|
-
|
525
|
-
change_column table_name, column_name,
|
341
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
342
|
+
default = extract_new_default_value(default_or_changes)
|
343
|
+
change_column table_name, column_name, nil, default: default
|
526
344
|
end
|
527
345
|
|
528
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
529
|
-
column = column_for(table_name, column_name)
|
530
|
-
|
346
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
531
347
|
unless null || default.nil?
|
532
348
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
533
349
|
end
|
534
350
|
|
535
|
-
change_column table_name, column_name,
|
351
|
+
change_column table_name, column_name, nil, null: null
|
352
|
+
end
|
353
|
+
|
354
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
355
|
+
comment = extract_new_comment_value(comment_or_changes)
|
356
|
+
change_column table_name, column_name, nil, comment: comment
|
536
357
|
end
|
537
358
|
|
538
359
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
539
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
360
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
540
361
|
end
|
541
362
|
|
542
363
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
543
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
364
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
544
365
|
rename_column_indexes(table_name, column_name, new_column_name)
|
545
366
|
end
|
546
367
|
|
547
368
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
548
|
-
index_name, index_type, index_columns,
|
549
|
-
|
369
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, **options)
|
370
|
+
sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
|
371
|
+
execute add_sql_comment!(sql, comment)
|
372
|
+
end
|
373
|
+
|
374
|
+
def add_sql_comment!(sql, comment) # :nodoc:
|
375
|
+
sql << " COMMENT #{quote(comment)}" if comment.present?
|
376
|
+
sql
|
550
377
|
end
|
551
378
|
|
552
379
|
def foreign_keys(table_name)
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
380
|
+
raise ArgumentError unless table_name.present?
|
381
|
+
|
382
|
+
scope = quoted_scope(table_name)
|
383
|
+
|
384
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
385
|
+
SELECT fk.referenced_table_name AS 'to_table',
|
386
|
+
fk.referenced_column_name AS 'primary_key',
|
387
|
+
fk.column_name AS 'column',
|
388
|
+
fk.constraint_name AS 'name',
|
389
|
+
rc.update_rule AS 'on_update',
|
390
|
+
rc.delete_rule AS 'on_delete'
|
391
|
+
FROM information_schema.referential_constraints rc
|
392
|
+
JOIN information_schema.key_column_usage fk
|
393
|
+
USING (constraint_schema, constraint_name)
|
394
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
395
|
+
AND fk.table_schema = #{scope[:schema]}
|
396
|
+
AND fk.table_name = #{scope[:name]}
|
397
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
398
|
+
AND rc.table_name = #{scope[:name]}
|
562
399
|
SQL
|
563
400
|
|
564
|
-
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
565
|
-
|
566
401
|
fk_info.map do |row|
|
567
402
|
options = {
|
568
|
-
column: row[
|
569
|
-
name: row[
|
570
|
-
primary_key: row[
|
403
|
+
column: row["column"],
|
404
|
+
name: row["name"],
|
405
|
+
primary_key: row["primary_key"]
|
571
406
|
}
|
572
407
|
|
573
|
-
options[:on_update] = extract_foreign_key_action(
|
574
|
-
options[:on_delete] = extract_foreign_key_action(
|
408
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
409
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
575
410
|
|
576
|
-
ForeignKeyDefinition.new(table_name, row[
|
411
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
577
412
|
end
|
578
413
|
end
|
579
414
|
|
580
|
-
|
581
|
-
|
582
|
-
case type.to_s
|
583
|
-
when 'binary'
|
584
|
-
case limit
|
585
|
-
when 0..0xfff; "varbinary(#{limit})"
|
586
|
-
when nil; "blob"
|
587
|
-
when 0x1000..0xffffffff; "blob(#{limit})"
|
588
|
-
else raise(ActiveRecordError, "No binary type has character length #{limit}")
|
589
|
-
end
|
590
|
-
when 'integer'
|
591
|
-
case limit
|
592
|
-
when 1; 'tinyint'
|
593
|
-
when 2; 'smallint'
|
594
|
-
when 3; 'mediumint'
|
595
|
-
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
596
|
-
when 5..8; 'bigint'
|
597
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
598
|
-
end
|
599
|
-
when 'text'
|
600
|
-
case limit
|
601
|
-
when 0..0xff; 'tinytext'
|
602
|
-
when nil, 0x100..0xffff; 'text'
|
603
|
-
when 0x10000..0xffffff; 'mediumtext'
|
604
|
-
when 0x1000000..0xffffffff; 'longtext'
|
605
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
606
|
-
end
|
607
|
-
when 'datetime'
|
608
|
-
return super unless precision
|
415
|
+
def table_options(table_name) # :nodoc:
|
416
|
+
table_options = {}
|
609
417
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
418
|
+
create_table_info = create_table_info(table_name)
|
419
|
+
|
420
|
+
# strip create_definitions and partition_options
|
421
|
+
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
422
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
423
|
+
|
424
|
+
# strip AUTO_INCREMENT
|
425
|
+
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
426
|
+
|
427
|
+
table_options[:options] = raw_table_options unless raw_table_options.blank?
|
428
|
+
|
429
|
+
# strip COMMENT
|
430
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
431
|
+
table_options[:comment] = table_comment(table_name)
|
616
432
|
end
|
433
|
+
|
434
|
+
table_options
|
617
435
|
end
|
618
436
|
|
619
437
|
# SHOW VARIABLES LIKE 'name'
|
620
438
|
def show_variable(name)
|
621
|
-
|
622
|
-
variables.first['Value'] unless variables.empty?
|
439
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
623
440
|
rescue ActiveRecord::StatementInvalid
|
624
441
|
nil
|
625
442
|
end
|
626
443
|
|
627
|
-
|
628
|
-
|
629
|
-
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
630
|
-
create_table = each_hash(result).first[:"Create Table"]
|
631
|
-
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
632
|
-
keys = $1.split(",").map { |key| key.delete('`"') }
|
633
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
634
|
-
else
|
635
|
-
nil
|
636
|
-
end
|
637
|
-
end
|
638
|
-
end
|
444
|
+
def primary_keys(table_name) # :nodoc:
|
445
|
+
raise ArgumentError unless table_name.present?
|
639
446
|
|
640
|
-
|
641
|
-
def primary_key(table)
|
642
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
643
|
-
pk_and_sequence && pk_and_sequence.first
|
644
|
-
end
|
447
|
+
scope = quoted_scope(table_name)
|
645
448
|
|
646
|
-
|
647
|
-
|
648
|
-
|
449
|
+
query_values(<<~SQL, "SCHEMA")
|
450
|
+
SELECT column_name
|
451
|
+
FROM information_schema.statistics
|
452
|
+
WHERE index_name = 'PRIMARY'
|
453
|
+
AND table_schema = #{scope[:schema]}
|
454
|
+
AND table_name = #{scope[:name]}
|
455
|
+
ORDER BY seq_in_index
|
456
|
+
SQL
|
649
457
|
end
|
650
458
|
|
651
|
-
def
|
652
|
-
|
653
|
-
|
459
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
460
|
+
column = column_for_attribute(attribute)
|
461
|
+
|
462
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
463
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
464
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
465
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
466
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
467
|
+
MSG
|
468
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
654
469
|
else
|
655
470
|
super
|
656
471
|
end
|
657
472
|
end
|
658
473
|
|
659
|
-
def
|
660
|
-
|
661
|
-
|
474
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
475
|
+
column = column_for_attribute(attribute)
|
476
|
+
|
477
|
+
if column.collation && !column.case_sensitive?
|
478
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
662
479
|
else
|
663
|
-
|
480
|
+
super
|
664
481
|
end
|
665
482
|
end
|
666
483
|
|
484
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
485
|
+
column.case_sensitive?
|
486
|
+
end
|
487
|
+
private :can_perform_case_insensitive_comparison_for?
|
488
|
+
|
667
489
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
668
490
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
669
491
|
# distinct queries, and requires that the ORDER BY include the distinct column.
|
@@ -671,276 +493,322 @@ module ActiveRecord
|
|
671
493
|
def columns_for_distinct(columns, orders) # :nodoc:
|
672
494
|
order_columns = orders.reject(&:blank?).map { |s|
|
673
495
|
# Convert Arel node to string
|
674
|
-
s = s
|
496
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
675
497
|
# Remove any ASC/DESC modifiers
|
676
|
-
s.gsub(/\s+(?:ASC|DESC)\b/i,
|
498
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
677
499
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
678
500
|
|
679
|
-
|
501
|
+
(order_columns << super).join(", ")
|
680
502
|
end
|
681
503
|
|
682
504
|
def strict_mode?
|
683
505
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
684
506
|
end
|
685
507
|
|
686
|
-
def
|
687
|
-
|
508
|
+
def default_index_type?(index) # :nodoc:
|
509
|
+
index.using == :btree || super
|
688
510
|
end
|
689
511
|
|
690
|
-
|
691
|
-
|
692
|
-
def initialize_type_map(m) # :nodoc:
|
693
|
-
super
|
512
|
+
def build_insert_sql(insert) # :nodoc:
|
513
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
694
514
|
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
702
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
703
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
704
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
705
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
706
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
707
|
-
|
708
|
-
register_integer_type m, %r(^bigint)i, limit: 8
|
709
|
-
register_integer_type m, %r(^int)i, limit: 4
|
710
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
711
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
712
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
713
|
-
|
714
|
-
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
|
715
|
-
m.alias_type %r(set)i, 'varchar'
|
716
|
-
m.alias_type %r(year)i, 'integer'
|
717
|
-
m.alias_type %r(bit)i, 'binary'
|
718
|
-
|
719
|
-
m.register_type(%r(datetime)i) do |sql_type|
|
720
|
-
precision = extract_precision(sql_type)
|
721
|
-
MysqlDateTime.new(precision: precision)
|
515
|
+
if insert.skip_duplicates?
|
516
|
+
no_op_column = quote_column_name(insert.keys.first)
|
517
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
518
|
+
elsif insert.update_duplicates?
|
519
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
520
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
722
521
|
end
|
723
522
|
|
724
|
-
|
725
|
-
limit = sql_type[/^enum\((.+)\)/i, 1]
|
726
|
-
.split(',').map{|enum| enum.strip.length - 2}.max
|
727
|
-
MysqlString.new(limit: limit)
|
728
|
-
end
|
523
|
+
sql
|
729
524
|
end
|
730
525
|
|
731
|
-
def
|
732
|
-
|
733
|
-
|
734
|
-
Type::UnsignedInteger.new(options)
|
735
|
-
else
|
736
|
-
Type::Integer.new(options)
|
737
|
-
end
|
526
|
+
def check_version # :nodoc:
|
527
|
+
if database_version < "5.5.8"
|
528
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
738
529
|
end
|
739
530
|
end
|
740
531
|
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
subsubselect = select.clone
|
745
|
-
subsubselect.projections = [key]
|
746
|
-
|
747
|
-
# Materialize subquery by adding distinct
|
748
|
-
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
749
|
-
subsubselect.distinct unless select.limit || select.offset || select.orders.any?
|
532
|
+
private
|
533
|
+
def initialize_type_map(m = type_map)
|
534
|
+
super
|
750
535
|
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
536
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
537
|
+
|
538
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
539
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
540
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
541
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
542
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
543
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
544
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
545
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
546
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
547
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
548
|
+
|
549
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
550
|
+
register_integer_type m, %r(^int)i, limit: 4
|
551
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
552
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
553
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
554
|
+
|
555
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
556
|
+
m.alias_type %r(year)i, "integer"
|
557
|
+
m.alias_type %r(bit)i, "binary"
|
558
|
+
|
559
|
+
m.register_type(%r(enum)i) do |sql_type|
|
560
|
+
limit = sql_type[/^enum\s*\((.+)\)/i, 1]
|
561
|
+
.split(",").map { |enum| enum.strip.length - 2 }.max
|
562
|
+
MysqlString.new(limit: limit)
|
563
|
+
end
|
755
564
|
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
|
761
|
-
when Integer
|
762
|
-
column_names.each {|name| option_strings[name] += "(#{length})"}
|
565
|
+
m.register_type(%r(^set)i) do |sql_type|
|
566
|
+
limit = sql_type[/^set\s*\((.+)\)/i, 1]
|
567
|
+
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
568
|
+
MysqlString.new(limit: limit)
|
763
569
|
end
|
764
570
|
end
|
765
571
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
# add index sort order
|
776
|
-
option_strings = add_index_sort_order(option_strings, column_names, options)
|
777
|
-
|
778
|
-
column_names.map {|name| quote_column_name(name) + option_strings[name]}
|
779
|
-
end
|
780
|
-
|
781
|
-
def translate_exception(exception, message)
|
782
|
-
case error_number(exception)
|
783
|
-
when 1062
|
784
|
-
RecordNotUnique.new(message, exception)
|
785
|
-
when 1452
|
786
|
-
InvalidForeignKey.new(message, exception)
|
787
|
-
else
|
788
|
-
super
|
572
|
+
def register_integer_type(mapping, key, **options)
|
573
|
+
mapping.register_type(key) do |sql_type|
|
574
|
+
if /\bunsigned\b/.match?(sql_type)
|
575
|
+
Type::UnsignedInteger.new(**options)
|
576
|
+
else
|
577
|
+
Type::Integer.new(**options)
|
578
|
+
end
|
579
|
+
end
|
789
580
|
end
|
790
|
-
end
|
791
|
-
|
792
|
-
def add_column_sql(table_name, column_name, type, options = {})
|
793
|
-
td = create_table_definition table_name, options[:temporary], options[:options]
|
794
|
-
cd = td.new_column_definition(column_name, type, options)
|
795
|
-
schema_creation.visit_AddColumn cd
|
796
|
-
end
|
797
581
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
582
|
+
def extract_precision(sql_type)
|
583
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
584
|
+
super || 0
|
585
|
+
else
|
586
|
+
super
|
587
|
+
end
|
803
588
|
end
|
804
589
|
|
805
|
-
|
806
|
-
|
590
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
591
|
+
ER_FILSORT_ABORT = 1028
|
592
|
+
ER_DUP_ENTRY = 1062
|
593
|
+
ER_NOT_NULL_VIOLATION = 1048
|
594
|
+
ER_NO_REFERENCED_ROW = 1216
|
595
|
+
ER_ROW_IS_REFERENCED = 1217
|
596
|
+
ER_DO_NOT_HAVE_DEFAULT = 1364
|
597
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
598
|
+
ER_NO_REFERENCED_ROW_2 = 1452
|
599
|
+
ER_DATA_TOO_LONG = 1406
|
600
|
+
ER_OUT_OF_RANGE = 1264
|
601
|
+
ER_LOCK_DEADLOCK = 1213
|
602
|
+
ER_CANNOT_ADD_FOREIGN = 1215
|
603
|
+
ER_CANNOT_CREATE_TABLE = 1005
|
604
|
+
ER_LOCK_WAIT_TIMEOUT = 1205
|
605
|
+
ER_QUERY_INTERRUPTED = 1317
|
606
|
+
ER_QUERY_TIMEOUT = 3024
|
607
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
608
|
+
|
609
|
+
def translate_exception(exception, message:, sql:, binds:)
|
610
|
+
case error_number(exception)
|
611
|
+
when ER_DUP_ENTRY
|
612
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
613
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
614
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
615
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
616
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
617
|
+
when ER_CANNOT_CREATE_TABLE
|
618
|
+
if message.include?("errno: 150")
|
619
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
620
|
+
else
|
621
|
+
super
|
622
|
+
end
|
623
|
+
when ER_DATA_TOO_LONG
|
624
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
625
|
+
when ER_OUT_OF_RANGE
|
626
|
+
RangeError.new(message, sql: sql, binds: binds)
|
627
|
+
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
628
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
629
|
+
when ER_LOCK_DEADLOCK
|
630
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
631
|
+
when ER_LOCK_WAIT_TIMEOUT
|
632
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
633
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
634
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
635
|
+
when ER_QUERY_INTERRUPTED
|
636
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
637
|
+
else
|
638
|
+
super
|
639
|
+
end
|
807
640
|
end
|
808
641
|
|
809
|
-
options
|
810
|
-
|
811
|
-
|
642
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
643
|
+
column = column_for(table_name, column_name)
|
644
|
+
type ||= column.sql_type
|
812
645
|
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
name: new_column_name,
|
817
|
-
default: column.default,
|
818
|
-
null: column.null,
|
819
|
-
auto_increment: column.extra == "auto_increment"
|
820
|
-
}
|
646
|
+
unless options.key?(:default)
|
647
|
+
options[:default] = column.default
|
648
|
+
end
|
821
649
|
|
822
|
-
|
823
|
-
|
824
|
-
|
650
|
+
unless options.key?(:null)
|
651
|
+
options[:null] = column.null
|
652
|
+
end
|
825
653
|
|
826
|
-
|
827
|
-
|
828
|
-
|
654
|
+
unless options.key?(:comment)
|
655
|
+
options[:comment] = column.comment
|
656
|
+
end
|
829
657
|
|
830
|
-
|
831
|
-
|
832
|
-
|
658
|
+
td = create_table_definition(table_name)
|
659
|
+
cd = td.new_column_definition(column.name, type, **options)
|
660
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
661
|
+
end
|
833
662
|
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
663
|
+
def rename_column_for_alter(table_name, column_name, new_column_name)
|
664
|
+
column = column_for(table_name, column_name)
|
665
|
+
options = {
|
666
|
+
default: column.default,
|
667
|
+
null: column.null,
|
668
|
+
auto_increment: column.auto_increment?
|
669
|
+
}
|
838
670
|
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
671
|
+
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
672
|
+
td = create_table_definition(table_name)
|
673
|
+
cd = td.new_column_definition(new_column_name, current_type, **options)
|
674
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
675
|
+
end
|
843
676
|
|
844
|
-
|
845
|
-
|
846
|
-
|
677
|
+
def add_index_for_alter(table_name, column_name, options = {})
|
678
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, **options)
|
679
|
+
index_algorithm[0, 0] = ", " if index_algorithm.present?
|
680
|
+
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
681
|
+
end
|
847
682
|
|
848
|
-
|
849
|
-
|
850
|
-
|
683
|
+
def remove_index_for_alter(table_name, options = {})
|
684
|
+
index_name = index_name_for_remove(table_name, options)
|
685
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
686
|
+
end
|
851
687
|
|
852
|
-
|
688
|
+
def supports_rename_index?
|
689
|
+
mariadb? ? false : database_version >= "5.7.6"
|
690
|
+
end
|
853
691
|
|
854
|
-
|
855
|
-
|
856
|
-
end
|
692
|
+
def configure_connection
|
693
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
857
694
|
|
858
|
-
|
859
|
-
|
860
|
-
end
|
695
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
696
|
+
variables["sql_auto_is_null"] = 0
|
861
697
|
|
862
|
-
|
863
|
-
|
864
|
-
|
698
|
+
# Increase timeout so the server doesn't disconnect us.
|
699
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
700
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
701
|
+
variables["wait_timeout"] = wait_timeout
|
865
702
|
|
866
|
-
|
867
|
-
variables = @config.fetch(:variables, {}).stringify_keys
|
703
|
+
defaults = [":default", :default].to_set
|
868
704
|
|
869
|
-
|
870
|
-
|
871
|
-
|
705
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
706
|
+
# https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
|
707
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
708
|
+
if sql_mode = variables.delete("sql_mode")
|
709
|
+
sql_mode = quote(sql_mode)
|
710
|
+
elsif !defaults.include?(strict_mode?)
|
711
|
+
if strict_mode?
|
712
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
713
|
+
else
|
714
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
715
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
716
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
717
|
+
end
|
718
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
719
|
+
end
|
720
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
721
|
+
|
722
|
+
# NAMES does not have an equals sign, see
|
723
|
+
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
724
|
+
# (trailing comma because variable_assignments will always have content)
|
725
|
+
if @config[:encoding]
|
726
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
727
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
728
|
+
encoding << ", "
|
729
|
+
end
|
872
730
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
731
|
+
# Gather up all of the SET variables...
|
732
|
+
variable_assignments = variables.map do |k, v|
|
733
|
+
if defaults.include?(v)
|
734
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
735
|
+
elsif !v.nil?
|
736
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
737
|
+
end
|
738
|
+
# or else nil; compact to clear nils out
|
739
|
+
end.compact.join(", ")
|
877
740
|
|
878
|
-
|
879
|
-
|
880
|
-
# If the user has provided another value for sql_mode, don't replace it.
|
881
|
-
unless variables.has_key?('sql_mode')
|
882
|
-
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
|
741
|
+
# ...and send them all in one query
|
742
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
883
743
|
end
|
884
744
|
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
encoding = "NAMES #{@config[:encoding]}"
|
890
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
891
|
-
encoding << ", "
|
745
|
+
def column_definitions(table_name) # :nodoc:
|
746
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
747
|
+
each_hash(result)
|
748
|
+
end
|
892
749
|
end
|
893
750
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
898
|
-
elsif !v.nil?
|
899
|
-
"@@SESSION.#{k} = #{quote(v)}"
|
900
|
-
end
|
901
|
-
# or else nil; compact to clear nils out
|
902
|
-
end.compact.join(', ')
|
751
|
+
def create_table_info(table_name) # :nodoc:
|
752
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
753
|
+
end
|
903
754
|
|
904
|
-
|
905
|
-
|
906
|
-
|
755
|
+
def arel_visitor
|
756
|
+
Arel::Visitors::MySQL.new(self)
|
757
|
+
end
|
907
758
|
|
908
|
-
|
909
|
-
|
910
|
-
case $1
|
911
|
-
when 'CASCADE'; :cascade
|
912
|
-
when 'SET NULL'; :nullify
|
913
|
-
end
|
759
|
+
def build_statement_pool
|
760
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
914
761
|
end
|
915
|
-
end
|
916
762
|
|
917
|
-
|
918
|
-
|
763
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
764
|
+
match = %r/
|
765
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
766
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
767
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
768
|
+
/xmi.match(sql)
|
919
769
|
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
770
|
+
options = {
|
771
|
+
message: message,
|
772
|
+
sql: sql,
|
773
|
+
binds: binds,
|
774
|
+
}
|
924
775
|
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
776
|
+
if match
|
777
|
+
options[:table] = match[:table]
|
778
|
+
options[:foreign_key] = match[:foreign_key]
|
779
|
+
options[:target_table] = match[:target_table]
|
780
|
+
options[:primary_key] = match[:primary_key]
|
781
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
931
782
|
end
|
783
|
+
|
784
|
+
MismatchedForeignKey.new(**options)
|
932
785
|
end
|
933
786
|
|
934
|
-
|
787
|
+
def version_string(full_version_string)
|
788
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
789
|
+
end
|
935
790
|
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
791
|
+
class MysqlString < Type::String # :nodoc:
|
792
|
+
def serialize(value)
|
793
|
+
case value
|
794
|
+
when true then "1"
|
795
|
+
when false then "0"
|
796
|
+
else super
|
797
|
+
end
|
941
798
|
end
|
799
|
+
|
800
|
+
private
|
801
|
+
def cast_value(value)
|
802
|
+
case value
|
803
|
+
when true then "1"
|
804
|
+
when false then "0"
|
805
|
+
else super
|
806
|
+
end
|
807
|
+
end
|
942
808
|
end
|
943
|
-
|
809
|
+
|
810
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
811
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
944
812
|
end
|
945
813
|
end
|
946
814
|
end
|