activerecord 7.0.10 → 7.1.0.beta1
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1348 -1672
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -17
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- 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 +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- 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 +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- 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 +48 -38
- 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 +65 -49
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- 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 +291 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -115
- 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 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -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 +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -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 +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +352 -55
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- 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 +42 -36
- 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 +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -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 +131 -142
- 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 +11 -6
- 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 +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +52 -12
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- 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 +113 -26
- data/lib/active_record/errors.rb +89 -15
- 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 +119 -83
- data/lib/active_record/future_result.rb +30 -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 +58 -11
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -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 +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -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.rb +215 -112
- data/lib/active_record/model_schema.rb +50 -32
- data/lib/active_record/nested_attributes.rb +31 -6
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- 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 +108 -46
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- 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 +169 -45
- 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 +152 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +85 -15
- 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/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 +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +75 -34
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -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 +14 -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 +52 -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
|
|
@@ -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,13 +531,6 @@ 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)
|
|
438
|
-
|
|
439
|
-
unless mariadb?
|
|
440
|
-
# MySQL returns check constraints expression in an already escaped form.
|
|
441
|
-
# This leads to duplicate escaping later (e.g. when the expression is used in the SchemaDumper).
|
|
442
|
-
expression = expression.gsub("\\'", "'")
|
|
443
|
-
end
|
|
444
|
-
|
|
445
534
|
CheckConstraintDefinition.new(table_name, expression, options)
|
|
446
535
|
end
|
|
447
536
|
else
|
|
@@ -564,15 +653,18 @@ module ActiveRecord
|
|
|
564
653
|
end
|
|
565
654
|
|
|
566
655
|
class << self
|
|
656
|
+
def extended_type_map(default_timezone: nil, emulate_booleans:) # :nodoc:
|
|
657
|
+
super(default_timezone: default_timezone).tap do |m|
|
|
658
|
+
if emulate_booleans
|
|
659
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
|
|
567
664
|
private
|
|
568
665
|
def initialize_type_map(m)
|
|
569
666
|
super
|
|
570
667
|
|
|
571
|
-
m.register_type(%r(char)i) do |sql_type|
|
|
572
|
-
limit = extract_limit(sql_type)
|
|
573
|
-
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
|
574
|
-
end
|
|
575
|
-
|
|
576
668
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
|
577
669
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
|
578
670
|
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
|
@@ -592,9 +684,6 @@ module ActiveRecord
|
|
|
592
684
|
|
|
593
685
|
m.alias_type %r(year)i, "integer"
|
|
594
686
|
m.alias_type %r(bit)i, "binary"
|
|
595
|
-
|
|
596
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
|
597
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
|
598
687
|
end
|
|
599
688
|
|
|
600
689
|
def register_integer_type(mapping, key, **options)
|
|
@@ -616,10 +705,8 @@ module ActiveRecord
|
|
|
616
705
|
end
|
|
617
706
|
end
|
|
618
707
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
|
622
|
-
end
|
|
708
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
709
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
|
623
710
|
|
|
624
711
|
private
|
|
625
712
|
def strip_whitespace_characters(expression)
|
|
@@ -628,25 +715,36 @@ module ActiveRecord
|
|
|
628
715
|
expression
|
|
629
716
|
end
|
|
630
717
|
|
|
631
|
-
def
|
|
632
|
-
|
|
718
|
+
def extended_type_map_key
|
|
719
|
+
if @default_timezone
|
|
720
|
+
{ default_timezone: @default_timezone, emulate_booleans: emulate_booleans }
|
|
721
|
+
elsif emulate_booleans
|
|
722
|
+
EMULATE_BOOLEANS_TRUE
|
|
723
|
+
end
|
|
633
724
|
end
|
|
634
725
|
|
|
635
|
-
def
|
|
636
|
-
|
|
637
|
-
end
|
|
726
|
+
def handle_warnings(sql)
|
|
727
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
|
638
728
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
729
|
+
@affected_rows_before_warnings = @raw_connection.affected_rows
|
|
730
|
+
result = @raw_connection.query("SHOW WARNINGS")
|
|
731
|
+
result.each do |level, code, message|
|
|
732
|
+
warning = SQLWarning.new(message, code, level, sql, @pool)
|
|
733
|
+
next if warning_ignored?(warning)
|
|
642
734
|
|
|
643
|
-
|
|
644
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
645
|
-
@connection.query(sql)
|
|
646
|
-
end
|
|
735
|
+
ActiveRecord.db_warnings_action.call(warning)
|
|
647
736
|
end
|
|
648
737
|
end
|
|
649
738
|
|
|
739
|
+
def warning_ignored?(warning)
|
|
740
|
+
warning.level == "Note" || super
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
744
|
+
# made since we established the connection
|
|
745
|
+
def sync_timezone_changes(raw_connection)
|
|
746
|
+
end
|
|
747
|
+
|
|
650
748
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
|
651
749
|
ER_DB_CREATE_EXISTS = 1007
|
|
652
750
|
ER_FILSORT_ABORT = 1028
|
|
@@ -664,77 +762,59 @@ module ActiveRecord
|
|
|
664
762
|
ER_CANNOT_CREATE_TABLE = 1005
|
|
665
763
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
|
666
764
|
ER_QUERY_INTERRUPTED = 1317
|
|
765
|
+
ER_CONNECTION_KILLED = 1927
|
|
766
|
+
CR_SERVER_GONE_ERROR = 2006
|
|
767
|
+
CR_SERVER_LOST = 2013
|
|
667
768
|
ER_QUERY_TIMEOUT = 3024
|
|
668
769
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
|
770
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
|
669
771
|
|
|
670
772
|
def translate_exception(exception, message:, sql:, binds:)
|
|
671
773
|
case error_number(exception)
|
|
672
774
|
when nil
|
|
673
775
|
if exception.message.match?(/MySQL client is not connected/i)
|
|
674
|
-
ConnectionNotEstablished.new(exception)
|
|
776
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
|
675
777
|
else
|
|
676
778
|
super
|
|
677
779
|
end
|
|
780
|
+
when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
|
781
|
+
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
678
782
|
when ER_DB_CREATE_EXISTS
|
|
679
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
|
783
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
680
784
|
when ER_DUP_ENTRY
|
|
681
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
785
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
682
786
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
|
683
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
787
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
684
788
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
|
685
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
789
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
686
790
|
when ER_CANNOT_CREATE_TABLE
|
|
687
791
|
if message.include?("errno: 150")
|
|
688
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
|
792
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
689
793
|
else
|
|
690
794
|
super
|
|
691
795
|
end
|
|
692
796
|
when ER_DATA_TOO_LONG
|
|
693
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
|
797
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
694
798
|
when ER_OUT_OF_RANGE
|
|
695
|
-
RangeError.new(message, sql: sql, binds: binds)
|
|
799
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
696
800
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
|
697
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
801
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
698
802
|
when ER_LOCK_DEADLOCK
|
|
699
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
|
803
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
700
804
|
when ER_LOCK_WAIT_TIMEOUT
|
|
701
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
|
805
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
702
806
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
|
703
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
|
807
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
704
808
|
when ER_QUERY_INTERRUPTED
|
|
705
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
|
809
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
706
810
|
else
|
|
707
811
|
super
|
|
708
812
|
end
|
|
709
813
|
end
|
|
710
814
|
|
|
711
815
|
def change_column_for_alter(table_name, column_name, type, **options)
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
unless options.key?(:default)
|
|
716
|
-
options[:default] = column.default
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
unless options.key?(:null)
|
|
720
|
-
options[:null] = column.null
|
|
721
|
-
end
|
|
722
|
-
|
|
723
|
-
unless options.key?(:comment)
|
|
724
|
-
options[:comment] = column.comment
|
|
725
|
-
end
|
|
726
|
-
|
|
727
|
-
unless options.key?(:collation)
|
|
728
|
-
options[:collation] = column.collation if text_type?(type)
|
|
729
|
-
end
|
|
730
|
-
|
|
731
|
-
unless options.key?(:auto_increment)
|
|
732
|
-
options[:auto_increment] = column.auto_increment?
|
|
733
|
-
end
|
|
734
|
-
|
|
735
|
-
td = create_table_definition(table_name)
|
|
736
|
-
cd = td.new_column_definition(column.name, type, **options)
|
|
737
|
-
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
|
816
|
+
cd = build_change_column_definition(table_name, column_name, type, **options)
|
|
817
|
+
schema_creation.accept(cd)
|
|
738
818
|
end
|
|
739
819
|
|
|
740
820
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
|
@@ -748,7 +828,7 @@ module ActiveRecord
|
|
|
748
828
|
comment: column.comment
|
|
749
829
|
}
|
|
750
830
|
|
|
751
|
-
current_type =
|
|
831
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
|
752
832
|
td = create_table_definition(table_name)
|
|
753
833
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
|
754
834
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
|
@@ -785,9 +865,6 @@ module ActiveRecord
|
|
|
785
865
|
def configure_connection
|
|
786
866
|
variables = @config.fetch(:variables, {}).stringify_keys
|
|
787
867
|
|
|
788
|
-
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
|
789
|
-
variables["sql_auto_is_null"] = 0
|
|
790
|
-
|
|
791
868
|
# Increase timeout so the server doesn't disconnect us.
|
|
792
869
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
|
793
870
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
|
@@ -831,7 +908,7 @@ module ActiveRecord
|
|
|
831
908
|
end.join(", ")
|
|
832
909
|
|
|
833
910
|
# ...and send them all in one query
|
|
834
|
-
|
|
911
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
|
835
912
|
end
|
|
836
913
|
|
|
837
914
|
def column_definitions(table_name) # :nodoc:
|
|
@@ -841,7 +918,7 @@ module ActiveRecord
|
|
|
841
918
|
end
|
|
842
919
|
|
|
843
920
|
def create_table_info(table_name) # :nodoc:
|
|
844
|
-
|
|
921
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
|
845
922
|
end
|
|
846
923
|
|
|
847
924
|
def arel_visitor
|
|
@@ -852,18 +929,17 @@ module ActiveRecord
|
|
|
852
929
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
|
853
930
|
end
|
|
854
931
|
|
|
855
|
-
def
|
|
932
|
+
def mismatched_foreign_key_details(message:, sql:)
|
|
933
|
+
foreign_key_pat =
|
|
934
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
|
935
|
+
|
|
856
936
|
match = %r/
|
|
857
937
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
|
858
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
|
938
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
|
859
939
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
|
860
940
|
/xmi.match(sql)
|
|
861
941
|
|
|
862
|
-
options = {
|
|
863
|
-
message: message,
|
|
864
|
-
sql: sql,
|
|
865
|
-
binds: binds,
|
|
866
|
-
}
|
|
942
|
+
options = {}
|
|
867
943
|
|
|
868
944
|
if match
|
|
869
945
|
options[:table] = match[:table]
|
|
@@ -873,20 +949,29 @@ module ActiveRecord
|
|
|
873
949
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
|
874
950
|
end
|
|
875
951
|
|
|
952
|
+
options
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
def mismatched_foreign_key(message, sql:, binds:, connection_pool:)
|
|
956
|
+
options = {
|
|
957
|
+
message: message,
|
|
958
|
+
sql: sql,
|
|
959
|
+
binds: binds,
|
|
960
|
+
connection_pool: connection_pool
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if sql
|
|
964
|
+
options.update mismatched_foreign_key_details(message: message, sql: sql)
|
|
965
|
+
else
|
|
966
|
+
options[:query_parser] = ->(sql) { mismatched_foreign_key_details(message: message, sql: sql) }
|
|
967
|
+
end
|
|
968
|
+
|
|
876
969
|
MismatchedForeignKey.new(**options)
|
|
877
970
|
end
|
|
878
971
|
|
|
879
972
|
def version_string(full_version_string)
|
|
880
973
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
|
881
974
|
end
|
|
882
|
-
|
|
883
|
-
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
|
884
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
|
885
|
-
end
|
|
886
|
-
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
|
887
|
-
Type::String.new(true: "1", false: "0", **args)
|
|
888
|
-
end
|
|
889
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
|
890
975
|
end
|
|
891
976
|
end
|
|
892
977
|
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 &&
|