activerecord 4.2.0 → 6.0.0
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 +612 -971
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -248
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +135 -56
- data/lib/active_record/associations/association_scope.rb +103 -131
- 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 +60 -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 +138 -274
- data/lib/active_record/associations/collection_proxy.rb +252 -151
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +35 -83
- 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 +38 -80
- 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 +138 -162
- data/lib/active_record/associations/preloader/association.rb +90 -119
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +92 -94
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1737 -1596
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +15 -5
- data/lib/active_record/attribute_methods/dirty.rb +174 -134
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
- data/lib/active_record/attribute_methods/write.rb +33 -55
- data/lib/active_record/attribute_methods.rb +124 -143
- data/lib/active_record/attributes.rb +214 -74
- data/lib/active_record/autosave_association.rb +115 -46
- data/lib/active_record/base.rb +60 -49
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +247 -108
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
- 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 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +366 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +706 -222
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -87
- data/lib/active_record/connection_adapters/abstract_adapter.rb +468 -194
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +535 -597
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- 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 +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +65 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -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 +9 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- 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 +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -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 +10 -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 +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +474 -286
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -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 +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -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 +288 -359
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +266 -233
- data/lib/active_record/counter_cache.rb +68 -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 +79 -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 -105
- data/lib/active_record/enum.rb +164 -88
- data/lib/active_record/errors.rb +189 -53
- data/lib/active_record/explain.rb +23 -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 +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +226 -495
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +158 -112
- 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 +91 -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 +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +634 -288
- data/lib/active_record/model_schema.rb +314 -112
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +559 -124
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +148 -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 +338 -202
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +460 -299
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/calculations.rb +269 -248
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +279 -255
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +83 -69
- 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 +116 -92
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +574 -391
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +518 -340
- data/lib/active_record/result.rb +79 -42
- 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 +101 -84
- data/lib/active_record/scoping/named.rb +86 -33
- data/lib/active_record/scoping.rb +45 -26
- 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 +75 -0
- data/lib/active_record/tasks/database_tasks.rb +309 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +58 -88
- data/lib/active_record/tasks/postgresql_database_tasks.rb +82 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +215 -139
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -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 +13 -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 +30 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +78 -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 +13 -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 +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +43 -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 +257 -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 +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -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 +31 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +166 -60
- 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/infinity.rb +0 -13
- 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,88 @@
|
|
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
|
+
|
49
|
+
def dealloc(stmt)
|
50
|
+
stmt.close
|
51
|
+
end
|
52
|
+
end
|
160
53
|
|
161
|
-
# FIXME: Make the first parameter more similar for the two adapters
|
162
54
|
def initialize(connection, logger, connection_options, config)
|
163
|
-
super(connection, logger)
|
164
|
-
|
165
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
55
|
+
super(connection, logger, config)
|
56
|
+
end
|
166
57
|
|
167
|
-
|
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)
|
62
|
+
end
|
168
63
|
|
169
|
-
|
170
|
-
|
171
|
-
else
|
172
|
-
@prepared_statements = false
|
173
|
-
end
|
64
|
+
def mariadb? # :nodoc:
|
65
|
+
/mariadb/i.match?(full_version)
|
174
66
|
end
|
175
67
|
|
176
|
-
|
177
|
-
def supports_migrations?
|
68
|
+
def supports_bulk_alter?
|
178
69
|
true
|
179
70
|
end
|
180
71
|
|
181
|
-
def
|
182
|
-
|
72
|
+
def supports_index_sort_order?
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
183
74
|
end
|
184
75
|
|
185
|
-
def
|
186
|
-
|
76
|
+
def supports_expression_index?
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
187
78
|
end
|
188
79
|
|
189
|
-
|
190
|
-
# but at the moment (5.5) it doesn't yet implement them
|
191
|
-
def supports_index_sort_order?
|
80
|
+
def supports_transaction_isolation?
|
192
81
|
true
|
193
82
|
end
|
194
83
|
|
195
|
-
|
196
|
-
|
197
|
-
#
|
198
|
-
# http://bugs.mysql.com/bug.php?id=39170
|
199
|
-
def supports_transaction_isolation?
|
200
|
-
version[0] >= 5
|
84
|
+
def supports_explain?
|
85
|
+
true
|
201
86
|
end
|
202
87
|
|
203
88
|
def supports_indexes_in_create?
|
@@ -209,73 +94,68 @@ module ActiveRecord
|
|
209
94
|
end
|
210
95
|
|
211
96
|
def supports_views?
|
212
|
-
|
97
|
+
true
|
213
98
|
end
|
214
99
|
|
215
|
-
def
|
216
|
-
|
100
|
+
def supports_datetime_with_precision?
|
101
|
+
mariadb? || database_version >= "5.6.4"
|
217
102
|
end
|
218
103
|
|
219
|
-
def
|
220
|
-
|
104
|
+
def supports_virtual_columns?
|
105
|
+
mariadb? || database_version >= "5.7.5"
|
221
106
|
end
|
222
107
|
|
223
|
-
#
|
224
|
-
|
225
|
-
|
226
|
-
# this method must be implemented to provide a uniform interface.
|
227
|
-
def each_hash(result) # :nodoc:
|
228
|
-
raise NotImplementedError
|
108
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
109
|
+
def supports_optimizer_hints?
|
110
|
+
!mariadb? && database_version >= "5.7.7"
|
229
111
|
end
|
230
112
|
|
231
|
-
def
|
232
|
-
|
113
|
+
def supports_advisory_locks?
|
114
|
+
true
|
233
115
|
end
|
234
116
|
|
235
|
-
|
236
|
-
|
237
|
-
def error_number(exception) # :nodoc:
|
238
|
-
raise NotImplementedError
|
117
|
+
def supports_insert_on_duplicate_skip?
|
118
|
+
true
|
239
119
|
end
|
240
120
|
|
241
|
-
|
242
|
-
|
243
|
-
def _quote(value) # :nodoc:
|
244
|
-
if value.is_a?(Type::Binary::Data)
|
245
|
-
"x'#{value.hex}'"
|
246
|
-
else
|
247
|
-
super
|
248
|
-
end
|
121
|
+
def supports_insert_on_duplicate_update?
|
122
|
+
true
|
249
123
|
end
|
250
124
|
|
251
|
-
def
|
252
|
-
|
125
|
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
126
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
253
127
|
end
|
254
128
|
|
255
|
-
def
|
256
|
-
|
129
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
130
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
257
131
|
end
|
258
132
|
|
259
|
-
def
|
260
|
-
|
133
|
+
def native_database_types
|
134
|
+
NATIVE_DATABASE_TYPES
|
261
135
|
end
|
262
136
|
|
263
|
-
def
|
264
|
-
|
137
|
+
def index_algorithms
|
138
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
265
139
|
end
|
266
140
|
|
267
|
-
|
268
|
-
|
141
|
+
# HELPER METHODS ===========================================
|
142
|
+
|
143
|
+
# The two drivers have slightly different ways of yielding hashes of results, so
|
144
|
+
# this method must be implemented to provide a uniform interface.
|
145
|
+
def each_hash(result) # :nodoc:
|
146
|
+
raise NotImplementedError
|
269
147
|
end
|
270
148
|
|
271
|
-
|
272
|
-
|
149
|
+
# Must return the MySQL error number from the exception, if the exception has an
|
150
|
+
# error number.
|
151
|
+
def error_number(exception) # :nodoc:
|
152
|
+
raise NotImplementedError
|
273
153
|
end
|
274
154
|
|
275
155
|
# REFERENTIAL INTEGRITY ====================================
|
276
156
|
|
277
157
|
def disable_referential_integrity #:nodoc:
|
278
|
-
old =
|
158
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
279
159
|
|
280
160
|
begin
|
281
161
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -285,30 +165,42 @@ module ActiveRecord
|
|
285
165
|
end
|
286
166
|
end
|
287
167
|
|
168
|
+
# CONNECTION MANAGEMENT ====================================
|
169
|
+
|
170
|
+
def clear_cache! # :nodoc:
|
171
|
+
reload_type_map
|
172
|
+
super
|
173
|
+
end
|
174
|
+
|
288
175
|
#--
|
289
176
|
# DATABASE STATEMENTS ======================================
|
290
177
|
#++
|
291
178
|
|
292
|
-
def
|
293
|
-
|
294
|
-
|
179
|
+
def explain(arel, binds = [])
|
180
|
+
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
181
|
+
start = Concurrent.monotonic_time
|
182
|
+
result = exec_query(sql, "EXPLAIN", binds)
|
183
|
+
elapsed = Concurrent.monotonic_time - start
|
184
|
+
|
185
|
+
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
295
186
|
end
|
296
187
|
|
297
188
|
# Executes the SQL statement in the context of this connection.
|
298
189
|
def execute(sql, name = nil)
|
299
|
-
|
300
|
-
end
|
190
|
+
materialize_transactions
|
301
191
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
192
|
+
log(sql, name) do
|
193
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
194
|
+
@connection.query(sql)
|
195
|
+
end
|
196
|
+
end
|
307
197
|
end
|
308
198
|
|
309
|
-
|
310
|
-
|
311
|
-
|
199
|
+
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
200
|
+
# to write stuff in an abstract way without concerning ourselves about whether it
|
201
|
+
# needs to be explicitly freed or not.
|
202
|
+
def execute_and_free(sql, name = nil) # :nodoc:
|
203
|
+
yield execute(sql, name)
|
312
204
|
end
|
313
205
|
|
314
206
|
def begin_db_transaction
|
@@ -324,23 +216,11 @@ module ActiveRecord
|
|
324
216
|
execute "COMMIT"
|
325
217
|
end
|
326
218
|
|
327
|
-
def
|
219
|
+
def exec_rollback_db_transaction #:nodoc:
|
328
220
|
execute "ROLLBACK"
|
329
221
|
end
|
330
222
|
|
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
|
223
|
+
def empty_insert_statement_value(primary_key = nil)
|
344
224
|
"VALUES ()"
|
345
225
|
end
|
346
226
|
|
@@ -356,7 +236,7 @@ module ActiveRecord
|
|
356
236
|
end
|
357
237
|
|
358
238
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
359
|
-
# Charset defaults to
|
239
|
+
# Charset defaults to utf8mb4.
|
360
240
|
#
|
361
241
|
# Example:
|
362
242
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -364,9 +244,13 @@ module ActiveRecord
|
|
364
244
|
# create_database 'matt_development', charset: :big5
|
365
245
|
def create_database(name, options = {})
|
366
246
|
if options[:collation]
|
367
|
-
execute "CREATE DATABASE
|
247
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
248
|
+
elsif options[:charset]
|
249
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
250
|
+
elsif row_format_dynamic_by_default?
|
251
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
368
252
|
else
|
369
|
-
|
253
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
370
254
|
end
|
371
255
|
end
|
372
256
|
|
@@ -375,106 +259,38 @@ module ActiveRecord
|
|
375
259
|
# Example:
|
376
260
|
# drop_database('sebastian_development')
|
377
261
|
def drop_database(name) #:nodoc:
|
378
|
-
execute "DROP DATABASE IF EXISTS
|
262
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
379
263
|
end
|
380
264
|
|
381
265
|
def current_database
|
382
|
-
|
266
|
+
query_value("SELECT database()", "SCHEMA")
|
383
267
|
end
|
384
268
|
|
385
269
|
# Returns the database character set.
|
386
270
|
def charset
|
387
|
-
show_variable
|
271
|
+
show_variable "character_set_database"
|
388
272
|
end
|
389
273
|
|
390
274
|
# Returns the database collation strategy.
|
391
275
|
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?
|
276
|
+
show_variable "collation_database"
|
422
277
|
end
|
423
278
|
|
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]
|
279
|
+
def table_comment(table_name) # :nodoc:
|
280
|
+
scope = quoted_scope(table_name)
|
433
281
|
|
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"))
|
282
|
+
query_value(<<~SQL, "SCHEMA").presence
|
283
|
+
SELECT table_comment
|
284
|
+
FROM information_schema.tables
|
285
|
+
WHERE table_schema = #{scope[:schema]}
|
286
|
+
AND table_name = #{scope[:name]}
|
287
|
+
SQL
|
463
288
|
end
|
464
289
|
|
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}")
|
290
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
291
|
+
comment = extract_new_comment_value(comment_or_changes)
|
292
|
+
comment = "" if comment.nil?
|
293
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
478
294
|
end
|
479
295
|
|
480
296
|
# Renames a table.
|
@@ -486,398 +302,520 @@ module ActiveRecord
|
|
486
302
|
rename_table_indexes(table_name, new_name)
|
487
303
|
end
|
488
304
|
|
305
|
+
# Drops a table from the database.
|
306
|
+
#
|
307
|
+
# [<tt>:force</tt>]
|
308
|
+
# Set to +:cascade+ to drop dependent objects as well.
|
309
|
+
# Defaults to false.
|
310
|
+
# [<tt>:if_exists</tt>]
|
311
|
+
# Set to +true+ to only drop the table if it exists.
|
312
|
+
# Defaults to false.
|
313
|
+
# [<tt>:temporary</tt>]
|
314
|
+
# Set to +true+ to drop temporary table.
|
315
|
+
# Defaults to false.
|
316
|
+
#
|
317
|
+
# Although this command ignores most +options+ and the block if one is given,
|
318
|
+
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
319
|
+
# In that case, +options+ and the block will be used by create_table.
|
489
320
|
def drop_table(table_name, options = {})
|
490
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
321
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
491
322
|
end
|
492
323
|
|
493
324
|
def rename_index(table_name, old_name, new_name)
|
494
325
|
if supports_rename_index?
|
326
|
+
validate_index_length!(table_name, new_name)
|
327
|
+
|
495
328
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
496
329
|
else
|
497
330
|
super
|
498
331
|
end
|
499
332
|
end
|
500
333
|
|
501
|
-
def change_column_default(table_name, column_name,
|
502
|
-
|
503
|
-
change_column table_name, column_name,
|
334
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
335
|
+
default = extract_new_default_value(default_or_changes)
|
336
|
+
change_column table_name, column_name, nil, default: default
|
504
337
|
end
|
505
338
|
|
506
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
507
|
-
column = column_for(table_name, column_name)
|
508
|
-
|
339
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
509
340
|
unless null || default.nil?
|
510
341
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
511
342
|
end
|
512
343
|
|
513
|
-
change_column table_name, column_name,
|
344
|
+
change_column table_name, column_name, nil, null: null
|
345
|
+
end
|
346
|
+
|
347
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
348
|
+
comment = extract_new_comment_value(comment_or_changes)
|
349
|
+
change_column table_name, column_name, nil, comment: comment
|
514
350
|
end
|
515
351
|
|
516
352
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
517
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
353
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
518
354
|
end
|
519
355
|
|
520
356
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
521
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
357
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
522
358
|
rename_column_indexes(table_name, column_name, new_column_name)
|
523
359
|
end
|
524
360
|
|
525
361
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
526
|
-
index_name, index_type, index_columns,
|
527
|
-
|
362
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
363
|
+
sql = +"CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns}) #{index_algorithm}"
|
364
|
+
execute add_sql_comment!(sql, comment)
|
365
|
+
end
|
366
|
+
|
367
|
+
def add_sql_comment!(sql, comment) # :nodoc:
|
368
|
+
sql << " COMMENT #{quote(comment)}" if comment.present?
|
369
|
+
sql
|
528
370
|
end
|
529
371
|
|
530
372
|
def foreign_keys(table_name)
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
373
|
+
raise ArgumentError unless table_name.present?
|
374
|
+
|
375
|
+
scope = quoted_scope(table_name)
|
376
|
+
|
377
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
378
|
+
SELECT fk.referenced_table_name AS 'to_table',
|
379
|
+
fk.referenced_column_name AS 'primary_key',
|
380
|
+
fk.column_name AS 'column',
|
381
|
+
fk.constraint_name AS 'name',
|
382
|
+
rc.update_rule AS 'on_update',
|
383
|
+
rc.delete_rule AS 'on_delete'
|
384
|
+
FROM information_schema.referential_constraints rc
|
385
|
+
JOIN information_schema.key_column_usage fk
|
386
|
+
USING (constraint_schema, constraint_name)
|
387
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
388
|
+
AND fk.table_schema = #{scope[:schema]}
|
389
|
+
AND fk.table_name = #{scope[:name]}
|
390
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
391
|
+
AND rc.table_name = #{scope[:name]}
|
540
392
|
SQL
|
541
393
|
|
542
|
-
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
543
|
-
|
544
394
|
fk_info.map do |row|
|
545
395
|
options = {
|
546
|
-
column: row[
|
547
|
-
name: row[
|
548
|
-
primary_key: row[
|
396
|
+
column: row["column"],
|
397
|
+
name: row["name"],
|
398
|
+
primary_key: row["primary_key"]
|
549
399
|
}
|
550
400
|
|
551
|
-
options[:on_update] = extract_foreign_key_action(
|
552
|
-
options[:on_delete] = extract_foreign_key_action(
|
401
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
402
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
553
403
|
|
554
|
-
ForeignKeyDefinition.new(table_name, row[
|
404
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
555
405
|
end
|
556
406
|
end
|
557
407
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
when 5..8; 'bigint'
|
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
|
408
|
+
def table_options(table_name) # :nodoc:
|
409
|
+
table_options = {}
|
410
|
+
|
411
|
+
create_table_info = create_table_info(table_name)
|
412
|
+
|
413
|
+
# strip create_definitions and partition_options
|
414
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) /m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
415
|
+
|
416
|
+
# strip AUTO_INCREMENT
|
417
|
+
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
418
|
+
|
419
|
+
table_options[:options] = raw_table_options
|
420
|
+
|
421
|
+
# strip COMMENT
|
422
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
423
|
+
table_options[:comment] = table_comment(table_name)
|
587
424
|
end
|
425
|
+
|
426
|
+
table_options
|
588
427
|
end
|
589
428
|
|
590
429
|
# SHOW VARIABLES LIKE 'name'
|
591
430
|
def show_variable(name)
|
592
|
-
|
593
|
-
|
431
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
432
|
+
rescue ActiveRecord::StatementInvalid
|
433
|
+
nil
|
594
434
|
end
|
595
435
|
|
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
|
436
|
+
def primary_keys(table_name) # :nodoc:
|
437
|
+
raise ArgumentError unless table_name.present?
|
608
438
|
|
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
|
439
|
+
scope = quoted_scope(table_name)
|
614
440
|
|
615
|
-
|
616
|
-
|
617
|
-
|
441
|
+
query_values(<<~SQL, "SCHEMA")
|
442
|
+
SELECT column_name
|
443
|
+
FROM information_schema.key_column_usage
|
444
|
+
WHERE constraint_name = 'PRIMARY'
|
445
|
+
AND table_schema = #{scope[:schema]}
|
446
|
+
AND table_name = #{scope[:name]}
|
447
|
+
ORDER BY ordinal_position
|
448
|
+
SQL
|
618
449
|
end
|
619
450
|
|
620
|
-
def
|
621
|
-
|
622
|
-
|
451
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
452
|
+
column = column_for_attribute(attribute)
|
453
|
+
|
454
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
455
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
456
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
457
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
458
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
459
|
+
MSG
|
460
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
623
461
|
else
|
624
462
|
super
|
625
463
|
end
|
626
464
|
end
|
627
465
|
|
628
|
-
def
|
629
|
-
|
630
|
-
|
466
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
467
|
+
column = column_for_attribute(attribute)
|
468
|
+
|
469
|
+
if column.collation && !column.case_sensitive?
|
470
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
631
471
|
else
|
632
|
-
|
472
|
+
super
|
633
473
|
end
|
634
474
|
end
|
635
475
|
|
476
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
477
|
+
column.case_sensitive?
|
478
|
+
end
|
479
|
+
private :can_perform_case_insensitive_comparison_for?
|
480
|
+
|
481
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
482
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
483
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
484
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
485
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
486
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
487
|
+
# Convert Arel node to string
|
488
|
+
s = s.to_sql unless s.is_a?(String)
|
489
|
+
# Remove any ASC/DESC modifiers
|
490
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
491
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
492
|
+
|
493
|
+
(order_columns << super).join(", ")
|
494
|
+
end
|
495
|
+
|
636
496
|
def strict_mode?
|
637
497
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
638
498
|
end
|
639
499
|
|
640
|
-
def
|
641
|
-
|
500
|
+
def default_index_type?(index) # :nodoc:
|
501
|
+
index.using == :btree || super
|
642
502
|
end
|
643
503
|
|
644
|
-
|
504
|
+
def build_insert_sql(insert) # :nodoc:
|
505
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
645
506
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
653
|
-
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
654
|
-
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
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)
|
507
|
+
if insert.skip_duplicates?
|
508
|
+
no_op_column = quote_column_name(insert.keys.first)
|
509
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
510
|
+
elsif insert.update_duplicates?
|
511
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
512
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
677
513
|
end
|
514
|
+
|
515
|
+
sql
|
678
516
|
end
|
679
517
|
|
680
|
-
def
|
681
|
-
|
682
|
-
|
683
|
-
Type::UnsignedInteger.new(options)
|
684
|
-
else
|
685
|
-
Type::Integer.new(options)
|
686
|
-
end
|
518
|
+
def check_version # :nodoc:
|
519
|
+
if database_version < "5.5.8"
|
520
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
687
521
|
end
|
688
522
|
end
|
689
523
|
|
690
|
-
|
691
|
-
# to give it some prompting in the form of a subsubquery. Ugh!
|
692
|
-
def subquery_for(key, select)
|
693
|
-
subsubselect = select.clone
|
694
|
-
subsubselect.projections = [key]
|
524
|
+
private
|
695
525
|
|
696
|
-
|
697
|
-
|
698
|
-
subselect.from subsubselect.as('__active_record_temp')
|
699
|
-
end
|
526
|
+
def initialize_type_map(m = type_map)
|
527
|
+
super
|
700
528
|
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
529
|
+
register_class_with_limit m, %r(char)i, MysqlString
|
530
|
+
|
531
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
532
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
533
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
534
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
535
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
536
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
537
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
538
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
539
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
540
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
541
|
+
|
542
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
543
|
+
register_integer_type m, %r(^int)i, limit: 4
|
544
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
545
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
546
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
547
|
+
|
548
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
549
|
+
m.alias_type %r(year)i, "integer"
|
550
|
+
m.alias_type %r(bit)i, "binary"
|
551
|
+
|
552
|
+
m.register_type(%r(enum)i) do |sql_type|
|
553
|
+
limit = sql_type[/^enum\s*\((.+)\)/i, 1]
|
554
|
+
.split(",").map { |enum| enum.strip.length - 2 }.max
|
555
|
+
MysqlString.new(limit: limit)
|
708
556
|
end
|
709
|
-
end
|
710
557
|
|
711
|
-
|
712
|
-
|
558
|
+
m.register_type(%r(^set)i) do |sql_type|
|
559
|
+
limit = sql_type[/^set\s*\((.+)\)/i, 1]
|
560
|
+
.split(",").map { |set| set.strip.length - 1 }.sum - 1
|
561
|
+
MysqlString.new(limit: limit)
|
562
|
+
end
|
563
|
+
end
|
713
564
|
|
714
|
-
|
715
|
-
|
565
|
+
def register_integer_type(mapping, key, options)
|
566
|
+
mapping.register_type(key) do |sql_type|
|
567
|
+
if /\bunsigned\b/.match?(sql_type)
|
568
|
+
Type::UnsignedInteger.new(options)
|
569
|
+
else
|
570
|
+
Type::Integer.new(options)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
716
574
|
|
717
|
-
|
718
|
-
|
575
|
+
def extract_precision(sql_type)
|
576
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
577
|
+
super || 0
|
578
|
+
else
|
579
|
+
super
|
580
|
+
end
|
581
|
+
end
|
719
582
|
|
720
|
-
#
|
721
|
-
|
583
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
584
|
+
ER_DUP_ENTRY = 1062
|
585
|
+
ER_NOT_NULL_VIOLATION = 1048
|
586
|
+
ER_NO_REFERENCED_ROW = 1216
|
587
|
+
ER_ROW_IS_REFERENCED = 1217
|
588
|
+
ER_DO_NOT_HAVE_DEFAULT = 1364
|
589
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
590
|
+
ER_NO_REFERENCED_ROW_2 = 1452
|
591
|
+
ER_DATA_TOO_LONG = 1406
|
592
|
+
ER_OUT_OF_RANGE = 1264
|
593
|
+
ER_LOCK_DEADLOCK = 1213
|
594
|
+
ER_CANNOT_ADD_FOREIGN = 1215
|
595
|
+
ER_CANNOT_CREATE_TABLE = 1005
|
596
|
+
ER_LOCK_WAIT_TIMEOUT = 1205
|
597
|
+
ER_QUERY_INTERRUPTED = 1317
|
598
|
+
ER_QUERY_TIMEOUT = 3024
|
599
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
600
|
+
|
601
|
+
def translate_exception(exception, message:, sql:, binds:)
|
602
|
+
case error_number(exception)
|
603
|
+
when ER_DUP_ENTRY
|
604
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
605
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
606
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
607
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
608
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
609
|
+
when ER_CANNOT_CREATE_TABLE
|
610
|
+
if message.include?("errno: 150")
|
611
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
612
|
+
else
|
613
|
+
super
|
614
|
+
end
|
615
|
+
when ER_DATA_TOO_LONG
|
616
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
617
|
+
when ER_OUT_OF_RANGE
|
618
|
+
RangeError.new(message, sql: sql, binds: binds)
|
619
|
+
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
620
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
621
|
+
when ER_LOCK_DEADLOCK
|
622
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
623
|
+
when ER_LOCK_WAIT_TIMEOUT
|
624
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
625
|
+
when ER_QUERY_TIMEOUT
|
626
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
627
|
+
when ER_QUERY_INTERRUPTED
|
628
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
629
|
+
else
|
630
|
+
super
|
631
|
+
end
|
632
|
+
end
|
722
633
|
|
723
|
-
|
724
|
-
|
634
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
635
|
+
column = column_for(table_name, column_name)
|
636
|
+
type ||= column.sql_type
|
725
637
|
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
RecordNotUnique.new(message, exception)
|
730
|
-
when 1452
|
731
|
-
InvalidForeignKey.new(message, exception)
|
732
|
-
else
|
733
|
-
super
|
734
|
-
end
|
735
|
-
end
|
638
|
+
unless options.key?(:default)
|
639
|
+
options[:default] = column.default
|
640
|
+
end
|
736
641
|
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
schema_creation.visit_AddColumn cd
|
741
|
-
end
|
642
|
+
unless options.key?(:null)
|
643
|
+
options[:null] = column.null
|
644
|
+
end
|
742
645
|
|
743
|
-
|
744
|
-
|
646
|
+
unless options.key?(:comment)
|
647
|
+
options[:comment] = column.comment
|
648
|
+
end
|
745
649
|
|
746
|
-
|
747
|
-
|
650
|
+
td = create_table_definition(table_name)
|
651
|
+
cd = td.new_column_definition(column.name, type, options)
|
652
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
748
653
|
end
|
749
654
|
|
750
|
-
|
751
|
-
|
655
|
+
def rename_column_for_alter(table_name, column_name, new_column_name)
|
656
|
+
column = column_for(table_name, column_name)
|
657
|
+
options = {
|
658
|
+
default: column.default,
|
659
|
+
null: column.null,
|
660
|
+
auto_increment: column.auto_increment?
|
661
|
+
}
|
662
|
+
|
663
|
+
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
664
|
+
td = create_table_definition(table_name)
|
665
|
+
cd = td.new_column_definition(new_column_name, current_type, options)
|
666
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
752
667
|
end
|
753
668
|
|
754
|
-
options
|
755
|
-
|
756
|
-
|
669
|
+
def add_index_for_alter(table_name, column_name, options = {})
|
670
|
+
index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
671
|
+
index_algorithm[0, 0] = ", " if index_algorithm.present?
|
672
|
+
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
673
|
+
end
|
757
674
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
default: column.default,
|
763
|
-
null: column.null,
|
764
|
-
auto_increment: column.extra == "auto_increment"
|
765
|
-
}
|
675
|
+
def remove_index_for_alter(table_name, options = {})
|
676
|
+
index_name = index_name_for_remove(table_name, options)
|
677
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
678
|
+
end
|
766
679
|
|
767
|
-
|
768
|
-
|
769
|
-
end
|
680
|
+
def add_timestamps_for_alter(table_name, options = {})
|
681
|
+
options[:null] = false if options[:null].nil?
|
770
682
|
|
771
|
-
|
772
|
-
|
773
|
-
|
683
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
684
|
+
options[:precision] = 6
|
685
|
+
end
|
774
686
|
|
775
|
-
|
776
|
-
|
777
|
-
end
|
687
|
+
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
688
|
+
end
|
778
689
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
end
|
690
|
+
def remove_timestamps_for_alter(table_name, options = {})
|
691
|
+
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
692
|
+
end
|
783
693
|
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
end
|
694
|
+
def supports_rename_index?
|
695
|
+
mariadb? ? false : database_version >= "5.7.6"
|
696
|
+
end
|
788
697
|
|
789
|
-
|
790
|
-
|
791
|
-
end
|
698
|
+
def configure_connection
|
699
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
792
700
|
|
793
|
-
|
794
|
-
|
795
|
-
end
|
701
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
702
|
+
variables["sql_auto_is_null"] = 0
|
796
703
|
|
797
|
-
|
704
|
+
# Increase timeout so the server doesn't disconnect us.
|
705
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
706
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
707
|
+
variables["wait_timeout"] = wait_timeout
|
798
708
|
|
799
|
-
|
800
|
-
@version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
801
|
-
end
|
709
|
+
defaults = [":default", :default].to_set
|
802
710
|
|
803
|
-
|
804
|
-
|
805
|
-
|
711
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
712
|
+
# https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
|
713
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
714
|
+
if sql_mode = variables.delete("sql_mode")
|
715
|
+
sql_mode = quote(sql_mode)
|
716
|
+
elsif !defaults.include?(strict_mode?)
|
717
|
+
if strict_mode?
|
718
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
719
|
+
else
|
720
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
721
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
722
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
723
|
+
end
|
724
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
725
|
+
end
|
726
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
727
|
+
|
728
|
+
# NAMES does not have an equals sign, see
|
729
|
+
# https://dev.mysql.com/doc/refman/5.7/en/set-names.html
|
730
|
+
# (trailing comma because variable_assignments will always have content)
|
731
|
+
if @config[:encoding]
|
732
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
733
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
734
|
+
encoding << ", "
|
735
|
+
end
|
806
736
|
|
807
|
-
|
808
|
-
|
809
|
-
|
737
|
+
# Gather up all of the SET variables...
|
738
|
+
variable_assignments = variables.map do |k, v|
|
739
|
+
if defaults.include?(v)
|
740
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
741
|
+
elsif !v.nil?
|
742
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
743
|
+
end
|
744
|
+
# or else nil; compact to clear nils out
|
745
|
+
end.compact.join(", ")
|
810
746
|
|
811
|
-
|
812
|
-
|
747
|
+
# ...and send them all in one query
|
748
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
749
|
+
end
|
813
750
|
|
814
|
-
|
815
|
-
|
816
|
-
|
751
|
+
def column_definitions(table_name) # :nodoc:
|
752
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
753
|
+
each_hash(result)
|
754
|
+
end
|
755
|
+
end
|
817
756
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
|
757
|
+
def create_table_info(table_name) # :nodoc:
|
758
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
759
|
+
end
|
822
760
|
|
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' : ''
|
761
|
+
def arel_visitor
|
762
|
+
Arel::Visitors::MySQL.new(self)
|
828
763
|
end
|
829
764
|
|
830
|
-
|
831
|
-
|
832
|
-
# (trailing comma because variable_assignments will always have content)
|
833
|
-
if @config[:encoding]
|
834
|
-
encoding = "NAMES #{@config[:encoding]}"
|
835
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
836
|
-
encoding << ", "
|
765
|
+
def build_statement_pool
|
766
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
837
767
|
end
|
838
768
|
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
end
|
846
|
-
# or else nil; compact to clear nils out
|
847
|
-
end.compact.join(', ')
|
769
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
770
|
+
match = %r/
|
771
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
772
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
773
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
774
|
+
/xmi.match(sql)
|
848
775
|
|
849
|
-
|
850
|
-
|
851
|
-
|
776
|
+
options = {
|
777
|
+
message: message,
|
778
|
+
sql: sql,
|
779
|
+
binds: binds,
|
780
|
+
}
|
852
781
|
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
782
|
+
if match
|
783
|
+
options[:table] = match[:table]
|
784
|
+
options[:foreign_key] = match[:foreign_key]
|
785
|
+
options[:target_table] = match[:target_table]
|
786
|
+
options[:primary_key] = match[:primary_key]
|
787
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
858
788
|
end
|
859
|
-
end
|
860
|
-
end
|
861
789
|
|
862
|
-
|
863
|
-
def type_cast_for_database(value)
|
864
|
-
case value
|
865
|
-
when true then "1"
|
866
|
-
when false then "0"
|
867
|
-
else super
|
868
|
-
end
|
790
|
+
MismatchedForeignKey.new(options)
|
869
791
|
end
|
870
792
|
|
871
|
-
|
793
|
+
def version_string(full_version_string)
|
794
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
795
|
+
end
|
872
796
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
797
|
+
class MysqlString < Type::String # :nodoc:
|
798
|
+
def serialize(value)
|
799
|
+
case value
|
800
|
+
when true then "1"
|
801
|
+
when false then "0"
|
802
|
+
else super
|
803
|
+
end
|
878
804
|
end
|
805
|
+
|
806
|
+
private
|
807
|
+
|
808
|
+
def cast_value(value)
|
809
|
+
case value
|
810
|
+
when true then "1"
|
811
|
+
when false then "0"
|
812
|
+
else super
|
813
|
+
end
|
814
|
+
end
|
879
815
|
end
|
880
|
-
|
816
|
+
|
817
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
818
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
881
819
|
end
|
882
820
|
end
|
883
821
|
end
|