activerecord 7.0.6 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1424 -1390
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -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 +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 +16 -10
- data/lib/active_record/associations/collection_proxy.rb +20 -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 -7
- 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 +13 -10
- data/lib/active_record/associations/singular_association.rb +6 -8
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +295 -199
- 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 +40 -26
- 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 +60 -18
- 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 +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -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 +289 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +496 -102
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +214 -113
- 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 +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +21 -14
- 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 +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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
- 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 +15 -8
- 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 +349 -55
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +338 -176
- 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 +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +210 -83
- 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 +71 -94
- data/lib/active_record/core.rb +136 -148
- 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 +8 -3
- 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 +66 -54
- 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 +113 -26
- data/lib/active_record/errors.rb +108 -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 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- 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 +5 -7
- 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 +142 -58
- 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 +265 -112
- data/lib/active_record/model_schema.rb +60 -40
- data/lib/active_record/nested_attributes.rb +21 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +187 -35
- 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 -8
- 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 +162 -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 +160 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- 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 +27 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +378 -70
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- 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 +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 +11 -2
- 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 +39 -13
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +4 -0
- 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/and.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 +0 -8
- 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 +50 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -3,6 +3,7 @@
|
|
3
3
|
require "active_record/connection_adapters/abstract_adapter"
|
4
4
|
require "active_record/connection_adapters/statement_pool"
|
5
5
|
require "active_record/connection_adapters/mysql/column"
|
6
|
+
require "active_record/connection_adapters/mysql/database_statements"
|
6
7
|
require "active_record/connection_adapters/mysql/explain_pretty_printer"
|
7
8
|
require "active_record/connection_adapters/mysql/quoting"
|
8
9
|
require "active_record/connection_adapters/mysql/schema_creation"
|
@@ -14,6 +15,7 @@ require "active_record/connection_adapters/mysql/type_metadata"
|
|
14
15
|
module ActiveRecord
|
15
16
|
module ConnectionAdapters
|
16
17
|
class AbstractMysqlAdapter < AbstractAdapter
|
18
|
+
include MySQL::DatabaseStatements
|
17
19
|
include MySQL::Quoting
|
18
20
|
include MySQL::SchemaStatements
|
19
21
|
|
@@ -51,8 +53,34 @@ module ActiveRecord
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
+
class << self
|
57
|
+
def dbconsole(config, options = {})
|
58
|
+
mysql_config = config.configuration_hash
|
59
|
+
|
60
|
+
args = {
|
61
|
+
host: "--host",
|
62
|
+
port: "--port",
|
63
|
+
socket: "--socket",
|
64
|
+
username: "--user",
|
65
|
+
encoding: "--default-character-set",
|
66
|
+
sslca: "--ssl-ca",
|
67
|
+
sslcert: "--ssl-cert",
|
68
|
+
sslcapath: "--ssl-capath",
|
69
|
+
sslcipher: "--ssl-cipher",
|
70
|
+
sslkey: "--ssl-key",
|
71
|
+
ssl_mode: "--ssl-mode"
|
72
|
+
}.filter_map { |opt, arg| "#{arg}=#{mysql_config[opt]}" if mysql_config[opt] }
|
73
|
+
|
74
|
+
if mysql_config[:password] && options[:include_password]
|
75
|
+
args << "--password=#{mysql_config[:password]}"
|
76
|
+
elsif mysql_config[:password] && !mysql_config[:password].to_s.empty?
|
77
|
+
args << "-p"
|
78
|
+
end
|
79
|
+
|
80
|
+
args << config.database
|
81
|
+
|
82
|
+
find_cmd_and_exec(["mysql", "mysql5"], *args)
|
83
|
+
end
|
56
84
|
end
|
57
85
|
|
58
86
|
def get_database_version # :nodoc:
|
@@ -81,6 +109,10 @@ module ActiveRecord
|
|
81
109
|
true
|
82
110
|
end
|
83
111
|
|
112
|
+
def supports_restart_db_transaction?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
84
116
|
def supports_explain?
|
85
117
|
true
|
86
118
|
end
|
@@ -95,7 +127,7 @@ module ActiveRecord
|
|
95
127
|
|
96
128
|
def supports_check_constraints?
|
97
129
|
if mariadb?
|
98
|
-
database_version >= "10.2.
|
130
|
+
database_version >= "10.3.10" || (database_version < "10.3" && database_version >= "10.2.22")
|
99
131
|
else
|
100
132
|
database_version >= "8.0.16"
|
101
133
|
end
|
@@ -138,11 +170,6 @@ module ActiveRecord
|
|
138
170
|
true
|
139
171
|
end
|
140
172
|
|
141
|
-
def field_ordered_value(column, values) # :nodoc:
|
142
|
-
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse.map { |value| Arel::Nodes.build_quoted(value) }])
|
143
|
-
Arel::Nodes::Descending.new(field)
|
144
|
-
end
|
145
|
-
|
146
173
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
147
174
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
148
175
|
end
|
@@ -195,33 +222,36 @@ module ActiveRecord
|
|
195
222
|
# DATABASE STATEMENTS ======================================
|
196
223
|
#++
|
197
224
|
|
198
|
-
# Executes the SQL statement in the context of this connection.
|
199
|
-
def execute(sql, name = nil, async: false)
|
200
|
-
raw_execute(sql, name, async: async)
|
201
|
-
end
|
202
|
-
|
203
225
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
204
226
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
205
227
|
# needs to be explicitly freed or not.
|
206
228
|
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
207
|
-
|
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)
|
208
234
|
end
|
209
235
|
|
210
236
|
def begin_db_transaction # :nodoc:
|
211
|
-
|
237
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
212
238
|
end
|
213
239
|
|
214
240
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
215
|
-
|
241
|
+
internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
216
242
|
begin_db_transaction
|
217
243
|
end
|
218
244
|
|
219
245
|
def commit_db_transaction # :nodoc:
|
220
|
-
|
246
|
+
internal_execute("COMMIT", "TRANSACTION", allow_retry: false, materialize_transactions: true)
|
221
247
|
end
|
222
248
|
|
223
249
|
def exec_rollback_db_transaction # :nodoc:
|
224
|
-
|
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)
|
225
255
|
end
|
226
256
|
|
227
257
|
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
@@ -301,7 +331,8 @@ module ActiveRecord
|
|
301
331
|
#
|
302
332
|
# Example:
|
303
333
|
# rename_table('octopuses', 'octopi')
|
304
|
-
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]
|
305
336
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
306
337
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
307
338
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
@@ -339,11 +370,20 @@ module ActiveRecord
|
|
339
370
|
end
|
340
371
|
|
341
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
|
+
|
342
380
|
default = extract_new_default_value(default_or_changes)
|
343
|
-
|
381
|
+
ChangeColumnDefaultDefinition.new(column, default)
|
344
382
|
end
|
345
383
|
|
346
384
|
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
385
|
+
validate_change_column_null_argument!(null)
|
386
|
+
|
347
387
|
unless null || default.nil?
|
348
388
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
349
389
|
end
|
@@ -360,18 +400,60 @@ module ActiveRecord
|
|
360
400
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
361
401
|
end
|
362
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
|
+
|
363
439
|
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
364
440
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
365
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
366
442
|
end
|
367
443
|
|
368
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:
|
369
452
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
370
453
|
|
371
454
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
372
455
|
|
373
|
-
|
374
|
-
execute schema_creation.accept(create_index)
|
456
|
+
CreateIndexDefinition.new(index, algorithm)
|
375
457
|
end
|
376
458
|
|
377
459
|
def add_sql_comment!(sql, comment) # :nodoc:
|
@@ -384,11 +466,13 @@ module ActiveRecord
|
|
384
466
|
|
385
467
|
scope = quoted_scope(table_name)
|
386
468
|
|
387
|
-
|
469
|
+
# MySQL returns 1 row for each column of composite foreign keys.
|
470
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
388
471
|
SELECT fk.referenced_table_name AS 'to_table',
|
389
472
|
fk.referenced_column_name AS 'primary_key',
|
390
473
|
fk.column_name AS 'column',
|
391
474
|
fk.constraint_name AS 'name',
|
475
|
+
fk.ordinal_position AS 'position',
|
392
476
|
rc.update_rule AS 'on_update',
|
393
477
|
rc.delete_rule AS 'on_delete'
|
394
478
|
FROM information_schema.referential_constraints rc
|
@@ -401,15 +485,22 @@ module ActiveRecord
|
|
401
485
|
AND rc.table_name = #{scope[:name]}
|
402
486
|
SQL
|
403
487
|
|
404
|
-
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
|
405
491
|
options = {
|
406
|
-
column: unquote_identifier(row["column"]),
|
407
492
|
name: row["name"],
|
408
|
-
|
493
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
494
|
+
on_delete: extract_foreign_key_action(row["on_delete"])
|
409
495
|
}
|
410
496
|
|
411
|
-
|
412
|
-
|
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
|
413
504
|
|
414
505
|
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
415
506
|
end
|
@@ -431,14 +522,22 @@ module ActiveRecord
|
|
431
522
|
SQL
|
432
523
|
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
433
524
|
|
434
|
-
chk_info =
|
525
|
+
chk_info = internal_exec_query(sql, "SCHEMA")
|
435
526
|
|
436
527
|
chk_info.map do |row|
|
437
528
|
options = {
|
438
529
|
name: row["name"]
|
439
530
|
}
|
440
531
|
expression = row["expression"]
|
441
|
-
expression = expression[1..-2]
|
532
|
+
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
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
|
+
|
442
541
|
CheckConstraintDefinition.new(table_name, expression, options)
|
443
542
|
end
|
444
543
|
else
|
@@ -561,15 +660,18 @@ module ActiveRecord
|
|
561
660
|
end
|
562
661
|
|
563
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
|
+
|
564
671
|
private
|
565
672
|
def initialize_type_map(m)
|
566
673
|
super
|
567
674
|
|
568
|
-
m.register_type(%r(char)i) do |sql_type|
|
569
|
-
limit = extract_limit(sql_type)
|
570
|
-
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
571
|
-
end
|
572
|
-
|
573
675
|
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
574
676
|
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
575
677
|
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
@@ -589,9 +691,6 @@ module ActiveRecord
|
|
589
691
|
|
590
692
|
m.alias_type %r(year)i, "integer"
|
591
693
|
m.alias_type %r(bit)i, "binary"
|
592
|
-
|
593
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
594
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
595
694
|
end
|
596
695
|
|
597
696
|
def register_integer_type(mapping, key, **options)
|
@@ -613,31 +712,46 @@ module ActiveRecord
|
|
613
712
|
end
|
614
713
|
end
|
615
714
|
|
616
|
-
|
617
|
-
|
618
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
619
|
-
end
|
715
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
716
|
+
EMULATE_BOOLEANS_TRUE = { emulate_booleans: true }.freeze
|
620
717
|
|
621
718
|
private
|
622
|
-
def
|
623
|
-
|
719
|
+
def strip_whitespace_characters(expression)
|
720
|
+
expression = expression.gsub(/\\n|\\\\/, "")
|
721
|
+
expression = expression.gsub(/\s{2,}/, " ")
|
722
|
+
expression
|
624
723
|
end
|
625
724
|
|
626
|
-
def
|
627
|
-
|
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
|
628
731
|
end
|
629
732
|
|
630
|
-
def
|
631
|
-
|
632
|
-
mark_transaction_written_if_write(sql)
|
733
|
+
def handle_warnings(sql)
|
734
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
633
735
|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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)
|
741
|
+
|
742
|
+
ActiveRecord.db_warnings_action.call(warning)
|
638
743
|
end
|
639
744
|
end
|
640
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
|
+
|
641
755
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
642
756
|
ER_DB_CREATE_EXISTS = 1007
|
643
757
|
ER_FILSORT_ABORT = 1028
|
@@ -655,77 +769,59 @@ module ActiveRecord
|
|
655
769
|
ER_CANNOT_CREATE_TABLE = 1005
|
656
770
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
657
771
|
ER_QUERY_INTERRUPTED = 1317
|
772
|
+
ER_CONNECTION_KILLED = 1927
|
773
|
+
CR_SERVER_GONE_ERROR = 2006
|
774
|
+
CR_SERVER_LOST = 2013
|
658
775
|
ER_QUERY_TIMEOUT = 3024
|
659
776
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
777
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
660
778
|
|
661
779
|
def translate_exception(exception, message:, sql:, binds:)
|
662
780
|
case error_number(exception)
|
663
781
|
when nil
|
664
782
|
if exception.message.match?(/MySQL client is not connected/i)
|
665
|
-
ConnectionNotEstablished.new(exception)
|
783
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
666
784
|
else
|
667
785
|
super
|
668
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)
|
669
789
|
when ER_DB_CREATE_EXISTS
|
670
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
790
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
671
791
|
when ER_DUP_ENTRY
|
672
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
792
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
673
793
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
674
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
794
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
675
795
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
676
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
796
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
677
797
|
when ER_CANNOT_CREATE_TABLE
|
678
798
|
if message.include?("errno: 150")
|
679
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
799
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
680
800
|
else
|
681
801
|
super
|
682
802
|
end
|
683
803
|
when ER_DATA_TOO_LONG
|
684
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
804
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
685
805
|
when ER_OUT_OF_RANGE
|
686
|
-
RangeError.new(message, sql: sql, binds: binds)
|
806
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
687
807
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
688
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
808
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
689
809
|
when ER_LOCK_DEADLOCK
|
690
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
810
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
691
811
|
when ER_LOCK_WAIT_TIMEOUT
|
692
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
812
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
693
813
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
694
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
814
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
695
815
|
when ER_QUERY_INTERRUPTED
|
696
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
816
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
697
817
|
else
|
698
818
|
super
|
699
819
|
end
|
700
820
|
end
|
701
821
|
|
702
822
|
def change_column_for_alter(table_name, column_name, type, **options)
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
unless options.key?(:default)
|
707
|
-
options[:default] = column.default
|
708
|
-
end
|
709
|
-
|
710
|
-
unless options.key?(:null)
|
711
|
-
options[:null] = column.null
|
712
|
-
end
|
713
|
-
|
714
|
-
unless options.key?(:comment)
|
715
|
-
options[:comment] = column.comment
|
716
|
-
end
|
717
|
-
|
718
|
-
unless options.key?(:collation)
|
719
|
-
options[:collation] = column.collation if text_type?(type)
|
720
|
-
end
|
721
|
-
|
722
|
-
unless options.key?(:auto_increment)
|
723
|
-
options[:auto_increment] = column.auto_increment?
|
724
|
-
end
|
725
|
-
|
726
|
-
td = create_table_definition(table_name)
|
727
|
-
cd = td.new_column_definition(column.name, type, **options)
|
728
|
-
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)
|
729
825
|
end
|
730
826
|
|
731
827
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -739,7 +835,7 @@ module ActiveRecord
|
|
739
835
|
comment: column.comment
|
740
836
|
}
|
741
837
|
|
742
|
-
current_type =
|
838
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
743
839
|
td = create_table_definition(table_name)
|
744
840
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
745
841
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -776,9 +872,6 @@ module ActiveRecord
|
|
776
872
|
def configure_connection
|
777
873
|
variables = @config.fetch(:variables, {}).stringify_keys
|
778
874
|
|
779
|
-
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
780
|
-
variables["sql_auto_is_null"] = 0
|
781
|
-
|
782
875
|
# Increase timeout so the server doesn't disconnect us.
|
783
876
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
784
877
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -822,7 +915,7 @@ module ActiveRecord
|
|
822
915
|
end.join(", ")
|
823
916
|
|
824
917
|
# ...and send them all in one query
|
825
|
-
|
918
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
826
919
|
end
|
827
920
|
|
828
921
|
def column_definitions(table_name) # :nodoc:
|
@@ -832,7 +925,7 @@ module ActiveRecord
|
|
832
925
|
end
|
833
926
|
|
834
927
|
def create_table_info(table_name) # :nodoc:
|
835
|
-
|
928
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
836
929
|
end
|
837
930
|
|
838
931
|
def arel_visitor
|
@@ -843,18 +936,17 @@ module ActiveRecord
|
|
843
936
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
844
937
|
end
|
845
938
|
|
846
|
-
def
|
939
|
+
def mismatched_foreign_key_details(message:, sql:)
|
940
|
+
foreign_key_pat =
|
941
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
942
|
+
|
847
943
|
match = %r/
|
848
944
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
849
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
945
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
850
946
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
851
947
|
/xmi.match(sql)
|
852
948
|
|
853
|
-
options = {
|
854
|
-
message: message,
|
855
|
-
sql: sql,
|
856
|
-
binds: binds,
|
857
|
-
}
|
949
|
+
options = {}
|
858
950
|
|
859
951
|
if match
|
860
952
|
options[:table] = match[:table]
|
@@ -864,20 +956,29 @@ module ActiveRecord
|
|
864
956
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
865
957
|
end
|
866
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
|
+
|
867
976
|
MismatchedForeignKey.new(**options)
|
868
977
|
end
|
869
978
|
|
870
979
|
def version_string(full_version_string)
|
871
980
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
872
981
|
end
|
873
|
-
|
874
|
-
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
875
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
876
|
-
end
|
877
|
-
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
878
|
-
Type::String.new(true: "1", false: "0", **args)
|
879
|
-
end
|
880
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
881
982
|
end
|
882
983
|
end
|
883
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 &&
|