activerecord 6.1.7 → 7.2.0
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 +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- 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 +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- 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 +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +18 -34
- 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 -46
- 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 +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- 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 +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- 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 +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- 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 +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- 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 +170 -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 +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- 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 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -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 +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- 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/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- 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 +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- 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 +100 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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" },
|
@@ -50,11 +53,37 @@ module ActiveRecord
|
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
|
-
|
54
|
-
|
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
|
55
84
|
end
|
56
85
|
|
57
|
-
def get_database_version
|
86
|
+
def get_database_version # :nodoc:
|
58
87
|
full_version_string = get_full_version
|
59
88
|
version_string = version_string(full_version_string)
|
60
89
|
Version.new(version_string, full_version_string)
|
@@ -80,6 +109,10 @@ module ActiveRecord
|
|
80
109
|
true
|
81
110
|
end
|
82
111
|
|
112
|
+
def supports_restart_db_transaction?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
83
116
|
def supports_explain?
|
84
117
|
true
|
85
118
|
end
|
@@ -94,7 +127,7 @@ module ActiveRecord
|
|
94
127
|
|
95
128
|
def supports_check_constraints?
|
96
129
|
if mariadb?
|
97
|
-
database_version >= "10.2.
|
130
|
+
database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
|
98
131
|
else
|
99
132
|
database_version >= "8.0.16"
|
100
133
|
end
|
@@ -137,6 +170,10 @@ module ActiveRecord
|
|
137
170
|
true
|
138
171
|
end
|
139
172
|
|
173
|
+
def supports_insert_returning?
|
174
|
+
mariadb? && database_version >= "10.5.0"
|
175
|
+
end
|
176
|
+
|
140
177
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
141
178
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
142
179
|
end
|
@@ -174,65 +211,54 @@ module ActiveRecord
|
|
174
211
|
|
175
212
|
# REFERENTIAL INTEGRITY ====================================
|
176
213
|
|
177
|
-
def disable_referential_integrity
|
214
|
+
def disable_referential_integrity # :nodoc:
|
178
215
|
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
179
216
|
|
180
217
|
begin
|
181
218
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
182
219
|
yield
|
183
220
|
ensure
|
184
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
221
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
|
185
222
|
end
|
186
223
|
end
|
187
224
|
|
188
|
-
# CONNECTION MANAGEMENT ====================================
|
189
|
-
|
190
|
-
def clear_cache! # :nodoc:
|
191
|
-
reload_type_map
|
192
|
-
super
|
193
|
-
end
|
194
|
-
|
195
225
|
#--
|
196
226
|
# DATABASE STATEMENTS ======================================
|
197
227
|
#++
|
198
228
|
|
199
|
-
# Executes the SQL statement in the context of this connection.
|
200
|
-
def execute(sql, name = nil)
|
201
|
-
materialize_transactions
|
202
|
-
mark_transaction_written_if_write(sql)
|
203
|
-
|
204
|
-
log(sql, name) do
|
205
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
206
|
-
@connection.query(sql)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
229
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
212
230
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
213
231
|
# needs to be explicitly freed or not.
|
214
|
-
def execute_and_free(sql, name = nil) # :nodoc:
|
215
|
-
|
232
|
+
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
233
|
+
sql = transform_query(sql)
|
234
|
+
check_if_write_query(sql)
|
235
|
+
|
236
|
+
mark_transaction_written_if_write(sql)
|
237
|
+
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
216
238
|
end
|
217
239
|
|
218
|
-
def begin_db_transaction
|
219
|
-
|
240
|
+
def begin_db_transaction # :nodoc:
|
241
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
220
242
|
end
|
221
243
|
|
222
|
-
def begin_isolated_db_transaction(isolation)
|
223
|
-
|
244
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
245
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
224
246
|
begin_db_transaction
|
225
247
|
end
|
226
248
|
|
227
|
-
def commit_db_transaction
|
228
|
-
|
249
|
+
def commit_db_transaction # :nodoc:
|
250
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
251
|
+
end
|
252
|
+
|
253
|
+
def exec_rollback_db_transaction # :nodoc:
|
254
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
229
255
|
end
|
230
256
|
|
231
|
-
def
|
232
|
-
|
257
|
+
def exec_restart_db_transaction # :nodoc:
|
258
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
233
259
|
end
|
234
260
|
|
235
|
-
def empty_insert_statement_value(primary_key = nil)
|
261
|
+
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
236
262
|
"VALUES ()"
|
237
263
|
end
|
238
264
|
|
@@ -270,7 +296,7 @@ module ActiveRecord
|
|
270
296
|
#
|
271
297
|
# Example:
|
272
298
|
# drop_database('sebastian_development')
|
273
|
-
def drop_database(name)
|
299
|
+
def drop_database(name) # :nodoc:
|
274
300
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
275
301
|
end
|
276
302
|
|
@@ -309,11 +335,12 @@ module ActiveRecord
|
|
309
335
|
#
|
310
336
|
# Example:
|
311
337
|
# rename_table('octopuses', 'octopi')
|
312
|
-
def rename_table(table_name, new_name)
|
338
|
+
def rename_table(table_name, new_name, **options)
|
339
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
313
340
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
314
341
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
315
342
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
316
|
-
rename_table_indexes(table_name, new_name)
|
343
|
+
rename_table_indexes(table_name, new_name, **options)
|
317
344
|
end
|
318
345
|
|
319
346
|
# Drops a table from the database.
|
@@ -346,12 +373,21 @@ module ActiveRecord
|
|
346
373
|
end
|
347
374
|
end
|
348
375
|
|
349
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
376
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
377
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
378
|
+
end
|
379
|
+
|
380
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
381
|
+
column = column_for(table_name, column_name)
|
382
|
+
return unless column
|
383
|
+
|
350
384
|
default = extract_new_default_value(default_or_changes)
|
351
|
-
|
385
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
352
386
|
end
|
353
387
|
|
354
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
388
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
389
|
+
validate_change_column_null_argument!(null)
|
390
|
+
|
355
391
|
unless null || default.nil?
|
356
392
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
357
393
|
end
|
@@ -364,22 +400,64 @@ module ActiveRecord
|
|
364
400
|
change_column table_name, column_name, nil, comment: comment
|
365
401
|
end
|
366
402
|
|
367
|
-
def change_column(table_name, column_name, type, **options)
|
403
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
368
404
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
369
405
|
end
|
370
406
|
|
371
|
-
|
407
|
+
# Builds a ChangeColumnDefinition object.
|
408
|
+
#
|
409
|
+
# This definition object contains information about the column change that would occur
|
410
|
+
# if the same arguments were passed to #change_column. See #change_column for information about
|
411
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
412
|
+
def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
|
413
|
+
column = column_for(table_name, column_name)
|
414
|
+
type ||= column.sql_type
|
415
|
+
|
416
|
+
unless options.key?(:default)
|
417
|
+
options[:default] = column.default
|
418
|
+
end
|
419
|
+
|
420
|
+
unless options.key?(:null)
|
421
|
+
options[:null] = column.null
|
422
|
+
end
|
423
|
+
|
424
|
+
unless options.key?(:comment)
|
425
|
+
options[:comment] = column.comment
|
426
|
+
end
|
427
|
+
|
428
|
+
if options[:collation] == :no_collation
|
429
|
+
options.delete(:collation)
|
430
|
+
else
|
431
|
+
options[:collation] ||= column.collation if text_type?(type)
|
432
|
+
end
|
433
|
+
|
434
|
+
unless options.key?(:auto_increment)
|
435
|
+
options[:auto_increment] = column.auto_increment?
|
436
|
+
end
|
437
|
+
|
438
|
+
td = create_table_definition(table_name)
|
439
|
+
cd = td.new_column_definition(column.name, type, **options)
|
440
|
+
ChangeColumnDefinition.new(cd, column.name)
|
441
|
+
end
|
442
|
+
|
443
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
372
444
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
373
445
|
rename_column_indexes(table_name, column_name, new_column_name)
|
374
446
|
end
|
375
447
|
|
376
|
-
def add_index(table_name, column_name, **options)
|
448
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
449
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
450
|
+
return unless create_index
|
451
|
+
|
452
|
+
execute schema_creation.accept(create_index)
|
453
|
+
end
|
454
|
+
|
455
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
377
456
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
378
457
|
|
379
458
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
380
459
|
|
381
|
-
|
382
|
-
execute schema_creation.accept(create_index)
|
460
|
+
CreateIndexDefinition.new(index, algorithm)
|
383
461
|
end
|
384
462
|
|
385
463
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -392,11 +470,13 @@ module ActiveRecord
|
|
392
470
|
|
393
471
|
scope = quoted_scope(table_name)
|
394
472
|
|
395
|
-
|
473
|
+
# MySQL returns 1 row for each column of composite foreign keys.
|
474
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
396
475
|
SELECT fk.referenced_table_name AS 'to_table',
|
397
476
|
fk.referenced_column_name AS 'primary_key',
|
398
477
|
fk.column_name AS 'column',
|
399
478
|
fk.constraint_name AS 'name',
|
479
|
+
fk.ordinal_position AS 'position',
|
400
480
|
rc.update_rule AS 'on_update',
|
401
481
|
rc.delete_rule AS 'on_delete'
|
402
482
|
FROM information_schema.referential_constraints rc
|
@@ -409,17 +489,24 @@ module ActiveRecord
|
|
409
489
|
AND rc.table_name = #{scope[:name]}
|
410
490
|
SQL
|
411
491
|
|
412
|
-
fk_info.
|
492
|
+
grouped_fk = fk_info.group_by { |row| row["name"] }.values.each { |group| group.sort_by! { |row| row["position"] } }
|
493
|
+
grouped_fk.map do |group|
|
494
|
+
row = group.first
|
413
495
|
options = {
|
414
|
-
column: row["column"],
|
415
496
|
name: row["name"],
|
416
|
-
|
497
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
498
|
+
on_delete: extract_foreign_key_action(row["on_delete"])
|
417
499
|
}
|
418
500
|
|
419
|
-
|
420
|
-
|
501
|
+
if group.one?
|
502
|
+
options[:column] = unquote_identifier(row["column"])
|
503
|
+
options[:primary_key] = row["primary_key"]
|
504
|
+
else
|
505
|
+
options[:column] = group.map { |row| unquote_identifier(row["column"]) }
|
506
|
+
options[:primary_key] = group.map { |row| row["primary_key"] }
|
507
|
+
end
|
421
508
|
|
422
|
-
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
509
|
+
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
423
510
|
end
|
424
511
|
end
|
425
512
|
|
@@ -427,7 +514,7 @@ module ActiveRecord
|
|
427
514
|
if supports_check_constraints?
|
428
515
|
scope = quoted_scope(table_name)
|
429
516
|
|
430
|
-
|
517
|
+
sql = <<~SQL
|
431
518
|
SELECT cc.constraint_name AS 'name',
|
432
519
|
cc.check_clause AS 'expression'
|
433
520
|
FROM information_schema.check_constraints cc
|
@@ -437,13 +524,24 @@ module ActiveRecord
|
|
437
524
|
AND tc.table_name = #{scope[:name]}
|
438
525
|
AND cc.constraint_schema = #{scope[:schema]}
|
439
526
|
SQL
|
527
|
+
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
528
|
+
|
529
|
+
chk_info = internal_exec_query(sql, "SCHEMA")
|
440
530
|
|
441
531
|
chk_info.map do |row|
|
442
532
|
options = {
|
443
533
|
name: row["name"]
|
444
534
|
}
|
445
535
|
expression = row["expression"]
|
446
|
-
expression = expression[1..-2]
|
536
|
+
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
537
|
+
expression = strip_whitespace_characters(expression)
|
538
|
+
|
539
|
+
unless mariadb?
|
540
|
+
# MySQL returns check constraints expression in an already escaped form.
|
541
|
+
# This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
|
542
|
+
expression = expression.gsub("\\'", "'")
|
543
|
+
end
|
544
|
+
|
447
545
|
CheckConstraintDefinition.new(table_name, expression, options)
|
448
546
|
end
|
449
547
|
else
|
@@ -541,82 +639,164 @@ module ActiveRecord
|
|
541
639
|
end
|
542
640
|
|
543
641
|
def build_insert_sql(insert) # :nodoc:
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
sql
|
551
|
-
|
552
|
-
|
642
|
+
no_op_column = quote_column_name(insert.keys.first)
|
643
|
+
|
644
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
646
|
+
if supports_insert_raw_alias_syntax?
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
648
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
|
+
|
650
|
+
if insert.skip_duplicates?
|
651
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
652
|
+
elsif insert.update_duplicates?
|
653
|
+
if insert.raw_update_sql?
|
654
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
655
|
+
else
|
656
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
657
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
658
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
659
|
+
end
|
660
|
+
end
|
661
|
+
else
|
662
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
663
|
+
|
664
|
+
if insert.skip_duplicates?
|
665
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
666
|
+
elsif insert.update_duplicates?
|
667
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
668
|
+
if insert.raw_update_sql?
|
669
|
+
sql << insert.raw_update_sql
|
670
|
+
else
|
671
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
672
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
673
|
+
end
|
674
|
+
end
|
553
675
|
end
|
554
676
|
|
677
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
555
678
|
sql
|
556
679
|
end
|
557
680
|
|
558
681
|
def check_version # :nodoc:
|
559
682
|
if database_version < "5.5.8"
|
560
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
683
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
561
684
|
end
|
562
685
|
end
|
563
686
|
|
564
|
-
|
565
|
-
|
566
|
-
|
687
|
+
#--
|
688
|
+
# QUOTING ==================================================
|
689
|
+
#++
|
567
690
|
|
568
|
-
|
569
|
-
|
570
|
-
|
691
|
+
# Quotes strings for use in SQL input.
|
692
|
+
def quote_string(string)
|
693
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
694
|
+
connection.escape(string)
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
class << self
|
699
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
700
|
+
super(default_timezone: default_timezone).tap do |m|
|
701
|
+
if emulate_booleans
|
702
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
703
|
+
end
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
private
|
708
|
+
def initialize_type_map(m)
|
709
|
+
super
|
710
|
+
|
711
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
712
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
713
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
714
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
715
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
716
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
717
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
718
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
719
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
720
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
721
|
+
|
722
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
723
|
+
register_integer_type m, %r(^int)i, limit: 4
|
724
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
725
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
726
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
727
|
+
|
728
|
+
m.alias_type %r(year)i, "integer"
|
729
|
+
m.alias_type %r(bit)i, "binary"
|
730
|
+
end
|
731
|
+
|
732
|
+
def register_integer_type(mapping, key, **options)
|
733
|
+
mapping.register_type(key) do |sql_type|
|
734
|
+
if /\bunsigned\b/.match?(sql_type)
|
735
|
+
Type::UnsignedInteger.new(**options)
|
736
|
+
else
|
737
|
+
Type::Integer.new(**options)
|
738
|
+
end
|
739
|
+
end
|
571
740
|
end
|
572
741
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
577
|
-
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
578
|
-
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
579
|
-
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
580
|
-
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
581
|
-
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
582
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
583
|
-
|
584
|
-
register_integer_type m, %r(^bigint)i, limit: 8
|
585
|
-
register_integer_type m, %r(^int)i, limit: 4
|
586
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
587
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
588
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
589
|
-
|
590
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
591
|
-
m.alias_type %r(year)i, "integer"
|
592
|
-
m.alias_type %r(bit)i, "binary"
|
593
|
-
|
594
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
595
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
596
|
-
end
|
597
|
-
|
598
|
-
def register_integer_type(mapping, key, **options)
|
599
|
-
mapping.register_type(key) do |sql_type|
|
600
|
-
if /\bunsigned\b/.match?(sql_type)
|
601
|
-
Type::UnsignedInteger.new(**options)
|
742
|
+
def extract_precision(sql_type)
|
743
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
744
|
+
super || 0
|
602
745
|
else
|
603
|
-
|
746
|
+
super
|
604
747
|
end
|
605
748
|
end
|
749
|
+
end
|
750
|
+
|
751
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
752
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
753
|
+
|
754
|
+
private
|
755
|
+
def strip_whitespace_characters(expression)
|
756
|
+
expression = expression.gsub(/\\n|\\\\/, "")
|
757
|
+
expression = expression.gsub(/\s{2,}/, " ")
|
758
|
+
expression
|
759
|
+
end
|
760
|
+
|
761
|
+
def extended_type_map_key
|
762
|
+
if @default_timezone
|
763
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
764
|
+
elsif emulate_booleans
|
765
|
+
EMULATE_BOOLEANS_TRUE
|
766
|
+
end
|
606
767
|
end
|
607
768
|
|
608
|
-
def
|
609
|
-
if
|
610
|
-
|
611
|
-
|
612
|
-
|
769
|
+
def handle_warnings(sql)
|
770
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
771
|
+
|
772
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
773
|
+
warning_count = @raw_connection.warning_count
|
774
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
775
|
+
result = [
|
776
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
777
|
+
] if result.count == 0
|
778
|
+
result.each do |level, code, message|
|
779
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
780
|
+
next if warning_ignored?(warning)
|
781
|
+
|
782
|
+
ActiveRecord.db_warnings_action.call(warning)
|
613
783
|
end
|
614
784
|
end
|
615
785
|
|
786
|
+
def warning_ignored?(warning)
|
787
|
+
warning.level == "Note" || super
|
788
|
+
end
|
789
|
+
|
790
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
791
|
+
# made since we established the connection
|
792
|
+
def sync_timezone_changes(raw_connection)
|
793
|
+
end
|
794
|
+
|
616
795
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
617
796
|
ER_DB_CREATE_EXISTS = 1007
|
618
797
|
ER_FILSORT_ABORT = 1028
|
619
798
|
ER_DUP_ENTRY = 1062
|
799
|
+
ER_SERVER_SHUTDOWN = 1053
|
620
800
|
ER_NOT_NULL_VIOLATION = 1048
|
621
801
|
ER_NO_REFERENCED_ROW = 1216
|
622
802
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -630,69 +810,59 @@ module ActiveRecord
|
|
630
810
|
ER_CANNOT_CREATE_TABLE = 1005
|
631
811
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
632
812
|
ER_QUERY_INTERRUPTED = 1317
|
813
|
+
ER_CONNECTION_KILLED = 1927
|
814
|
+
CR_SERVER_GONE_ERROR = 2006
|
815
|
+
CR_SERVER_LOST = 2013
|
633
816
|
ER_QUERY_TIMEOUT = 3024
|
634
817
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
818
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
635
819
|
|
636
820
|
def translate_exception(exception, message:, sql:, binds:)
|
637
821
|
case error_number(exception)
|
638
822
|
when nil
|
639
823
|
if exception.message.match?(/MySQL client is not connected/i)
|
640
|
-
ConnectionNotEstablished.new(exception)
|
824
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
641
825
|
else
|
642
826
|
super
|
643
827
|
end
|
828
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
829
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
644
830
|
when ER_DB_CREATE_EXISTS
|
645
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
831
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
646
832
|
when ER_DUP_ENTRY
|
647
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
833
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
648
834
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
649
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
835
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
650
836
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
651
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
837
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
652
838
|
when ER_CANNOT_CREATE_TABLE
|
653
839
|
if message.include?("errno: 150")
|
654
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
840
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
655
841
|
else
|
656
842
|
super
|
657
843
|
end
|
658
844
|
when ER_DATA_TOO_LONG
|
659
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
845
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
660
846
|
when ER_OUT_OF_RANGE
|
661
|
-
RangeError.new(message, sql: sql, binds: binds)
|
847
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
662
848
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
663
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
849
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
664
850
|
when ER_LOCK_DEADLOCK
|
665
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
851
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
666
852
|
when ER_LOCK_WAIT_TIMEOUT
|
667
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
853
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
668
854
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
669
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
855
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
670
856
|
when ER_QUERY_INTERRUPTED
|
671
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
857
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
672
858
|
else
|
673
859
|
super
|
674
860
|
end
|
675
861
|
end
|
676
862
|
|
677
863
|
def change_column_for_alter(table_name, column_name, type, **options)
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
unless options.key?(:default)
|
682
|
-
options[:default] = column.default
|
683
|
-
end
|
684
|
-
|
685
|
-
unless options.key?(:null)
|
686
|
-
options[:null] = column.null
|
687
|
-
end
|
688
|
-
|
689
|
-
unless options.key?(:comment)
|
690
|
-
options[:comment] = column.comment
|
691
|
-
end
|
692
|
-
|
693
|
-
td = create_table_definition(table_name)
|
694
|
-
cd = td.new_column_definition(column.name, type, **options)
|
695
|
-
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
864
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
865
|
+
schema_creation.accept(cd)
|
696
866
|
end
|
697
867
|
|
698
868
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -706,7 +876,7 @@ module ActiveRecord
|
|
706
876
|
comment: column.comment
|
707
877
|
}
|
708
878
|
|
709
|
-
current_type =
|
879
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
710
880
|
td = create_table_definition(table_name)
|
711
881
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
712
882
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -724,6 +894,10 @@ module ActiveRecord
|
|
724
894
|
"DROP INDEX #{quote_column_name(index_name)}"
|
725
895
|
end
|
726
896
|
|
897
|
+
def supports_insert_raw_alias_syntax?
|
898
|
+
!mariadb? && database_version >= "8.0.19"
|
899
|
+
end
|
900
|
+
|
727
901
|
def supports_rename_index?
|
728
902
|
if mariadb?
|
729
903
|
database_version >= "10.5.2"
|
@@ -741,11 +915,9 @@ module ActiveRecord
|
|
741
915
|
end
|
742
916
|
|
743
917
|
def configure_connection
|
918
|
+
super
|
744
919
|
variables = @config.fetch(:variables, {}).stringify_keys
|
745
920
|
|
746
|
-
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
747
|
-
variables["sql_auto_is_null"] = 0
|
748
|
-
|
749
921
|
# Increase timeout so the server doesn't disconnect us.
|
750
922
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
751
923
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -780,17 +952,16 @@ module ActiveRecord
|
|
780
952
|
end
|
781
953
|
|
782
954
|
# Gather up all of the SET variables...
|
783
|
-
variable_assignments = variables.
|
955
|
+
variable_assignments = variables.filter_map do |k, v|
|
784
956
|
if defaults.include?(v)
|
785
957
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
786
958
|
elsif !v.nil?
|
787
959
|
"@@SESSION.#{k} = #{quote(v)}"
|
788
960
|
end
|
789
|
-
|
790
|
-
end.compact.join(", ")
|
961
|
+
end.join(", ")
|
791
962
|
|
792
963
|
# ...and send them all in one query
|
793
|
-
|
964
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
794
965
|
end
|
795
966
|
|
796
967
|
def column_definitions(table_name) # :nodoc:
|
@@ -800,7 +971,7 @@ module ActiveRecord
|
|
800
971
|
end
|
801
972
|
|
802
973
|
def create_table_info(table_name) # :nodoc:
|
803
|
-
|
974
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
804
975
|
end
|
805
976
|
|
806
977
|
def arel_visitor
|
@@ -811,18 +982,17 @@ module ActiveRecord
|
|
811
982
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
812
983
|
end
|
813
984
|
|
814
|
-
def
|
985
|
+
def mismatched_foreign_key_details(message:, sql:)
|
986
|
+
foreign_key_pat =
|
987
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
988
|
+
|
815
989
|
match = %r/
|
816
990
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
817
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
991
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
818
992
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
819
993
|
/xmi.match(sql)
|
820
994
|
|
821
|
-
options = {
|
822
|
-
message: message,
|
823
|
-
sql: sql,
|
824
|
-
binds: binds,
|
825
|
-
}
|
995
|
+
options = {}
|
826
996
|
|
827
997
|
if match
|
828
998
|
options[:table] = match[:table]
|
@@ -832,24 +1002,33 @@ module ActiveRecord
|
|
832
1002
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
833
1003
|
end
|
834
1004
|
|
835
|
-
|
1005
|
+
options
|
836
1006
|
end
|
837
1007
|
|
838
|
-
def
|
839
|
-
|
840
|
-
|
1008
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
1009
|
+
options = {
|
1010
|
+
message: message,
|
1011
|
+
sql: sql,
|
1012
|
+
binds: binds,
|
1013
|
+
connection_pool: connection_pool
|
1014
|
+
}
|
841
1015
|
|
842
|
-
|
843
|
-
|
844
|
-
|
1016
|
+
if sql
|
1017
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
1018
|
+
else
|
1019
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
1020
|
+
end
|
845
1021
|
|
846
|
-
|
847
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
1022
|
+
MismatchedForeignKey.new(**options)
|
848
1023
|
end
|
849
|
-
|
850
|
-
|
1024
|
+
|
1025
|
+
def version_string(full_version_string)
|
1026
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1027
|
+
matches[1]
|
1028
|
+
else
|
1029
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
1030
|
+
end
|
851
1031
|
end
|
852
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
853
1032
|
end
|
854
1033
|
end
|
855
1034
|
end
|