activerecord 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- 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 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- 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 +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- 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 +345 -219
- 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 +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- data/lib/active_record/callbacks.rb +16 -32
- 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 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
- 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 +52 -8
- 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 +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- 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 +29 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -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 +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- 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 +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- 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 +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- 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 +73 -96
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- 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 +87 -34
- 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/disable_joins_association_relation.rb +1 -1
- 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 +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
- 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.rb +1 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +114 -27
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- 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 +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- 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 +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +104 -9
- data/lib/active_record/migration/compatibility.rb +158 -64
- 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 +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +16 -3
- data/lib/active_record/railtie.rb +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- 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 +177 -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 +200 -83
- 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 +31 -3
- 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 +25 -1
- data/lib/active_record/relation/query_methods.rb +429 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- 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/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +9 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +138 -107
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +26 -14
- 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/translation.rb +1 -1
- 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 +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- 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/filter.rb +1 -1
- 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])
|
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)
|
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,17 +485,24 @@ 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: 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
|
-
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
505
|
+
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
|
415
506
|
end
|
416
507
|
end
|
417
508
|
|
@@ -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,27 +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
|
-
|
628
|
-
|
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
|
731
|
+
end
|
629
732
|
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
733
|
+
def handle_warnings(sql)
|
734
|
+
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
735
|
+
|
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)
|
634
743
|
end
|
635
744
|
end
|
636
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
|
+
|
637
755
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
638
756
|
ER_DB_CREATE_EXISTS = 1007
|
639
757
|
ER_FILSORT_ABORT = 1028
|
@@ -651,69 +769,59 @@ module ActiveRecord
|
|
651
769
|
ER_CANNOT_CREATE_TABLE = 1005
|
652
770
|
ER_LOCK_WAIT_TIMEOUT = 1205
|
653
771
|
ER_QUERY_INTERRUPTED = 1317
|
772
|
+
ER_CONNECTION_KILLED = 1927
|
773
|
+
CR_SERVER_GONE_ERROR = 2006
|
774
|
+
CR_SERVER_LOST = 2013
|
654
775
|
ER_QUERY_TIMEOUT = 3024
|
655
776
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
777
|
+
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
656
778
|
|
657
779
|
def translate_exception(exception, message:, sql:, binds:)
|
658
780
|
case error_number(exception)
|
659
781
|
when nil
|
660
782
|
if exception.message.match?(/MySQL client is not connected/i)
|
661
|
-
ConnectionNotEstablished.new(exception)
|
783
|
+
ConnectionNotEstablished.new(exception, connection_pool: @pool)
|
662
784
|
else
|
663
785
|
super
|
664
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)
|
665
789
|
when ER_DB_CREATE_EXISTS
|
666
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
790
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
667
791
|
when ER_DUP_ENTRY
|
668
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
792
|
+
RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
669
793
|
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
|
670
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
794
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
671
795
|
when ER_CANNOT_ADD_FOREIGN, ER_FK_INCOMPATIBLE_COLUMNS
|
672
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
796
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
673
797
|
when ER_CANNOT_CREATE_TABLE
|
674
798
|
if message.include?("errno: 150")
|
675
|
-
mismatched_foreign_key(message, sql: sql, binds: binds)
|
799
|
+
mismatched_foreign_key(message, sql: sql, binds: binds, connection_pool: @pool)
|
676
800
|
else
|
677
801
|
super
|
678
802
|
end
|
679
803
|
when ER_DATA_TOO_LONG
|
680
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
804
|
+
ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
681
805
|
when ER_OUT_OF_RANGE
|
682
|
-
RangeError.new(message, sql: sql, binds: binds)
|
806
|
+
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
683
807
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
684
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
808
|
+
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
685
809
|
when ER_LOCK_DEADLOCK
|
686
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
810
|
+
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
687
811
|
when ER_LOCK_WAIT_TIMEOUT
|
688
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
812
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
689
813
|
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
690
|
-
StatementTimeout.new(message, sql: sql, binds: binds)
|
814
|
+
StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
691
815
|
when ER_QUERY_INTERRUPTED
|
692
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
816
|
+
QueryCanceled.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
693
817
|
else
|
694
818
|
super
|
695
819
|
end
|
696
820
|
end
|
697
821
|
|
698
822
|
def change_column_for_alter(table_name, column_name, type, **options)
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
unless options.key?(:default)
|
703
|
-
options[:default] = column.default
|
704
|
-
end
|
705
|
-
|
706
|
-
unless options.key?(:null)
|
707
|
-
options[:null] = column.null
|
708
|
-
end
|
709
|
-
|
710
|
-
unless options.key?(:comment)
|
711
|
-
options[:comment] = column.comment
|
712
|
-
end
|
713
|
-
|
714
|
-
td = create_table_definition(table_name)
|
715
|
-
cd = td.new_column_definition(column.name, type, **options)
|
716
|
-
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)
|
717
825
|
end
|
718
826
|
|
719
827
|
def rename_column_for_alter(table_name, column_name, new_column_name)
|
@@ -727,7 +835,7 @@ module ActiveRecord
|
|
727
835
|
comment: column.comment
|
728
836
|
}
|
729
837
|
|
730
|
-
current_type =
|
838
|
+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
|
731
839
|
td = create_table_definition(table_name)
|
732
840
|
cd = td.new_column_definition(new_column_name, current_type, **options)
|
733
841
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -764,9 +872,6 @@ module ActiveRecord
|
|
764
872
|
def configure_connection
|
765
873
|
variables = @config.fetch(:variables, {}).stringify_keys
|
766
874
|
|
767
|
-
# By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
|
768
|
-
variables["sql_auto_is_null"] = 0
|
769
|
-
|
770
875
|
# Increase timeout so the server doesn't disconnect us.
|
771
876
|
wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
|
772
877
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
|
@@ -810,7 +915,7 @@ module ActiveRecord
|
|
810
915
|
end.join(", ")
|
811
916
|
|
812
917
|
# ...and send them all in one query
|
813
|
-
|
918
|
+
internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
|
814
919
|
end
|
815
920
|
|
816
921
|
def column_definitions(table_name) # :nodoc:
|
@@ -820,7 +925,7 @@ module ActiveRecord
|
|
820
925
|
end
|
821
926
|
|
822
927
|
def create_table_info(table_name) # :nodoc:
|
823
|
-
|
928
|
+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
|
824
929
|
end
|
825
930
|
|
826
931
|
def arel_visitor
|
@@ -831,18 +936,17 @@ module ActiveRecord
|
|
831
936
|
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
832
937
|
end
|
833
938
|
|
834
|
-
def
|
939
|
+
def mismatched_foreign_key_details(message:, sql:)
|
940
|
+
foreign_key_pat =
|
941
|
+
/Referencing column '(\w+)' and referenced/i =~ message ? $1 : '\w+'
|
942
|
+
|
835
943
|
match = %r/
|
836
944
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
837
|
-
FOREIGN\s+KEY\s*\(`?(?<foreign_key
|
945
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>#{foreign_key_pat})`?\)\s*
|
838
946
|
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
839
947
|
/xmi.match(sql)
|
840
948
|
|
841
|
-
options = {
|
842
|
-
message: message,
|
843
|
-
sql: sql,
|
844
|
-
binds: binds,
|
845
|
-
}
|
949
|
+
options = {}
|
846
950
|
|
847
951
|
if match
|
848
952
|
options[:table] = match[:table]
|
@@ -852,20 +956,29 @@ module ActiveRecord
|
|
852
956
|
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
853
957
|
end
|
854
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
|
+
|
855
976
|
MismatchedForeignKey.new(**options)
|
856
977
|
end
|
857
978
|
|
858
979
|
def version_string(full_version_string)
|
859
980
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
860
981
|
end
|
861
|
-
|
862
|
-
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
863
|
-
Type::ImmutableString.new(true: "1", false: "0", **args)
|
864
|
-
end
|
865
|
-
ActiveRecord::Type.register(:string, adapter: :mysql2) do |_, **args|
|
866
|
-
Type::String.new(true: "1", false: "0", **args)
|
867
|
-
end
|
868
|
-
ActiveRecord::Type.register(:unsigned_integer, Type::UnsignedInteger, adapter: :mysql2)
|
869
982
|
end
|
870
983
|
end
|
871
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 &&
|