activerecord 3.2.6 → 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 +7 -0
- data/CHANGELOG.md +611 -6417
- data/MIT-LICENSE +4 -2
- data/README.rdoc +44 -47
- data/examples/performance.rb +79 -71
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +268 -238
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +173 -81
- data/lib/active_record/associations/association_scope.rb +124 -92
- data/lib/active_record/associations/belongs_to_association.rb +83 -38
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
- data/lib/active_record/associations/builder/association.rb +113 -32
- data/lib/active_record/associations/builder/belongs_to.rb +105 -60
- data/lib/active_record/associations/builder/collection_association.rb +53 -56
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
- data/lib/active_record/associations/builder/has_many.rb +11 -63
- data/lib/active_record/associations/builder/has_one.rb +47 -45
- data/lib/active_record/associations/builder/singular_association.rb +30 -18
- data/lib/active_record/associations/collection_association.rb +217 -295
- data/lib/active_record/associations/collection_proxy.rb +1074 -77
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +78 -50
- data/lib/active_record/associations/has_many_through_association.rb +99 -61
- data/lib/active_record/associations/has_one_association.rb +75 -30
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +208 -164
- data/lib/active_record/associations/preloader/association.rb +93 -87
- data/lib/active_record/associations/preloader/through_association.rb +87 -38
- data/lib/active_record/associations/preloader.rb +134 -110
- data/lib/active_record/associations/singular_association.rb +19 -24
- data/lib/active_record/associations/through_association.rb +61 -27
- data/lib/active_record/associations.rb +1766 -1505
- data/lib/active_record/attribute_assignment.rb +57 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
- data/lib/active_record/attribute_methods/dirty.rb +187 -67
- data/lib/active_record/attribute_methods/primary_key.rb +100 -78
- data/lib/active_record/attribute_methods/query.rb +10 -8
- data/lib/active_record/attribute_methods/read.rb +29 -118
- data/lib/active_record/attribute_methods/serialization.rb +60 -72
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
- data/lib/active_record/attribute_methods/write.rb +36 -44
- data/lib/active_record/attribute_methods.rb +306 -161
- data/lib/active_record/attributes.rb +279 -0
- data/lib/active_record/autosave_association.rb +324 -238
- data/lib/active_record/base.rb +114 -507
- data/lib/active_record/callbacks.rb +147 -83
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
- data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
- data/lib/active_record/connection_adapters/column.rb +58 -233
- data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
- 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 +75 -207
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
- data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
- 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 +528 -26
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +267 -0
- data/lib/active_record/core.rb +599 -0
- data/lib/active_record/counter_cache.rb +177 -103
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -64
- data/lib/active_record/enum.rb +274 -0
- data/lib/active_record/errors.rb +254 -61
- data/lib/active_record/explain.rb +35 -70
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -8
- data/lib/active_record/fixture_set/file.rb +82 -0
- 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 +291 -475
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +219 -100
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +175 -17
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +106 -92
- data/lib/active_record/locking/pessimistic.rb +23 -11
- data/lib/active_record/log_subscriber.rb +80 -30
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +235 -56
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +917 -301
- data/lib/active_record/model_schema.rb +351 -175
- data/lib/active_record/nested_attributes.rb +366 -235
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +761 -166
- data/lib/active_record/query_cache.rb +22 -44
- data/lib/active_record/querying.rb +55 -31
- data/lib/active_record/railtie.rb +185 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +35 -33
- data/lib/active_record/railties/databases.rake +366 -463
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +736 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +252 -52
- data/lib/active_record/relation/calculations.rb +340 -270
- data/lib/active_record/relation/delegation.rb +117 -36
- data/lib/active_record/relation/finder_methods.rb +439 -286
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +184 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
- 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 +19 -0
- data/lib/active_record/relation/predicate_builder.rb +131 -39
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +1163 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +49 -120
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +671 -349
- data/lib/active_record/result.rb +149 -15
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +153 -133
- data/lib/active_record/schema.rb +22 -19
- data/lib/active_record/schema_dumper.rb +178 -112
- data/lib/active_record/schema_migration.rb +60 -0
- data/lib/active_record/scoping/default.rb +107 -98
- data/lib/active_record/scoping/named.rb +130 -115
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +148 -0
- data/lib/active_record/store.rb +256 -16
- 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 +506 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +93 -39
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +260 -129
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- 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 +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +78 -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/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +123 -77
- data/lib/active_record/validations.rb +54 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +97 -49
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
- 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.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +41 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +24 -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} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +285 -149
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- 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_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- 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_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,177 +1,143 @@
|
|
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
|
-
|
8
|
-
|
17
|
+
include MySQL::Quoting
|
18
|
+
include MySQL::SchemaStatements
|
9
19
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
20
|
+
##
|
21
|
+
# :singleton-method:
|
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
|
24
|
+
# to your application.rb file:
|
25
|
+
#
|
26
|
+
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
27
|
+
class_attribute :emulate_booleans, default: true
|
14
28
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
NATIVE_DATABASE_TYPES = {
|
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" },
|
44
|
+
}
|
28
45
|
|
29
|
-
|
30
|
-
|
31
|
-
super
|
32
|
-
end
|
46
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
47
|
+
private
|
33
48
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
def dealloc(stmt)
|
50
|
+
stmt.close
|
51
|
+
end
|
52
|
+
end
|
38
53
|
|
39
|
-
|
40
|
-
|
41
|
-
|
54
|
+
def initialize(connection, logger, connection_options, config)
|
55
|
+
super(connection, logger, config)
|
56
|
+
end
|
42
57
|
|
43
|
-
|
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
|
44
63
|
|
45
|
-
|
46
|
-
|
64
|
+
def mariadb? # :nodoc:
|
65
|
+
/mariadb/i.match?(full_version)
|
66
|
+
end
|
47
67
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
when /bit/i then :binary
|
52
|
-
else
|
53
|
-
super
|
54
|
-
end
|
55
|
-
end
|
68
|
+
def supports_bulk_alter?
|
69
|
+
true
|
70
|
+
end
|
56
71
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
case sql_type
|
61
|
-
when /tiny/i
|
62
|
-
255
|
63
|
-
when /medium/i
|
64
|
-
16777215
|
65
|
-
when /long/i
|
66
|
-
2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
|
67
|
-
else
|
68
|
-
super # we could return 65535 here, but we leave it undecorated by default
|
69
|
-
end
|
70
|
-
when /^bigint/i; 8
|
71
|
-
when /^int/i; 4
|
72
|
-
when /^mediumint/i; 3
|
73
|
-
when /^smallint/i; 2
|
74
|
-
when /^tinyint/i; 1
|
75
|
-
else
|
76
|
-
super
|
77
|
-
end
|
78
|
-
end
|
72
|
+
def supports_index_sort_order?
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
74
|
+
end
|
79
75
|
|
80
|
-
|
81
|
-
|
82
|
-
# default (string) but we can for others (integer, datetime, boolean,
|
83
|
-
# and the rest).
|
84
|
-
#
|
85
|
-
# Test whether the column has default '', is not null, and is not
|
86
|
-
# a type allowing default ''.
|
87
|
-
def missing_default_forged_as_empty_string?(default)
|
88
|
-
type != :string && !null && default == ''
|
89
|
-
end
|
76
|
+
def supports_expression_index?
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
90
78
|
end
|
91
79
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# as boolean. If you wish to disable this emulation (which was the default
|
96
|
-
# behavior in versions 0.13.1 and earlier) you can add the following line
|
97
|
-
# to your application.rb file:
|
98
|
-
#
|
99
|
-
# ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
|
100
|
-
class_attribute :emulate_booleans
|
101
|
-
self.emulate_booleans = true
|
80
|
+
def supports_transaction_isolation?
|
81
|
+
true
|
82
|
+
end
|
102
83
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
"Lost connection to MySQL server during query",
|
107
|
-
"MySQL server has gone away" ]
|
84
|
+
def supports_explain?
|
85
|
+
true
|
86
|
+
end
|
108
87
|
|
109
|
-
|
88
|
+
def supports_indexes_in_create?
|
89
|
+
true
|
90
|
+
end
|
110
91
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
:text => { :name => "text" },
|
115
|
-
:integer => { :name => "int", :limit => 4 },
|
116
|
-
:float => { :name => "float" },
|
117
|
-
:decimal => { :name => "decimal" },
|
118
|
-
:datetime => { :name => "datetime" },
|
119
|
-
:timestamp => { :name => "datetime" },
|
120
|
-
:time => { :name => "time" },
|
121
|
-
:date => { :name => "date" },
|
122
|
-
:binary => { :name => "blob" },
|
123
|
-
:boolean => { :name => "tinyint", :limit => 1 }
|
124
|
-
}
|
92
|
+
def supports_foreign_keys?
|
93
|
+
true
|
94
|
+
end
|
125
95
|
|
126
|
-
|
127
|
-
|
96
|
+
def supports_views?
|
97
|
+
true
|
128
98
|
end
|
129
99
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
@connection_options, @config = connection_options, config
|
134
|
-
@quoted_column_names, @quoted_table_names = {}, {}
|
100
|
+
def supports_datetime_with_precision?
|
101
|
+
mariadb? || database_version >= "5.6.4"
|
102
|
+
end
|
135
103
|
|
136
|
-
|
137
|
-
|
138
|
-
else
|
139
|
-
@visitor = BindSubstitution.new self
|
140
|
-
end
|
104
|
+
def supports_virtual_columns?
|
105
|
+
mariadb? || database_version >= "5.7.5"
|
141
106
|
end
|
142
107
|
|
143
|
-
|
144
|
-
|
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"
|
145
111
|
end
|
146
112
|
|
147
|
-
|
148
|
-
def supports_migrations?
|
113
|
+
def supports_advisory_locks?
|
149
114
|
true
|
150
115
|
end
|
151
116
|
|
152
|
-
def
|
117
|
+
def supports_insert_on_duplicate_skip?
|
153
118
|
true
|
154
119
|
end
|
155
120
|
|
156
|
-
|
157
|
-
def supports_savepoints?
|
121
|
+
def supports_insert_on_duplicate_update?
|
158
122
|
true
|
159
123
|
end
|
160
124
|
|
161
|
-
def
|
162
|
-
|
125
|
+
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
126
|
+
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
163
127
|
end
|
164
128
|
|
165
|
-
|
166
|
-
|
167
|
-
def supports_index_sort_order?
|
168
|
-
true
|
129
|
+
def release_advisory_lock(lock_name) # :nodoc:
|
130
|
+
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
169
131
|
end
|
170
132
|
|
171
133
|
def native_database_types
|
172
134
|
NATIVE_DATABASE_TYPES
|
173
135
|
end
|
174
136
|
|
137
|
+
def index_algorithms
|
138
|
+
{ default: +"ALGORITHM = DEFAULT", copy: +"ALGORITHM = COPY", inplace: +"ALGORITHM = INPLACE" }
|
139
|
+
end
|
140
|
+
|
175
141
|
# HELPER METHODS ===========================================
|
176
142
|
|
177
143
|
# The two drivers have slightly different ways of yielding hashes of results, so
|
@@ -180,50 +146,16 @@ module ActiveRecord
|
|
180
146
|
raise NotImplementedError
|
181
147
|
end
|
182
148
|
|
183
|
-
#
|
184
|
-
def new_column(field, default, type, null, collation) # :nodoc:
|
185
|
-
Column.new(field, default, type, null, collation)
|
186
|
-
end
|
187
|
-
|
188
|
-
# Must return the Mysql error number from the exception, if the exception has an
|
149
|
+
# Must return the MySQL error number from the exception, if the exception has an
|
189
150
|
# error number.
|
190
151
|
def error_number(exception) # :nodoc:
|
191
152
|
raise NotImplementedError
|
192
153
|
end
|
193
154
|
|
194
|
-
# QUOTING ==================================================
|
195
|
-
|
196
|
-
def quote(value, column = nil)
|
197
|
-
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
198
|
-
s = column.class.string_to_binary(value).unpack("H*")[0]
|
199
|
-
"x'#{s}'"
|
200
|
-
elsif value.kind_of?(BigDecimal)
|
201
|
-
value.to_s("F")
|
202
|
-
else
|
203
|
-
super
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def quote_column_name(name) #:nodoc:
|
208
|
-
@quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
|
209
|
-
end
|
210
|
-
|
211
|
-
def quote_table_name(name) #:nodoc:
|
212
|
-
@quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
|
213
|
-
end
|
214
|
-
|
215
|
-
def quoted_true
|
216
|
-
QUOTED_TRUE
|
217
|
-
end
|
218
|
-
|
219
|
-
def quoted_false
|
220
|
-
QUOTED_FALSE
|
221
|
-
end
|
222
|
-
|
223
155
|
# REFERENTIAL INTEGRITY ====================================
|
224
156
|
|
225
|
-
def disable_referential_integrity
|
226
|
-
old =
|
157
|
+
def disable_referential_integrity #:nodoc:
|
158
|
+
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
227
159
|
|
228
160
|
begin
|
229
161
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
@@ -233,121 +165,92 @@ module ActiveRecord
|
|
233
165
|
end
|
234
166
|
end
|
235
167
|
|
168
|
+
# CONNECTION MANAGEMENT ====================================
|
169
|
+
|
170
|
+
def clear_cache! # :nodoc:
|
171
|
+
reload_type_map
|
172
|
+
super
|
173
|
+
end
|
174
|
+
|
175
|
+
#--
|
236
176
|
# DATABASE STATEMENTS ======================================
|
177
|
+
#++
|
178
|
+
|
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)
|
186
|
+
end
|
237
187
|
|
238
188
|
# Executes the SQL statement in the context of this connection.
|
239
189
|
def execute(sql, name = nil)
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
if exception.message.split(":").first =~ /Packets out of order/
|
247
|
-
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
248
|
-
else
|
249
|
-
raise
|
190
|
+
materialize_transactions
|
191
|
+
|
192
|
+
log(sql, name) do
|
193
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
194
|
+
@connection.query(sql)
|
195
|
+
end
|
250
196
|
end
|
251
197
|
end
|
252
198
|
|
253
|
-
#
|
254
|
-
# stuff in
|
255
|
-
# explicitly freed or not.
|
256
|
-
def execute_and_free(sql, name = nil)
|
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:
|
257
203
|
yield execute(sql, name)
|
258
204
|
end
|
259
205
|
|
260
|
-
def update_sql(sql, name = nil) #:nodoc:
|
261
|
-
super
|
262
|
-
@connection.affected_rows
|
263
|
-
end
|
264
|
-
|
265
206
|
def begin_db_transaction
|
266
207
|
execute "BEGIN"
|
267
|
-
|
268
|
-
|
208
|
+
end
|
209
|
+
|
210
|
+
def begin_isolated_db_transaction(isolation)
|
211
|
+
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
212
|
+
begin_db_transaction
|
269
213
|
end
|
270
214
|
|
271
215
|
def commit_db_transaction #:nodoc:
|
272
216
|
execute "COMMIT"
|
273
|
-
rescue Exception
|
274
|
-
# Transactions aren't supported
|
275
217
|
end
|
276
218
|
|
277
|
-
def
|
219
|
+
def exec_rollback_db_transaction #:nodoc:
|
278
220
|
execute "ROLLBACK"
|
279
|
-
rescue Exception
|
280
|
-
# Transactions aren't supported
|
281
|
-
end
|
282
|
-
|
283
|
-
def create_savepoint
|
284
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
285
|
-
end
|
286
|
-
|
287
|
-
def rollback_to_savepoint
|
288
|
-
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
289
|
-
end
|
290
|
-
|
291
|
-
def release_savepoint
|
292
|
-
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
293
221
|
end
|
294
222
|
|
295
|
-
|
296
|
-
|
297
|
-
# these, we must use a subquery. However, MySQL is too stupid to create a
|
298
|
-
# temporary table for this automatically, so we have to give it some prompting
|
299
|
-
# in the form of a subsubquery. Ugh!
|
300
|
-
def join_to_update(update, select) #:nodoc:
|
301
|
-
if select.limit || select.offset || select.orders.any?
|
302
|
-
subsubselect = select.clone
|
303
|
-
subsubselect.projections = [update.key]
|
304
|
-
|
305
|
-
subselect = Arel::SelectManager.new(select.engine)
|
306
|
-
subselect.project Arel.sql(update.key.name)
|
307
|
-
subselect.from subsubselect.as('__active_record_temp')
|
308
|
-
|
309
|
-
update.where update.key.in(subselect)
|
310
|
-
else
|
311
|
-
update.table select.source
|
312
|
-
update.wheres = select.constraints
|
313
|
-
end
|
223
|
+
def empty_insert_statement_value(primary_key = nil)
|
224
|
+
"VALUES ()"
|
314
225
|
end
|
315
226
|
|
316
227
|
# SCHEMA STATEMENTS ========================================
|
317
228
|
|
318
|
-
def structure_dump #:nodoc:
|
319
|
-
if supports_views?
|
320
|
-
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
321
|
-
else
|
322
|
-
sql = "SHOW TABLES"
|
323
|
-
end
|
324
|
-
|
325
|
-
select_all(sql).map { |table|
|
326
|
-
table.delete('Table_type')
|
327
|
-
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
328
|
-
exec_without_stmt(sql).first['Create Table'] + ";\n\n"
|
329
|
-
}.join
|
330
|
-
end
|
331
|
-
|
332
229
|
# Drops the database specified on the +name+ attribute
|
333
230
|
# and creates it again using the provided +options+.
|
334
231
|
def recreate_database(name, options = {})
|
335
232
|
drop_database(name)
|
336
|
-
create_database(name, options)
|
233
|
+
sql = create_database(name, options)
|
234
|
+
reconnect!
|
235
|
+
sql
|
337
236
|
end
|
338
237
|
|
339
238
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
340
|
-
# Charset defaults to
|
239
|
+
# Charset defaults to utf8mb4.
|
341
240
|
#
|
342
241
|
# Example:
|
343
|
-
# create_database 'charset_test', :
|
242
|
+
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
344
243
|
# create_database 'matt_development'
|
345
|
-
# create_database 'matt_development', :
|
244
|
+
# create_database 'matt_development', charset: :big5
|
346
245
|
def create_database(name, options = {})
|
347
246
|
if options[:collation]
|
348
|
-
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`"
|
349
252
|
else
|
350
|
-
|
253
|
+
raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
|
351
254
|
end
|
352
255
|
end
|
353
256
|
|
@@ -356,319 +259,563 @@ module ActiveRecord
|
|
356
259
|
# Example:
|
357
260
|
# drop_database('sebastian_development')
|
358
261
|
def drop_database(name) #:nodoc:
|
359
|
-
execute "DROP DATABASE IF EXISTS
|
262
|
+
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
360
263
|
end
|
361
264
|
|
362
265
|
def current_database
|
363
|
-
|
266
|
+
query_value("SELECT database()", "SCHEMA")
|
364
267
|
end
|
365
268
|
|
366
269
|
# Returns the database character set.
|
367
270
|
def charset
|
368
|
-
show_variable
|
271
|
+
show_variable "character_set_database"
|
369
272
|
end
|
370
273
|
|
371
274
|
# Returns the database collation strategy.
|
372
275
|
def collation
|
373
|
-
show_variable
|
276
|
+
show_variable "collation_database"
|
374
277
|
end
|
375
278
|
|
376
|
-
def
|
377
|
-
|
378
|
-
sql << "IN #{quote_table_name(database)} " if database
|
379
|
-
sql << "LIKE #{quote(like)}" if like
|
279
|
+
def table_comment(table_name) # :nodoc:
|
280
|
+
scope = quoted_scope(table_name)
|
380
281
|
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
384
288
|
end
|
385
289
|
|
386
|
-
def
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
schema, table = name.split('.', 2)
|
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)}")
|
294
|
+
end
|
392
295
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
296
|
+
# Renames a table.
|
297
|
+
#
|
298
|
+
# Example:
|
299
|
+
# rename_table('octopuses', 'octopi')
|
300
|
+
def rename_table(table_name, new_name)
|
301
|
+
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
302
|
+
rename_table_indexes(table_name, new_name)
|
303
|
+
end
|
397
304
|
|
398
|
-
|
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.
|
320
|
+
def drop_table(table_name, options = {})
|
321
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
399
322
|
end
|
400
323
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
current_index = nil
|
405
|
-
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
406
|
-
each_hash(result) do |row|
|
407
|
-
if current_index != row[:Key_name]
|
408
|
-
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
409
|
-
current_index = row[:Key_name]
|
410
|
-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
|
411
|
-
end
|
324
|
+
def rename_index(table_name, old_name, new_name)
|
325
|
+
if supports_rename_index?
|
326
|
+
validate_index_length!(table_name, new_name)
|
412
327
|
|
413
|
-
|
414
|
-
|
415
|
-
|
328
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
|
329
|
+
else
|
330
|
+
super
|
416
331
|
end
|
332
|
+
end
|
417
333
|
|
418
|
-
|
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
|
419
337
|
end
|
420
338
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
execute_and_free(sql, 'SCHEMA') do |result|
|
425
|
-
each_hash(result).map do |field|
|
426
|
-
new_column(field[:Field], field[:Default], field[:Type], field[:Null] == "YES", field[:Collation])
|
427
|
-
end
|
339
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
340
|
+
unless null || default.nil?
|
341
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
428
342
|
end
|
429
|
-
end
|
430
343
|
|
431
|
-
|
432
|
-
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
344
|
+
change_column table_name, column_name, nil, null: null
|
433
345
|
end
|
434
346
|
|
435
|
-
def
|
436
|
-
|
437
|
-
|
438
|
-
method = :"#{command}_sql"
|
439
|
-
|
440
|
-
if respond_to?(method, true)
|
441
|
-
send(method, table, *arguments)
|
442
|
-
else
|
443
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
444
|
-
end
|
445
|
-
end.flatten.join(", ")
|
446
|
-
|
447
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
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
|
448
350
|
end
|
449
351
|
|
450
|
-
|
451
|
-
|
452
|
-
# Example:
|
453
|
-
# rename_table('octopuses', 'octopi')
|
454
|
-
def rename_table(table_name, new_name)
|
455
|
-
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
352
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
353
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
456
354
|
end
|
457
355
|
|
458
|
-
def
|
459
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{
|
356
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
357
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
358
|
+
rename_column_indexes(table_name, column_name, new_column_name)
|
359
|
+
end
|
360
|
+
|
361
|
+
def add_index(table_name, column_name, options = {}) #:nodoc:
|
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
|
370
|
+
end
|
371
|
+
|
372
|
+
def foreign_keys(table_name)
|
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]}
|
392
|
+
SQL
|
393
|
+
|
394
|
+
fk_info.map do |row|
|
395
|
+
options = {
|
396
|
+
column: row["column"],
|
397
|
+
name: row["name"],
|
398
|
+
primary_key: row["primary_key"]
|
399
|
+
}
|
400
|
+
|
401
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
402
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
403
|
+
|
404
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
405
|
+
end
|
460
406
|
end
|
461
407
|
|
462
|
-
def
|
463
|
-
|
464
|
-
change_column table_name, column_name, column.sql_type, :default => default
|
465
|
-
end
|
408
|
+
def table_options(table_name) # :nodoc:
|
409
|
+
table_options = {}
|
466
410
|
|
467
|
-
|
468
|
-
column = column_for(table_name, column_name)
|
411
|
+
create_table_info = create_table_info(table_name)
|
469
412
|
|
470
|
-
|
471
|
-
|
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)
|
472
424
|
end
|
473
425
|
|
474
|
-
|
426
|
+
table_options
|
475
427
|
end
|
476
428
|
|
477
|
-
|
478
|
-
|
429
|
+
# SHOW VARIABLES LIKE 'name'
|
430
|
+
def show_variable(name)
|
431
|
+
query_value("SELECT @@#{name}", "SCHEMA")
|
432
|
+
rescue ActiveRecord::StatementInvalid
|
433
|
+
nil
|
479
434
|
end
|
480
435
|
|
481
|
-
def
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
436
|
+
def primary_keys(table_name) # :nodoc:
|
437
|
+
raise ArgumentError unless table_name.present?
|
438
|
+
|
439
|
+
scope = quoted_scope(table_name)
|
440
|
+
|
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
|
449
|
+
end
|
450
|
+
|
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))
|
505
461
|
else
|
506
462
|
super
|
507
463
|
end
|
508
464
|
end
|
509
465
|
|
510
|
-
def
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
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))
|
471
|
+
else
|
472
|
+
super
|
515
473
|
end
|
516
474
|
end
|
517
475
|
|
518
|
-
|
519
|
-
|
520
|
-
variables = select_all("SHOW VARIABLES LIKE '#{name}'")
|
521
|
-
variables.first['Value'] unless variables.empty?
|
476
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
477
|
+
column.case_sensitive?
|
522
478
|
end
|
479
|
+
private :can_perform_case_insensitive_comparison_for?
|
523
480
|
|
524
|
-
#
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
481
|
+
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
482
|
+
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
483
|
+
# distinct queries, and requires that the ORDER BY include the distinct column.
|
484
|
+
# See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
|
485
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
486
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
487
|
+
# Convert Arel node to string
|
488
|
+
s = s.to_sql unless s.is_a?(String)
|
489
|
+
# Remove any ASC/DESC modifiers
|
490
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
491
|
+
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
492
|
+
|
493
|
+
(order_columns << super).join(", ")
|
535
494
|
end
|
536
495
|
|
537
|
-
|
538
|
-
|
539
|
-
pk_and_sequence = pk_and_sequence_for(table)
|
540
|
-
pk_and_sequence && pk_and_sequence.first
|
496
|
+
def strict_mode?
|
497
|
+
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
541
498
|
end
|
542
499
|
|
543
|
-
def
|
544
|
-
|
500
|
+
def default_index_type?(index) # :nodoc:
|
501
|
+
index.using == :btree || super
|
545
502
|
end
|
546
503
|
|
547
|
-
def
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
504
|
+
def build_insert_sql(insert) # :nodoc:
|
505
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
506
|
+
|
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(",")
|
552
513
|
end
|
514
|
+
|
515
|
+
sql
|
553
516
|
end
|
554
517
|
|
555
|
-
def
|
556
|
-
|
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."
|
521
|
+
end
|
557
522
|
end
|
558
523
|
|
559
|
-
|
524
|
+
private
|
525
|
+
|
526
|
+
def initialize_type_map(m = type_map)
|
527
|
+
super
|
560
528
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
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
|
557
|
+
|
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)
|
568
562
|
end
|
569
563
|
end
|
570
564
|
|
571
|
-
|
572
|
-
|
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
|
573
574
|
|
574
|
-
|
575
|
-
|
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
|
582
|
+
|
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
|
576
633
|
|
577
|
-
|
578
|
-
|
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
|
579
637
|
|
580
|
-
|
581
|
-
|
638
|
+
unless options.key?(:default)
|
639
|
+
options[:default] = column.default
|
640
|
+
end
|
582
641
|
|
583
|
-
|
584
|
-
|
642
|
+
unless options.key?(:null)
|
643
|
+
options[:null] = column.null
|
644
|
+
end
|
585
645
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
super
|
646
|
+
unless options.key?(:comment)
|
647
|
+
options[:comment] = column.comment
|
648
|
+
end
|
649
|
+
|
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))
|
594
653
|
end
|
595
|
-
end
|
596
654
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
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))
|
667
|
+
end
|
668
|
+
|
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
|
674
|
+
|
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
|
679
|
+
|
680
|
+
def add_timestamps_for_alter(table_name, options = {})
|
681
|
+
options[:null] = false if options[:null].nil?
|
603
682
|
|
604
|
-
|
605
|
-
|
683
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
684
|
+
options[:precision] = 6
|
685
|
+
end
|
606
686
|
|
607
|
-
|
608
|
-
options[:default] = column.default
|
687
|
+
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
609
688
|
end
|
610
689
|
|
611
|
-
|
612
|
-
|
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)]
|
613
692
|
end
|
614
693
|
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
change_column_sql
|
619
|
-
end
|
694
|
+
def supports_rename_index?
|
695
|
+
mariadb? ? false : database_version >= "5.7.6"
|
696
|
+
end
|
620
697
|
|
621
|
-
|
622
|
-
|
698
|
+
def configure_connection
|
699
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
623
700
|
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
701
|
+
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
702
|
+
variables["sql_auto_is_null"] = 0
|
703
|
+
|
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
|
708
|
+
|
709
|
+
defaults = [":default", :default].to_set
|
710
|
+
|
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
|
736
|
+
|
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(", ")
|
746
|
+
|
747
|
+
# ...and send them all in one query
|
748
|
+
execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
|
629
749
|
end
|
630
750
|
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
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
|
636
756
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
alias :remove_columns_sql :remove_column
|
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
|
641
760
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
end
|
761
|
+
def arel_visitor
|
762
|
+
Arel::Visitors::MySQL.new(self)
|
763
|
+
end
|
646
764
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
end
|
765
|
+
def build_statement_pool
|
766
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
767
|
+
end
|
651
768
|
|
652
|
-
|
653
|
-
|
654
|
-
|
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)
|
775
|
+
|
776
|
+
options = {
|
777
|
+
message: message,
|
778
|
+
sql: sql,
|
779
|
+
binds: binds,
|
780
|
+
}
|
781
|
+
|
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])
|
788
|
+
end
|
655
789
|
|
656
|
-
|
657
|
-
|
658
|
-
end
|
790
|
+
MismatchedForeignKey.new(options)
|
791
|
+
end
|
659
792
|
|
660
|
-
|
793
|
+
def version_string(full_version_string)
|
794
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
795
|
+
end
|
661
796
|
|
662
|
-
|
663
|
-
|
664
|
-
|
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
|
804
|
+
end
|
805
|
+
|
806
|
+
private
|
665
807
|
|
666
|
-
|
667
|
-
|
668
|
-
|
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
|
669
815
|
end
|
670
|
-
|
671
|
-
|
816
|
+
|
817
|
+
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
|
818
|
+
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
672
819
|
end
|
673
820
|
end
|
674
821
|
end
|