activerecord 5.2.8 → 7.0.2
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 +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- 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 +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- 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 +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- 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 +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +9 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -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 +48 -0
- data/lib/arel/delete_manager.rb +32 -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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -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 +44 -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/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -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 +46 -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 +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -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 +55 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- 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 +10 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +162 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -11,8 +11,6 @@ require "active_record/connection_adapters/mysql/schema_dumper"
|
|
11
11
|
require "active_record/connection_adapters/mysql/schema_statements"
|
12
12
|
require "active_record/connection_adapters/mysql/type_metadata"
|
13
13
|
|
14
|
-
require "active_support/core_ext/string/strip"
|
15
|
-
|
16
14
|
module ActiveRecord
|
17
15
|
module ConnectionAdapters
|
18
16
|
class AbstractMysqlAdapter < AbstractAdapter
|
@@ -31,49 +29,52 @@ module ActiveRecord
|
|
31
29
|
NATIVE_DATABASE_TYPES = {
|
32
30
|
primary_key: "bigint auto_increment PRIMARY KEY",
|
33
31
|
string: { name: "varchar", limit: 255 },
|
34
|
-
text: { name: "text"
|
32
|
+
text: { name: "text" },
|
35
33
|
integer: { name: "int", limit: 4 },
|
34
|
+
bigint: { name: "bigint" },
|
36
35
|
float: { name: "float", limit: 24 },
|
37
36
|
decimal: { name: "decimal" },
|
38
37
|
datetime: { name: "datetime" },
|
39
38
|
timestamp: { name: "timestamp" },
|
40
39
|
time: { name: "time" },
|
41
40
|
date: { name: "date" },
|
42
|
-
binary: { name: "blob"
|
41
|
+
binary: { name: "blob" },
|
42
|
+
blob: { name: "blob" },
|
43
43
|
boolean: { name: "tinyint", limit: 1 },
|
44
44
|
json: { name: "json" },
|
45
45
|
}
|
46
46
|
|
47
47
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
48
|
-
private
|
49
|
-
stmt
|
50
|
-
|
48
|
+
private
|
49
|
+
def dealloc(stmt)
|
50
|
+
stmt.close
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
54
|
def initialize(connection, logger, connection_options, config)
|
54
55
|
super(connection, logger, config)
|
55
|
-
|
56
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
57
|
-
|
58
|
-
if version < "5.1.10"
|
59
|
-
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
|
60
|
-
end
|
61
56
|
end
|
62
57
|
|
63
|
-
def
|
64
|
-
|
58
|
+
def get_database_version # :nodoc:
|
59
|
+
full_version_string = get_full_version
|
60
|
+
version_string = version_string(full_version_string)
|
61
|
+
Version.new(version_string, full_version_string)
|
65
62
|
end
|
66
63
|
|
67
64
|
def mariadb? # :nodoc:
|
68
65
|
/mariadb/i.match?(full_version)
|
69
66
|
end
|
70
67
|
|
71
|
-
def supports_bulk_alter?
|
68
|
+
def supports_bulk_alter?
|
72
69
|
true
|
73
70
|
end
|
74
71
|
|
75
72
|
def supports_index_sort_order?
|
76
|
-
!mariadb? &&
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
74
|
+
end
|
75
|
+
|
76
|
+
def supports_expression_index?
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
77
78
|
end
|
78
79
|
|
79
80
|
def supports_transaction_isolation?
|
@@ -92,23 +93,36 @@ module ActiveRecord
|
|
92
93
|
true
|
93
94
|
end
|
94
95
|
|
96
|
+
def supports_check_constraints?
|
97
|
+
if mariadb?
|
98
|
+
database_version >= "10.2.1"
|
99
|
+
else
|
100
|
+
database_version >= "8.0.16"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
95
104
|
def supports_views?
|
96
105
|
true
|
97
106
|
end
|
98
107
|
|
99
108
|
def supports_datetime_with_precision?
|
100
|
-
|
101
|
-
version >= "5.3.0"
|
102
|
-
else
|
103
|
-
version >= "5.6.4"
|
104
|
-
end
|
109
|
+
mariadb? || database_version >= "5.6.4"
|
105
110
|
end
|
106
111
|
|
107
112
|
def supports_virtual_columns?
|
113
|
+
mariadb? || database_version >= "5.7.5"
|
114
|
+
end
|
115
|
+
|
116
|
+
# See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
|
117
|
+
def supports_optimizer_hints?
|
118
|
+
!mariadb? && database_version >= "5.7.7"
|
119
|
+
end
|
120
|
+
|
121
|
+
def supports_common_table_expressions?
|
108
122
|
if mariadb?
|
109
|
-
|
123
|
+
database_version >= "10.2.1"
|
110
124
|
else
|
111
|
-
|
125
|
+
database_version >= "8.0.1"
|
112
126
|
end
|
113
127
|
end
|
114
128
|
|
@@ -116,6 +130,19 @@ module ActiveRecord
|
|
116
130
|
true
|
117
131
|
end
|
118
132
|
|
133
|
+
def supports_insert_on_duplicate_skip?
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
def supports_insert_on_duplicate_update?
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
def field_ordered_value(column, values) # :nodoc:
|
142
|
+
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse])
|
143
|
+
Arel::Nodes::Descending.new(field)
|
144
|
+
end
|
145
|
+
|
119
146
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
120
147
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
121
148
|
end
|
@@ -129,7 +156,12 @@ module ActiveRecord
|
|
129
156
|
end
|
130
157
|
|
131
158
|
def index_algorithms
|
132
|
-
{
|
159
|
+
{
|
160
|
+
default: "ALGORITHM = DEFAULT",
|
161
|
+
copy: "ALGORITHM = COPY",
|
162
|
+
inplace: "ALGORITHM = INPLACE",
|
163
|
+
instant: "ALGORITHM = INSTANT",
|
164
|
+
}
|
133
165
|
end
|
134
166
|
|
135
167
|
# HELPER METHODS ===========================================
|
@@ -148,7 +180,7 @@ module ActiveRecord
|
|
148
180
|
|
149
181
|
# REFERENTIAL INTEGRITY ====================================
|
150
182
|
|
151
|
-
def disable_referential_integrity
|
183
|
+
def disable_referential_integrity # :nodoc:
|
152
184
|
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
153
185
|
|
154
186
|
begin
|
@@ -159,73 +191,40 @@ module ActiveRecord
|
|
159
191
|
end
|
160
192
|
end
|
161
193
|
|
162
|
-
# CONNECTION MANAGEMENT ====================================
|
163
|
-
|
164
|
-
# Clears the prepared statements cache.
|
165
|
-
def clear_cache!
|
166
|
-
reload_type_map
|
167
|
-
@statements.clear
|
168
|
-
end
|
169
|
-
|
170
194
|
#--
|
171
195
|
# DATABASE STATEMENTS ======================================
|
172
196
|
#++
|
173
197
|
|
174
|
-
def explain(arel, binds = [])
|
175
|
-
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
176
|
-
start = Time.now
|
177
|
-
result = exec_query(sql, "EXPLAIN", binds)
|
178
|
-
elapsed = Time.now - start
|
179
|
-
|
180
|
-
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
181
|
-
end
|
182
|
-
|
183
198
|
# Executes the SQL statement in the context of this connection.
|
184
|
-
def execute(sql, name = nil)
|
185
|
-
|
186
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
187
|
-
@connection.query(sql)
|
188
|
-
end
|
189
|
-
end
|
199
|
+
def execute(sql, name = nil, async: false)
|
200
|
+
raw_execute(sql, name, async: async)
|
190
201
|
end
|
191
202
|
|
192
203
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
193
204
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
194
205
|
# needs to be explicitly freed or not.
|
195
|
-
def execute_and_free(sql, name = nil) # :nodoc:
|
196
|
-
yield execute(sql, name)
|
206
|
+
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
207
|
+
yield execute(sql, name, async: async)
|
197
208
|
end
|
198
209
|
|
199
|
-
def begin_db_transaction
|
200
|
-
execute
|
210
|
+
def begin_db_transaction # :nodoc:
|
211
|
+
execute("BEGIN", "TRANSACTION")
|
201
212
|
end
|
202
213
|
|
203
|
-
def begin_isolated_db_transaction(isolation)
|
214
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
204
215
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
205
216
|
begin_db_transaction
|
206
217
|
end
|
207
218
|
|
208
|
-
def commit_db_transaction
|
209
|
-
execute
|
219
|
+
def commit_db_transaction # :nodoc:
|
220
|
+
execute("COMMIT", "TRANSACTION")
|
210
221
|
end
|
211
222
|
|
212
|
-
def exec_rollback_db_transaction
|
213
|
-
execute
|
223
|
+
def exec_rollback_db_transaction # :nodoc:
|
224
|
+
execute("ROLLBACK", "TRANSACTION")
|
214
225
|
end
|
215
226
|
|
216
|
-
|
217
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
218
|
-
# these, we must use a subquery.
|
219
|
-
def join_to_update(update, select, key) # :nodoc:
|
220
|
-
if select.limit || select.offset || select.orders.any?
|
221
|
-
super
|
222
|
-
else
|
223
|
-
update.table select.source
|
224
|
-
update.wheres = select.constraints
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
def empty_insert_statement_value
|
227
|
+
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
229
228
|
"VALUES ()"
|
230
229
|
end
|
231
230
|
|
@@ -241,7 +240,7 @@ module ActiveRecord
|
|
241
240
|
end
|
242
241
|
|
243
242
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
244
|
-
# Charset defaults to
|
243
|
+
# Charset defaults to utf8mb4.
|
245
244
|
#
|
246
245
|
# Example:
|
247
246
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -250,8 +249,12 @@ module ActiveRecord
|
|
250
249
|
def create_database(name, options = {})
|
251
250
|
if options[:collation]
|
252
251
|
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
252
|
+
elsif options[:charset]
|
253
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
254
|
+
elsif row_format_dynamic_by_default?
|
255
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
253
256
|
else
|
254
|
-
|
257
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
255
258
|
end
|
256
259
|
end
|
257
260
|
|
@@ -259,7 +262,7 @@ module ActiveRecord
|
|
259
262
|
#
|
260
263
|
# Example:
|
261
264
|
# drop_database('sebastian_development')
|
262
|
-
def drop_database(name)
|
265
|
+
def drop_database(name) # :nodoc:
|
263
266
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
264
267
|
end
|
265
268
|
|
@@ -277,14 +280,10 @@ module ActiveRecord
|
|
277
280
|
show_variable "collation_database"
|
278
281
|
end
|
279
282
|
|
280
|
-
def truncate(table_name, name = nil)
|
281
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
282
|
-
end
|
283
|
-
|
284
283
|
def table_comment(table_name) # :nodoc:
|
285
284
|
scope = quoted_scope(table_name)
|
286
285
|
|
287
|
-
query_value(
|
286
|
+
query_value(<<~SQL, "SCHEMA").presence
|
288
287
|
SELECT table_comment
|
289
288
|
FROM information_schema.tables
|
290
289
|
WHERE table_schema = #{scope[:schema]}
|
@@ -292,22 +291,8 @@ module ActiveRecord
|
|
292
291
|
SQL
|
293
292
|
end
|
294
293
|
|
295
|
-
def
|
296
|
-
|
297
|
-
table, arguments = args.shift, args
|
298
|
-
method = :"#{command}_for_alter"
|
299
|
-
|
300
|
-
if respond_to?(method, true)
|
301
|
-
send(method, table, *arguments)
|
302
|
-
else
|
303
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
304
|
-
end
|
305
|
-
end.join(", ")
|
306
|
-
|
307
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
308
|
-
end
|
309
|
-
|
310
|
-
def change_table_comment(table_name, comment) #:nodoc:
|
294
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
295
|
+
comment = extract_new_comment_value(comment_or_changes)
|
311
296
|
comment = "" if comment.nil?
|
312
297
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
313
298
|
end
|
@@ -317,6 +302,8 @@ module ActiveRecord
|
|
317
302
|
# Example:
|
318
303
|
# rename_table('octopuses', 'octopi')
|
319
304
|
def rename_table(table_name, new_name)
|
305
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
306
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
320
307
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
321
308
|
rename_table_indexes(table_name, new_name)
|
322
309
|
end
|
@@ -336,7 +323,8 @@ module ActiveRecord
|
|
336
323
|
# Although this command ignores most +options+ and the block if one is given,
|
337
324
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
338
325
|
# In that case, +options+ and the block will be used by create_table.
|
339
|
-
def drop_table(table_name, options
|
326
|
+
def drop_table(table_name, **options)
|
327
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
340
328
|
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
341
329
|
end
|
342
330
|
|
@@ -350,12 +338,12 @@ module ActiveRecord
|
|
350
338
|
end
|
351
339
|
end
|
352
340
|
|
353
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
341
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
354
342
|
default = extract_new_default_value(default_or_changes)
|
355
343
|
change_column table_name, column_name, nil, default: default
|
356
344
|
end
|
357
345
|
|
358
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
346
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
359
347
|
unless null || default.nil?
|
360
348
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
361
349
|
end
|
@@ -363,23 +351,27 @@ module ActiveRecord
|
|
363
351
|
change_column table_name, column_name, nil, null: null
|
364
352
|
end
|
365
353
|
|
366
|
-
def change_column_comment(table_name, column_name,
|
354
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
355
|
+
comment = extract_new_comment_value(comment_or_changes)
|
367
356
|
change_column table_name, column_name, nil, comment: comment
|
368
357
|
end
|
369
358
|
|
370
|
-
def change_column(table_name, column_name, type, options
|
371
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
359
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
360
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
372
361
|
end
|
373
362
|
|
374
|
-
def rename_column(table_name, column_name, new_column_name)
|
363
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
375
364
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
376
365
|
rename_column_indexes(table_name, column_name, new_column_name)
|
377
366
|
end
|
378
367
|
|
379
|
-
def add_index(table_name, column_name, options
|
380
|
-
|
381
|
-
|
382
|
-
|
368
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
369
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
370
|
+
|
371
|
+
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
372
|
+
|
373
|
+
create_index = CreateIndexDefinition.new(index, algorithm)
|
374
|
+
execute schema_creation.accept(create_index)
|
383
375
|
end
|
384
376
|
|
385
377
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -392,7 +384,7 @@ module ActiveRecord
|
|
392
384
|
|
393
385
|
scope = quoted_scope(table_name)
|
394
386
|
|
395
|
-
fk_info = exec_query(
|
387
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
396
388
|
SELECT fk.referenced_table_name AS 'to_table',
|
397
389
|
fk.referenced_column_name AS 'primary_key',
|
398
390
|
fk.column_name AS 'column',
|
@@ -423,51 +415,66 @@ module ActiveRecord
|
|
423
415
|
end
|
424
416
|
end
|
425
417
|
|
426
|
-
def
|
427
|
-
|
418
|
+
def check_constraints(table_name)
|
419
|
+
if supports_check_constraints?
|
420
|
+
scope = quoted_scope(table_name)
|
421
|
+
|
422
|
+
sql = <<~SQL
|
423
|
+
SELECT cc.constraint_name AS 'name',
|
424
|
+
cc.check_clause AS 'expression'
|
425
|
+
FROM information_schema.check_constraints cc
|
426
|
+
JOIN information_schema.table_constraints tc
|
427
|
+
USING (constraint_schema, constraint_name)
|
428
|
+
WHERE tc.table_schema = #{scope[:schema]}
|
429
|
+
AND tc.table_name = #{scope[:name]}
|
430
|
+
AND cc.constraint_schema = #{scope[:schema]}
|
431
|
+
SQL
|
432
|
+
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
433
|
+
|
434
|
+
chk_info = exec_query(sql, "SCHEMA")
|
435
|
+
|
436
|
+
chk_info.map do |row|
|
437
|
+
options = {
|
438
|
+
name: row["name"]
|
439
|
+
}
|
440
|
+
expression = row["expression"]
|
441
|
+
expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
|
442
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
443
|
+
end
|
444
|
+
else
|
445
|
+
raise NotImplementedError
|
446
|
+
end
|
447
|
+
end
|
428
448
|
|
449
|
+
def table_options(table_name) # :nodoc:
|
429
450
|
create_table_info = create_table_info(table_name)
|
430
451
|
|
431
452
|
# strip create_definitions and partition_options
|
432
|
-
|
453
|
+
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
454
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
455
|
+
|
456
|
+
return if raw_table_options.empty?
|
457
|
+
|
458
|
+
table_options = {}
|
459
|
+
|
460
|
+
if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
|
461
|
+
raw_table_options = $` + $' # before part + after part
|
462
|
+
table_options[:charset] = charset
|
463
|
+
table_options[:collation] = collation if collation
|
464
|
+
end
|
433
465
|
|
434
466
|
# strip AUTO_INCREMENT
|
435
467
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
436
468
|
|
437
|
-
table_options[:options] = raw_table_options
|
438
|
-
|
439
469
|
# strip COMMENT
|
440
470
|
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
441
471
|
table_options[:comment] = table_comment(table_name)
|
442
472
|
end
|
443
473
|
|
474
|
+
table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
|
444
475
|
table_options
|
445
476
|
end
|
446
477
|
|
447
|
-
# Maps logical Rails types to MySQL-specific data types.
|
448
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **) # :nodoc:
|
449
|
-
sql = \
|
450
|
-
case type.to_s
|
451
|
-
when "integer"
|
452
|
-
integer_to_sql(limit)
|
453
|
-
when "text"
|
454
|
-
text_to_sql(limit)
|
455
|
-
when "blob"
|
456
|
-
binary_to_sql(limit)
|
457
|
-
when "binary"
|
458
|
-
if (0..0xfff) === limit
|
459
|
-
"varbinary(#{limit})"
|
460
|
-
else
|
461
|
-
binary_to_sql(limit)
|
462
|
-
end
|
463
|
-
else
|
464
|
-
super
|
465
|
-
end
|
466
|
-
|
467
|
-
sql = "#{sql} unsigned" if unsigned && type != :primary_key
|
468
|
-
sql
|
469
|
-
end
|
470
|
-
|
471
478
|
# SHOW VARIABLES LIKE 'name'
|
472
479
|
def show_variable(name)
|
473
480
|
query_value("SELECT @@#{name}", "SCHEMA")
|
@@ -480,19 +487,21 @@ module ActiveRecord
|
|
480
487
|
|
481
488
|
scope = quoted_scope(table_name)
|
482
489
|
|
483
|
-
query_values(
|
490
|
+
query_values(<<~SQL, "SCHEMA")
|
484
491
|
SELECT column_name
|
485
|
-
FROM information_schema.
|
486
|
-
WHERE
|
492
|
+
FROM information_schema.statistics
|
493
|
+
WHERE index_name = 'PRIMARY'
|
487
494
|
AND table_schema = #{scope[:schema]}
|
488
495
|
AND table_name = #{scope[:name]}
|
489
|
-
ORDER BY
|
496
|
+
ORDER BY seq_in_index
|
490
497
|
SQL
|
491
498
|
end
|
492
499
|
|
493
|
-
def case_sensitive_comparison(
|
500
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
501
|
+
column = column_for_attribute(attribute)
|
502
|
+
|
494
503
|
if column.collation && !column.case_sensitive?
|
495
|
-
|
504
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
496
505
|
else
|
497
506
|
super
|
498
507
|
end
|
@@ -506,14 +515,14 @@ module ActiveRecord
|
|
506
515
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
507
516
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
508
517
|
# distinct queries, and requires that the ORDER BY include the distinct column.
|
509
|
-
# See https://dev.mysql.com/doc/refman/
|
518
|
+
# See https://dev.mysql.com/doc/refman/en/group-by-handling.html
|
510
519
|
def columns_for_distinct(columns, orders) # :nodoc:
|
511
|
-
order_columns = orders.
|
520
|
+
order_columns = orders.compact_blank.map { |s|
|
512
521
|
# Convert Arel node to string
|
513
|
-
s = s
|
522
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
514
523
|
# Remove any ASC/DESC modifiers
|
515
524
|
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
516
|
-
}.
|
525
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
517
526
|
|
518
527
|
(order_columns << super).join(", ")
|
519
528
|
end
|
@@ -526,101 +535,114 @@ module ActiveRecord
|
|
526
535
|
index.using == :btree || super
|
527
536
|
end
|
528
537
|
|
529
|
-
def
|
530
|
-
|
531
|
-
super { discard_remaining_results }
|
532
|
-
end
|
533
|
-
end
|
538
|
+
def build_insert_sql(insert) # :nodoc:
|
539
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
534
540
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
else
|
543
|
-
previous_packet << sql
|
544
|
-
end
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
def max_allowed_packet_reached?(current_packet, previous_packet)
|
549
|
-
if current_packet.bytesize > max_allowed_packet
|
550
|
-
raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
|
551
|
-
elsif previous_packet.nil?
|
552
|
-
false
|
541
|
+
if insert.skip_duplicates?
|
542
|
+
no_op_column = quote_column_name(insert.keys.first)
|
543
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
544
|
+
elsif insert.update_duplicates?
|
545
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
546
|
+
if insert.raw_update_sql?
|
547
|
+
sql << insert.raw_update_sql
|
553
548
|
else
|
554
|
-
|
549
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
550
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
555
551
|
end
|
556
552
|
end
|
557
553
|
|
558
|
-
|
559
|
-
|
560
|
-
|
554
|
+
sql
|
555
|
+
end
|
556
|
+
|
557
|
+
def check_version # :nodoc:
|
558
|
+
if database_version < "5.5.8"
|
559
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
561
560
|
end
|
561
|
+
end
|
562
562
|
|
563
|
-
|
564
|
-
|
563
|
+
class << self
|
564
|
+
private
|
565
|
+
def initialize_type_map(m)
|
566
|
+
super
|
565
567
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
568
|
+
m.register_type(%r(char)i) do |sql_type|
|
569
|
+
limit = extract_limit(sql_type)
|
570
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
571
|
+
end
|
572
|
+
|
573
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
574
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
575
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
576
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
577
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
578
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
579
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
580
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
581
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
582
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
583
|
+
|
584
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
585
|
+
register_integer_type m, %r(^int)i, limit: 4
|
586
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
587
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
588
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
589
|
+
|
590
|
+
m.alias_type %r(year)i, "integer"
|
591
|
+
m.alias_type %r(bit)i, "binary"
|
592
|
+
|
593
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
594
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
593
595
|
end
|
594
596
|
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
597
|
+
def register_integer_type(mapping, key, **options)
|
598
|
+
mapping.register_type(key) do |sql_type|
|
599
|
+
if /\bunsigned\b/.match?(sql_type)
|
600
|
+
Type::UnsignedInteger.new(**options)
|
601
|
+
else
|
602
|
+
Type::Integer.new(**options)
|
603
|
+
end
|
604
|
+
end
|
599
605
|
end
|
600
|
-
end
|
601
606
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
Type::UnsignedInteger.new(options)
|
607
|
+
def extract_precision(sql_type)
|
608
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
609
|
+
super || 0
|
606
610
|
else
|
607
|
-
|
611
|
+
super
|
608
612
|
end
|
609
613
|
end
|
614
|
+
end
|
615
|
+
|
616
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
617
|
+
TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
|
618
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
619
|
+
end
|
620
|
+
|
621
|
+
private
|
622
|
+
def type_map
|
623
|
+
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
|
610
624
|
end
|
611
625
|
|
612
|
-
def
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
626
|
+
def raw_execute(sql, name, async: false)
|
627
|
+
materialize_transactions
|
628
|
+
mark_transaction_written_if_write(sql)
|
629
|
+
|
630
|
+
log(sql, name, async: async) do
|
631
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
632
|
+
@connection.query(sql)
|
633
|
+
end
|
617
634
|
end
|
618
635
|
end
|
619
636
|
|
620
|
-
# See https://dev.mysql.com/doc/
|
637
|
+
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
638
|
+
ER_DB_CREATE_EXISTS = 1007
|
639
|
+
ER_FILSORT_ABORT = 1028
|
621
640
|
ER_DUP_ENTRY = 1062
|
622
641
|
ER_NOT_NULL_VIOLATION = 1048
|
642
|
+
ER_NO_REFERENCED_ROW = 1216
|
643
|
+
ER_ROW_IS_REFERENCED = 1217
|
623
644
|
ER_DO_NOT_HAVE_DEFAULT = 1364
|
645
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
624
646
|
ER_NO_REFERENCED_ROW_2 = 1452
|
625
647
|
ER_DATA_TOO_LONG = 1406
|
626
648
|
ER_OUT_OF_RANGE = 1264
|
@@ -630,41 +652,50 @@ module ActiveRecord
|
|
630
652
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
631
653
|
ER_QUERY_INTERRUPTED = 1317
|
632
654
|
ER_QUERY_TIMEOUT = 3024
|
655
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
633
656
|
|
634
|
-
def translate_exception(exception, message)
|
657
|
+
def translate_exception(exception, message:, sql:, binds:)
|
635
658
|
case error_number(exception)
|
659
|
+
when nil
|
660
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
661
|
+
ConnectionNotEstablished.new(exception)
|
662
|
+
else
|
663
|
+
super
|
664
|
+
end
|
665
|
+
when ER_DB_CREATE_EXISTS
|
666
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
636
667
|
when ER_DUP_ENTRY
|
637
|
-
RecordNotUnique.new(message)
|
638
|
-
when ER_NO_REFERENCED_ROW_2
|
639
|
-
InvalidForeignKey.new(message)
|
640
|
-
when ER_CANNOT_ADD_FOREIGN
|
641
|
-
mismatched_foreign_key(message)
|
668
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
669
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
670
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
671
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
672
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
642
673
|
when ER_CANNOT_CREATE_TABLE
|
643
674
|
if message.include?("errno: 150")
|
644
|
-
mismatched_foreign_key(message)
|
675
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
645
676
|
else
|
646
677
|
super
|
647
678
|
end
|
648
679
|
when ER_DATA_TOO_LONG
|
649
|
-
ValueTooLong.new(message)
|
680
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
650
681
|
when ER_OUT_OF_RANGE
|
651
|
-
RangeError.new(message)
|
682
|
+
RangeError.new(message, sql: sql, binds: binds)
|
652
683
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
653
|
-
NotNullViolation.new(message)
|
684
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
654
685
|
when ER_LOCK_DEADLOCK
|
655
|
-
Deadlocked.new(message)
|
686
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
656
687
|
when ER_LOCK_WAIT_TIMEOUT
|
657
|
-
LockWaitTimeout.new(message)
|
658
|
-
when ER_QUERY_TIMEOUT
|
659
|
-
StatementTimeout.new(message)
|
688
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
689
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
690
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
660
691
|
when ER_QUERY_INTERRUPTED
|
661
|
-
QueryCanceled.new(message)
|
692
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
662
693
|
else
|
663
694
|
super
|
664
695
|
end
|
665
696
|
end
|
666
697
|
|
667
|
-
def change_column_for_alter(table_name, column_name, type, options
|
698
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
668
699
|
column = column_for(table_name, column_name)
|
669
700
|
type ||= column.sql_type
|
670
701
|
|
@@ -681,59 +712,53 @@ module ActiveRecord
|
|
681
712
|
end
|
682
713
|
|
683
714
|
td = create_table_definition(table_name)
|
684
|
-
cd = td.new_column_definition(column.name, type, options)
|
715
|
+
cd = td.new_column_definition(column.name, type, **options)
|
685
716
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
686
717
|
end
|
687
718
|
|
688
719
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
720
|
+
return rename_column_sql(table_name, column_name, new_column_name) if supports_rename_column?
|
721
|
+
|
689
722
|
column = column_for(table_name, column_name)
|
690
723
|
options = {
|
691
724
|
default: column.default,
|
692
725
|
null: column.null,
|
693
|
-
auto_increment: column.auto_increment
|
726
|
+
auto_increment: column.auto_increment?,
|
727
|
+
comment: column.comment
|
694
728
|
}
|
695
729
|
|
696
730
|
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
697
731
|
td = create_table_definition(table_name)
|
698
|
-
cd = td.new_column_definition(new_column_name, current_type, options)
|
732
|
+
cd = td.new_column_definition(new_column_name, current_type, **options)
|
699
733
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
700
734
|
end
|
701
735
|
|
702
|
-
def add_index_for_alter(table_name, column_name, options
|
703
|
-
|
704
|
-
|
705
|
-
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
706
|
-
end
|
736
|
+
def add_index_for_alter(table_name, column_name, **options)
|
737
|
+
index, algorithm, _ = add_index_options(table_name, column_name, **options)
|
738
|
+
algorithm = ", #{algorithm}" if algorithm
|
707
739
|
|
708
|
-
|
709
|
-
index_name = index_name_for_remove(table_name, options)
|
710
|
-
"DROP INDEX #{quote_column_name(index_name)}"
|
711
|
-
end
|
712
|
-
|
713
|
-
def add_timestamps_for_alter(table_name, options = {})
|
714
|
-
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
740
|
+
"ADD #{schema_creation.accept(index)}#{algorithm}"
|
715
741
|
end
|
716
742
|
|
717
|
-
def
|
718
|
-
|
743
|
+
def remove_index_for_alter(table_name, column_name = nil, **options)
|
744
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
745
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
719
746
|
end
|
720
747
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
# Materialize subquery by adding distinct
|
728
|
-
# to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
|
729
|
-
subselect.distinct unless select.limit || select.offset || select.orders.any?
|
730
|
-
|
731
|
-
key_name = quote_column_name(key.name)
|
732
|
-
Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name))
|
748
|
+
def supports_rename_index?
|
749
|
+
if mariadb?
|
750
|
+
database_version >= "10.5.2"
|
751
|
+
else
|
752
|
+
database_version >= "5.7.6"
|
753
|
+
end
|
733
754
|
end
|
734
755
|
|
735
|
-
def
|
736
|
-
mariadb?
|
756
|
+
def supports_rename_column?
|
757
|
+
if mariadb?
|
758
|
+
database_version >= "10.5.2"
|
759
|
+
else
|
760
|
+
database_version >= "8.0.3"
|
761
|
+
end
|
737
762
|
end
|
738
763
|
|
739
764
|
def configure_connection
|
@@ -750,7 +775,7 @@ module ActiveRecord
|
|
750
775
|
defaults = [":default", :default].to_set
|
751
776
|
|
752
777
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
753
|
-
# https://dev.mysql.com/doc/refman/
|
778
|
+
# https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_strict_all_tables
|
754
779
|
# If the user has provided another value for sql_mode, don't replace it.
|
755
780
|
if sql_mode = variables.delete("sql_mode")
|
756
781
|
sql_mode = quote(sql_mode)
|
@@ -767,26 +792,25 @@ module ActiveRecord
|
|
767
792
|
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
768
793
|
|
769
794
|
# NAMES does not have an equals sign, see
|
770
|
-
# https://dev.mysql.com/doc/refman/
|
795
|
+
# https://dev.mysql.com/doc/refman/en/set-names.html
|
771
796
|
# (trailing comma because variable_assignments will always have content)
|
772
797
|
if @config[:encoding]
|
773
|
-
encoding = "NAMES #{@config[:encoding]}"
|
798
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
774
799
|
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
775
800
|
encoding << ", "
|
776
801
|
end
|
777
802
|
|
778
803
|
# Gather up all of the SET variables...
|
779
|
-
variable_assignments = variables.
|
804
|
+
variable_assignments = variables.filter_map do |k, v|
|
780
805
|
if defaults.include?(v)
|
781
806
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
782
807
|
elsif !v.nil?
|
783
808
|
"@@SESSION.#{k} = #{quote(v)}"
|
784
809
|
end
|
785
|
-
|
786
|
-
end.compact.join(", ")
|
810
|
+
end.join(", ")
|
787
811
|
|
788
812
|
# ...and send them all in one query
|
789
|
-
execute
|
813
|
+
execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
790
814
|
end
|
791
815
|
|
792
816
|
def column_definitions(table_name) # :nodoc:
|
@@ -803,15 +827,21 @@ module ActiveRecord
|
|
803
827
|
Arel::Visitors::MySQL.new(self)
|
804
828
|
end
|
805
829
|
|
806
|
-
def
|
830
|
+
def build_statement_pool
|
831
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
832
|
+
end
|
833
|
+
|
834
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
807
835
|
match = %r/
|
808
836
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
809
837
|
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
810
838
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
811
|
-
/xmi.match(
|
839
|
+
/xmi.match(sql)
|
812
840
|
|
813
841
|
options = {
|
814
842
|
message: message,
|
843
|
+
sql: sql,
|
844
|
+
binds: binds,
|
815
845
|
}
|
816
846
|
|
817
847
|
if match
|
@@ -822,65 +852,19 @@ module ActiveRecord
|
|
822
852
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
823
853
|
end
|
824
854
|
|
825
|
-
MismatchedForeignKey.new(options)
|
855
|
+
MismatchedForeignKey.new(**options)
|
826
856
|
end
|
827
857
|
|
828
|
-
def
|
829
|
-
|
830
|
-
when 1; "tinyint"
|
831
|
-
when 2; "smallint"
|
832
|
-
when 3; "mediumint"
|
833
|
-
when nil, 4; "int"
|
834
|
-
when 5..8; "bigint"
|
835
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead.")
|
836
|
-
end
|
858
|
+
def version_string(full_version_string)
|
859
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
837
860
|
end
|
838
861
|
|
839
|
-
|
840
|
-
|
841
|
-
when 0..0xff; "tinytext"
|
842
|
-
when nil, 0x100..0xffff; "text"
|
843
|
-
when 0x10000..0xffffff; "mediumtext"
|
844
|
-
when 0x1000000..0xffffffff; "longtext"
|
845
|
-
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
846
|
-
end
|
862
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
863
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
847
864
|
end
|
848
|
-
|
849
|
-
|
850
|
-
case limit
|
851
|
-
when 0..0xff; "tinyblob"
|
852
|
-
when nil, 0x100..0xffff; "blob"
|
853
|
-
when 0x10000..0xffffff; "mediumblob"
|
854
|
-
when 0x1000000..0xffffffff; "longblob"
|
855
|
-
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
856
|
-
end
|
857
|
-
end
|
858
|
-
|
859
|
-
def version_string
|
860
|
-
full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
865
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
866
|
+
Type::String.new(true: "1", false: "0", **args)
|
861
867
|
end
|
862
|
-
|
863
|
-
class MysqlString < Type::String # :nodoc:
|
864
|
-
def serialize(value)
|
865
|
-
case value
|
866
|
-
when true then "1"
|
867
|
-
when false then "0"
|
868
|
-
else super
|
869
|
-
end
|
870
|
-
end
|
871
|
-
|
872
|
-
private
|
873
|
-
|
874
|
-
def cast_value(value)
|
875
|
-
case value
|
876
|
-
when true then "1"
|
877
|
-
when false then "0"
|
878
|
-
else super
|
879
|
-
end
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
884
868
|
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
885
869
|
end
|
886
870
|
end
|