activerecord 4.2.0 → 6.0.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +852 -801
- data/MIT-LICENSE +4 -2
- data/README.rdoc +14 -13
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +26 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -36
- data/lib/active_record/associations/association.rb +137 -55
- data/lib/active_record/associations/association_scope.rb +110 -132
- data/lib/active_record/associations/belongs_to_association.rb +67 -54
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +150 -275
- data/lib/active_record/associations/collection_proxy.rb +253 -152
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -84
- data/lib/active_record/associations/has_many_through_association.rb +62 -80
- data/lib/active_record/associations/has_one_association.rb +62 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +159 -162
- data/lib/active_record/associations/preloader/association.rb +102 -113
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +96 -95
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +57 -185
- data/lib/active_record/attribute_decorators.rb +39 -17
- data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +90 -84
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -77
- data/lib/active_record/attribute_methods/serialization.rb +40 -21
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
- data/lib/active_record/attribute_methods/write.rb +33 -56
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +213 -74
- data/lib/active_record/autosave_association.rb +125 -54
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +101 -76
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +36 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
- data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -364
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_handling.rb +167 -41
- data/lib/active_record/core.rb +277 -233
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -106
- data/lib/active_record/enum.rb +172 -89
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +22 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +225 -497
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +158 -115
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +99 -98
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/migration/command_recorder.rb +166 -91
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +636 -290
- data/lib/active_record/model_schema.rb +344 -112
- data/lib/active_record/nested_attributes.rb +265 -215
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +559 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +44 -30
- data/lib/active_record/railtie.rb +166 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +341 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +461 -302
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +206 -55
- data/lib/active_record/relation/calculations.rb +270 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +287 -255
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +86 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +112 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +612 -392
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -17
- data/lib/active_record/relation/where_clause.rb +189 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +533 -340
- data/lib/active_record/result.rb +79 -43
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -20
- data/lib/active_record/scoping/default.rb +98 -82
- data/lib/active_record/scoping/named.rb +91 -33
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +90 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +243 -0
- data/lib/active_record/timestamp.rb +86 -41
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +222 -146
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +29 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +77 -23
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +43 -46
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -21
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +256 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +62 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
- data/lib/rails/generators/active_record/migration.rb +30 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +174 -63
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,203 +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
|
-
class Column < ConnectionAdapters::Column # :nodoc:
|
62
|
-
attr_reader :collation, :strict, :extra
|
63
|
-
|
64
|
-
def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
65
|
-
@strict = strict
|
66
|
-
@collation = collation
|
67
|
-
@extra = extra
|
68
|
-
super(name, default, cast_type, sql_type, null)
|
69
|
-
assert_valid_default(default)
|
70
|
-
extract_default
|
71
|
-
end
|
72
|
-
|
73
|
-
def extract_default
|
74
|
-
if blob_or_text_column?
|
75
|
-
@default = null || strict ? nil : ''
|
76
|
-
elsif missing_default_forged_as_empty_string?(@default)
|
77
|
-
@default = nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def has_default?
|
82
|
-
return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
|
83
|
-
super
|
84
|
-
end
|
85
|
-
|
86
|
-
def blob_or_text_column?
|
87
|
-
sql_type =~ /blob/i || type == :text
|
88
|
-
end
|
89
|
-
|
90
|
-
def case_sensitive?
|
91
|
-
collation && !collation.match(/_ci$/)
|
92
|
-
end
|
93
|
-
|
94
|
-
def ==(other)
|
95
|
-
super &&
|
96
|
-
collation == other.collation &&
|
97
|
-
strict == other.strict &&
|
98
|
-
extra == other.extra
|
99
|
-
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
# MySQL misreports NOT NULL column default when none is given.
|
104
|
-
# We can't detect this for columns which may have a legitimate ''
|
105
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
106
|
-
# and the rest).
|
107
|
-
#
|
108
|
-
# Test whether the column has default '', is not null, and is not
|
109
|
-
# a type allowing default ''.
|
110
|
-
def missing_default_forged_as_empty_string?(default)
|
111
|
-
type != :string && !null && default == ''
|
112
|
-
end
|
113
|
-
|
114
|
-
def assert_valid_default(default)
|
115
|
-
if blob_or_text_column? && default.present?
|
116
|
-
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def attributes_for_hash
|
121
|
-
super + [collation, strict, extra]
|
122
|
-
end
|
123
|
-
end
|
17
|
+
include MySQL::Quoting
|
18
|
+
include MySQL::SchemaStatements
|
124
19
|
|
125
20
|
##
|
126
21
|
# :singleton-method:
|
127
|
-
# By default, the
|
128
|
-
# as boolean. If you wish to disable this emulation
|
129
|
-
# 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
|
130
24
|
# to your application.rb file:
|
131
25
|
#
|
132
|
-
# ActiveRecord::ConnectionAdapters::
|
133
|
-
class_attribute :emulate_booleans
|
134
|
-
self.emulate_booleans = true
|
135
|
-
|
136
|
-
LOST_CONNECTION_ERROR_MESSAGES = [
|
137
|
-
"Server shutdown in progress",
|
138
|
-
"Broken pipe",
|
139
|
-
"Lost connection to MySQL server during query",
|
140
|
-
"MySQL server has gone away" ]
|
141
|
-
|
142
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
26
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
27
|
+
class_attribute :emulate_booleans, default: true
|
143
28
|
|
144
29
|
NATIVE_DATABASE_TYPES = {
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
:
|
155
|
-
:
|
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" },
|
156
44
|
}
|
157
45
|
|
158
|
-
|
159
|
-
|
46
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
47
|
+
private
|
48
|
+
def dealloc(stmt)
|
49
|
+
stmt.close
|
50
|
+
end
|
51
|
+
end
|
160
52
|
|
161
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
162
53
|
def initialize(connection, logger, connection_options, config)
|
163
|
-
super(connection, logger)
|
164
|
-
|
165
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
54
|
+
super(connection, logger, config)
|
55
|
+
end
|
166
56
|
|
167
|
-
|
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
|
168
62
|
|
169
|
-
|
170
|
-
|
171
|
-
else
|
172
|
-
@prepared_statements = false
|
173
|
-
end
|
63
|
+
def mariadb? # :nodoc:
|
64
|
+
/mariadb/i.match?(full_version)
|
174
65
|
end
|
175
66
|
|
176
|
-
|
177
|
-
def supports_migrations?
|
67
|
+
def supports_bulk_alter?
|
178
68
|
true
|
179
69
|
end
|
180
70
|
|
181
|
-
def
|
182
|
-
|
71
|
+
def supports_index_sort_order?
|
72
|
+
!mariadb? && database_version >= "8.0.1"
|
183
73
|
end
|
184
74
|
|
185
|
-
def
|
186
|
-
|
75
|
+
def supports_expression_index?
|
76
|
+
!mariadb? && database_version >= "8.0.13"
|
187
77
|
end
|
188
78
|
|
189
|
-
|
190
|
-
# but at the moment (5.5) it doesn't yet implement them
|
191
|
-
def supports_index_sort_order?
|
79
|
+
def supports_transaction_isolation?
|
192
80
|
true
|
193
81
|
end
|
194
82
|
|
195
|
-
|
196
|
-
|
197
|
-
#
|
198
|
-
# http://bugs.mysql.com/bug.php?id=39170
|
199
|
-
def supports_transaction_isolation?
|
200
|
-
version[0] >= 5
|
83
|
+
def supports_explain?
|
84
|
+
true
|
201
85
|
end
|
202
86
|
|
203
87
|
def supports_indexes_in_create?
|
@@ -209,73 +93,76 @@ module ActiveRecord
|
|
209
93
|
end
|
210
94
|
|
211
95
|
def supports_views?
|
212
|
-
|
96
|
+
true
|
213
97
|
end
|
214
98
|
|
215
|
-
def
|
216
|
-
|
99
|
+
def supports_datetime_with_precision?
|
100
|
+
mariadb? || database_version >= "5.6.4"
|
217
101
|
end
|
218
102
|
|
219
|
-
def
|
220
|
-
|
103
|
+
def supports_virtual_columns?
|
104
|
+
mariadb? || database_version >= "5.7.5"
|
221
105
|
end
|
222
106
|
|
223
|
-
#
|
224
|
-
|
225
|
-
|
226
|
-
# this method must be implemented to provide a uniform interface.
|
227
|
-
def each_hash(result) # :nodoc:
|
228
|
-
raise NotImplementedError
|
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"
|
229
110
|
end
|
230
111
|
|
231
|
-
def
|
232
|
-
|
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
|
233
118
|
end
|
234
119
|
|
235
|
-
|
236
|
-
|
237
|
-
def error_number(exception) # :nodoc:
|
238
|
-
raise NotImplementedError
|
120
|
+
def supports_advisory_locks?
|
121
|
+
true
|
239
122
|
end
|
240
123
|
|
241
|
-
|
124
|
+
def supports_insert_on_duplicate_skip?
|
125
|
+
true
|
126
|
+
end
|
242
127
|
|
243
|
-
def
|
244
|
-
|
245
|
-
"x'#{value.hex}'"
|
246
|
-
else
|
247
|
-
super
|
248
|
-
end
|
128
|
+
def supports_insert_on_duplicate_update?
|
129
|
+
true
|
249
130
|
end
|
250
131
|
|
251
|
-
def
|
252
|
-
|
132
|
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
133
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
253
134
|
end
|
254
135
|
|
255
|
-
def
|
256
|
-
|
136
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
137
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
257
138
|
end
|
258
139
|
|
259
|
-
def
|
260
|
-
|
140
|
+
def native_database_types
|
141
|
+
NATIVE_DATABASE_TYPES
|
261
142
|
end
|
262
143
|
|
263
|
-
def
|
264
|
-
|
144
|
+
def index_algorithms
|
145
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
265
146
|
end
|
266
147
|
|
267
|
-
|
268
|
-
|
148
|
+
# HELPER METHODS ===========================================
|
149
|
+
|
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
|
269
154
|
end
|
270
155
|
|
271
|
-
|
272
|
-
|
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
|
273
160
|
end
|
274
161
|
|
275
162
|
# REFERENTIAL INTEGRITY ====================================
|
276
163
|
|
277
164
|
def disable_referential_integrity #:nodoc:
|
278
|
-
old =
|
165
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
279
166
|
|
280
167
|
begin
|
281
168
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -285,30 +172,42 @@ module ActiveRecord
|
|
285
172
|
end
|
286
173
|
end
|
287
174
|
|
175
|
+
# CONNECTION MANAGEMENT ====================================
|
176
|
+
|
177
|
+
def clear_cache! # :nodoc:
|
178
|
+
reload_type_map
|
179
|
+
super
|
180
|
+
end
|
181
|
+
|
288
182
|
#--
|
289
183
|
# DATABASE STATEMENTS ======================================
|
290
184
|
#++
|
291
185
|
|
292
|
-
def
|
293
|
-
|
294
|
-
|
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)
|
295
193
|
end
|
296
194
|
|
297
195
|
# Executes the SQL statement in the context of this connection.
|
298
196
|
def execute(sql, name = nil)
|
299
|
-
|
300
|
-
end
|
197
|
+
materialize_transactions
|
301
198
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
199
|
+
log(sql, name) do
|
200
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
201
|
+
@connection.query(sql)
|
202
|
+
end
|
203
|
+
end
|
307
204
|
end
|
308
205
|
|
309
|
-
|
310
|
-
|
311
|
-
|
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)
|
312
211
|
end
|
313
212
|
|
314
213
|
def begin_db_transaction
|
@@ -324,23 +223,11 @@ module ActiveRecord
|
|
324
223
|
execute "COMMIT"
|
325
224
|
end
|
326
225
|
|
327
|
-
def
|
226
|
+
def exec_rollback_db_transaction #:nodoc:
|
328
227
|
execute "ROLLBACK"
|
329
228
|
end
|
330
229
|
|
331
|
-
|
332
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
333
|
-
# these, we must use a subquery.
|
334
|
-
def join_to_update(update, select) #:nodoc:
|
335
|
-
if select.limit || select.offset || select.orders.any?
|
336
|
-
super
|
337
|
-
else
|
338
|
-
update.table select.source
|
339
|
-
update.wheres = select.constraints
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
def empty_insert_statement_value
|
230
|
+
def empty_insert_statement_value(primary_key = nil)
|
344
231
|
"VALUES ()"
|
345
232
|
end
|
346
233
|
|
@@ -356,7 +243,7 @@ module ActiveRecord
|
|
356
243
|
end
|
357
244
|
|
358
245
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
359
|
-
# Charset defaults to
|
246
|
+
# Charset defaults to utf8mb4.
|
360
247
|
#
|
361
248
|
# Example:
|
362
249
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -364,9 +251,13 @@ module ActiveRecord
|
|
364
251
|
# create_database 'matt_development', charset: :big5
|
365
252
|
def create_database(name, options = {})
|
366
253
|
if options[:collation]
|
367
|
-
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`"
|
368
259
|
else
|
369
|
-
|
260
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
370
261
|
end
|
371
262
|
end
|
372
263
|
|
@@ -375,106 +266,38 @@ module ActiveRecord
|
|
375
266
|
# Example:
|
376
267
|
# drop_database('sebastian_development')
|
377
268
|
def drop_database(name) #:nodoc:
|
378
|
-
execute "DROP DATABASE IF EXISTS
|
269
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
379
270
|
end
|
380
271
|
|
381
272
|
def current_database
|
382
|
-
|
273
|
+
query_value("SELECT database()", "SCHEMA")
|
383
274
|
end
|
384
275
|
|
385
276
|
# Returns the database character set.
|
386
277
|
def charset
|
387
|
-
show_variable
|
278
|
+
show_variable "character_set_database"
|
388
279
|
end
|
389
280
|
|
390
281
|
# Returns the database collation strategy.
|
391
282
|
def collation
|
392
|
-
show_variable
|
393
|
-
end
|
394
|
-
|
395
|
-
def tables(name = nil, database = nil, like = nil) #:nodoc:
|
396
|
-
sql = "SHOW TABLES "
|
397
|
-
sql << "IN #{quote_table_name(database)} " if database
|
398
|
-
sql << "LIKE #{quote(like)}" if like
|
399
|
-
|
400
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
401
|
-
result.collect { |field| field.first }
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
def truncate(table_name, name = nil)
|
406
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
407
|
-
end
|
408
|
-
|
409
|
-
def table_exists?(name)
|
410
|
-
return false unless name.present?
|
411
|
-
return true if tables(nil, nil, name).any?
|
412
|
-
|
413
|
-
name = name.to_s
|
414
|
-
schema, table = name.split('.', 2)
|
415
|
-
|
416
|
-
unless table # A table was provided without a schema
|
417
|
-
table = schema
|
418
|
-
schema = nil
|
419
|
-
end
|
420
|
-
|
421
|
-
tables(nil, schema, table).any?
|
283
|
+
show_variable "collation_database"
|
422
284
|
end
|
423
285
|
|
424
|
-
|
425
|
-
|
426
|
-
indexes = []
|
427
|
-
current_index = nil
|
428
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
429
|
-
each_hash(result) do |row|
|
430
|
-
if current_index != row[:Key_name]
|
431
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
432
|
-
current_index = row[:Key_name]
|
286
|
+
def table_comment(table_name) # :nodoc:
|
287
|
+
scope = quoted_scope(table_name)
|
433
288
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
indexes.last.columns << row[:Column_name]
|
441
|
-
indexes.last.lengths << row[:Sub_part]
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
indexes
|
446
|
-
end
|
447
|
-
|
448
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
449
|
-
def columns(table_name)#:nodoc:
|
450
|
-
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
451
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
452
|
-
each_hash(result).map do |field|
|
453
|
-
field_name = set_field_encoding(field[:Field])
|
454
|
-
sql_type = field[:Type]
|
455
|
-
cast_type = lookup_cast_type(sql_type)
|
456
|
-
new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
def create_table(table_name, options = {}) #:nodoc:
|
462
|
-
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
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
|
463
295
|
end
|
464
296
|
|
465
|
-
def
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
if respond_to?(method, true)
|
471
|
-
send(method, table, *arguments)
|
472
|
-
else
|
473
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
474
|
-
end
|
475
|
-
end.join(", ")
|
476
|
-
|
477
|
-
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)}")
|
478
301
|
end
|
479
302
|
|
480
303
|
# Renames a table.
|
@@ -486,398 +309,511 @@ module ActiveRecord
|
|
486
309
|
rename_table_indexes(table_name, new_name)
|
487
310
|
end
|
488
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.
|
489
327
|
def drop_table(table_name, options = {})
|
490
|
-
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}"
|
491
329
|
end
|
492
330
|
|
493
331
|
def rename_index(table_name, old_name, new_name)
|
494
332
|
if supports_rename_index?
|
333
|
+
validate_index_length!(table_name, new_name)
|
334
|
+
|
495
335
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
496
336
|
else
|
497
337
|
super
|
498
338
|
end
|
499
339
|
end
|
500
340
|
|
501
|
-
def change_column_default(table_name, column_name,
|
502
|
-
|
503
|
-
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
|
504
344
|
end
|
505
345
|
|
506
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
507
|
-
column = column_for(table_name, column_name)
|
508
|
-
|
346
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
509
347
|
unless null || default.nil?
|
510
348
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
511
349
|
end
|
512
350
|
|
513
|
-
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
|
514
357
|
end
|
515
358
|
|
516
359
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
517
|
-
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)}")
|
518
361
|
end
|
519
362
|
|
520
363
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
521
|
-
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)}")
|
522
365
|
rename_column_indexes(table_name, column_name, new_column_name)
|
523
366
|
end
|
524
367
|
|
525
368
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
526
|
-
index_name, index_type, index_columns,
|
527
|
-
|
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
|
528
377
|
end
|
529
378
|
|
530
379
|
def foreign_keys(table_name)
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
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]}
|
540
399
|
SQL
|
541
400
|
|
542
|
-
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
543
|
-
|
544
401
|
fk_info.map do |row|
|
545
402
|
options = {
|
546
|
-
column: row[
|
547
|
-
name: row[
|
548
|
-
primary_key: row[
|
403
|
+
column: row["column"],
|
404
|
+
name: row["name"],
|
405
|
+
primary_key: row["primary_key"]
|
549
406
|
}
|
550
407
|
|
551
|
-
options[:on_update] = extract_foreign_key_action(
|
552
|
-
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"])
|
553
410
|
|
554
|
-
ForeignKeyDefinition.new(table_name, row[
|
411
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
555
412
|
end
|
556
413
|
end
|
557
414
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
576
|
-
end
|
577
|
-
when 'text'
|
578
|
-
case limit
|
579
|
-
when 0..0xff; 'tinytext'
|
580
|
-
when nil, 0x100..0xffff; 'text'
|
581
|
-
when 0x10000..0xffffff; 'mediumtext'
|
582
|
-
when 0x1000000..0xffffffff; 'longtext'
|
583
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
584
|
-
end
|
585
|
-
else
|
586
|
-
super
|
415
|
+
def table_options(table_name) # :nodoc:
|
416
|
+
table_options = {}
|
417
|
+
|
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)
|
587
432
|
end
|
433
|
+
|
434
|
+
table_options
|
588
435
|
end
|
589
436
|
|
590
437
|
# SHOW VARIABLES LIKE 'name'
|
591
438
|
def show_variable(name)
|
592
|
-
|
593
|
-
|
439
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
440
|
+
rescue ActiveRecord::StatementInvalid
|
441
|
+
nil
|
594
442
|
end
|
595
443
|
|
596
|
-
|
597
|
-
|
598
|
-
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
599
|
-
create_table = each_hash(result).first[:"Create Table"]
|
600
|
-
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
601
|
-
keys = $1.split(",").map { |key| key.delete('`"') }
|
602
|
-
keys.length == 1 ? [keys.first, nil] : nil
|
603
|
-
else
|
604
|
-
nil
|
605
|
-
end
|
606
|
-
end
|
607
|
-
end
|
444
|
+
def primary_keys(table_name) # :nodoc:
|
445
|
+
raise ArgumentError unless table_name.present?
|
608
446
|
|
609
|
-
|
610
|
-
def primary_key(table)
|
611
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
612
|
-
pk_and_sequence && pk_and_sequence.first
|
613
|
-
end
|
447
|
+
scope = quoted_scope(table_name)
|
614
448
|
|
615
|
-
|
616
|
-
|
617
|
-
|
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
|
618
457
|
end
|
619
458
|
|
620
|
-
def
|
621
|
-
|
622
|
-
|
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))
|
623
469
|
else
|
624
470
|
super
|
625
471
|
end
|
626
472
|
end
|
627
473
|
|
628
|
-
def
|
629
|
-
|
630
|
-
|
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))
|
631
479
|
else
|
632
|
-
|
480
|
+
super
|
633
481
|
end
|
634
482
|
end
|
635
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
|
+
|
489
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
490
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
491
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
492
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
493
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
494
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
495
|
+
# Convert Arel node to string
|
496
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
497
|
+
# Remove any ASC/DESC modifiers
|
498
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
499
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
500
|
+
|
501
|
+
(order_columns << super).join(", ")
|
502
|
+
end
|
503
|
+
|
636
504
|
def strict_mode?
|
637
505
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
638
506
|
end
|
639
507
|
|
640
|
-
def
|
641
|
-
|
508
|
+
def default_index_type?(index) # :nodoc:
|
509
|
+
index.using == :btree || super
|
642
510
|
end
|
643
511
|
|
644
|
-
|
645
|
-
|
646
|
-
def initialize_type_map(m) # :nodoc:
|
647
|
-
super
|
512
|
+
def build_insert_sql(insert) # :nodoc:
|
513
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
648
514
|
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
656
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
657
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
658
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
659
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
660
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
661
|
-
|
662
|
-
register_integer_type m, %r(^bigint)i, limit: 8
|
663
|
-
register_integer_type m, %r(^int)i, limit: 4
|
664
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
665
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
666
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
667
|
-
|
668
|
-
m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
|
669
|
-
m.alias_type %r(set)i, 'varchar'
|
670
|
-
m.alias_type %r(year)i, 'integer'
|
671
|
-
m.alias_type %r(bit)i, 'binary'
|
672
|
-
|
673
|
-
m.register_type(%r(enum)i) do |sql_type|
|
674
|
-
limit = sql_type[/^enum\((.+)\)/i, 1]
|
675
|
-
.split(',').map{|enum| enum.strip.length - 2}.max
|
676
|
-
MysqlString.new(limit: limit)
|
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(",")
|
677
521
|
end
|
522
|
+
|
523
|
+
sql
|
678
524
|
end
|
679
525
|
|
680
|
-
def
|
681
|
-
|
682
|
-
|
683
|
-
Type::UnsignedInteger.new(options)
|
684
|
-
else
|
685
|
-
Type::Integer.new(options)
|
686
|
-
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."
|
687
529
|
end
|
688
530
|
end
|
689
531
|
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
subsubselect = select.clone
|
694
|
-
subsubselect.projections = [key]
|
532
|
+
private
|
533
|
+
def initialize_type_map(m = type_map)
|
534
|
+
super
|
695
535
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
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
|
700
564
|
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
|
706
|
-
when Fixnum
|
707
|
-
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)
|
708
569
|
end
|
709
570
|
end
|
710
571
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
# add index sort order
|
721
|
-
option_strings = add_index_sort_order(option_strings, column_names, options)
|
722
|
-
|
723
|
-
column_names.map {|name| quote_column_name(name) + option_strings[name]}
|
724
|
-
end
|
725
|
-
|
726
|
-
def translate_exception(exception, message)
|
727
|
-
case error_number(exception)
|
728
|
-
when 1062
|
729
|
-
RecordNotUnique.new(message, exception)
|
730
|
-
when 1452
|
731
|
-
InvalidForeignKey.new(message, exception)
|
732
|
-
else
|
733
|
-
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
|
734
580
|
end
|
735
|
-
end
|
736
|
-
|
737
|
-
def add_column_sql(table_name, column_name, type, options = {})
|
738
|
-
td = create_table_definition table_name, options[:temporary], options[:options]
|
739
|
-
cd = td.new_column_definition(column_name, type, options)
|
740
|
-
schema_creation.visit_AddColumn cd
|
741
|
-
end
|
742
|
-
|
743
|
-
def change_column_sql(table_name, column_name, type, options = {})
|
744
|
-
column = column_for(table_name, column_name)
|
745
581
|
|
746
|
-
|
747
|
-
|
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
|
748
588
|
end
|
749
589
|
|
750
|
-
|
751
|
-
|
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
|
752
640
|
end
|
753
641
|
|
754
|
-
options
|
755
|
-
|
756
|
-
|
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
|
757
645
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
name: new_column_name,
|
762
|
-
default: column.default,
|
763
|
-
null: column.null,
|
764
|
-
auto_increment: column.extra == "auto_increment"
|
765
|
-
}
|
646
|
+
unless options.key?(:default)
|
647
|
+
options[:default] = column.default
|
648
|
+
end
|
766
649
|
|
767
|
-
|
768
|
-
|
769
|
-
|
650
|
+
unless options.key?(:null)
|
651
|
+
options[:null] = column.null
|
652
|
+
end
|
770
653
|
|
771
|
-
|
772
|
-
|
773
|
-
|
654
|
+
unless options.key?(:comment)
|
655
|
+
options[:comment] = column.comment
|
656
|
+
end
|
774
657
|
|
775
|
-
|
776
|
-
|
777
|
-
|
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
|
778
662
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
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
|
+
}
|
783
670
|
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
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
|
788
676
|
|
789
|
-
|
790
|
-
|
791
|
-
|
677
|
+
def add_index_for_alter(table_name, column_name, options = {})
|
678
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, **options)
|
679
|
+
index_algorithm[0, 0] = ", " if index_algorithm.present?
|
680
|
+
sql = +"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
681
|
+
add_sql_comment!(sql, comment)
|
682
|
+
end
|
792
683
|
|
793
|
-
|
794
|
-
|
795
|
-
|
684
|
+
def remove_index_for_alter(table_name, options = {})
|
685
|
+
index_name = index_name_for_remove(table_name, options)
|
686
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
687
|
+
end
|
796
688
|
|
797
|
-
|
689
|
+
def supports_rename_index?
|
690
|
+
if mariadb?
|
691
|
+
database_version >= "10.5.2"
|
692
|
+
else
|
693
|
+
database_version >= "5.7.6"
|
694
|
+
end
|
695
|
+
end
|
798
696
|
|
799
|
-
|
800
|
-
|
801
|
-
end
|
697
|
+
def configure_connection
|
698
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
802
699
|
|
803
|
-
|
804
|
-
|
805
|
-
end
|
700
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
701
|
+
variables["sql_auto_is_null"] = 0
|
806
702
|
|
807
|
-
|
808
|
-
|
809
|
-
|
703
|
+
# Increase timeout so the server doesn't disconnect us.
|
704
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
705
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
706
|
+
variables["wait_timeout"] = wait_timeout
|
810
707
|
|
811
|
-
|
812
|
-
variables = @config.fetch(:variables, {}).stringify_keys
|
708
|
+
defaults = [":default", :default].to_set
|
813
709
|
|
814
|
-
|
815
|
-
|
816
|
-
|
710
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
711
|
+
# https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
|
712
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
713
|
+
if sql_mode = variables.delete("sql_mode")
|
714
|
+
sql_mode = quote(sql_mode)
|
715
|
+
elsif !defaults.include?(strict_mode?)
|
716
|
+
if strict_mode?
|
717
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
718
|
+
else
|
719
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
720
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
721
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
722
|
+
end
|
723
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
724
|
+
end
|
725
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
726
|
+
|
727
|
+
# NAMES does not have an equals sign, see
|
728
|
+
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
729
|
+
# (trailing comma because variable_assignments will always have content)
|
730
|
+
if @config[:encoding]
|
731
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
732
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
733
|
+
encoding << ", "
|
734
|
+
end
|
817
735
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
736
|
+
# Gather up all of the SET variables...
|
737
|
+
variable_assignments = variables.map do |k, v|
|
738
|
+
if defaults.include?(v)
|
739
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
740
|
+
elsif !v.nil?
|
741
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
742
|
+
end
|
743
|
+
# or else nil; compact to clear nils out
|
744
|
+
end.compact.join(", ")
|
822
745
|
|
823
|
-
|
824
|
-
|
825
|
-
# If the user has provided another value for sql_mode, don't replace it.
|
826
|
-
unless variables.has_key?('sql_mode')
|
827
|
-
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
|
746
|
+
# ...and send them all in one query
|
747
|
+
execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
828
748
|
end
|
829
749
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
encoding = "NAMES #{@config[:encoding]}"
|
835
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
836
|
-
encoding << ", "
|
750
|
+
def column_definitions(table_name) # :nodoc:
|
751
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
752
|
+
each_hash(result)
|
753
|
+
end
|
837
754
|
end
|
838
755
|
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
843
|
-
elsif !v.nil?
|
844
|
-
"@@SESSION.#{k} = #{quote(v)}"
|
845
|
-
end
|
846
|
-
# or else nil; compact to clear nils out
|
847
|
-
end.compact.join(', ')
|
756
|
+
def create_table_info(table_name) # :nodoc:
|
757
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
758
|
+
end
|
848
759
|
|
849
|
-
|
850
|
-
|
851
|
-
|
760
|
+
def arel_visitor
|
761
|
+
Arel::Visitors::MySQL.new(self)
|
762
|
+
end
|
852
763
|
|
853
|
-
|
854
|
-
|
855
|
-
case $1
|
856
|
-
when 'CASCADE'; :cascade
|
857
|
-
when 'SET NULL'; :nullify
|
858
|
-
end
|
764
|
+
def build_statement_pool
|
765
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
859
766
|
end
|
860
|
-
end
|
861
767
|
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
768
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
769
|
+
match = %r/
|
770
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
771
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
772
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
773
|
+
/xmi.match(sql)
|
774
|
+
|
775
|
+
options = {
|
776
|
+
message: message,
|
777
|
+
sql: sql,
|
778
|
+
binds: binds,
|
779
|
+
}
|
780
|
+
|
781
|
+
if match
|
782
|
+
options[:table] = match[:table]
|
783
|
+
options[:foreign_key] = match[:foreign_key]
|
784
|
+
options[:target_table] = match[:target_table]
|
785
|
+
options[:primary_key] = match[:primary_key]
|
786
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
868
787
|
end
|
788
|
+
|
789
|
+
MismatchedForeignKey.new(**options)
|
869
790
|
end
|
870
791
|
|
871
|
-
|
792
|
+
def version_string(full_version_string)
|
793
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
794
|
+
end
|
872
795
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
796
|
+
class MysqlString < Type::String # :nodoc:
|
797
|
+
def serialize(value)
|
798
|
+
case value
|
799
|
+
when true then "1"
|
800
|
+
when false then "0"
|
801
|
+
else super
|
802
|
+
end
|
878
803
|
end
|
804
|
+
|
805
|
+
private
|
806
|
+
def cast_value(value)
|
807
|
+
case value
|
808
|
+
when true then "1"
|
809
|
+
when false then "0"
|
810
|
+
else super
|
811
|
+
end
|
812
|
+
end
|
879
813
|
end
|
880
|
-
|
814
|
+
|
815
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
816
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
881
817
|
end
|
882
818
|
end
|
883
819
|
end
|