activerecord 7.0.8.1 → 7.1.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1552 -1455
- 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 &&
|