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