activerecord 6.0.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -25
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +171 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- 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.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "active_record/connection_adapters/abstract_adapter"
|
|
4
4
|
require "active_record/connection_adapters/statement_pool"
|
|
5
5
|
require "active_record/connection_adapters/mysql/column"
|
|
6
|
+
require "active_record/connection_adapters/mysql/database_statements"
|
|
6
7
|
require "active_record/connection_adapters/mysql/explain_pretty_printer"
|
|
7
8
|
require "active_record/connection_adapters/mysql/quoting"
|
|
8
9
|
require "active_record/connection_adapters/mysql/schema_creation"
|
|
@@ -14,6 +15,7 @@ require "active_record/connection_adapters/mysql/type_metadata"
|
|
|
14
15
|
module ActiveRecord
|
|
15
16
|
module ConnectionAdapters
|
|
16
17
|
class AbstractMysqlAdapter < AbstractAdapter
|
|
18
|
+
include MySQL::DatabaseStatements
|
|
17
19
|
include MySQL::Quoting
|
|
18
20
|
include MySQL::SchemaStatements
|
|
19
21
|
|
|
@@ -31,6 +33,7 @@ module ActiveRecord
|
|
|
31
33
|
string: { name: "varchar", limit: 255 },
|
|
32
34
|
text: { name: "text" },
|
|
33
35
|
integer: { name: "int", limit: 4 },
|
|
36
|
+
bigint: { name: "bigint" },
|
|
34
37
|
float: { name: "float", limit: 24 },
|
|
35
38
|
decimal: { name: "decimal" },
|
|
36
39
|
datetime: { name: "datetime" },
|
|
@@ -45,17 +48,42 @@ module ActiveRecord
|
|
|
45
48
|
|
|
46
49
|
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
|
47
50
|
private
|
|
48
|
-
|
|
49
51
|
def dealloc(stmt)
|
|
50
52
|
stmt.close
|
|
51
53
|
end
|
|
52
54
|
end
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
class << self
|
|
57
|
+
def dbconsole(config, options = {})
|
|
58
|
+
mysql_config = config.configuration_hash
|
|
59
|
+
|
|
60
|
+
args = {
|
|
61
|
+
host: "--host",
|
|
62
|
+
port: "--port",
|
|
63
|
+
socket: "--socket",
|
|
64
|
+
username: "--user",
|
|
65
|
+
encoding: "--default-character-set",
|
|
66
|
+
sslca: "--ssl-ca",
|
|
67
|
+
sslcert: "--ssl-cert",
|
|
68
|
+
sslcapath: "--ssl-capath",
|
|
69
|
+
sslcipher: "--ssl-cipher",
|
|
70
|
+
sslkey: "--ssl-key",
|
|
71
|
+
ssl_mode: "--ssl-mode"
|
|
72
|
+
}.filter_map { |opt, arg| "#{arg}=#{mysql_config[opt]}" if mysql_config[opt] }
|
|
73
|
+
|
|
74
|
+
if mysql_config[:password] && options[:include_password]
|
|
75
|
+
args << "--password=#{mysql_config[:password]}"
|
|
76
|
+
elsif mysql_config[:password] && !mysql_config[:password].to_s.empty?
|
|
77
|
+
args << "-p"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
args << config.database
|
|
81
|
+
|
|
82
|
+
find_cmd_and_exec(["mysql", "mysql5"], *args)
|
|
83
|
+
end
|
|
56
84
|
end
|
|
57
85
|
|
|
58
|
-
def get_database_version
|
|
86
|
+
def get_database_version # :nodoc:
|
|
59
87
|
full_version_string = get_full_version
|
|
60
88
|
version_string = version_string(full_version_string)
|
|
61
89
|
Version.new(version_string, full_version_string)
|
|
@@ -81,6 +109,10 @@ module ActiveRecord
|
|
|
81
109
|
true
|
|
82
110
|
end
|
|
83
111
|
|
|
112
|
+
def supports_restart_db_transaction?
|
|
113
|
+
true
|
|
114
|
+
end
|
|
115
|
+
|
|
84
116
|
def supports_explain?
|
|
85
117
|
true
|
|
86
118
|
end
|
|
@@ -93,6 +125,14 @@ module ActiveRecord
|
|
|
93
125
|
true
|
|
94
126
|
end
|
|
95
127
|
|
|
128
|
+
def supports_check_constraints?
|
|
129
|
+
if mariadb?
|
|
130
|
+
database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
|
|
131
|
+
else
|
|
132
|
+
database_version >= "8.0.16"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
96
136
|
def supports_views?
|
|
97
137
|
true
|
|
98
138
|
end
|
|
@@ -105,11 +145,19 @@ module ActiveRecord
|
|
|
105
145
|
mariadb? || database_version >= "5.7.5"
|
|
106
146
|
end
|
|
107
147
|
|
|
108
|
-
# See https://dev.mysql.com/doc/refman/
|
|
148
|
+
# See https://dev.mysql.com/doc/refman/en/optimizer-hints.html for more details.
|
|
109
149
|
def supports_optimizer_hints?
|
|
110
150
|
!mariadb? && database_version >= "5.7.7"
|
|
111
151
|
end
|
|
112
152
|
|
|
153
|
+
def supports_common_table_expressions?
|
|
154
|
+
if mariadb?
|
|
155
|
+
database_version >= "10.2.1"
|
|
156
|
+
else
|
|
157
|
+
database_version >= "8.0.1"
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
113
161
|
def supports_advisory_locks?
|
|
114
162
|
true
|
|
115
163
|
end
|
|
@@ -122,6 +170,14 @@ module ActiveRecord
|
|
|
122
170
|
true
|
|
123
171
|
end
|
|
124
172
|
|
|
173
|
+
def supports_insert_returning?
|
|
174
|
+
mariadb? && database_version >= "10.5.0"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def return_value_after_insert?(column) # :nodoc:
|
|
178
|
+
supports_insert_returning? ? column.auto_populated? : column.auto_increment?
|
|
179
|
+
end
|
|
180
|
+
|
|
125
181
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
|
126
182
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
|
127
183
|
end
|
|
@@ -135,7 +191,12 @@ module ActiveRecord
|
|
|
135
191
|
end
|
|
136
192
|
|
|
137
193
|
def index_algorithms
|
|
138
|
-
{
|
|
194
|
+
{
|
|
195
|
+
default: "ALGORITHM = DEFAULT",
|
|
196
|
+
copy: "ALGORITHM = COPY",
|
|
197
|
+
inplace: "ALGORITHM = INPLACE",
|
|
198
|
+
instant: "ALGORITHM = INSTANT",
|
|
199
|
+
}
|
|
139
200
|
end
|
|
140
201
|
|
|
141
202
|
# HELPER METHODS ===========================================
|
|
@@ -154,73 +215,54 @@ module ActiveRecord
|
|
|
154
215
|
|
|
155
216
|
# REFERENTIAL INTEGRITY ====================================
|
|
156
217
|
|
|
157
|
-
def disable_referential_integrity
|
|
218
|
+
def disable_referential_integrity # :nodoc:
|
|
158
219
|
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
|
159
220
|
|
|
160
221
|
begin
|
|
161
222
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
|
162
223
|
yield
|
|
163
224
|
ensure
|
|
164
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
|
225
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
|
|
165
226
|
end
|
|
166
227
|
end
|
|
167
228
|
|
|
168
|
-
# CONNECTION MANAGEMENT ====================================
|
|
169
|
-
|
|
170
|
-
def clear_cache! # :nodoc:
|
|
171
|
-
reload_type_map
|
|
172
|
-
super
|
|
173
|
-
end
|
|
174
|
-
|
|
175
229
|
#--
|
|
176
230
|
# DATABASE STATEMENTS ======================================
|
|
177
231
|
#++
|
|
178
232
|
|
|
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
|
|
187
|
-
|
|
188
|
-
# Executes the SQL statement in the context of this connection.
|
|
189
|
-
def execute(sql, name = nil)
|
|
190
|
-
materialize_transactions
|
|
191
|
-
|
|
192
|
-
log(sql, name) do
|
|
193
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
194
|
-
@connection.query(sql)
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
233
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
|
200
234
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
|
201
235
|
# needs to be explicitly freed or not.
|
|
202
|
-
def execute_and_free(sql, name = nil) # :nodoc:
|
|
203
|
-
|
|
236
|
+
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
|
237
|
+
sql = transform_query(sql)
|
|
238
|
+
check_if_write_query(sql)
|
|
239
|
+
|
|
240
|
+
mark_transaction_written_if_write(sql)
|
|
241
|
+
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
|
204
242
|
end
|
|
205
243
|
|
|
206
|
-
def begin_db_transaction
|
|
207
|
-
|
|
244
|
+
def begin_db_transaction # :nodoc:
|
|
245
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
208
246
|
end
|
|
209
247
|
|
|
210
|
-
def begin_isolated_db_transaction(isolation)
|
|
211
|
-
|
|
248
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
|
249
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
212
250
|
begin_db_transaction
|
|
213
251
|
end
|
|
214
252
|
|
|
215
|
-
def commit_db_transaction
|
|
216
|
-
|
|
253
|
+
def commit_db_transaction # :nodoc:
|
|
254
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
217
255
|
end
|
|
218
256
|
|
|
219
|
-
def exec_rollback_db_transaction
|
|
220
|
-
|
|
257
|
+
def exec_rollback_db_transaction # :nodoc:
|
|
258
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
221
259
|
end
|
|
222
260
|
|
|
223
|
-
def
|
|
261
|
+
def exec_restart_db_transaction # :nodoc:
|
|
262
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
|
224
266
|
"VALUES ()"
|
|
225
267
|
end
|
|
226
268
|
|
|
@@ -258,7 +300,7 @@ module ActiveRecord
|
|
|
258
300
|
#
|
|
259
301
|
# Example:
|
|
260
302
|
# drop_database('sebastian_development')
|
|
261
|
-
def drop_database(name)
|
|
303
|
+
def drop_database(name) # :nodoc:
|
|
262
304
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
|
263
305
|
end
|
|
264
306
|
|
|
@@ -297,9 +339,12 @@ module ActiveRecord
|
|
|
297
339
|
#
|
|
298
340
|
# Example:
|
|
299
341
|
# rename_table('octopuses', 'octopi')
|
|
300
|
-
def rename_table(table_name, new_name)
|
|
342
|
+
def rename_table(table_name, new_name, **options)
|
|
343
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
|
344
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
345
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
301
346
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
|
302
|
-
rename_table_indexes(table_name, new_name)
|
|
347
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
303
348
|
end
|
|
304
349
|
|
|
305
350
|
# Drops a table from the database.
|
|
@@ -317,7 +362,8 @@ module ActiveRecord
|
|
|
317
362
|
# Although this command ignores most +options+ and the block if one is given,
|
|
318
363
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
319
364
|
# In that case, +options+ and the block will be used by create_table.
|
|
320
|
-
def drop_table(table_name, options
|
|
365
|
+
def drop_table(table_name, **options)
|
|
366
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
321
367
|
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
|
322
368
|
end
|
|
323
369
|
|
|
@@ -331,12 +377,21 @@ module ActiveRecord
|
|
|
331
377
|
end
|
|
332
378
|
end
|
|
333
379
|
|
|
334
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
|
380
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
|
381
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
|
385
|
+
column = column_for(table_name, column_name)
|
|
386
|
+
return unless column
|
|
387
|
+
|
|
335
388
|
default = extract_new_default_value(default_or_changes)
|
|
336
|
-
|
|
389
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
|
337
390
|
end
|
|
338
391
|
|
|
339
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
|
392
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
|
393
|
+
validate_change_column_null_argument!(null)
|
|
394
|
+
|
|
340
395
|
unless null || default.nil?
|
|
341
396
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
|
342
397
|
end
|
|
@@ -349,19 +404,68 @@ module ActiveRecord
|
|
|
349
404
|
change_column table_name, column_name, nil, comment: comment
|
|
350
405
|
end
|
|
351
406
|
|
|
352
|
-
def change_column(table_name, column_name, type, options
|
|
353
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}")
|
|
407
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
|
408
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Builds a ChangeColumnDefinition object.
|
|
412
|
+
#
|
|
413
|
+
# This definition object contains information about the column change that would occur
|
|
414
|
+
# if the same arguments were passed to #change_column. See #change_column for information about
|
|
415
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
|
416
|
+
def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
|
|
417
|
+
column = column_for(table_name, column_name)
|
|
418
|
+
type ||= column.sql_type
|
|
419
|
+
|
|
420
|
+
unless options.key?(:default)
|
|
421
|
+
options[:default] = if column.default_function
|
|
422
|
+
-> { column.default_function }
|
|
423
|
+
else
|
|
424
|
+
column.default
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
unless options.key?(:null)
|
|
429
|
+
options[:null] = column.null
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
unless options.key?(:comment)
|
|
433
|
+
options[:comment] = column.comment
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
if options[:collation] == :no_collation
|
|
437
|
+
options.delete(:collation)
|
|
438
|
+
else
|
|
439
|
+
options[:collation] ||= column.collation if text_type?(type)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
unless options.key?(:auto_increment)
|
|
443
|
+
options[:auto_increment] = column.auto_increment?
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
td = create_table_definition(table_name)
|
|
447
|
+
cd = td.new_column_definition(column.name, type, **options)
|
|
448
|
+
ChangeColumnDefinition.new(cd, column.name)
|
|
354
449
|
end
|
|
355
450
|
|
|
356
|
-
def rename_column(table_name, column_name, new_column_name)
|
|
451
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
|
357
452
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
|
358
453
|
rename_column_indexes(table_name, column_name, new_column_name)
|
|
359
454
|
end
|
|
360
455
|
|
|
361
|
-
def add_index(table_name, column_name, options
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
456
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
|
457
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
|
458
|
+
return unless create_index
|
|
459
|
+
|
|
460
|
+
execute schema_creation.accept(create_index)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
|
464
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
|
465
|
+
|
|
466
|
+
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
|
467
|
+
|
|
468
|
+
CreateIndexDefinition.new(index, algorithm)
|
|
365
469
|
end
|
|
366
470
|
|
|
367
471
|
def add_sql_comment!(sql, comment) # :nodoc:
|
|
@@ -374,11 +478,13 @@ module ActiveRecord
|
|
|
374
478
|
|
|
375
479
|
scope = quoted_scope(table_name)
|
|
376
480
|
|
|
377
|
-
|
|
481
|
+
# MySQL returns 1 row for each column of composite foreign keys.
|
|
482
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
|
378
483
|
SELECT fk.referenced_table_name AS 'to_table',
|
|
379
484
|
fk.referenced_column_name AS 'primary_key',
|
|
380
485
|
fk.column_name AS 'column',
|
|
381
486
|
fk.constraint_name AS 'name',
|
|
487
|
+
fk.ordinal_position AS 'position',
|
|
382
488
|
rc.update_rule AS 'on_update',
|
|
383
489
|
rc.delete_rule AS 'on_delete'
|
|
384
490
|
FROM information_schema.referential_constraints rc
|
|
@@ -391,38 +497,92 @@ module ActiveRecord
|
|
|
391
497
|
AND rc.table_name = #{scope[:name]}
|
|
392
498
|
SQL
|
|
393
499
|
|
|
394
|
-
fk_info.
|
|
500
|
+
grouped_fk = fk_info.group_by { |row| row["name"] }.values.each { |group| group.sort_by! { |row| row["position"] } }
|
|
501
|
+
grouped_fk.map do |group|
|
|
502
|
+
row = group.first
|
|
395
503
|
options = {
|
|
396
|
-
column: row["column"],
|
|
397
504
|
name: row["name"],
|
|
398
|
-
|
|
505
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
|
506
|
+
on_delete: extract_foreign_key_action(row["on_delete"])
|
|
399
507
|
}
|
|
400
508
|
|
|
401
|
-
|
|
402
|
-
|
|
509
|
+
if group.one?
|
|
510
|
+
options[:column] = unquote_identifier(row["column"])
|
|
511
|
+
options[:primary_key] = row["primary_key"]
|
|
512
|
+
else
|
|
513
|
+
options[:column] = group.map { |row| unquote_identifier(row["column"]) }
|
|
514
|
+
options[:primary_key] = group.map { |row| row["primary_key"] }
|
|
515
|
+
end
|
|
403
516
|
|
|
404
|
-
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
|
517
|
+
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
|
405
518
|
end
|
|
406
519
|
end
|
|
407
520
|
|
|
408
|
-
def
|
|
409
|
-
|
|
521
|
+
def check_constraints(table_name)
|
|
522
|
+
if supports_check_constraints?
|
|
523
|
+
scope = quoted_scope(table_name)
|
|
524
|
+
|
|
525
|
+
sql = <<~SQL
|
|
526
|
+
SELECT cc.constraint_name AS 'name',
|
|
527
|
+
cc.check_clause AS 'expression'
|
|
528
|
+
FROM information_schema.check_constraints cc
|
|
529
|
+
JOIN information_schema.table_constraints tc
|
|
530
|
+
USING (constraint_schema, constraint_name)
|
|
531
|
+
WHERE tc.table_schema = #{scope[:schema]}
|
|
532
|
+
AND tc.table_name = #{scope[:name]}
|
|
533
|
+
AND cc.constraint_schema = #{scope[:schema]}
|
|
534
|
+
SQL
|
|
535
|
+
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
|
536
|
+
|
|
537
|
+
chk_info = internal_exec_query(sql, "SCHEMA")
|
|
538
|
+
|
|
539
|
+
chk_info.map do |row|
|
|
540
|
+
options = {
|
|
541
|
+
name: row["name"]
|
|
542
|
+
}
|
|
543
|
+
expression = row["expression"]
|
|
544
|
+
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
|
545
|
+
expression = strip_whitespace_characters(expression)
|
|
546
|
+
|
|
547
|
+
unless mariadb?
|
|
548
|
+
# MySQL returns check constraints expression in an already escaped form.
|
|
549
|
+
# This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
|
|
550
|
+
expression = expression.gsub("\\'", "'")
|
|
551
|
+
end
|
|
410
552
|
|
|
553
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
|
554
|
+
end
|
|
555
|
+
else
|
|
556
|
+
raise NotImplementedError
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def table_options(table_name) # :nodoc:
|
|
411
561
|
create_table_info = create_table_info(table_name)
|
|
412
562
|
|
|
413
563
|
# strip create_definitions and partition_options
|
|
414
|
-
|
|
564
|
+
# Be aware that `create_table_info` might not include any table options due to `NO_TABLE_OPTIONS` sql mode.
|
|
565
|
+
raw_table_options = create_table_info.sub(/\A.*\n\) ?/m, "").sub(/\n\/\*!.*\*\/\n\z/m, "").strip
|
|
566
|
+
|
|
567
|
+
return if raw_table_options.empty?
|
|
568
|
+
|
|
569
|
+
table_options = {}
|
|
570
|
+
|
|
571
|
+
if / DEFAULT CHARSET=(?<charset>\w+)(?: COLLATE=(?<collation>\w+))?/ =~ raw_table_options
|
|
572
|
+
raw_table_options = $` + $' # before part + after part
|
|
573
|
+
table_options[:charset] = charset
|
|
574
|
+
table_options[:collation] = collation if collation
|
|
575
|
+
end
|
|
415
576
|
|
|
416
577
|
# strip AUTO_INCREMENT
|
|
417
578
|
raw_table_options.sub!(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
|
418
579
|
|
|
419
|
-
table_options[:options] = raw_table_options
|
|
420
|
-
|
|
421
580
|
# strip COMMENT
|
|
422
581
|
if raw_table_options.sub!(/ COMMENT='.+'/, "")
|
|
423
582
|
table_options[:comment] = table_comment(table_name)
|
|
424
583
|
end
|
|
425
584
|
|
|
585
|
+
table_options[:options] = raw_table_options unless raw_table_options == "ENGINE=InnoDB"
|
|
426
586
|
table_options
|
|
427
587
|
end
|
|
428
588
|
|
|
@@ -440,29 +600,14 @@ module ActiveRecord
|
|
|
440
600
|
|
|
441
601
|
query_values(<<~SQL, "SCHEMA")
|
|
442
602
|
SELECT column_name
|
|
443
|
-
FROM information_schema.
|
|
444
|
-
WHERE
|
|
603
|
+
FROM information_schema.statistics
|
|
604
|
+
WHERE index_name = 'PRIMARY'
|
|
445
605
|
AND table_schema = #{scope[:schema]}
|
|
446
606
|
AND table_name = #{scope[:name]}
|
|
447
|
-
ORDER BY
|
|
607
|
+
ORDER BY seq_in_index
|
|
448
608
|
SQL
|
|
449
609
|
end
|
|
450
610
|
|
|
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))
|
|
461
|
-
else
|
|
462
|
-
super
|
|
463
|
-
end
|
|
464
|
-
end
|
|
465
|
-
|
|
466
611
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
|
467
612
|
column = column_for_attribute(attribute)
|
|
468
613
|
|
|
@@ -481,14 +626,14 @@ module ActiveRecord
|
|
|
481
626
|
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
|
|
482
627
|
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
|
|
483
628
|
# distinct queries, and requires that the ORDER BY include the distinct column.
|
|
484
|
-
# See https://dev.mysql.com/doc/refman/
|
|
629
|
+
# See https://dev.mysql.com/doc/refman/en/group-by-handling.html
|
|
485
630
|
def columns_for_distinct(columns, orders) # :nodoc:
|
|
486
|
-
order_columns = orders.
|
|
631
|
+
order_columns = orders.compact_blank.map { |s|
|
|
487
632
|
# Convert Arel node to string
|
|
488
|
-
s = s
|
|
633
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
|
489
634
|
# Remove any ASC/DESC modifiers
|
|
490
635
|
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
|
491
|
-
}.
|
|
636
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
|
492
637
|
|
|
493
638
|
(order_columns << super).join(", ")
|
|
494
639
|
end
|
|
@@ -502,86 +647,168 @@ module ActiveRecord
|
|
|
502
647
|
end
|
|
503
648
|
|
|
504
649
|
def build_insert_sql(insert) # :nodoc:
|
|
505
|
-
|
|
650
|
+
# Can use any column as it will be assigned to itself.
|
|
651
|
+
no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
|
|
652
|
+
|
|
653
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
|
654
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
|
655
|
+
if supports_insert_raw_alias_syntax?
|
|
656
|
+
quoted_table_name = insert.model.quoted_table_name
|
|
657
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
|
658
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
|
659
|
+
|
|
660
|
+
if insert.skip_duplicates?
|
|
661
|
+
if no_op_column
|
|
662
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
|
|
663
|
+
end
|
|
664
|
+
elsif insert.update_duplicates?
|
|
665
|
+
if insert.raw_update_sql?
|
|
666
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
|
667
|
+
else
|
|
668
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
669
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
|
670
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
else
|
|
674
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
|
506
675
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
676
|
+
if insert.skip_duplicates?
|
|
677
|
+
if no_op_column
|
|
678
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
|
679
|
+
end
|
|
680
|
+
elsif insert.update_duplicates?
|
|
681
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
682
|
+
if insert.raw_update_sql?
|
|
683
|
+
sql << insert.raw_update_sql
|
|
684
|
+
else
|
|
685
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
|
686
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
|
687
|
+
end
|
|
688
|
+
end
|
|
513
689
|
end
|
|
514
690
|
|
|
691
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
515
692
|
sql
|
|
516
693
|
end
|
|
517
694
|
|
|
518
695
|
def check_version # :nodoc:
|
|
519
696
|
if database_version < "5.5.8"
|
|
520
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
|
697
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
|
521
698
|
end
|
|
522
699
|
end
|
|
523
700
|
|
|
524
|
-
|
|
701
|
+
#--
|
|
702
|
+
# QUOTING ==================================================
|
|
703
|
+
#++
|
|
525
704
|
|
|
526
|
-
|
|
527
|
-
|
|
705
|
+
# Quotes strings for use in SQL input.
|
|
706
|
+
def quote_string(string)
|
|
707
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
|
708
|
+
connection.escape(string)
|
|
709
|
+
end
|
|
710
|
+
end
|
|
528
711
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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)
|
|
712
|
+
class << self
|
|
713
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
|
714
|
+
super(default_timezone: default_timezone).tap do |m|
|
|
715
|
+
if emulate_booleans
|
|
716
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
|
717
|
+
end
|
|
556
718
|
end
|
|
719
|
+
end
|
|
557
720
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
721
|
+
private
|
|
722
|
+
def initialize_type_map(m)
|
|
723
|
+
super
|
|
724
|
+
|
|
725
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
|
726
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
|
727
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
|
728
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
|
729
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
|
730
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
|
731
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
|
732
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
|
733
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
|
734
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
|
735
|
+
|
|
736
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
|
737
|
+
register_integer_type m, %r(^int)i, limit: 4
|
|
738
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
|
739
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
|
740
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
|
741
|
+
|
|
742
|
+
m.alias_type %r(year)i, "integer"
|
|
743
|
+
m.alias_type %r(bit)i, "binary"
|
|
562
744
|
end
|
|
563
|
-
end
|
|
564
745
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
746
|
+
def register_integer_type(mapping, key, **options)
|
|
747
|
+
mapping.register_type(key) do |sql_type|
|
|
748
|
+
if /\bunsigned\b/.match?(sql_type)
|
|
749
|
+
Type::UnsignedInteger.new(**options)
|
|
750
|
+
else
|
|
751
|
+
Type::Integer.new(**options)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def extract_precision(sql_type)
|
|
757
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
|
758
|
+
super || 0
|
|
569
759
|
else
|
|
570
|
-
|
|
760
|
+
super
|
|
571
761
|
end
|
|
572
762
|
end
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
766
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
|
767
|
+
|
|
768
|
+
private
|
|
769
|
+
def strip_whitespace_characters(expression)
|
|
770
|
+
expression.gsub('\\\n', "").gsub("x0A", "").squish
|
|
573
771
|
end
|
|
574
772
|
|
|
575
|
-
def
|
|
576
|
-
if
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
773
|
+
def extended_type_map_key
|
|
774
|
+
if @default_timezone
|
|
775
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
|
776
|
+
elsif emulate_booleans
|
|
777
|
+
EMULATE_BOOLEANS_TRUE
|
|
580
778
|
end
|
|
581
779
|
end
|
|
582
780
|
|
|
583
|
-
|
|
781
|
+
def handle_warnings(sql)
|
|
782
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
|
783
|
+
|
|
784
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
|
785
|
+
warning_count = @raw_connection.warning_count
|
|
786
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
|
787
|
+
result = [
|
|
788
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
|
789
|
+
] if result.count == 0
|
|
790
|
+
result.each do |level, code, message|
|
|
791
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
|
792
|
+
next if warning_ignored?(warning)
|
|
793
|
+
|
|
794
|
+
ActiveRecord.db_warnings_action.call(warning)
|
|
795
|
+
end
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
def warning_ignored?(warning)
|
|
799
|
+
warning.level == "Note" || super
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
803
|
+
# made since we established the connection
|
|
804
|
+
def sync_timezone_changes(raw_connection)
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
|
808
|
+
ER_DB_CREATE_EXISTS = 1007
|
|
809
|
+
ER_FILSORT_ABORT = 1028
|
|
584
810
|
ER_DUP_ENTRY = 1062
|
|
811
|
+
ER_SERVER_SHUTDOWN = 1053
|
|
585
812
|
ER_NOT_NULL_VIOLATION = 1048
|
|
586
813
|
ER_NO_REFERENCED_ROW = 1216
|
|
587
814
|
ER_ROW_IS_REFERENCED = 1217
|
|
@@ -595,112 +822,114 @@ module ActiveRecord
|
|
|
595
822
|
ER_CANNOT_CREATE_TABLE = 1005
|
|
596
823
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
|
597
824
|
ER_QUERY_INTERRUPTED = 1317
|
|
825
|
+
ER_CONNECTION_KILLED = 1927
|
|
826
|
+
CR_SERVER_GONE_ERROR = 2006
|
|
827
|
+
CR_SERVER_LOST = 2013
|
|
598
828
|
ER_QUERY_TIMEOUT = 3024
|
|
599
829
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
|
830
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
|
600
831
|
|
|
601
832
|
def translate_exception(exception, message:, sql:, binds:)
|
|
602
833
|
case error_number(exception)
|
|
834
|
+
when nil
|
|
835
|
+
if exception.message.match?(/MySQL client is not connected/i)
|
|
836
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
837
|
+
else
|
|
838
|
+
super
|
|
839
|
+
end
|
|
840
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
|
841
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
842
|
+
when ER_DB_CREATE_EXISTS
|
|
843
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
603
844
|
when ER_DUP_ENTRY
|
|
604
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
845
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
605
846
|
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)
|
|
847
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
607
848
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
|
608
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
849
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
609
850
|
when ER_CANNOT_CREATE_TABLE
|
|
610
851
|
if message.include?("errno: 150")
|
|
611
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
852
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
612
853
|
else
|
|
613
854
|
super
|
|
614
855
|
end
|
|
615
856
|
when ER_DATA_TOO_LONG
|
|
616
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
|
857
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
617
858
|
when ER_OUT_OF_RANGE
|
|
618
|
-
RangeError.new(message, sql: sql, binds: binds)
|
|
859
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
619
860
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
|
620
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
861
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
621
862
|
when ER_LOCK_DEADLOCK
|
|
622
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
|
863
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
623
864
|
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)
|
|
865
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
866
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
|
867
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
627
868
|
when ER_QUERY_INTERRUPTED
|
|
628
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
|
869
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
629
870
|
else
|
|
630
871
|
super
|
|
631
872
|
end
|
|
632
873
|
end
|
|
633
874
|
|
|
634
|
-
def change_column_for_alter(table_name, column_name, type, options
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
unless options.key?(:default)
|
|
639
|
-
options[:default] = column.default
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
unless options.key?(:null)
|
|
643
|
-
options[:null] = column.null
|
|
644
|
-
end
|
|
645
|
-
|
|
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))
|
|
875
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
|
876
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
|
877
|
+
schema_creation.accept(cd)
|
|
653
878
|
end
|
|
654
879
|
|
|
655
880
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
|
881
|
+
return rename_column_sql(table_name, column_name, new_column_name) if supports_rename_column?
|
|
882
|
+
|
|
656
883
|
column = column_for(table_name, column_name)
|
|
657
884
|
options = {
|
|
658
885
|
default: column.default,
|
|
659
886
|
null: column.null,
|
|
660
|
-
auto_increment: column.auto_increment
|
|
887
|
+
auto_increment: column.auto_increment?,
|
|
888
|
+
comment: column.comment
|
|
661
889
|
}
|
|
662
890
|
|
|
663
|
-
current_type =
|
|
891
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
|
664
892
|
td = create_table_definition(table_name)
|
|
665
|
-
cd = td.new_column_definition(new_column_name, current_type, options)
|
|
893
|
+
cd = td.new_column_definition(new_column_name, current_type, **options)
|
|
666
894
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
|
667
895
|
end
|
|
668
896
|
|
|
669
|
-
def add_index_for_alter(table_name, column_name, options
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
897
|
+
def add_index_for_alter(table_name, column_name, **options)
|
|
898
|
+
index, algorithm, _ = add_index_options(table_name, column_name, **options)
|
|
899
|
+
algorithm = ", #{algorithm}" if algorithm
|
|
900
|
+
|
|
901
|
+
"ADD #{schema_creation.accept(index)}#{algorithm}"
|
|
673
902
|
end
|
|
674
903
|
|
|
675
|
-
def remove_index_for_alter(table_name,
|
|
676
|
-
index_name = index_name_for_remove(table_name, options)
|
|
904
|
+
def remove_index_for_alter(table_name, column_name = nil, **options)
|
|
905
|
+
index_name = index_name_for_remove(table_name, column_name, options)
|
|
677
906
|
"DROP INDEX #{quote_column_name(index_name)}"
|
|
678
907
|
end
|
|
679
908
|
|
|
680
|
-
def
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if !options.key?(:precision) && supports_datetime_with_precision?
|
|
684
|
-
options[:precision] = 6
|
|
685
|
-
end
|
|
686
|
-
|
|
687
|
-
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
|
909
|
+
def supports_insert_raw_alias_syntax?
|
|
910
|
+
!mariadb? && database_version >= "8.0.19"
|
|
688
911
|
end
|
|
689
912
|
|
|
690
|
-
def
|
|
691
|
-
|
|
913
|
+
def supports_rename_index?
|
|
914
|
+
if mariadb?
|
|
915
|
+
database_version >= "10.5.2"
|
|
916
|
+
else
|
|
917
|
+
database_version >= "5.7.6"
|
|
918
|
+
end
|
|
692
919
|
end
|
|
693
920
|
|
|
694
|
-
def
|
|
695
|
-
mariadb?
|
|
921
|
+
def supports_rename_column?
|
|
922
|
+
if mariadb?
|
|
923
|
+
database_version >= "10.5.2"
|
|
924
|
+
else
|
|
925
|
+
database_version >= "8.0.3"
|
|
926
|
+
end
|
|
696
927
|
end
|
|
697
928
|
|
|
698
929
|
def configure_connection
|
|
930
|
+
super
|
|
699
931
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
700
932
|
|
|
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
933
|
# Increase timeout so the server doesn't disconnect us.
|
|
705
934
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
|
706
935
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
|
@@ -709,7 +938,7 @@ module ActiveRecord
|
|
|
709
938
|
defaults = [":default", :default].to_set
|
|
710
939
|
|
|
711
940
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
|
712
|
-
# https://dev.mysql.com/doc/refman/
|
|
941
|
+
# https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_strict_all_tables
|
|
713
942
|
# If the user has provided another value for sql_mode, don't replace it.
|
|
714
943
|
if sql_mode = variables.delete("sql_mode")
|
|
715
944
|
sql_mode = quote(sql_mode)
|
|
@@ -726,7 +955,7 @@ module ActiveRecord
|
|
|
726
955
|
sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode
|
|
727
956
|
|
|
728
957
|
# NAMES does not have an equals sign, see
|
|
729
|
-
# https://dev.mysql.com/doc/refman/
|
|
958
|
+
# https://dev.mysql.com/doc/refman/en/set-names.html
|
|
730
959
|
# (trailing comma because variable_assignments will always have content)
|
|
731
960
|
if @config[:encoding]
|
|
732
961
|
encoding = +"NAMES #{@config[:encoding]}"
|
|
@@ -735,17 +964,16 @@ module ActiveRecord
|
|
|
735
964
|
end
|
|
736
965
|
|
|
737
966
|
# Gather up all of the SET variables...
|
|
738
|
-
variable_assignments = variables.
|
|
967
|
+
variable_assignments = variables.filter_map do |k, v|
|
|
739
968
|
if defaults.include?(v)
|
|
740
969
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
|
741
970
|
elsif !v.nil?
|
|
742
971
|
"@@SESSION.#{k} = #{quote(v)}"
|
|
743
972
|
end
|
|
744
|
-
|
|
745
|
-
end.compact.join(", ")
|
|
973
|
+
end.join(", ")
|
|
746
974
|
|
|
747
975
|
# ...and send them all in one query
|
|
748
|
-
|
|
976
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
|
749
977
|
end
|
|
750
978
|
|
|
751
979
|
def column_definitions(table_name) # :nodoc:
|
|
@@ -755,7 +983,7 @@ module ActiveRecord
|
|
|
755
983
|
end
|
|
756
984
|
|
|
757
985
|
def create_table_info(table_name) # :nodoc:
|
|
758
|
-
|
|
986
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
|
759
987
|
end
|
|
760
988
|
|
|
761
989
|
def arel_visitor
|
|
@@ -766,18 +994,17 @@ module ActiveRecord
|
|
|
766
994
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
|
767
995
|
end
|
|
768
996
|
|
|
769
|
-
def
|
|
997
|
+
def mismatched_foreign_key_details(message:, sql:)
|
|
998
|
+
foreign_key_pat =
|
|
999
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
|
1000
|
+
|
|
770
1001
|
match = %r/
|
|
771
1002
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
|
772
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
|
1003
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
|
773
1004
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
|
774
1005
|
/xmi.match(sql)
|
|
775
1006
|
|
|
776
|
-
options = {
|
|
777
|
-
message: message,
|
|
778
|
-
sql: sql,
|
|
779
|
-
binds: binds,
|
|
780
|
-
}
|
|
1007
|
+
options = {}
|
|
781
1008
|
|
|
782
1009
|
if match
|
|
783
1010
|
options[:table] = match[:table]
|
|
@@ -787,35 +1014,33 @@ module ActiveRecord
|
|
|
787
1014
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
|
788
1015
|
end
|
|
789
1016
|
|
|
790
|
-
|
|
1017
|
+
options
|
|
791
1018
|
end
|
|
792
1019
|
|
|
793
|
-
def
|
|
794
|
-
|
|
795
|
-
|
|
1020
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
|
1021
|
+
options = {
|
|
1022
|
+
message: message,
|
|
1023
|
+
sql: sql,
|
|
1024
|
+
binds: binds,
|
|
1025
|
+
connection_pool: connection_pool
|
|
1026
|
+
}
|
|
796
1027
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
when false then "0"
|
|
802
|
-
else super
|
|
803
|
-
end
|
|
1028
|
+
if sql
|
|
1029
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
|
1030
|
+
else
|
|
1031
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
|
804
1032
|
end
|
|
805
1033
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
def cast_value(value)
|
|
809
|
-
case value
|
|
810
|
-
when true then "1"
|
|
811
|
-
when false then "0"
|
|
812
|
-
else super
|
|
813
|
-
end
|
|
814
|
-
end
|
|
1034
|
+
MismatchedForeignKey.new(**options)
|
|
815
1035
|
end
|
|
816
1036
|
|
|
817
|
-
|
|
818
|
-
|
|
1037
|
+
def version_string(full_version_string)
|
|
1038
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
|
1039
|
+
matches[1]
|
|
1040
|
+
else
|
|
1041
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
819
1044
|
end
|
|
820
1045
|
end
|
|
821
1046
|
end
|