activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- 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 +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
- 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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- 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 +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- 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 +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- 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/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +56 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -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
|
|
@@ -51,8 +53,34 @@ module ActiveRecord
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
+
class << self
|
57
|
+
def dbconsole(config, options = {})
|
58
|
+
mysql_config = config.configuration_hash
|
59
|
+
|
60
|
+
args = {
|
61
|
+
host: "--host",
|
62
|
+
port: "--port",
|
63
|
+
socket: "--socket",
|
64
|
+
username: "--user",
|
65
|
+
encoding: "--default-character-set",
|
66
|
+
sslca: "--ssl-ca",
|
67
|
+
sslcert: "--ssl-cert",
|
68
|
+
sslcapath: "--ssl-capath",
|
69
|
+
sslcipher: "--ssl-cipher",
|
70
|
+
sslkey: "--ssl-key",
|
71
|
+
ssl_mode: "--ssl-mode"
|
72
|
+
}.filter_map { |opt, arg| "#{arg}=#{mysql_config[opt]}" if mysql_config[opt] }
|
73
|
+
|
74
|
+
if mysql_config[:password] && options[:include_password]
|
75
|
+
args << "--password=#{mysql_config[:password]}"
|
76
|
+
elsif mysql_config[:password] && !mysql_config[:password].to_s.empty?
|
77
|
+
args << "-p"
|
78
|
+
end
|
79
|
+
|
80
|
+
args << config.database
|
81
|
+
|
82
|
+
find_cmd_and_exec(["mysql", "mysql5"], *args)
|
83
|
+
end
|
56
84
|
end
|
57
85
|
|
58
86
|
def get_database_version # :nodoc:
|
@@ -81,6 +109,10 @@ module ActiveRecord
|
|
81
109
|
true
|
82
110
|
end
|
83
111
|
|
112
|
+
def supports_restart_db_transaction?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
84
116
|
def supports_explain?
|
85
117
|
true
|
86
118
|
end
|
@@ -95,7 +127,7 @@ module ActiveRecord
|
|
95
127
|
|
96
128
|
def supports_check_constraints?
|
97
129
|
if mariadb?
|
98
|
-
database_version >= "10.2.
|
130
|
+
database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
|
99
131
|
else
|
100
132
|
database_version >= "8.0.16"
|
101
133
|
end
|
@@ -138,6 +170,10 @@ module ActiveRecord
|
|
138
170
|
true
|
139
171
|
end
|
140
172
|
|
173
|
+
def supports_insert_returning?
|
174
|
+
mariadb? && database_version >= "10.5.0"
|
175
|
+
end
|
176
|
+
|
141
177
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
142
178
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
143
179
|
end
|
@@ -182,7 +218,7 @@ module ActiveRecord
|
|
182
218
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
183
219
|
yield
|
184
220
|
ensure
|
185
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
221
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
|
186
222
|
end
|
187
223
|
end
|
188
224
|
|
@@ -190,33 +226,36 @@ module ActiveRecord
|
|
190
226
|
# DATABASE STATEMENTS ======================================
|
191
227
|
#++
|
192
228
|
|
193
|
-
# Executes the SQL statement in the context of this connection.
|
194
|
-
def execute(sql, name = nil, async: false)
|
195
|
-
raw_execute(sql, name, async: async)
|
196
|
-
end
|
197
|
-
|
198
229
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
199
230
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
200
231
|
# needs to be explicitly freed or not.
|
201
|
-
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
202
|
-
|
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)
|
203
238
|
end
|
204
239
|
|
205
240
|
def begin_db_transaction # :nodoc:
|
206
|
-
|
241
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
207
242
|
end
|
208
243
|
|
209
244
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
210
|
-
|
245
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
211
246
|
begin_db_transaction
|
212
247
|
end
|
213
248
|
|
214
249
|
def commit_db_transaction # :nodoc:
|
215
|
-
|
250
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
216
251
|
end
|
217
252
|
|
218
253
|
def exec_rollback_db_transaction # :nodoc:
|
219
|
-
|
254
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
255
|
+
end
|
256
|
+
|
257
|
+
def exec_restart_db_transaction # :nodoc:
|
258
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
220
259
|
end
|
221
260
|
|
222
261
|
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
@@ -296,11 +335,12 @@ module ActiveRecord
|
|
296
335
|
#
|
297
336
|
# Example:
|
298
337
|
# rename_table('octopuses', 'octopi')
|
299
|
-
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]
|
300
340
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
301
341
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
302
342
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
303
|
-
rename_table_indexes(table_name, new_name)
|
343
|
+
rename_table_indexes(table_name, new_name, **options)
|
304
344
|
end
|
305
345
|
|
306
346
|
# Drops a table from the database.
|
@@ -334,11 +374,20 @@ module ActiveRecord
|
|
334
374
|
end
|
335
375
|
|
336
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
|
+
|
337
384
|
default = extract_new_default_value(default_or_changes)
|
338
|
-
|
385
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
339
386
|
end
|
340
387
|
|
341
388
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
389
|
+
validate_change_column_null_argument!(null)
|
390
|
+
|
342
391
|
unless null || default.nil?
|
343
392
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
344
393
|
end
|
@@ -355,18 +404,60 @@ module ActiveRecord
|
|
355
404
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
356
405
|
end
|
357
406
|
|
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
|
+
|
358
443
|
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
359
444
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
360
445
|
rename_column_indexes(table_name, column_name, new_column_name)
|
361
446
|
end
|
362
447
|
|
363
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:
|
364
456
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
365
457
|
|
366
458
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
367
459
|
|
368
|
-
|
369
|
-
execute schema_creation.accept(create_index)
|
460
|
+
CreateIndexDefinition.new(index, algorithm)
|
370
461
|
end
|
371
462
|
|
372
463
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -379,11 +470,13 @@ module ActiveRecord
|
|
379
470
|
|
380
471
|
scope = quoted_scope(table_name)
|
381
472
|
|
382
|
-
|
473
|
+
# MySQL returns 1 row for each column of composite foreign keys.
|
474
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
383
475
|
SELECT fk.referenced_table_name AS 'to_table',
|
384
476
|
fk.referenced_column_name AS 'primary_key',
|
385
477
|
fk.column_name AS 'column',
|
386
478
|
fk.constraint_name AS 'name',
|
479
|
+
fk.ordinal_position AS 'position',
|
387
480
|
rc.update_rule AS 'on_update',
|
388
481
|
rc.delete_rule AS 'on_delete'
|
389
482
|
FROM information_schema.referential_constraints rc
|
@@ -396,15 +489,22 @@ module ActiveRecord
|
|
396
489
|
AND rc.table_name = #{scope[:name]}
|
397
490
|
SQL
|
398
491
|
|
399
|
-
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
|
400
495
|
options = {
|
401
|
-
column: unquote_identifier(row["column"]),
|
402
496
|
name: row["name"],
|
403
|
-
|
497
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
498
|
+
on_delete: extract_foreign_key_action(row["on_delete"])
|
404
499
|
}
|
405
500
|
|
406
|
-
|
407
|
-
|
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
|
408
508
|
|
409
509
|
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
410
510
|
end
|
@@ -426,7 +526,7 @@ module ActiveRecord
|
|
426
526
|
SQL
|
427
527
|
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
428
528
|
|
429
|
-
chk_info =
|
529
|
+
chk_info = internal_exec_query(sql, "SCHEMA")
|
430
530
|
|
431
531
|
chk_info.map do |row|
|
432
532
|
options = {
|
@@ -435,6 +535,13 @@ module ActiveRecord
|
|
435
535
|
expression = row["expression"]
|
436
536
|
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
437
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
|
+
|
438
545
|
CheckConstraintDefinition.new(table_name, expression, options)
|
439
546
|
end
|
440
547
|
else
|
@@ -532,40 +639,79 @@ module ActiveRecord
|
|
532
639
|
end
|
533
640
|
|
534
641
|
def build_insert_sql(insert) # :nodoc:
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
sql
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
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}"
|
665
|
+
|
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
|
547
678
|
end
|
548
679
|
end
|
549
680
|
|
681
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
550
682
|
sql
|
551
683
|
end
|
552
684
|
|
553
685
|
def check_version # :nodoc:
|
554
686
|
if database_version < "5.5.8"
|
555
|
-
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."
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
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)
|
556
699
|
end
|
557
700
|
end
|
558
701
|
|
559
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
|
708
|
+
end
|
709
|
+
end
|
710
|
+
|
560
711
|
private
|
561
712
|
def initialize_type_map(m)
|
562
713
|
super
|
563
714
|
|
564
|
-
m.register_type(%r(char)i) do |sql_type|
|
565
|
-
limit = extract_limit(sql_type)
|
566
|
-
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
567
|
-
end
|
568
|
-
|
569
715
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
570
716
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
571
717
|
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
@@ -585,9 +731,6 @@ module ActiveRecord
|
|
585
731
|
|
586
732
|
m.alias_type %r(year)i, "integer"
|
587
733
|
m.alias_type %r(bit)i, "binary"
|
588
|
-
|
589
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
590
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
591
734
|
end
|
592
735
|
|
593
736
|
def register_integer_type(mapping, key, **options)
|
@@ -609,10 +752,8 @@ module ActiveRecord
|
|
609
752
|
end
|
610
753
|
end
|
611
754
|
|
612
|
-
|
613
|
-
|
614
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
615
|
-
end
|
755
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
756
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
616
757
|
|
617
758
|
private
|
618
759
|
def strip_whitespace_characters(expression)
|
@@ -621,29 +762,45 @@ module ActiveRecord
|
|
621
762
|
expression
|
622
763
|
end
|
623
764
|
|
624
|
-
def
|
625
|
-
|
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
|
626
771
|
end
|
627
772
|
|
628
|
-
def
|
629
|
-
|
630
|
-
end
|
773
|
+
def handle_warnings(sql)
|
774
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
631
775
|
|
632
|
-
|
633
|
-
|
634
|
-
|
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)
|
635
785
|
|
636
|
-
|
637
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
638
|
-
@connection.query(sql)
|
639
|
-
end
|
786
|
+
ActiveRecord.db_warnings_action.call(warning)
|
640
787
|
end
|
641
788
|
end
|
642
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
|
+
|
643
799
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
644
800
|
ER_DB_CREATE_EXISTS = 1007
|
645
801
|
ER_FILSORT_ABORT = 1028
|
646
802
|
ER_DUP_ENTRY = 1062
|
803
|
+
ER_SERVER_SHUTDOWN = 1053
|
647
804
|
ER_NOT_NULL_VIOLATION = 1048
|
648
805
|
ER_NO_REFERENCED_ROW = 1216
|
649
806
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -657,77 +814,59 @@ module ActiveRecord
|
|
657
814
|
ER_CANNOT_CREATE_TABLE = 1005
|
658
815
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
659
816
|
ER_QUERY_INTERRUPTED = 1317
|
817
|
+
ER_CONNECTION_KILLED = 1927
|
818
|
+
CR_SERVER_GONE_ERROR = 2006
|
819
|
+
CR_SERVER_LOST = 2013
|
660
820
|
ER_QUERY_TIMEOUT = 3024
|
661
821
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
822
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
662
823
|
|
663
824
|
def translate_exception(exception, message:, sql:, binds:)
|
664
825
|
case error_number(exception)
|
665
826
|
when nil
|
666
827
|
if exception.message.match?(/MySQL client is not connected/i)
|
667
|
-
ConnectionNotEstablished.new(exception)
|
828
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
668
829
|
else
|
669
830
|
super
|
670
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)
|
671
834
|
when ER_DB_CREATE_EXISTS
|
672
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
835
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
673
836
|
when ER_DUP_ENTRY
|
674
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
837
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
675
838
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
676
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
839
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
677
840
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
678
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
841
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
679
842
|
when ER_CANNOT_CREATE_TABLE
|
680
843
|
if message.include?("errno: 150")
|
681
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
844
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
682
845
|
else
|
683
846
|
super
|
684
847
|
end
|
685
848
|
when ER_DATA_TOO_LONG
|
686
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
849
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
687
850
|
when ER_OUT_OF_RANGE
|
688
|
-
RangeError.new(message, sql: sql, binds: binds)
|
851
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
689
852
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
690
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
853
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
691
854
|
when ER_LOCK_DEADLOCK
|
692
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
855
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
693
856
|
when ER_LOCK_WAIT_TIMEOUT
|
694
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
857
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
695
858
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
696
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
859
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
697
860
|
when ER_QUERY_INTERRUPTED
|
698
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
861
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
699
862
|
else
|
700
863
|
super
|
701
864
|
end
|
702
865
|
end
|
703
866
|
|
704
867
|
def change_column_for_alter(table_name, column_name, type, **options)
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
unless options.key?(:default)
|
709
|
-
options[:default] = column.default
|
710
|
-
end
|
711
|
-
|
712
|
-
unless options.key?(:null)
|
713
|
-
options[:null] = column.null
|
714
|
-
end
|
715
|
-
|
716
|
-
unless options.key?(:comment)
|
717
|
-
options[:comment] = column.comment
|
718
|
-
end
|
719
|
-
|
720
|
-
unless options.key?(:collation)
|
721
|
-
options[:collation] = column.collation if text_type?(type)
|
722
|
-
end
|
723
|
-
|
724
|
-
unless options.key?(:auto_increment)
|
725
|
-
options[:auto_increment] = column.auto_increment?
|
726
|
-
end
|
727
|
-
|
728
|
-
td = create_table_definition(table_name)
|
729
|
-
cd = td.new_column_definition(column.name, type, **options)
|
730
|
-
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)
|
731
870
|
end
|
732
871
|
|
733
872
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -741,7 +880,7 @@ module ActiveRecord
|
|
741
880
|
comment: column.comment
|
742
881
|
}
|
743
882
|
|
744
|
-
current_type =
|
883
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
745
884
|
td = create_table_definition(table_name)
|
746
885
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
747
886
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -759,6 +898,10 @@ module ActiveRecord
|
|
759
898
|
"DROP INDEX #{quote_column_name(index_name)}"
|
760
899
|
end
|
761
900
|
|
901
|
+
def supports_insert_raw_alias_syntax?
|
902
|
+
!mariadb? && database_version >= "8.0.19"
|
903
|
+
end
|
904
|
+
|
762
905
|
def supports_rename_index?
|
763
906
|
if mariadb?
|
764
907
|
database_version >= "10.5.2"
|
@@ -776,11 +919,9 @@ module ActiveRecord
|
|
776
919
|
end
|
777
920
|
|
778
921
|
def configure_connection
|
922
|
+
super
|
779
923
|
variables = @config.fetch(:variables, {}).stringify_keys
|
780
924
|
|
781
|
-
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
782
|
-
variables["sql_auto_is_null"] = 0
|
783
|
-
|
784
925
|
# Increase timeout so the server doesn't disconnect us.
|
785
926
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
786
927
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -824,7 +965,7 @@ module ActiveRecord
|
|
824
965
|
end.join(", ")
|
825
966
|
|
826
967
|
# ...and send them all in one query
|
827
|
-
|
968
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
828
969
|
end
|
829
970
|
|
830
971
|
def column_definitions(table_name) # :nodoc:
|
@@ -834,7 +975,7 @@ module ActiveRecord
|
|
834
975
|
end
|
835
976
|
|
836
977
|
def create_table_info(table_name) # :nodoc:
|
837
|
-
|
978
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
838
979
|
end
|
839
980
|
|
840
981
|
def arel_visitor
|
@@ -845,18 +986,17 @@ module ActiveRecord
|
|
845
986
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
846
987
|
end
|
847
988
|
|
848
|
-
def
|
989
|
+
def mismatched_foreign_key_details(message:, sql:)
|
990
|
+
foreign_key_pat =
|
991
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
992
|
+
|
849
993
|
match = %r/
|
850
994
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
851
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
995
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
852
996
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
853
997
|
/xmi.match(sql)
|
854
998
|
|
855
|
-
options = {
|
856
|
-
message: message,
|
857
|
-
sql: sql,
|
858
|
-
binds: binds,
|
859
|
-
}
|
999
|
+
options = {}
|
860
1000
|
|
861
1001
|
if match
|
862
1002
|
options[:table] = match[:table]
|
@@ -866,20 +1006,33 @@ module ActiveRecord
|
|
866
1006
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
867
1007
|
end
|
868
1008
|
|
869
|
-
|
1009
|
+
options
|
870
1010
|
end
|
871
1011
|
|
872
|
-
def
|
873
|
-
|
874
|
-
|
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
|
+
}
|
1019
|
+
|
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
|
875
1025
|
|
876
|
-
|
877
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
1026
|
+
MismatchedForeignKey.new(**options)
|
878
1027
|
end
|
879
|
-
|
880
|
-
|
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
|
881
1035
|
end
|
882
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
883
1036
|
end
|
884
1037
|
end
|
885
1038
|
end
|