activerecord 7.0.8 → 7.1.3.4
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 +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- 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.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +313 -217
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +52 -34
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- 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 +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- 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 +289 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
- 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 +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- 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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- 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 +74 -40
- 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/quoting.rb +10 -6
- 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 +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
- 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 +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- 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_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- 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 +135 -71
- data/lib/active_record/future_result.rb +31 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- 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 +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -5
- data/lib/active_record/migration/default_strategy.rb +23 -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 +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- 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 +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- 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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- 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/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -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/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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 +48 -12
- 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
|
@@ -190,33 +222,36 @@ module ActiveRecord
|
|
190
222
|
# DATABASE STATEMENTS ======================================
|
191
223
|
#++
|
192
224
|
|
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
225
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
199
226
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
200
227
|
# needs to be explicitly freed or not.
|
201
228
|
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
202
|
-
|
229
|
+
sql = transform_query(sql)
|
230
|
+
check_if_write_query(sql)
|
231
|
+
|
232
|
+
mark_transaction_written_if_write(sql)
|
233
|
+
yield raw_execute(sql, name, async: async)
|
203
234
|
end
|
204
235
|
|
205
236
|
def begin_db_transaction # :nodoc:
|
206
|
-
|
237
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
207
238
|
end
|
208
239
|
|
209
240
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
210
|
-
|
241
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
211
242
|
begin_db_transaction
|
212
243
|
end
|
213
244
|
|
214
245
|
def commit_db_transaction # :nodoc:
|
215
|
-
|
246
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
216
247
|
end
|
217
248
|
|
218
249
|
def exec_rollback_db_transaction # :nodoc:
|
219
|
-
|
250
|
+
internal_execute("ROLLBACK", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
251
|
+
end
|
252
|
+
|
253
|
+
def exec_restart_db_transaction # :nodoc:
|
254
|
+
internal_execute("ROLLBACK AND CHAIN", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
220
255
|
end
|
221
256
|
|
222
257
|
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
@@ -296,7 +331,8 @@ module ActiveRecord
|
|
296
331
|
#
|
297
332
|
# Example:
|
298
333
|
# rename_table('octopuses', 'octopi')
|
299
|
-
def rename_table(table_name, new_name)
|
334
|
+
def rename_table(table_name, new_name, **options)
|
335
|
+
validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
|
300
336
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
301
337
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
302
338
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
@@ -334,11 +370,20 @@ module ActiveRecord
|
|
334
370
|
end
|
335
371
|
|
336
372
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
373
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
374
|
+
end
|
375
|
+
|
376
|
+
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
|
377
|
+
column = column_for(table_name, column_name)
|
378
|
+
return unless column
|
379
|
+
|
337
380
|
default = extract_new_default_value(default_or_changes)
|
338
|
-
|
381
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
339
382
|
end
|
340
383
|
|
341
384
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
385
|
+
validate_change_column_null_argument!(null)
|
386
|
+
|
342
387
|
unless null || default.nil?
|
343
388
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
344
389
|
end
|
@@ -355,18 +400,60 @@ module ActiveRecord
|
|
355
400
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
356
401
|
end
|
357
402
|
|
403
|
+
# Builds a ChangeColumnDefinition object.
|
404
|
+
#
|
405
|
+
# This definition object contains information about the column change that would occur
|
406
|
+
# if the same arguments were passed to #change_column. See #change_column for information about
|
407
|
+
# passing a +table_name+, +column_name+, +type+ and other options that can be passed.
|
408
|
+
def build_change_column_definition(table_name, column_name, type, **options) # :nodoc:
|
409
|
+
column = column_for(table_name, column_name)
|
410
|
+
type ||= column.sql_type
|
411
|
+
|
412
|
+
unless options.key?(:default)
|
413
|
+
options[:default] = column.default
|
414
|
+
end
|
415
|
+
|
416
|
+
unless options.key?(:null)
|
417
|
+
options[:null] = column.null
|
418
|
+
end
|
419
|
+
|
420
|
+
unless options.key?(:comment)
|
421
|
+
options[:comment] = column.comment
|
422
|
+
end
|
423
|
+
|
424
|
+
if options[:collation] == :no_collation
|
425
|
+
options.delete(:collation)
|
426
|
+
else
|
427
|
+
options[:collation] ||= column.collation if text_type?(type)
|
428
|
+
end
|
429
|
+
|
430
|
+
unless options.key?(:auto_increment)
|
431
|
+
options[:auto_increment] = column.auto_increment?
|
432
|
+
end
|
433
|
+
|
434
|
+
td = create_table_definition(table_name)
|
435
|
+
cd = td.new_column_definition(column.name, type, **options)
|
436
|
+
ChangeColumnDefinition.new(cd, column.name)
|
437
|
+
end
|
438
|
+
|
358
439
|
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
359
440
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
360
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
361
442
|
end
|
362
443
|
|
363
444
|
def add_index(table_name, column_name, **options) # :nodoc:
|
445
|
+
create_index = build_create_index_definition(table_name, column_name, **options)
|
446
|
+
return unless create_index
|
447
|
+
|
448
|
+
execute schema_creation.accept(create_index)
|
449
|
+
end
|
450
|
+
|
451
|
+
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
|
364
452
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
365
453
|
|
366
454
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
367
455
|
|
368
|
-
|
369
|
-
execute schema_creation.accept(create_index)
|
456
|
+
CreateIndexDefinition.new(index, algorithm)
|
370
457
|
end
|
371
458
|
|
372
459
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -379,11 +466,13 @@ module ActiveRecord
|
|
379
466
|
|
380
467
|
scope = quoted_scope(table_name)
|
381
468
|
|
382
|
-
|
469
|
+
# MySQL returns 1 row for each column of composite foreign keys.
|
470
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
383
471
|
SELECT fk.referenced_table_name AS 'to_table',
|
384
472
|
fk.referenced_column_name AS 'primary_key',
|
385
473
|
fk.column_name AS 'column',
|
386
474
|
fk.constraint_name AS 'name',
|
475
|
+
fk.ordinal_position AS 'position',
|
387
476
|
rc.update_rule AS 'on_update',
|
388
477
|
rc.delete_rule AS 'on_delete'
|
389
478
|
FROM information_schema.referential_constraints rc
|
@@ -396,15 +485,22 @@ module ActiveRecord
|
|
396
485
|
AND rc.table_name = #{scope[:name]}
|
397
486
|
SQL
|
398
487
|
|
399
|
-
fk_info.
|
488
|
+
grouped_fk = fk_info.group_by { |row| row["name"] }.values.each { |group| group.sort_by! { |row| row["position"] } }
|
489
|
+
grouped_fk.map do |group|
|
490
|
+
row = group.first
|
400
491
|
options = {
|
401
|
-
column: unquote_identifier(row["column"]),
|
402
492
|
name: row["name"],
|
403
|
-
|
493
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
494
|
+
on_delete: extract_foreign_key_action(row["on_delete"])
|
404
495
|
}
|
405
496
|
|
406
|
-
|
407
|
-
|
497
|
+
if group.one?
|
498
|
+
options[:column] = unquote_identifier(row["column"])
|
499
|
+
options[:primary_key] = row["primary_key"]
|
500
|
+
else
|
501
|
+
options[:column] = group.map { |row| unquote_identifier(row["column"]) }
|
502
|
+
options[:primary_key] = group.map { |row| row["primary_key"] }
|
503
|
+
end
|
408
504
|
|
409
505
|
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
410
506
|
end
|
@@ -426,7 +522,7 @@ module ActiveRecord
|
|
426
522
|
SQL
|
427
523
|
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
428
524
|
|
429
|
-
chk_info =
|
525
|
+
chk_info = internal_exec_query(sql, "SCHEMA")
|
430
526
|
|
431
527
|
chk_info.map do |row|
|
432
528
|
options = {
|
@@ -435,6 +531,13 @@ module ActiveRecord
|
|
435
531
|
expression = row["expression"]
|
436
532
|
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
437
533
|
expression = strip_whitespace_characters(expression)
|
534
|
+
|
535
|
+
unless mariadb?
|
536
|
+
# MySQL returns check constraints expression in an already escaped form.
|
537
|
+
# This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
|
538
|
+
expression = expression.gsub("\\'", "'")
|
539
|
+
end
|
540
|
+
|
438
541
|
CheckConstraintDefinition.new(table_name, expression, options)
|
439
542
|
end
|
440
543
|
else
|
@@ -557,15 +660,18 @@ module ActiveRecord
|
|
557
660
|
end
|
558
661
|
|
559
662
|
class << self
|
663
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
664
|
+
super(default_timezone: default_timezone).tap do |m|
|
665
|
+
if emulate_booleans
|
666
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
560
671
|
private
|
561
672
|
def initialize_type_map(m)
|
562
673
|
super
|
563
674
|
|
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
675
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
570
676
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
571
677
|
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
@@ -585,9 +691,6 @@ module ActiveRecord
|
|
585
691
|
|
586
692
|
m.alias_type %r(year)i, "integer"
|
587
693
|
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
694
|
end
|
592
695
|
|
593
696
|
def register_integer_type(mapping, key, **options)
|
@@ -609,10 +712,8 @@ module ActiveRecord
|
|
609
712
|
end
|
610
713
|
end
|
611
714
|
|
612
|
-
|
613
|
-
|
614
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
615
|
-
end
|
715
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
716
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
616
717
|
|
617
718
|
private
|
618
719
|
def strip_whitespace_characters(expression)
|
@@ -621,25 +722,36 @@ module ActiveRecord
|
|
621
722
|
expression
|
622
723
|
end
|
623
724
|
|
624
|
-
def
|
625
|
-
|
725
|
+
def extended_type_map_key
|
726
|
+
if @default_timezone
|
727
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
728
|
+
elsif emulate_booleans
|
729
|
+
EMULATE_BOOLEANS_TRUE
|
730
|
+
end
|
626
731
|
end
|
627
732
|
|
628
|
-
def
|
629
|
-
|
630
|
-
end
|
733
|
+
def handle_warnings(sql)
|
734
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
631
735
|
|
632
|
-
|
633
|
-
|
634
|
-
|
736
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
737
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
738
|
+
result.each do |level, code, message|
|
739
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
740
|
+
next if warning_ignored?(warning)
|
635
741
|
|
636
|
-
|
637
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
638
|
-
@connection.query(sql)
|
639
|
-
end
|
742
|
+
ActiveRecord.db_warnings_action.call(warning)
|
640
743
|
end
|
641
744
|
end
|
642
745
|
|
746
|
+
def warning_ignored?(warning)
|
747
|
+
warning.level == "Note" || super
|
748
|
+
end
|
749
|
+
|
750
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
751
|
+
# made since we established the connection
|
752
|
+
def sync_timezone_changes(raw_connection)
|
753
|
+
end
|
754
|
+
|
643
755
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
644
756
|
ER_DB_CREATE_EXISTS = 1007
|
645
757
|
ER_FILSORT_ABORT = 1028
|
@@ -657,77 +769,59 @@ module ActiveRecord
|
|
657
769
|
ER_CANNOT_CREATE_TABLE = 1005
|
658
770
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
659
771
|
ER_QUERY_INTERRUPTED = 1317
|
772
|
+
ER_CONNECTION_KILLED = 1927
|
773
|
+
CR_SERVER_GONE_ERROR = 2006
|
774
|
+
CR_SERVER_LOST = 2013
|
660
775
|
ER_QUERY_TIMEOUT = 3024
|
661
776
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
777
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
662
778
|
|
663
779
|
def translate_exception(exception, message:, sql:, binds:)
|
664
780
|
case error_number(exception)
|
665
781
|
when nil
|
666
782
|
if exception.message.match?(/MySQL client is not connected/i)
|
667
|
-
ConnectionNotEstablished.new(exception)
|
783
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
668
784
|
else
|
669
785
|
super
|
670
786
|
end
|
787
|
+
when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
788
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
671
789
|
when ER_DB_CREATE_EXISTS
|
672
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
790
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
673
791
|
when ER_DUP_ENTRY
|
674
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
792
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
675
793
|
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)
|
794
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
677
795
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
678
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
796
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
679
797
|
when ER_CANNOT_CREATE_TABLE
|
680
798
|
if message.include?("errno: 150")
|
681
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
799
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
682
800
|
else
|
683
801
|
super
|
684
802
|
end
|
685
803
|
when ER_DATA_TOO_LONG
|
686
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
804
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
687
805
|
when ER_OUT_OF_RANGE
|
688
|
-
RangeError.new(message, sql: sql, binds: binds)
|
806
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
689
807
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
690
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
808
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
691
809
|
when ER_LOCK_DEADLOCK
|
692
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
810
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
693
811
|
when ER_LOCK_WAIT_TIMEOUT
|
694
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
812
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
695
813
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
696
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
814
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
697
815
|
when ER_QUERY_INTERRUPTED
|
698
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
816
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
699
817
|
else
|
700
818
|
super
|
701
819
|
end
|
702
820
|
end
|
703
821
|
|
704
822
|
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))
|
823
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
824
|
+
schema_creation.accept(cd)
|
731
825
|
end
|
732
826
|
|
733
827
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -741,7 +835,7 @@ module ActiveRecord
|
|
741
835
|
comment: column.comment
|
742
836
|
}
|
743
837
|
|
744
|
-
current_type =
|
838
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
745
839
|
td = create_table_definition(table_name)
|
746
840
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
747
841
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -778,9 +872,6 @@ module ActiveRecord
|
|
778
872
|
def configure_connection
|
779
873
|
variables = @config.fetch(:variables, {}).stringify_keys
|
780
874
|
|
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
875
|
# Increase timeout so the server doesn't disconnect us.
|
785
876
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
786
877
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -824,7 +915,7 @@ module ActiveRecord
|
|
824
915
|
end.join(", ")
|
825
916
|
|
826
917
|
# ...and send them all in one query
|
827
|
-
|
918
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
828
919
|
end
|
829
920
|
|
830
921
|
def column_definitions(table_name) # :nodoc:
|
@@ -834,7 +925,7 @@ module ActiveRecord
|
|
834
925
|
end
|
835
926
|
|
836
927
|
def create_table_info(table_name) # :nodoc:
|
837
|
-
|
928
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
838
929
|
end
|
839
930
|
|
840
931
|
def arel_visitor
|
@@ -845,18 +936,17 @@ module ActiveRecord
|
|
845
936
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
846
937
|
end
|
847
938
|
|
848
|
-
def
|
939
|
+
def mismatched_foreign_key_details(message:, sql:)
|
940
|
+
foreign_key_pat =
|
941
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
942
|
+
|
849
943
|
match = %r/
|
850
944
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
851
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
945
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
852
946
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
853
947
|
/xmi.match(sql)
|
854
948
|
|
855
|
-
options = {
|
856
|
-
message: message,
|
857
|
-
sql: sql,
|
858
|
-
binds: binds,
|
859
|
-
}
|
949
|
+
options = {}
|
860
950
|
|
861
951
|
if match
|
862
952
|
options[:table] = match[:table]
|
@@ -866,20 +956,29 @@ module ActiveRecord
|
|
866
956
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
867
957
|
end
|
868
958
|
|
959
|
+
options
|
960
|
+
end
|
961
|
+
|
962
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
963
|
+
options = {
|
964
|
+
message: message,
|
965
|
+
sql: sql,
|
966
|
+
binds: binds,
|
967
|
+
connection_pool: connection_pool
|
968
|
+
}
|
969
|
+
|
970
|
+
if sql
|
971
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
972
|
+
else
|
973
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
974
|
+
end
|
975
|
+
|
869
976
|
MismatchedForeignKey.new(**options)
|
870
977
|
end
|
871
978
|
|
872
979
|
def version_string(full_version_string)
|
873
980
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
874
981
|
end
|
875
|
-
|
876
|
-
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
877
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
878
|
-
end
|
879
|
-
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
880
|
-
Type::String.new(true: "1", false: "0", **args)
|
881
|
-
end
|
882
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
883
982
|
end
|
884
983
|
end
|
885
984
|
end
|
@@ -63,6 +63,15 @@ module ActiveRecord
|
|
63
63
|
coder["comment"] = @comment
|
64
64
|
end
|
65
65
|
|
66
|
+
# whether the column is auto-populated by the database using a sequence
|
67
|
+
def auto_incremented_by_db?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def auto_populated?
|
72
|
+
auto_incremented_by_db? || default_function
|
73
|
+
end
|
74
|
+
|
66
75
|
def ==(other)
|
67
76
|
other.is_a?(Column) &&
|
68
77
|
name == other.name &&
|