activerecord 6.1.7 → 7.2.2
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 +616 -1290
- 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 +19 -8
- 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 +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- 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 +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- 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 +15 -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 +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +325 -604
- 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 +230 -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 +378 -143
- 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 +348 -165
- 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 +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- 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 +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- 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 +58 -0
- data/lib/active_record/enum.rb +170 -62
- 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 +59 -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 +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- 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 +18 -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 +229 -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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- 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 +870 -163
- 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 +6 -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 +288 -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 +65 -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/sqlite.rb +25 -0
- 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 +103 -17
- 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)
|
229
251
|
end
|
230
252
|
|
231
|
-
def exec_rollback_db_transaction
|
232
|
-
|
253
|
+
def exec_rollback_db_transaction # :nodoc:
|
254
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
233
255
|
end
|
234
256
|
|
235
|
-
def
|
257
|
+
def exec_restart_db_transaction # :nodoc:
|
258
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
259
|
+
end
|
260
|
+
|
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,168 @@ module ActiveRecord
|
|
541
639
|
end
|
542
640
|
|
543
641
|
def build_insert_sql(insert) # :nodoc:
|
544
|
-
|
642
|
+
no_op_column = quote_column_name(insert.keys.first) if 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
|
+
if no_op_column
|
652
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
653
|
+
end
|
654
|
+
elsif insert.update_duplicates?
|
655
|
+
if insert.raw_update_sql?
|
656
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
657
|
+
else
|
658
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
659
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
660
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
661
|
+
end
|
662
|
+
end
|
663
|
+
else
|
664
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
545
665
|
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
666
|
+
if insert.skip_duplicates?
|
667
|
+
if no_op_column
|
668
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
669
|
+
end
|
670
|
+
elsif insert.update_duplicates?
|
671
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
672
|
+
if insert.raw_update_sql?
|
673
|
+
sql << insert.raw_update_sql
|
674
|
+
else
|
675
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
676
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
677
|
+
end
|
678
|
+
end
|
553
679
|
end
|
554
680
|
|
681
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
555
682
|
sql
|
556
683
|
end
|
557
684
|
|
558
685
|
def check_version # :nodoc:
|
559
686
|
if database_version < "5.5.8"
|
560
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
687
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
561
688
|
end
|
562
689
|
end
|
563
690
|
|
564
|
-
|
565
|
-
|
566
|
-
|
691
|
+
#--
|
692
|
+
# QUOTING ==================================================
|
693
|
+
#++
|
694
|
+
|
695
|
+
# Quotes strings for use in SQL input.
|
696
|
+
def quote_string(string)
|
697
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
698
|
+
connection.escape(string)
|
699
|
+
end
|
700
|
+
end
|
567
701
|
|
568
|
-
|
569
|
-
|
570
|
-
|
702
|
+
class << self
|
703
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
704
|
+
super(default_timezone: default_timezone).tap do |m|
|
705
|
+
if emulate_booleans
|
706
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
707
|
+
end
|
571
708
|
end
|
709
|
+
end
|
572
710
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
711
|
+
private
|
712
|
+
def initialize_type_map(m)
|
713
|
+
super
|
714
|
+
|
715
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
716
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
717
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
718
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
719
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
720
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
721
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
722
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
723
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
724
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
725
|
+
|
726
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
727
|
+
register_integer_type m, %r(^int)i, limit: 4
|
728
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
729
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
730
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
731
|
+
|
732
|
+
m.alias_type %r(year)i, "integer"
|
733
|
+
m.alias_type %r(bit)i, "binary"
|
734
|
+
end
|
735
|
+
|
736
|
+
def register_integer_type(mapping, key, **options)
|
737
|
+
mapping.register_type(key) do |sql_type|
|
738
|
+
if /\bunsigned\b/.match?(sql_type)
|
739
|
+
Type::UnsignedInteger.new(**options)
|
740
|
+
else
|
741
|
+
Type::Integer.new(**options)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
def extract_precision(sql_type)
|
747
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
748
|
+
super || 0
|
602
749
|
else
|
603
|
-
|
750
|
+
super
|
604
751
|
end
|
605
752
|
end
|
753
|
+
end
|
754
|
+
|
755
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
756
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
757
|
+
|
758
|
+
private
|
759
|
+
def strip_whitespace_characters(expression)
|
760
|
+
expression = expression.gsub(/\\n|\\\\/, "")
|
761
|
+
expression = expression.gsub(/\s{2,}/, " ")
|
762
|
+
expression
|
606
763
|
end
|
607
764
|
|
608
|
-
def
|
609
|
-
if
|
610
|
-
|
611
|
-
|
612
|
-
|
765
|
+
def extended_type_map_key
|
766
|
+
if @default_timezone
|
767
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
768
|
+
elsif emulate_booleans
|
769
|
+
EMULATE_BOOLEANS_TRUE
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
def handle_warnings(sql)
|
774
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
775
|
+
|
776
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
777
|
+
warning_count = @raw_connection.warning_count
|
778
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
779
|
+
result = [
|
780
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
781
|
+
] if result.count == 0
|
782
|
+
result.each do |level, code, message|
|
783
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
784
|
+
next if warning_ignored?(warning)
|
785
|
+
|
786
|
+
ActiveRecord.db_warnings_action.call(warning)
|
613
787
|
end
|
614
788
|
end
|
615
789
|
|
790
|
+
def warning_ignored?(warning)
|
791
|
+
warning.level == "Note" || super
|
792
|
+
end
|
793
|
+
|
794
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
795
|
+
# made since we established the connection
|
796
|
+
def sync_timezone_changes(raw_connection)
|
797
|
+
end
|
798
|
+
|
616
799
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
617
800
|
ER_DB_CREATE_EXISTS = 1007
|
618
801
|
ER_FILSORT_ABORT = 1028
|
619
802
|
ER_DUP_ENTRY = 1062
|
803
|
+
ER_SERVER_SHUTDOWN = 1053
|
620
804
|
ER_NOT_NULL_VIOLATION = 1048
|
621
805
|
ER_NO_REFERENCED_ROW = 1216
|
622
806
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -630,69 +814,59 @@ module ActiveRecord
|
|
630
814
|
ER_CANNOT_CREATE_TABLE = 1005
|
631
815
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
632
816
|
ER_QUERY_INTERRUPTED = 1317
|
817
|
+
ER_CONNECTION_KILLED = 1927
|
818
|
+
CR_SERVER_GONE_ERROR = 2006
|
819
|
+
CR_SERVER_LOST = 2013
|
633
820
|
ER_QUERY_TIMEOUT = 3024
|
634
821
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
822
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
635
823
|
|
636
824
|
def translate_exception(exception, message:, sql:, binds:)
|
637
825
|
case error_number(exception)
|
638
826
|
when nil
|
639
827
|
if exception.message.match?(/MySQL client is not connected/i)
|
640
|
-
ConnectionNotEstablished.new(exception)
|
828
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
641
829
|
else
|
642
830
|
super
|
643
831
|
end
|
832
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
833
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
644
834
|
when ER_DB_CREATE_EXISTS
|
645
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
835
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
646
836
|
when ER_DUP_ENTRY
|
647
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
837
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
648
838
|
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)
|
839
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
650
840
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
651
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
841
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
652
842
|
when ER_CANNOT_CREATE_TABLE
|
653
843
|
if message.include?("errno: 150")
|
654
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
844
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
655
845
|
else
|
656
846
|
super
|
657
847
|
end
|
658
848
|
when ER_DATA_TOO_LONG
|
659
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
849
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
660
850
|
when ER_OUT_OF_RANGE
|
661
|
-
RangeError.new(message, sql: sql, binds: binds)
|
851
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
662
852
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
663
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
853
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
664
854
|
when ER_LOCK_DEADLOCK
|
665
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
855
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
666
856
|
when ER_LOCK_WAIT_TIMEOUT
|
667
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
857
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
668
858
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
669
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
859
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
670
860
|
when ER_QUERY_INTERRUPTED
|
671
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
861
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
672
862
|
else
|
673
863
|
super
|
674
864
|
end
|
675
865
|
end
|
676
866
|
|
677
867
|
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))
|
868
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
869
|
+
schema_creation.accept(cd)
|
696
870
|
end
|
697
871
|
|
698
872
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -706,7 +880,7 @@ module ActiveRecord
|
|
706
880
|
comment: column.comment
|
707
881
|
}
|
708
882
|
|
709
|
-
current_type =
|
883
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
710
884
|
td = create_table_definition(table_name)
|
711
885
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
712
886
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -724,6 +898,10 @@ module ActiveRecord
|
|
724
898
|
"DROP INDEX #{quote_column_name(index_name)}"
|
725
899
|
end
|
726
900
|
|
901
|
+
def supports_insert_raw_alias_syntax?
|
902
|
+
!mariadb? && database_version >= "8.0.19"
|
903
|
+
end
|
904
|
+
|
727
905
|
def supports_rename_index?
|
728
906
|
if mariadb?
|
729
907
|
database_version >= "10.5.2"
|
@@ -741,11 +919,9 @@ module ActiveRecord
|
|
741
919
|
end
|
742
920
|
|
743
921
|
def configure_connection
|
922
|
+
super
|
744
923
|
variables = @config.fetch(:variables, {}).stringify_keys
|
745
924
|
|
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
925
|
# Increase timeout so the server doesn't disconnect us.
|
750
926
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
751
927
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -780,17 +956,16 @@ module ActiveRecord
|
|
780
956
|
end
|
781
957
|
|
782
958
|
# Gather up all of the SET variables...
|
783
|
-
variable_assignments = variables.
|
959
|
+
variable_assignments = variables.filter_map do |k, v|
|
784
960
|
if defaults.include?(v)
|
785
961
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
786
962
|
elsif !v.nil?
|
787
963
|
"@@SESSION.#{k} = #{quote(v)}"
|
788
964
|
end
|
789
|
-
|
790
|
-
end.compact.join(", ")
|
965
|
+
end.join(", ")
|
791
966
|
|
792
967
|
# ...and send them all in one query
|
793
|
-
|
968
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
794
969
|
end
|
795
970
|
|
796
971
|
def column_definitions(table_name) # :nodoc:
|
@@ -800,7 +975,7 @@ module ActiveRecord
|
|
800
975
|
end
|
801
976
|
|
802
977
|
def create_table_info(table_name) # :nodoc:
|
803
|
-
|
978
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
804
979
|
end
|
805
980
|
|
806
981
|
def arel_visitor
|
@@ -811,18 +986,17 @@ module ActiveRecord
|
|
811
986
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
812
987
|
end
|
813
988
|
|
814
|
-
def
|
989
|
+
def mismatched_foreign_key_details(message:, sql:)
|
990
|
+
foreign_key_pat =
|
991
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
992
|
+
|
815
993
|
match = %r/
|
816
994
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
817
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
995
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
818
996
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
819
997
|
/xmi.match(sql)
|
820
998
|
|
821
|
-
options = {
|
822
|
-
message: message,
|
823
|
-
sql: sql,
|
824
|
-
binds: binds,
|
825
|
-
}
|
999
|
+
options = {}
|
826
1000
|
|
827
1001
|
if match
|
828
1002
|
options[:table] = match[:table]
|
@@ -832,24 +1006,33 @@ module ActiveRecord
|
|
832
1006
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
833
1007
|
end
|
834
1008
|
|
835
|
-
|
1009
|
+
options
|
836
1010
|
end
|
837
1011
|
|
838
|
-
def
|
839
|
-
|
840
|
-
|
1012
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
1013
|
+
options = {
|
1014
|
+
message: message,
|
1015
|
+
sql: sql,
|
1016
|
+
binds: binds,
|
1017
|
+
connection_pool: connection_pool
|
1018
|
+
}
|
841
1019
|
|
842
|
-
|
843
|
-
|
844
|
-
|
1020
|
+
if sql
|
1021
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
1022
|
+
else
|
1023
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
1024
|
+
end
|
845
1025
|
|
846
|
-
|
847
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
1026
|
+
MismatchedForeignKey.new(**options)
|
848
1027
|
end
|
849
|
-
|
850
|
-
|
1028
|
+
|
1029
|
+
def version_string(full_version_string)
|
1030
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1031
|
+
matches[1]
|
1032
|
+
else
|
1033
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
1034
|
+
end
|
851
1035
|
end
|
852
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
853
1036
|
end
|
854
1037
|
end
|
855
1038
|
end
|