activerecord 7.0.8 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +530 -2004
- 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 +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 +328 -471
- data/lib/active_record/attribute_assignment.rb +1 -13
- 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 +7 -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 +58 -45
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +10 -24
- 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 +317 -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 +188 -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 +306 -128
- 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 +274 -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 +368 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +364 -198
- 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 +217 -174
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- 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 +44 -20
- data/lib/active_record/encryption/encrypted_attribute_type.rb +45 -10
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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/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 +1 -0
- data/lib/active_record/enum.rb +122 -29
- 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 +3 -3
- 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 +56 -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 +88 -103
- data/lib/active_record/nested_attributes.rb +35 -9
- 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 +19 -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 +135 -86
- 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 +259 -68
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +196 -61
- data/lib/active_record/relation/calculations.rb +249 -92
- 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 +548 -94
- 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 +2 -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 +180 -119
- 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 +60 -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/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,75 @@ 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)
|
|
643
|
+
|
|
644
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
|
645
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
|
646
|
+
if supports_insert_raw_alias_syntax?
|
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
|
648
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
|
649
|
+
|
|
650
|
+
if insert.skip_duplicates?
|
|
651
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
|
652
|
+
elsif insert.update_duplicates?
|
|
653
|
+
if insert.raw_update_sql?
|
|
654
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
|
655
|
+
else
|
|
656
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
657
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
|
658
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
else
|
|
662
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
|
663
|
+
|
|
664
|
+
if insert.skip_duplicates?
|
|
665
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
|
666
|
+
elsif insert.update_duplicates?
|
|
667
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
668
|
+
if insert.raw_update_sql?
|
|
669
|
+
sql << insert.raw_update_sql
|
|
670
|
+
else
|
|
671
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
|
672
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
|
673
|
+
end
|
|
547
674
|
end
|
|
548
675
|
end
|
|
549
676
|
|
|
677
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
|
550
678
|
sql
|
|
551
679
|
end
|
|
552
680
|
|
|
553
681
|
def check_version # :nodoc:
|
|
554
682
|
if database_version < "5.5.8"
|
|
555
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
|
683
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
#--
|
|
688
|
+
# QUOTING ==================================================
|
|
689
|
+
#++
|
|
690
|
+
|
|
691
|
+
# Quotes strings for use in SQL input.
|
|
692
|
+
def quote_string(string)
|
|
693
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
|
694
|
+
connection.escape(string)
|
|
556
695
|
end
|
|
557
696
|
end
|
|
558
697
|
|
|
559
698
|
class << self
|
|
699
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
|
700
|
+
super(default_timezone: default_timezone).tap do |m|
|
|
701
|
+
if emulate_booleans
|
|
702
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
560
707
|
private
|
|
561
708
|
def initialize_type_map(m)
|
|
562
709
|
super
|
|
563
710
|
|
|
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
711
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
|
570
712
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
|
571
713
|
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
|
@@ -585,9 +727,6 @@ module ActiveRecord
|
|
|
585
727
|
|
|
586
728
|
m.alias_type %r(year)i, "integer"
|
|
587
729
|
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
730
|
end
|
|
592
731
|
|
|
593
732
|
def register_integer_type(mapping, key, **options)
|
|
@@ -609,10 +748,8 @@ module ActiveRecord
|
|
|
609
748
|
end
|
|
610
749
|
end
|
|
611
750
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
|
615
|
-
end
|
|
751
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
752
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
|
616
753
|
|
|
617
754
|
private
|
|
618
755
|
def strip_whitespace_characters(expression)
|
|
@@ -621,29 +758,45 @@ module ActiveRecord
|
|
|
621
758
|
expression
|
|
622
759
|
end
|
|
623
760
|
|
|
624
|
-
def
|
|
625
|
-
|
|
761
|
+
def extended_type_map_key
|
|
762
|
+
if @default_timezone
|
|
763
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
|
764
|
+
elsif emulate_booleans
|
|
765
|
+
EMULATE_BOOLEANS_TRUE
|
|
766
|
+
end
|
|
626
767
|
end
|
|
627
768
|
|
|
628
|
-
def
|
|
629
|
-
|
|
630
|
-
end
|
|
769
|
+
def handle_warnings(sql)
|
|
770
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
|
631
771
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
772
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
|
773
|
+
warning_count = @raw_connection.warning_count
|
|
774
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
|
775
|
+
result = [
|
|
776
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
|
777
|
+
] if result.count == 0
|
|
778
|
+
result.each do |level, code, message|
|
|
779
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
|
780
|
+
next if warning_ignored?(warning)
|
|
635
781
|
|
|
636
|
-
|
|
637
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
638
|
-
@connection.query(sql)
|
|
639
|
-
end
|
|
782
|
+
ActiveRecord.db_warnings_action.call(warning)
|
|
640
783
|
end
|
|
641
784
|
end
|
|
642
785
|
|
|
786
|
+
def warning_ignored?(warning)
|
|
787
|
+
warning.level == "Note" || super
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
791
|
+
# made since we established the connection
|
|
792
|
+
def sync_timezone_changes(raw_connection)
|
|
793
|
+
end
|
|
794
|
+
|
|
643
795
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
|
644
796
|
ER_DB_CREATE_EXISTS = 1007
|
|
645
797
|
ER_FILSORT_ABORT = 1028
|
|
646
798
|
ER_DUP_ENTRY = 1062
|
|
799
|
+
ER_SERVER_SHUTDOWN = 1053
|
|
647
800
|
ER_NOT_NULL_VIOLATION = 1048
|
|
648
801
|
ER_NO_REFERENCED_ROW = 1216
|
|
649
802
|
ER_ROW_IS_REFERENCED = 1217
|
|
@@ -657,77 +810,59 @@ module ActiveRecord
|
|
|
657
810
|
ER_CANNOT_CREATE_TABLE = 1005
|
|
658
811
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
|
659
812
|
ER_QUERY_INTERRUPTED = 1317
|
|
813
|
+
ER_CONNECTION_KILLED = 1927
|
|
814
|
+
CR_SERVER_GONE_ERROR = 2006
|
|
815
|
+
CR_SERVER_LOST = 2013
|
|
660
816
|
ER_QUERY_TIMEOUT = 3024
|
|
661
817
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
|
818
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
|
662
819
|
|
|
663
820
|
def translate_exception(exception, message:, sql:, binds:)
|
|
664
821
|
case error_number(exception)
|
|
665
822
|
when nil
|
|
666
823
|
if exception.message.match?(/MySQL client is not connected/i)
|
|
667
|
-
ConnectionNotEstablished.new(exception)
|
|
824
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
668
825
|
else
|
|
669
826
|
super
|
|
670
827
|
end
|
|
828
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
|
829
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
671
830
|
when ER_DB_CREATE_EXISTS
|
|
672
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
|
831
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
673
832
|
when ER_DUP_ENTRY
|
|
674
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
833
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
675
834
|
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)
|
|
835
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
677
836
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
|
678
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
837
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
679
838
|
when ER_CANNOT_CREATE_TABLE
|
|
680
839
|
if message.include?("errno: 150")
|
|
681
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
840
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
682
841
|
else
|
|
683
842
|
super
|
|
684
843
|
end
|
|
685
844
|
when ER_DATA_TOO_LONG
|
|
686
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
|
845
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
687
846
|
when ER_OUT_OF_RANGE
|
|
688
|
-
RangeError.new(message, sql: sql, binds: binds)
|
|
847
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
689
848
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
|
690
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
849
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
691
850
|
when ER_LOCK_DEADLOCK
|
|
692
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
|
851
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
693
852
|
when ER_LOCK_WAIT_TIMEOUT
|
|
694
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
|
853
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
695
854
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
|
696
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
|
855
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
697
856
|
when ER_QUERY_INTERRUPTED
|
|
698
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
|
857
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
699
858
|
else
|
|
700
859
|
super
|
|
701
860
|
end
|
|
702
861
|
end
|
|
703
862
|
|
|
704
863
|
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))
|
|
864
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
|
865
|
+
schema_creation.accept(cd)
|
|
731
866
|
end
|
|
732
867
|
|
|
733
868
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
|
@@ -741,7 +876,7 @@ module ActiveRecord
|
|
|
741
876
|
comment: column.comment
|
|
742
877
|
}
|
|
743
878
|
|
|
744
|
-
current_type =
|
|
879
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
|
745
880
|
td = create_table_definition(table_name)
|
|
746
881
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
|
747
882
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
|
@@ -759,6 +894,10 @@ module ActiveRecord
|
|
|
759
894
|
"DROP INDEX #{quote_column_name(index_name)}"
|
|
760
895
|
end
|
|
761
896
|
|
|
897
|
+
def supports_insert_raw_alias_syntax?
|
|
898
|
+
!mariadb? && database_version >= "8.0.19"
|
|
899
|
+
end
|
|
900
|
+
|
|
762
901
|
def supports_rename_index?
|
|
763
902
|
if mariadb?
|
|
764
903
|
database_version >= "10.5.2"
|
|
@@ -776,11 +915,9 @@ module ActiveRecord
|
|
|
776
915
|
end
|
|
777
916
|
|
|
778
917
|
def configure_connection
|
|
918
|
+
super
|
|
779
919
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
780
920
|
|
|
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
921
|
# Increase timeout so the server doesn't disconnect us.
|
|
785
922
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
|
786
923
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
|
@@ -824,7 +961,7 @@ module ActiveRecord
|
|
|
824
961
|
end.join(", ")
|
|
825
962
|
|
|
826
963
|
# ...and send them all in one query
|
|
827
|
-
|
|
964
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
|
828
965
|
end
|
|
829
966
|
|
|
830
967
|
def column_definitions(table_name) # :nodoc:
|
|
@@ -834,7 +971,7 @@ module ActiveRecord
|
|
|
834
971
|
end
|
|
835
972
|
|
|
836
973
|
def create_table_info(table_name) # :nodoc:
|
|
837
|
-
|
|
974
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
|
838
975
|
end
|
|
839
976
|
|
|
840
977
|
def arel_visitor
|
|
@@ -845,18 +982,17 @@ module ActiveRecord
|
|
|
845
982
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
|
846
983
|
end
|
|
847
984
|
|
|
848
|
-
def
|
|
985
|
+
def mismatched_foreign_key_details(message:, sql:)
|
|
986
|
+
foreign_key_pat =
|
|
987
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
|
988
|
+
|
|
849
989
|
match = %r/
|
|
850
990
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
|
851
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
|
991
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
|
852
992
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
|
853
993
|
/xmi.match(sql)
|
|
854
994
|
|
|
855
|
-
options = {
|
|
856
|
-
message: message,
|
|
857
|
-
sql: sql,
|
|
858
|
-
binds: binds,
|
|
859
|
-
}
|
|
995
|
+
options = {}
|
|
860
996
|
|
|
861
997
|
if match
|
|
862
998
|
options[:table] = match[:table]
|
|
@@ -866,20 +1002,33 @@ module ActiveRecord
|
|
|
866
1002
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
|
867
1003
|
end
|
|
868
1004
|
|
|
869
|
-
|
|
1005
|
+
options
|
|
870
1006
|
end
|
|
871
1007
|
|
|
872
|
-
def
|
|
873
|
-
|
|
874
|
-
|
|
1008
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
|
1009
|
+
options = {
|
|
1010
|
+
message: message,
|
|
1011
|
+
sql: sql,
|
|
1012
|
+
binds: binds,
|
|
1013
|
+
connection_pool: connection_pool
|
|
1014
|
+
}
|
|
875
1015
|
|
|
876
|
-
|
|
877
|
-
|
|
1016
|
+
if sql
|
|
1017
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
|
1018
|
+
else
|
|
1019
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
MismatchedForeignKey.new(**options)
|
|
878
1023
|
end
|
|
879
|
-
|
|
880
|
-
|
|
1024
|
+
|
|
1025
|
+
def version_string(full_version_string)
|
|
1026
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
|
1027
|
+
matches[1]
|
|
1028
|
+
else
|
|
1029
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
|
1030
|
+
end
|
|
881
1031
|
end
|
|
882
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
|
883
1032
|
end
|
|
884
1033
|
end
|
|
885
1034
|
end
|