activerecord 5.0.7.2 → 6.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- 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 +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- 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 +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
- 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 +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +20 -22
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- 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 +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +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 +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +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 +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -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 +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- 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 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_decorators.rb +0 -67
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,32 +1,21 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
|
11
|
-
require
|
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"
|
12
13
|
|
13
14
|
module ActiveRecord
|
14
15
|
module ConnectionAdapters
|
15
16
|
class AbstractMysqlAdapter < AbstractAdapter
|
16
17
|
include MySQL::Quoting
|
17
|
-
include MySQL::
|
18
|
-
|
19
|
-
def update_table_definition(table_name, base) # :nodoc:
|
20
|
-
MySQL::Table.new(table_name, base)
|
21
|
-
end
|
22
|
-
|
23
|
-
def schema_creation # :nodoc:
|
24
|
-
MySQL::SchemaCreation.new(self)
|
25
|
-
end
|
26
|
-
|
27
|
-
def arel_visitor # :nodoc:
|
28
|
-
Arel::Visitors::MySQL.new(self)
|
29
|
-
end
|
18
|
+
include MySQL::SchemaStatements
|
30
19
|
|
31
20
|
##
|
32
21
|
# :singleton-method:
|
@@ -35,82 +24,56 @@ module ActiveRecord
|
|
35
24
|
# to your application.rb file:
|
36
25
|
#
|
37
26
|
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
38
|
-
class_attribute :emulate_booleans
|
39
|
-
self.emulate_booleans = true
|
27
|
+
class_attribute :emulate_booleans, default: true
|
40
28
|
|
41
29
|
NATIVE_DATABASE_TYPES = {
|
42
|
-
primary_key: "
|
30
|
+
primary_key: "bigint auto_increment PRIMARY KEY",
|
43
31
|
string: { name: "varchar", limit: 255 },
|
44
32
|
text: { name: "text" },
|
45
33
|
integer: { name: "int", limit: 4 },
|
46
|
-
float: { name: "float" },
|
34
|
+
float: { name: "float", limit: 24 },
|
47
35
|
decimal: { name: "decimal" },
|
48
36
|
datetime: { name: "datetime" },
|
37
|
+
timestamp: { name: "timestamp" },
|
49
38
|
time: { name: "time" },
|
50
39
|
date: { name: "date" },
|
51
40
|
binary: { name: "blob" },
|
41
|
+
blob: { name: "blob" },
|
52
42
|
boolean: { name: "tinyint", limit: 1 },
|
53
43
|
json: { name: "json" },
|
54
44
|
}
|
55
45
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
stmt[:stmt].close
|
62
|
-
end
|
46
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
47
|
+
private
|
48
|
+
def dealloc(stmt)
|
49
|
+
stmt.close
|
50
|
+
end
|
63
51
|
end
|
64
52
|
|
65
53
|
def initialize(connection, logger, connection_options, config)
|
66
54
|
super(connection, logger, config)
|
67
|
-
|
68
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
69
|
-
|
70
|
-
if version < '5.0.0'
|
71
|
-
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.0."
|
72
|
-
end
|
73
55
|
end
|
74
56
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
options[:collation] = collation.sub(/\A[^_]+/, 'utf8') if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
80
|
-
}
|
81
|
-
end
|
82
|
-
|
83
|
-
def version #:nodoc:
|
84
|
-
@version ||= Version.new(version_string)
|
57
|
+
def get_database_version #:nodoc:
|
58
|
+
full_version_string = get_full_version
|
59
|
+
version_string = version_string(full_version_string)
|
60
|
+
Version.new(version_string, full_version_string)
|
85
61
|
end
|
86
62
|
|
87
63
|
def mariadb? # :nodoc:
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
# Returns true, since this connection adapter supports migrations.
|
92
|
-
def supports_migrations?
|
93
|
-
true
|
94
|
-
end
|
95
|
-
|
96
|
-
def supports_primary_key?
|
97
|
-
true
|
64
|
+
/mariadb/i.match?(full_version)
|
98
65
|
end
|
99
66
|
|
100
|
-
def supports_bulk_alter?
|
67
|
+
def supports_bulk_alter?
|
101
68
|
true
|
102
69
|
end
|
103
70
|
|
104
|
-
|
105
|
-
|
106
|
-
def supports_statement_cache?
|
107
|
-
true
|
71
|
+
def supports_index_sort_order?
|
72
|
+
!mariadb? && database_version >= "8.0.1"
|
108
73
|
end
|
109
74
|
|
110
|
-
|
111
|
-
|
112
|
-
def supports_index_sort_order?
|
113
|
-
true
|
75
|
+
def supports_expression_index?
|
76
|
+
!mariadb? && database_version >= "8.0.13"
|
114
77
|
end
|
115
78
|
|
116
79
|
def supports_transaction_isolation?
|
@@ -129,15 +92,36 @@ module ActiveRecord
|
|
129
92
|
true
|
130
93
|
end
|
131
94
|
|
95
|
+
def supports_check_constraints?
|
96
|
+
if mariadb?
|
97
|
+
database_version >= "10.2.1"
|
98
|
+
else
|
99
|
+
database_version >= "8.0.16"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
132
103
|
def supports_views?
|
133
104
|
true
|
134
105
|
end
|
135
106
|
|
136
107
|
def supports_datetime_with_precision?
|
108
|
+
mariadb? || database_version >= "5.6.4"
|
109
|
+
end
|
110
|
+
|
111
|
+
def supports_virtual_columns?
|
112
|
+
mariadb? || database_version >= "5.7.5"
|
113
|
+
end
|
114
|
+
|
115
|
+
# See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
|
116
|
+
def supports_optimizer_hints?
|
117
|
+
!mariadb? && database_version >= "5.7.7"
|
118
|
+
end
|
119
|
+
|
120
|
+
def supports_common_table_expressions?
|
137
121
|
if mariadb?
|
138
|
-
|
122
|
+
database_version >= "10.2.1"
|
139
123
|
else
|
140
|
-
|
124
|
+
database_version >= "8.0.1"
|
141
125
|
end
|
142
126
|
end
|
143
127
|
|
@@ -145,12 +129,20 @@ module ActiveRecord
|
|
145
129
|
true
|
146
130
|
end
|
147
131
|
|
132
|
+
def supports_insert_on_duplicate_skip?
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
def supports_insert_on_duplicate_update?
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
148
140
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
149
|
-
|
141
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
150
142
|
end
|
151
143
|
|
152
144
|
def release_advisory_lock(lock_name) # :nodoc:
|
153
|
-
|
145
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
154
146
|
end
|
155
147
|
|
156
148
|
def native_database_types
|
@@ -158,7 +150,12 @@ module ActiveRecord
|
|
158
150
|
end
|
159
151
|
|
160
152
|
def index_algorithms
|
161
|
-
{
|
153
|
+
{
|
154
|
+
default: "ALGORITHM = DEFAULT",
|
155
|
+
copy: "ALGORITHM = COPY",
|
156
|
+
inplace: "ALGORITHM = INPLACE",
|
157
|
+
instant: "ALGORITHM = INSTANT",
|
158
|
+
}
|
162
159
|
end
|
163
160
|
|
164
161
|
# HELPER METHODS ===========================================
|
@@ -169,10 +166,6 @@ module ActiveRecord
|
|
169
166
|
raise NotImplementedError
|
170
167
|
end
|
171
168
|
|
172
|
-
def new_column(*args) #:nodoc:
|
173
|
-
MySQL::Column.new(*args)
|
174
|
-
end
|
175
|
-
|
176
169
|
# Must return the MySQL error number from the exception, if the exception has an
|
177
170
|
# error number.
|
178
171
|
def error_number(exception) # :nodoc:
|
@@ -182,7 +175,7 @@ module ActiveRecord
|
|
182
175
|
# REFERENTIAL INTEGRITY ====================================
|
183
176
|
|
184
177
|
def disable_referential_integrity #:nodoc:
|
185
|
-
old =
|
178
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
186
179
|
|
187
180
|
begin
|
188
181
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -194,28 +187,25 @@ module ActiveRecord
|
|
194
187
|
|
195
188
|
# CONNECTION MANAGEMENT ====================================
|
196
189
|
|
197
|
-
|
198
|
-
def clear_cache!
|
190
|
+
def clear_cache! # :nodoc:
|
199
191
|
reload_type_map
|
200
|
-
|
192
|
+
super
|
201
193
|
end
|
202
194
|
|
203
195
|
#--
|
204
196
|
# DATABASE STATEMENTS ======================================
|
205
197
|
#++
|
206
198
|
|
207
|
-
def explain(arel, binds = [])
|
208
|
-
sql = "EXPLAIN #{to_sql(arel, binds)}"
|
209
|
-
start = Time.now
|
210
|
-
result = exec_query(sql, 'EXPLAIN', binds)
|
211
|
-
elapsed = Time.now - start
|
212
|
-
|
213
|
-
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
|
214
|
-
end
|
215
|
-
|
216
199
|
# Executes the SQL statement in the context of this connection.
|
217
200
|
def execute(sql, name = nil)
|
218
|
-
|
201
|
+
materialize_transactions
|
202
|
+
mark_transaction_written_if_write(sql)
|
203
|
+
|
204
|
+
log(sql, name) do
|
205
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
206
|
+
@connection.query(sql)
|
207
|
+
end
|
208
|
+
end
|
219
209
|
end
|
220
210
|
|
221
211
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
@@ -226,7 +216,7 @@ module ActiveRecord
|
|
226
216
|
end
|
227
217
|
|
228
218
|
def begin_db_transaction
|
229
|
-
execute
|
219
|
+
execute("BEGIN", "TRANSACTION")
|
230
220
|
end
|
231
221
|
|
232
222
|
def begin_isolated_db_transaction(isolation)
|
@@ -235,26 +225,14 @@ module ActiveRecord
|
|
235
225
|
end
|
236
226
|
|
237
227
|
def commit_db_transaction #:nodoc:
|
238
|
-
execute
|
228
|
+
execute("COMMIT", "TRANSACTION")
|
239
229
|
end
|
240
230
|
|
241
231
|
def exec_rollback_db_transaction #:nodoc:
|
242
|
-
execute
|
232
|
+
execute("ROLLBACK", "TRANSACTION")
|
243
233
|
end
|
244
234
|
|
245
|
-
|
246
|
-
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
247
|
-
# these, we must use a subquery.
|
248
|
-
def join_to_update(update, select, key) # :nodoc:
|
249
|
-
if select.limit || select.offset || select.orders.any?
|
250
|
-
super
|
251
|
-
else
|
252
|
-
update.table select.source
|
253
|
-
update.wheres = select.constraints
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
def empty_insert_statement_value
|
235
|
+
def empty_insert_statement_value(primary_key = nil)
|
258
236
|
"VALUES ()"
|
259
237
|
end
|
260
238
|
|
@@ -270,7 +248,7 @@ module ActiveRecord
|
|
270
248
|
end
|
271
249
|
|
272
250
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
273
|
-
# Charset defaults to
|
251
|
+
# Charset defaults to utf8mb4.
|
274
252
|
#
|
275
253
|
# Example:
|
276
254
|
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
@@ -278,9 +256,13 @@ module ActiveRecord
|
|
278
256
|
# create_database 'matt_development', charset: :big5
|
279
257
|
def create_database(name, options = {})
|
280
258
|
if options[:collation]
|
281
|
-
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT
|
259
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
|
260
|
+
elsif options[:charset]
|
261
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
|
262
|
+
elsif row_format_dynamic_by_default?
|
263
|
+
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
|
282
264
|
else
|
283
|
-
|
265
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
284
266
|
end
|
285
267
|
end
|
286
268
|
|
@@ -293,149 +275,34 @@ module ActiveRecord
|
|
293
275
|
end
|
294
276
|
|
295
277
|
def current_database
|
296
|
-
|
278
|
+
query_value("SELECT database()", "SCHEMA")
|
297
279
|
end
|
298
280
|
|
299
281
|
# Returns the database character set.
|
300
282
|
def charset
|
301
|
-
show_variable
|
283
|
+
show_variable "character_set_database"
|
302
284
|
end
|
303
285
|
|
304
286
|
# Returns the database collation strategy.
|
305
287
|
def collation
|
306
|
-
show_variable
|
307
|
-
end
|
308
|
-
|
309
|
-
def tables(name = nil) # :nodoc:
|
310
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
311
|
-
#tables currently returns both tables and views.
|
312
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
|
313
|
-
Use #data_sources instead.
|
314
|
-
MSG
|
315
|
-
|
316
|
-
if name
|
317
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
318
|
-
Passing arguments to #tables is deprecated without replacement.
|
319
|
-
MSG
|
320
|
-
end
|
321
|
-
|
322
|
-
data_sources
|
323
|
-
end
|
324
|
-
|
325
|
-
def data_sources
|
326
|
-
sql = "SELECT table_name FROM information_schema.tables "
|
327
|
-
sql << "WHERE table_schema = DATABASE()"
|
328
|
-
|
329
|
-
select_values(sql, 'SCHEMA')
|
330
|
-
end
|
331
|
-
|
332
|
-
def truncate(table_name, name = nil)
|
333
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
334
|
-
end
|
335
|
-
|
336
|
-
def table_exists?(table_name)
|
337
|
-
# Update lib/active_record/internal_metadata.rb when this gets removed
|
338
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
339
|
-
#table_exists? currently checks both tables and views.
|
340
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
341
|
-
Use #data_source_exists? instead.
|
342
|
-
MSG
|
343
|
-
|
344
|
-
data_source_exists?(table_name)
|
345
|
-
end
|
346
|
-
|
347
|
-
def data_source_exists?(table_name)
|
348
|
-
return false unless table_name.present?
|
349
|
-
|
350
|
-
schema, name = extract_schema_qualified_name(table_name)
|
351
|
-
|
352
|
-
sql = "SELECT table_name FROM information_schema.tables "
|
353
|
-
sql << "WHERE table_schema = #{schema} AND table_name = #{name}"
|
354
|
-
|
355
|
-
select_values(sql, 'SCHEMA').any?
|
356
|
-
end
|
357
|
-
|
358
|
-
def views # :nodoc:
|
359
|
-
select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
|
360
|
-
end
|
361
|
-
|
362
|
-
def view_exists?(view_name) # :nodoc:
|
363
|
-
return false unless view_name.present?
|
364
|
-
|
365
|
-
schema, name = extract_schema_qualified_name(view_name)
|
366
|
-
|
367
|
-
sql = "SELECT table_name FROM information_schema.tables WHERE table_type = 'VIEW'"
|
368
|
-
sql << " AND table_schema = #{schema} AND table_name = #{name}"
|
369
|
-
|
370
|
-
select_values(sql, 'SCHEMA').any?
|
371
|
-
end
|
372
|
-
|
373
|
-
# Returns an array of indexes for the given table.
|
374
|
-
def indexes(table_name, name = nil) #:nodoc:
|
375
|
-
indexes = []
|
376
|
-
current_index = nil
|
377
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
378
|
-
each_hash(result) do |row|
|
379
|
-
if current_index != row[:Key_name]
|
380
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
381
|
-
current_index = row[:Key_name]
|
382
|
-
|
383
|
-
mysql_index_type = row[:Index_type].downcase.to_sym
|
384
|
-
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
385
|
-
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
386
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence)
|
387
|
-
end
|
388
|
-
|
389
|
-
indexes.last.columns << row[:Column_name]
|
390
|
-
indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
indexes
|
395
|
-
end
|
396
|
-
|
397
|
-
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
398
|
-
def columns(table_name) # :nodoc:
|
399
|
-
table_name = table_name.to_s
|
400
|
-
column_definitions(table_name).map do |field|
|
401
|
-
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
402
|
-
if type_metadata.type == :datetime && field[:Default] =~ /\ACURRENT_TIMESTAMP(?:\(\))?\z/i
|
403
|
-
default, default_function = nil, "CURRENT_TIMESTAMP"
|
404
|
-
else
|
405
|
-
default, default_function = field[:Default], nil
|
406
|
-
end
|
407
|
-
new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence)
|
408
|
-
end
|
288
|
+
show_variable "collation_database"
|
409
289
|
end
|
410
290
|
|
411
291
|
def table_comment(table_name) # :nodoc:
|
412
|
-
|
292
|
+
scope = quoted_scope(table_name)
|
413
293
|
|
414
|
-
|
294
|
+
query_value(<<~SQL, "SCHEMA").presence
|
415
295
|
SELECT table_comment
|
416
296
|
FROM information_schema.tables
|
417
|
-
WHERE table_schema = #{schema}
|
418
|
-
AND table_name = #{name}
|
297
|
+
WHERE table_schema = #{scope[:schema]}
|
298
|
+
AND table_name = #{scope[:name]}
|
419
299
|
SQL
|
420
300
|
end
|
421
301
|
|
422
|
-
def
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
def bulk_change_table(table_name, operations) #:nodoc:
|
427
|
-
sqls = operations.flat_map do |command, args|
|
428
|
-
table, arguments = args.shift, args
|
429
|
-
method = :"#{command}_sql"
|
430
|
-
|
431
|
-
if respond_to?(method, true)
|
432
|
-
send(method, table, *arguments)
|
433
|
-
else
|
434
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
435
|
-
end
|
436
|
-
end.join(", ")
|
437
|
-
|
438
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
302
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
303
|
+
comment = extract_new_comment_value(comment_or_changes)
|
304
|
+
comment = "" if comment.nil?
|
305
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
439
306
|
end
|
440
307
|
|
441
308
|
# Renames a table.
|
@@ -443,6 +310,8 @@ module ActiveRecord
|
|
443
310
|
# Example:
|
444
311
|
# rename_table('octopuses', 'octopi')
|
445
312
|
def rename_table(table_name, new_name)
|
313
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
314
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
446
315
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
447
316
|
rename_table_indexes(table_name, new_name)
|
448
317
|
end
|
@@ -462,7 +331,8 @@ module ActiveRecord
|
|
462
331
|
# Although this command ignores most +options+ and the block if one is given,
|
463
332
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
464
333
|
# In that case, +options+ and the block will be used by create_table.
|
465
|
-
def drop_table(table_name, options
|
334
|
+
def drop_table(table_name, **options)
|
335
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
466
336
|
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
467
337
|
end
|
468
338
|
|
@@ -478,33 +348,38 @@ module ActiveRecord
|
|
478
348
|
|
479
349
|
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
480
350
|
default = extract_new_default_value(default_or_changes)
|
481
|
-
|
482
|
-
change_column table_name, column_name, column.sql_type, :default => default
|
351
|
+
change_column table_name, column_name, nil, default: default
|
483
352
|
end
|
484
353
|
|
485
354
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
486
|
-
column = column_for(table_name, column_name)
|
487
|
-
|
488
355
|
unless null || default.nil?
|
489
356
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
490
357
|
end
|
491
358
|
|
492
|
-
change_column table_name, column_name,
|
359
|
+
change_column table_name, column_name, nil, null: null
|
493
360
|
end
|
494
361
|
|
495
|
-
def
|
496
|
-
|
362
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
363
|
+
comment = extract_new_comment_value(comment_or_changes)
|
364
|
+
change_column table_name, column_name, nil, comment: comment
|
365
|
+
end
|
366
|
+
|
367
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
368
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
497
369
|
end
|
498
370
|
|
499
371
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
500
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
372
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
501
373
|
rename_column_indexes(table_name, column_name, new_column_name)
|
502
374
|
end
|
503
375
|
|
504
|
-
def add_index(table_name, column_name, options
|
505
|
-
|
506
|
-
|
507
|
-
|
376
|
+
def add_index(table_name, column_name, **options) #:nodoc:
|
377
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
378
|
+
|
379
|
+
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
380
|
+
|
381
|
+
create_index = CreateIndexDefinition.new(index, algorithm)
|
382
|
+
execute schema_creation.accept(create_index)
|
508
383
|
end
|
509
384
|
|
510
385
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -515,85 +390,99 @@ module ActiveRecord
|
|
515
390
|
def foreign_keys(table_name)
|
516
391
|
raise ArgumentError unless table_name.present?
|
517
392
|
|
518
|
-
|
393
|
+
scope = quoted_scope(table_name)
|
519
394
|
|
520
|
-
fk_info =
|
395
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
521
396
|
SELECT fk.referenced_table_name AS 'to_table',
|
522
397
|
fk.referenced_column_name AS 'primary_key',
|
523
398
|
fk.column_name AS 'column',
|
524
399
|
fk.constraint_name AS 'name',
|
525
400
|
rc.update_rule AS 'on_update',
|
526
401
|
rc.delete_rule AS 'on_delete'
|
527
|
-
FROM information_schema.
|
528
|
-
JOIN information_schema.
|
402
|
+
FROM information_schema.referential_constraints rc
|
403
|
+
JOIN information_schema.key_column_usage fk
|
529
404
|
USING (constraint_schema, constraint_name)
|
530
405
|
WHERE fk.referenced_column_name IS NOT NULL
|
531
|
-
AND fk.table_schema = #{schema}
|
532
|
-
AND fk.table_name = #{name}
|
533
|
-
AND rc.
|
406
|
+
AND fk.table_schema = #{scope[:schema]}
|
407
|
+
AND fk.table_name = #{scope[:name]}
|
408
|
+
AND rc.constraint_schema = #{scope[:schema]}
|
409
|
+
AND rc.table_name = #{scope[:name]}
|
534
410
|
SQL
|
535
411
|
|
536
412
|
fk_info.map do |row|
|
537
413
|
options = {
|
538
|
-
column: row[
|
539
|
-
name: row[
|
540
|
-
primary_key: row[
|
414
|
+
column: row["column"],
|
415
|
+
name: row["name"],
|
416
|
+
primary_key: row["primary_key"]
|
541
417
|
}
|
542
418
|
|
543
|
-
options[:on_update] = extract_foreign_key_action(row[
|
544
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
419
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
420
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
545
421
|
|
546
|
-
ForeignKeyDefinition.new(table_name, row[
|
422
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
547
423
|
end
|
548
424
|
end
|
549
425
|
|
550
|
-
def
|
551
|
-
|
426
|
+
def check_constraints(table_name)
|
427
|
+
if supports_check_constraints?
|
428
|
+
scope = quoted_scope(table_name)
|
552
429
|
|
430
|
+
chk_info = exec_query(<<~SQL, "SCHEMA")
|
431
|
+
SELECT cc.constraint_name AS 'name',
|
432
|
+
cc.check_clause AS 'expression'
|
433
|
+
FROM information_schema.check_constraints cc
|
434
|
+
JOIN information_schema.table_constraints tc
|
435
|
+
USING (constraint_schema, constraint_name)
|
436
|
+
WHERE tc.table_schema = #{scope[:schema]}
|
437
|
+
AND tc.table_name = #{scope[:name]}
|
438
|
+
AND cc.constraint_schema = #{scope[:schema]}
|
439
|
+
SQL
|
440
|
+
|
441
|
+
chk_info.map do |row|
|
442
|
+
options = {
|
443
|
+
name: row["name"]
|
444
|
+
}
|
445
|
+
expression = row["expression"]
|
446
|
+
expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
|
447
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
448
|
+
end
|
449
|
+
else
|
450
|
+
raise NotImplementedError
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def table_options(table_name) # :nodoc:
|
553
455
|
create_table_info = create_table_info(table_name)
|
554
456
|
|
555
457
|
# strip create_definitions and partition_options
|
556
|
-
|
458
|
+
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
459
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
460
|
+
|
461
|
+
return if raw_table_options.empty?
|
462
|
+
|
463
|
+
table_options = {}
|
464
|
+
|
465
|
+
if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
|
466
|
+
raw_table_options = $` + $' # before part + after part
|
467
|
+
table_options[:charset] = charset
|
468
|
+
table_options[:collation] = collation if collation
|
469
|
+
end
|
557
470
|
|
558
471
|
# strip AUTO_INCREMENT
|
559
472
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
560
473
|
|
561
|
-
table_options[:options] = raw_table_options
|
562
|
-
|
563
474
|
# strip COMMENT
|
564
|
-
if raw_table_options.sub!(/ COMMENT='.+'/,
|
475
|
+
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
565
476
|
table_options[:comment] = table_comment(table_name)
|
566
477
|
end
|
567
478
|
|
479
|
+
table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
|
568
480
|
table_options
|
569
481
|
end
|
570
482
|
|
571
|
-
# Maps logical Rails types to MySQL-specific data types.
|
572
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = nil)
|
573
|
-
sql = case type.to_s
|
574
|
-
when 'integer'
|
575
|
-
integer_to_sql(limit)
|
576
|
-
when 'text'
|
577
|
-
text_to_sql(limit)
|
578
|
-
when 'blob'
|
579
|
-
binary_to_sql(limit)
|
580
|
-
when 'binary'
|
581
|
-
if (0..0xfff) === limit
|
582
|
-
"varbinary(#{limit})"
|
583
|
-
else
|
584
|
-
binary_to_sql(limit)
|
585
|
-
end
|
586
|
-
else
|
587
|
-
super(type, limit, precision, scale)
|
588
|
-
end
|
589
|
-
|
590
|
-
sql << ' unsigned' if unsigned && type != :primary_key
|
591
|
-
sql
|
592
|
-
end
|
593
|
-
|
594
483
|
# SHOW VARIABLES LIKE 'name'
|
595
484
|
def show_variable(name)
|
596
|
-
|
485
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
597
486
|
rescue ActiveRecord::StatementInvalid
|
598
487
|
nil
|
599
488
|
end
|
@@ -601,21 +490,23 @@ module ActiveRecord
|
|
601
490
|
def primary_keys(table_name) # :nodoc:
|
602
491
|
raise ArgumentError unless table_name.present?
|
603
492
|
|
604
|
-
|
493
|
+
scope = quoted_scope(table_name)
|
605
494
|
|
606
|
-
|
495
|
+
query_values(<<~SQL, "SCHEMA")
|
607
496
|
SELECT column_name
|
608
|
-
FROM information_schema.
|
609
|
-
WHERE
|
610
|
-
AND table_schema = #{schema}
|
611
|
-
AND table_name = #{name}
|
612
|
-
ORDER BY
|
497
|
+
FROM information_schema.statistics
|
498
|
+
WHERE index_name = 'PRIMARY'
|
499
|
+
AND table_schema = #{scope[:schema]}
|
500
|
+
AND table_name = #{scope[:name]}
|
501
|
+
ORDER BY seq_in_index
|
613
502
|
SQL
|
614
503
|
end
|
615
504
|
|
616
|
-
def case_sensitive_comparison(
|
617
|
-
|
618
|
-
|
505
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
506
|
+
column = column_for_attribute(attribute)
|
507
|
+
|
508
|
+
if column.collation && !column.case_sensitive?
|
509
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
619
510
|
else
|
620
511
|
super
|
621
512
|
end
|
@@ -629,352 +520,336 @@ module ActiveRecord
|
|
629
520
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
630
521
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
631
522
|
# distinct queries, and requires that the ORDER BY include the distinct column.
|
632
|
-
# See https://dev.mysql.com/doc/refman/
|
523
|
+
# See https://dev.mysql.com/doc/refman/en/group-by-handling.html
|
633
524
|
def columns_for_distinct(columns, orders) # :nodoc:
|
634
|
-
order_columns = orders.
|
525
|
+
order_columns = orders.compact_blank.map { |s|
|
635
526
|
# Convert Arel node to string
|
636
|
-
s = s
|
527
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
637
528
|
# Remove any ASC/DESC modifiers
|
638
|
-
s.gsub(/\s+(?:ASC|DESC)\b/i,
|
639
|
-
}.
|
529
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
530
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
640
531
|
|
641
|
-
|
532
|
+
(order_columns << super).join(", ")
|
642
533
|
end
|
643
534
|
|
644
535
|
def strict_mode?
|
645
536
|
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
646
537
|
end
|
647
538
|
|
648
|
-
def
|
649
|
-
|
539
|
+
def default_index_type?(index) # :nodoc:
|
540
|
+
index.using == :btree || super
|
650
541
|
end
|
651
542
|
|
652
|
-
|
653
|
-
|
654
|
-
def initialize_type_map(m) # :nodoc:
|
655
|
-
super
|
656
|
-
|
657
|
-
register_class_with_limit m, %r(char)i, MysqlString
|
658
|
-
|
659
|
-
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
660
|
-
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
661
|
-
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
662
|
-
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
663
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
664
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
665
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
666
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
667
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
668
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
669
|
-
m.register_type %r(^json)i, MysqlJson.new
|
670
|
-
|
671
|
-
register_integer_type m, %r(^bigint)i, limit: 8
|
672
|
-
register_integer_type m, %r(^int)i, limit: 4
|
673
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
674
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
675
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
543
|
+
def build_insert_sql(insert) # :nodoc:
|
544
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
676
545
|
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
MysqlString.new(limit: limit)
|
546
|
+
if insert.skip_duplicates?
|
547
|
+
no_op_column = quote_column_name(insert.keys.first)
|
548
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
549
|
+
elsif insert.update_duplicates?
|
550
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
551
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
552
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
685
553
|
end
|
686
554
|
|
687
|
-
|
688
|
-
limit = sql_type[/^set\((.+)\)/i, 1]
|
689
|
-
.split(',').map{|set| set.strip.length - 1}.sum - 1
|
690
|
-
MysqlString.new(limit: limit)
|
691
|
-
end
|
555
|
+
sql
|
692
556
|
end
|
693
557
|
|
694
|
-
def
|
695
|
-
|
696
|
-
|
697
|
-
Type::UnsignedInteger.new(options)
|
698
|
-
else
|
699
|
-
Type::Integer.new(options)
|
700
|
-
end
|
558
|
+
def check_version # :nodoc:
|
559
|
+
if database_version < "5.5.8"
|
560
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
701
561
|
end
|
702
562
|
end
|
703
563
|
|
704
|
-
|
705
|
-
|
706
|
-
super || 0
|
707
|
-
else
|
564
|
+
private
|
565
|
+
def initialize_type_map(m = type_map)
|
708
566
|
super
|
709
|
-
end
|
710
|
-
end
|
711
|
-
|
712
|
-
def fetch_type_metadata(sql_type, extra = "")
|
713
|
-
MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
|
714
|
-
end
|
715
567
|
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
when Hash
|
720
|
-
length = length.symbolize_keys
|
721
|
-
quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
|
722
|
-
when Integer
|
723
|
-
quoted_columns.each { |name, column| column << "(#{length})" }
|
568
|
+
m.register_type(%r(char)i) do |sql_type|
|
569
|
+
limit = extract_limit(sql_type)
|
570
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
724
571
|
end
|
725
|
-
end
|
726
|
-
|
727
|
-
quoted_columns
|
728
|
-
end
|
729
572
|
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
573
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
574
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
575
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
576
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
577
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
578
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
579
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
580
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
581
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
582
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
583
|
+
|
584
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
585
|
+
register_integer_type m, %r(^int)i, limit: 4
|
586
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
587
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
588
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
589
|
+
|
590
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
591
|
+
m.alias_type %r(year)i, "integer"
|
592
|
+
m.alias_type %r(bit)i, "binary"
|
593
|
+
|
594
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
595
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
596
|
+
end
|
597
|
+
|
598
|
+
def register_integer_type(mapping, key, **options)
|
599
|
+
mapping.register_type(key) do |sql_type|
|
600
|
+
if /\bunsigned\b/.match?(sql_type)
|
601
|
+
Type::UnsignedInteger.new(**options)
|
602
|
+
else
|
603
|
+
Type::Integer.new(**options)
|
604
|
+
end
|
605
|
+
end
|
745
606
|
end
|
746
|
-
end
|
747
|
-
|
748
|
-
def add_column_sql(table_name, column_name, type, options = {})
|
749
|
-
td = create_table_definition(table_name)
|
750
|
-
cd = td.new_column_definition(column_name, type, options)
|
751
|
-
schema_creation.accept(AddColumnDefinition.new(cd))
|
752
|
-
end
|
753
|
-
|
754
|
-
def change_column_sql(table_name, column_name, type, options = {})
|
755
|
-
column = column_for(table_name, column_name)
|
756
607
|
|
757
|
-
|
758
|
-
|
608
|
+
def extract_precision(sql_type)
|
609
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
610
|
+
super || 0
|
611
|
+
else
|
612
|
+
super
|
613
|
+
end
|
759
614
|
end
|
760
615
|
|
761
|
-
|
762
|
-
|
616
|
+
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
617
|
+
ER_DB_CREATE_EXISTS = 1007
|
618
|
+
ER_FILSORT_ABORT = 1028
|
619
|
+
ER_DUP_ENTRY = 1062
|
620
|
+
ER_NOT_NULL_VIOLATION = 1048
|
621
|
+
ER_NO_REFERENCED_ROW = 1216
|
622
|
+
ER_ROW_IS_REFERENCED = 1217
|
623
|
+
ER_DO_NOT_HAVE_DEFAULT = 1364
|
624
|
+
ER_ROW_IS_REFERENCED_2 = 1451
|
625
|
+
ER_NO_REFERENCED_ROW_2 = 1452
|
626
|
+
ER_DATA_TOO_LONG = 1406
|
627
|
+
ER_OUT_OF_RANGE = 1264
|
628
|
+
ER_LOCK_DEADLOCK = 1213
|
629
|
+
ER_CANNOT_ADD_FOREIGN = 1215
|
630
|
+
ER_CANNOT_CREATE_TABLE = 1005
|
631
|
+
ER_LOCK_WAIT_TIMEOUT = 1205
|
632
|
+
ER_QUERY_INTERRUPTED = 1317
|
633
|
+
ER_QUERY_TIMEOUT = 3024
|
634
|
+
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
635
|
+
|
636
|
+
def translate_exception(exception, message:, sql:, binds:)
|
637
|
+
case error_number(exception)
|
638
|
+
when nil
|
639
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
640
|
+
ConnectionNotEstablished.new(exception)
|
641
|
+
else
|
642
|
+
super
|
643
|
+
end
|
644
|
+
when ER_DB_CREATE_EXISTS
|
645
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
646
|
+
when ER_DUP_ENTRY
|
647
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
648
|
+
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
649
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
650
|
+
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
651
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
652
|
+
when ER_CANNOT_CREATE_TABLE
|
653
|
+
if message.include?("errno: 150")
|
654
|
+
mismatched_foreign_key(message, sql: sql, binds: binds)
|
655
|
+
else
|
656
|
+
super
|
657
|
+
end
|
658
|
+
when ER_DATA_TOO_LONG
|
659
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
660
|
+
when ER_OUT_OF_RANGE
|
661
|
+
RangeError.new(message, sql: sql, binds: binds)
|
662
|
+
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
663
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
664
|
+
when ER_LOCK_DEADLOCK
|
665
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
666
|
+
when ER_LOCK_WAIT_TIMEOUT
|
667
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
668
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
669
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
670
|
+
when ER_QUERY_INTERRUPTED
|
671
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
672
|
+
else
|
673
|
+
super
|
674
|
+
end
|
763
675
|
end
|
764
676
|
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
end
|
677
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
678
|
+
column = column_for(table_name, column_name)
|
679
|
+
type ||= column.sql_type
|
769
680
|
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
default: column.default,
|
774
|
-
null: column.null,
|
775
|
-
auto_increment: column.auto_increment?
|
776
|
-
}
|
777
|
-
|
778
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
|
779
|
-
td = create_table_definition(table_name)
|
780
|
-
cd = td.new_column_definition(new_column_name, current_type, options)
|
781
|
-
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
782
|
-
end
|
681
|
+
unless options.key?(:default)
|
682
|
+
options[:default] = column.default
|
683
|
+
end
|
783
684
|
|
784
|
-
|
785
|
-
|
786
|
-
|
685
|
+
unless options.key?(:null)
|
686
|
+
options[:null] = column.null
|
687
|
+
end
|
787
688
|
|
788
|
-
|
789
|
-
|
790
|
-
|
689
|
+
unless options.key?(:comment)
|
690
|
+
options[:comment] = column.comment
|
691
|
+
end
|
791
692
|
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
end
|
693
|
+
td = create_table_definition(table_name)
|
694
|
+
cd = td.new_column_definition(column.name, type, **options)
|
695
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
696
|
+
end
|
797
697
|
|
798
|
-
|
799
|
-
|
800
|
-
"DROP INDEX #{index_name}"
|
801
|
-
end
|
698
|
+
def rename_column_for_alter(table_name, column_name, new_column_name)
|
699
|
+
return rename_column_sql(table_name, column_name, new_column_name) if supports_rename_column?
|
802
700
|
|
803
|
-
|
804
|
-
|
805
|
-
|
701
|
+
column = column_for(table_name, column_name)
|
702
|
+
options = {
|
703
|
+
default: column.default,
|
704
|
+
null: column.null,
|
705
|
+
auto_increment: column.auto_increment?,
|
706
|
+
comment: column.comment
|
707
|
+
}
|
806
708
|
|
807
|
-
|
808
|
-
|
809
|
-
|
709
|
+
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
710
|
+
td = create_table_definition(table_name)
|
711
|
+
cd = td.new_column_definition(new_column_name, current_type, **options)
|
712
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
713
|
+
end
|
810
714
|
|
811
|
-
|
715
|
+
def add_index_for_alter(table_name, column_name, **options)
|
716
|
+
index, algorithm, _ = add_index_options(table_name, column_name, **options)
|
717
|
+
algorithm = ", #{algorithm}" if algorithm
|
812
718
|
|
813
|
-
|
814
|
-
|
815
|
-
def subquery_for(key, select)
|
816
|
-
subsubselect = select.clone
|
817
|
-
subsubselect.projections = [key]
|
719
|
+
"ADD #{schema_creation.accept(index)}#{algorithm}"
|
720
|
+
end
|
818
721
|
|
819
|
-
|
820
|
-
|
821
|
-
|
722
|
+
def remove_index_for_alter(table_name, column_name = nil, **options)
|
723
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
724
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
725
|
+
end
|
822
726
|
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
727
|
+
def supports_rename_index?
|
728
|
+
if mariadb?
|
729
|
+
database_version >= "10.5.2"
|
730
|
+
else
|
731
|
+
database_version >= "5.7.6"
|
732
|
+
end
|
733
|
+
end
|
827
734
|
|
828
|
-
|
829
|
-
|
830
|
-
|
735
|
+
def supports_rename_column?
|
736
|
+
if mariadb?
|
737
|
+
database_version >= "10.5.2"
|
738
|
+
else
|
739
|
+
database_version >= "8.0.3"
|
740
|
+
end
|
741
|
+
end
|
831
742
|
|
832
|
-
|
833
|
-
|
743
|
+
def configure_connection
|
744
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
834
745
|
|
835
|
-
|
836
|
-
|
746
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
747
|
+
variables["sql_auto_is_null"] = 0
|
837
748
|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
749
|
+
# Increase timeout so the server doesn't disconnect us.
|
750
|
+
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
751
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
752
|
+
variables["wait_timeout"] = wait_timeout
|
842
753
|
|
843
|
-
|
754
|
+
defaults = [":default", :default].to_set
|
844
755
|
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
756
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
757
|
+
# https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_strict_all_tables
|
758
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
759
|
+
if sql_mode = variables.delete("sql_mode")
|
760
|
+
sql_mode = quote(sql_mode)
|
761
|
+
elsif !defaults.include?(strict_mode?)
|
762
|
+
if strict_mode?
|
763
|
+
sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
|
764
|
+
else
|
765
|
+
sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
|
766
|
+
sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
|
767
|
+
sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
|
768
|
+
end
|
769
|
+
sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
|
857
770
|
end
|
858
|
-
sql_mode =
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
868
|
-
encoding << ", "
|
869
|
-
end
|
870
|
-
|
871
|
-
# Gather up all of the SET variables...
|
872
|
-
variable_assignments = variables.map do |k, v|
|
873
|
-
if defaults.include?(v)
|
874
|
-
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
875
|
-
elsif !v.nil?
|
876
|
-
"@@SESSION.#{k} = #{quote(v)}"
|
771
|
+
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
772
|
+
|
773
|
+
# NAMES does not have an equals sign, see
|
774
|
+
# https://dev.mysql.com/doc/refman/en/set-names.html
|
775
|
+
# (trailing comma because variable_assignments will always have content)
|
776
|
+
if @config[:encoding]
|
777
|
+
encoding = +"NAMES #{@config[:encoding]}"
|
778
|
+
encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
|
779
|
+
encoding << ", "
|
877
780
|
end
|
878
|
-
# or else nil; compact to clear nils out
|
879
|
-
end.compact.join(', ')
|
880
781
|
|
881
|
-
|
882
|
-
|
883
|
-
|
782
|
+
# Gather up all of the SET variables...
|
783
|
+
variable_assignments = variables.map do |k, v|
|
784
|
+
if defaults.include?(v)
|
785
|
+
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
786
|
+
elsif !v.nil?
|
787
|
+
"@@SESSION.#{k} = #{quote(v)}"
|
788
|
+
end
|
789
|
+
# or else nil; compact to clear nils out
|
790
|
+
end.compact.join(", ")
|
884
791
|
|
885
|
-
|
886
|
-
|
887
|
-
each_hash(result)
|
792
|
+
# ...and send them all in one query
|
793
|
+
execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
888
794
|
end
|
889
|
-
end
|
890
795
|
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
796
|
+
def column_definitions(table_name) # :nodoc:
|
797
|
+
execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
|
798
|
+
each_hash(result)
|
799
|
+
end
|
895
800
|
end
|
896
|
-
end
|
897
|
-
|
898
|
-
def create_table_info(table_name) # :nodoc:
|
899
|
-
select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
900
|
-
end
|
901
|
-
|
902
|
-
def create_table_definition(*args) # :nodoc:
|
903
|
-
MySQL::TableDefinition.new(*args)
|
904
|
-
end
|
905
801
|
|
906
|
-
|
907
|
-
|
908
|
-
schema, name = "DATABASE()", schema unless name
|
909
|
-
[schema, name]
|
910
|
-
end
|
911
|
-
|
912
|
-
def integer_to_sql(limit) # :nodoc:
|
913
|
-
case limit
|
914
|
-
when 1; 'tinyint'
|
915
|
-
when 2; 'smallint'
|
916
|
-
when 3; 'mediumint'
|
917
|
-
when nil, 4; 'int'
|
918
|
-
when 5..8; 'bigint'
|
919
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
802
|
+
def create_table_info(table_name) # :nodoc:
|
803
|
+
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
920
804
|
end
|
921
|
-
end
|
922
805
|
|
923
|
-
|
924
|
-
|
925
|
-
when 0..0xff; 'tinytext'
|
926
|
-
when nil, 0x100..0xffff; 'text'
|
927
|
-
when 0x10000..0xffffff; 'mediumtext'
|
928
|
-
when 0x1000000..0xffffffff; 'longtext'
|
929
|
-
else raise(ActiveRecordError, "No text type has byte length #{limit}")
|
806
|
+
def arel_visitor
|
807
|
+
Arel::Visitors::MySQL.new(self)
|
930
808
|
end
|
931
|
-
end
|
932
809
|
|
933
|
-
|
934
|
-
|
935
|
-
when 0..0xff; 'tinyblob'
|
936
|
-
when nil, 0x100..0xffff; 'blob'
|
937
|
-
when 0x10000..0xffffff; 'mediumblob'
|
938
|
-
when 0x1000000..0xffffffff; 'longblob'
|
939
|
-
else raise(ActiveRecordError, "No binary type has byte length #{limit}")
|
810
|
+
def build_statement_pool
|
811
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
940
812
|
end
|
941
|
-
end
|
942
813
|
|
943
|
-
|
944
|
-
|
945
|
-
|
814
|
+
def mismatched_foreign_key(message, sql:, binds:)
|
815
|
+
match = %r/
|
816
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
817
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
818
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
819
|
+
/xmi.match(sql)
|
946
820
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
end
|
953
|
-
end
|
821
|
+
options = {
|
822
|
+
message: message,
|
823
|
+
sql: sql,
|
824
|
+
binds: binds,
|
825
|
+
}
|
954
826
|
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
827
|
+
if match
|
828
|
+
options[:table] = match[:table]
|
829
|
+
options[:foreign_key] = match[:foreign_key]
|
830
|
+
options[:target_table] = match[:target_table]
|
831
|
+
options[:primary_key] = match[:primary_key]
|
832
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
961
833
|
end
|
962
|
-
end
|
963
834
|
|
964
|
-
|
835
|
+
MismatchedForeignKey.new(**options)
|
836
|
+
end
|
965
837
|
|
966
|
-
def
|
967
|
-
|
968
|
-
when true then MySQL::Quoting::QUOTED_TRUE
|
969
|
-
when false then MySQL::Quoting::QUOTED_FALSE
|
970
|
-
else super
|
971
|
-
end
|
838
|
+
def version_string(full_version_string)
|
839
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
972
840
|
end
|
973
|
-
end
|
974
841
|
|
975
|
-
|
976
|
-
|
977
|
-
|
842
|
+
# Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
|
843
|
+
# TODO: Remove the constant alias once Rails 6.1 has released.
|
844
|
+
MysqlString = Type::String # :nodoc:
|
845
|
+
|
846
|
+
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
847
|
+
Type::ImmutableString.new(true: "1", false: "0", **args)
|
848
|
+
end
|
849
|
+
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
850
|
+
Type::String.new(true: "1", false: "0", **args)
|
851
|
+
end
|
852
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
978
853
|
end
|
979
854
|
end
|
980
855
|
end
|