activerecord 7.2.4 → 8.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1013 -878
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +69 -27
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +36 -8
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +7 -3
- data/lib/active_record/associations/collection_proxy.rb +26 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency.rb +3 -2
- data/lib/active_record/associations/preloader/association.rb +3 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +18 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +16 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
- data/lib/active_record/connection_adapters/column.rb +14 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +35 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +74 -9
- data/lib/active_record/database_configurations/url_config.rb +15 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +76 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +9 -4
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +31 -29
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +56 -13
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +40 -15
- data/lib/active_record/nested_attributes.rb +14 -12
- data/lib/active_record/persistence.rb +163 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +54 -22
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +60 -43
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +73 -43
- data/lib/active_record/relation/merger.rb +12 -10
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +23 -8
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +200 -96
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +127 -87
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +17 -10
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +44 -16
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +53 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +83 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +18 -4
- data/lib/active_record/test_fixtures.rb +52 -6
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +56 -32
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +85 -50
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +4 -22
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +15 -12
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -28,6 +28,21 @@ module ActiveRecord
|
|
|
28
28
|
# ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false
|
|
29
29
|
class_attribute :emulate_booleans, default: true
|
|
30
30
|
|
|
31
|
+
class_attribute :cli_arg_map, instance_accessor: false, default: {
|
|
32
|
+
host: "--host",
|
|
33
|
+
port: "--port",
|
|
34
|
+
socket: "--socket",
|
|
35
|
+
username: "--user",
|
|
36
|
+
password: "--password",
|
|
37
|
+
encoding: "--default-character-set",
|
|
38
|
+
sslca: "--ssl-ca",
|
|
39
|
+
sslcert: "--ssl-cert",
|
|
40
|
+
sslcapath: "--ssl-capath",
|
|
41
|
+
sslcipher: "--ssl-cipher",
|
|
42
|
+
sslkey: "--ssl-key",
|
|
43
|
+
ssl_mode: "--ssl-mode"
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
31
46
|
NATIVE_DATABASE_TYPES = {
|
|
32
47
|
primary_key: "bigint auto_increment PRIMARY KEY",
|
|
33
48
|
string: { name: "varchar", limit: 255 },
|
|
@@ -42,7 +57,7 @@ module ActiveRecord
|
|
|
42
57
|
date: { name: "date" },
|
|
43
58
|
binary: { name: "blob" },
|
|
44
59
|
blob: { name: "blob" },
|
|
45
|
-
boolean: { name: "
|
|
60
|
+
boolean: { name: "boolean" },
|
|
46
61
|
json: { name: "json" },
|
|
47
62
|
}
|
|
48
63
|
|
|
@@ -54,22 +69,14 @@ module ActiveRecord
|
|
|
54
69
|
end
|
|
55
70
|
|
|
56
71
|
class << self
|
|
72
|
+
def cli_args(config) # :nodoc:
|
|
73
|
+
cli_arg_map.filter_map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }
|
|
74
|
+
end
|
|
75
|
+
|
|
57
76
|
def dbconsole(config, options = {})
|
|
58
77
|
mysql_config = config.configuration_hash
|
|
59
78
|
|
|
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] }
|
|
79
|
+
args = cli_args(mysql_config.except(:password))
|
|
73
80
|
|
|
74
81
|
if mysql_config[:password] && options[:include_password]
|
|
75
82
|
args << "--password=#{mysql_config[:password]}"
|
|
@@ -79,7 +86,11 @@ module ActiveRecord
|
|
|
79
86
|
|
|
80
87
|
args << config.database
|
|
81
88
|
|
|
82
|
-
find_cmd_and_exec([
|
|
89
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def native_database_types # :nodoc:
|
|
93
|
+
NATIVE_DATABASE_TYPES
|
|
83
94
|
end
|
|
84
95
|
end
|
|
85
96
|
|
|
@@ -98,7 +109,7 @@ module ActiveRecord
|
|
|
98
109
|
end
|
|
99
110
|
|
|
100
111
|
def supports_index_sort_order?
|
|
101
|
-
|
|
112
|
+
mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
|
|
102
113
|
end
|
|
103
114
|
|
|
104
115
|
def supports_expression_index?
|
|
@@ -138,7 +149,7 @@ module ActiveRecord
|
|
|
138
149
|
end
|
|
139
150
|
|
|
140
151
|
def supports_datetime_with_precision?
|
|
141
|
-
|
|
152
|
+
true
|
|
142
153
|
end
|
|
143
154
|
|
|
144
155
|
def supports_virtual_columns?
|
|
@@ -178,6 +189,26 @@ module ActiveRecord
|
|
|
178
189
|
supports_insert_returning? ? column.auto_populated? : column.auto_increment?
|
|
179
190
|
end
|
|
180
191
|
|
|
192
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/invisible-indexes.html for more details on MySQL feature.
|
|
193
|
+
# See https://mariadb.com/kb/en/ignored-indexes/ for more details on the MariaDB feature.
|
|
194
|
+
def supports_disabling_indexes?
|
|
195
|
+
if mariadb?
|
|
196
|
+
database_version >= "10.6.0"
|
|
197
|
+
else
|
|
198
|
+
database_version >= "8.0.0"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def create_or_find_by_lock # :nodoc:
|
|
203
|
+
# A shared lock provides a current read under REPEATABLE READ without
|
|
204
|
+
# upgrading the shared record lock acquired by a duplicate INSERT.
|
|
205
|
+
if mariadb? || database_version < "8.0.1"
|
|
206
|
+
"LOCK IN SHARE MODE"
|
|
207
|
+
else
|
|
208
|
+
"FOR SHARE"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
181
212
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
|
182
213
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
|
183
214
|
end
|
|
@@ -186,10 +217,6 @@ module ActiveRecord
|
|
|
186
217
|
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
|
187
218
|
end
|
|
188
219
|
|
|
189
|
-
def native_database_types
|
|
190
|
-
NATIVE_DATABASE_TYPES
|
|
191
|
-
end
|
|
192
|
-
|
|
193
220
|
def index_algorithms
|
|
194
221
|
{
|
|
195
222
|
default: "ALGORITHM = DEFAULT",
|
|
@@ -199,14 +226,6 @@ module ActiveRecord
|
|
|
199
226
|
}
|
|
200
227
|
end
|
|
201
228
|
|
|
202
|
-
# HELPER METHODS ===========================================
|
|
203
|
-
|
|
204
|
-
# The two drivers have slightly different ways of yielding hashes of results, so
|
|
205
|
-
# this method must be implemented to provide a uniform interface.
|
|
206
|
-
def each_hash(result) # :nodoc:
|
|
207
|
-
raise NotImplementedError
|
|
208
|
-
end
|
|
209
|
-
|
|
210
229
|
# Must return the MySQL error number from the exception, if the exception has an
|
|
211
230
|
# error number.
|
|
212
231
|
def error_number(exception) # :nodoc:
|
|
@@ -230,24 +249,31 @@ module ActiveRecord
|
|
|
230
249
|
# DATABASE STATEMENTS ======================================
|
|
231
250
|
#++
|
|
232
251
|
|
|
233
|
-
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
|
234
|
-
# to write stuff in an abstract way without concerning ourselves about whether it
|
|
235
|
-
# needs to be explicitly freed or not.
|
|
236
|
-
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
|
237
|
-
sql = transform_query(sql)
|
|
238
|
-
check_if_write_query(sql)
|
|
239
|
-
|
|
240
|
-
mark_transaction_written_if_write(sql)
|
|
241
|
-
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
252
|
def begin_db_transaction # :nodoc:
|
|
245
253
|
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
246
254
|
end
|
|
247
255
|
|
|
248
256
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
|
249
|
-
|
|
250
|
-
|
|
257
|
+
# From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
|
|
258
|
+
# So we don't need to implement #reset_isolation_level
|
|
259
|
+
if multi_statements_enabled?
|
|
260
|
+
execute_batch(
|
|
261
|
+
["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
|
|
262
|
+
"TRANSACTION",
|
|
263
|
+
allow_retry: true,
|
|
264
|
+
materialize_transactions: false,
|
|
265
|
+
)
|
|
266
|
+
else
|
|
267
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do
|
|
268
|
+
internal_execute(
|
|
269
|
+
"SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}",
|
|
270
|
+
"TRANSACTION",
|
|
271
|
+
allow_retry: false,
|
|
272
|
+
materialize_transactions: false,
|
|
273
|
+
)
|
|
274
|
+
internal_execute("BEGIN", "TRANSACTION", allow_retry: false, materialize_transactions: false)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
251
277
|
end
|
|
252
278
|
|
|
253
279
|
def commit_db_transaction # :nodoc:
|
|
@@ -347,7 +373,7 @@ module ActiveRecord
|
|
|
347
373
|
rename_table_indexes(table_name, new_name, **options)
|
|
348
374
|
end
|
|
349
375
|
|
|
350
|
-
# Drops a table from the database.
|
|
376
|
+
# Drops a table or tables from the database.
|
|
351
377
|
#
|
|
352
378
|
# [<tt>:force</tt>]
|
|
353
379
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
@@ -361,10 +387,10 @@ module ActiveRecord
|
|
|
361
387
|
#
|
|
362
388
|
# Although this command ignores most +options+ and the block if one is given,
|
|
363
389
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
|
364
|
-
# In that case, +options+ and the block will be used by create_table.
|
|
365
|
-
def drop_table(
|
|
366
|
-
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
367
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
|
390
|
+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
|
|
391
|
+
def drop_table(*table_names, **options)
|
|
392
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
|
393
|
+
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
|
|
368
394
|
end
|
|
369
395
|
|
|
370
396
|
def rename_index(table_name, old_name, new_name)
|
|
@@ -468,6 +494,24 @@ module ActiveRecord
|
|
|
468
494
|
CreateIndexDefinition.new(index, algorithm)
|
|
469
495
|
end
|
|
470
496
|
|
|
497
|
+
def enable_index(table_name, index_name) # :nodoc:
|
|
498
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
|
499
|
+
|
|
500
|
+
query = <<~SQL
|
|
501
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{quote_column_name(index_name)} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
|
|
502
|
+
SQL
|
|
503
|
+
execute(query)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def disable_index(table_name, index_name) # :nodoc:
|
|
507
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
|
508
|
+
|
|
509
|
+
query = <<~SQL
|
|
510
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{quote_column_name(index_name)} #{mariadb? ? "IGNORED" : "INVISIBLE"}
|
|
511
|
+
SQL
|
|
512
|
+
execute(query)
|
|
513
|
+
end
|
|
514
|
+
|
|
471
515
|
def add_sql_comment!(sql, comment) # :nodoc:
|
|
472
516
|
sql << " COMMENT #{quote(comment)}" if comment.present?
|
|
473
517
|
sql
|
|
@@ -588,7 +632,7 @@ module ActiveRecord
|
|
|
588
632
|
|
|
589
633
|
# SHOW VARIABLES LIKE 'name'
|
|
590
634
|
def show_variable(name)
|
|
591
|
-
query_value("SELECT @@#{name}", "SCHEMA")
|
|
635
|
+
query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
|
|
592
636
|
rescue ActiveRecord::StatementInvalid
|
|
593
637
|
nil
|
|
594
638
|
end
|
|
@@ -693,8 +737,8 @@ module ActiveRecord
|
|
|
693
737
|
end
|
|
694
738
|
|
|
695
739
|
def check_version # :nodoc:
|
|
696
|
-
if database_version < "5.
|
|
697
|
-
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.
|
|
740
|
+
if database_version < "5.6.4"
|
|
741
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
|
|
698
742
|
end
|
|
699
743
|
end
|
|
700
744
|
|
|
@@ -743,12 +787,12 @@ module ActiveRecord
|
|
|
743
787
|
m.alias_type %r(bit)i, "binary"
|
|
744
788
|
end
|
|
745
789
|
|
|
746
|
-
def register_integer_type(mapping, key,
|
|
790
|
+
def register_integer_type(mapping, key, limit:)
|
|
747
791
|
mapping.register_type(key) do |sql_type|
|
|
748
792
|
if /\bunsigned\b/.match?(sql_type)
|
|
749
|
-
Type::UnsignedInteger.new(
|
|
793
|
+
Type::UnsignedInteger.new(limit: limit)
|
|
750
794
|
else
|
|
751
|
-
Type::Integer.new(
|
|
795
|
+
Type::Integer.new(limit: limit)
|
|
752
796
|
end
|
|
753
797
|
end
|
|
754
798
|
end
|
|
@@ -778,14 +822,13 @@ module ActiveRecord
|
|
|
778
822
|
end
|
|
779
823
|
end
|
|
780
824
|
|
|
781
|
-
def handle_warnings(sql)
|
|
825
|
+
def handle_warnings(_initial_result, sql)
|
|
782
826
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
|
783
827
|
|
|
784
|
-
@affected_rows_before_warnings = @raw_connection.affected_rows
|
|
785
828
|
warning_count = @raw_connection.warning_count
|
|
786
829
|
result = @raw_connection.query("SHOW WARNINGS")
|
|
787
830
|
result = [
|
|
788
|
-
["Warning", nil, "Query had warning_count=#{warning_count} but
|
|
831
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but `SHOW WARNINGS` did not return the warnings. Check MySQL logs or database configuration."],
|
|
789
832
|
] if result.count == 0
|
|
790
833
|
result.each do |level, code, message|
|
|
791
834
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
|
@@ -799,14 +842,10 @@ module ActiveRecord
|
|
|
799
842
|
warning.level == "Note" || super
|
|
800
843
|
end
|
|
801
844
|
|
|
802
|
-
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
803
|
-
# made since we established the connection
|
|
804
|
-
def sync_timezone_changes(raw_connection)
|
|
805
|
-
end
|
|
806
|
-
|
|
807
845
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
|
808
846
|
ER_DB_CREATE_EXISTS = 1007
|
|
809
847
|
ER_FILSORT_ABORT = 1028
|
|
848
|
+
ER_NO_DB_ERROR = 1046
|
|
810
849
|
ER_DUP_ENTRY = 1062
|
|
811
850
|
ER_SERVER_SHUTDOWN = 1053
|
|
812
851
|
ER_NOT_NULL_VIOLATION = 1048
|
|
@@ -827,6 +866,8 @@ module ActiveRecord
|
|
|
827
866
|
CR_SERVER_LOST = 2013
|
|
828
867
|
ER_QUERY_TIMEOUT = 3024
|
|
829
868
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
|
869
|
+
ER_CHECK_CONSTRAINT_VIOLATED = 3819
|
|
870
|
+
ER_CONSTRAINT_FAILED = 4025
|
|
830
871
|
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
|
831
872
|
|
|
832
873
|
def translate_exception(exception, message:, sql:, binds:)
|
|
@@ -837,7 +878,7 @@ module ActiveRecord
|
|
|
837
878
|
else
|
|
838
879
|
super
|
|
839
880
|
end
|
|
840
|
-
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
|
881
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT, ER_NO_DB_ERROR
|
|
841
882
|
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
842
883
|
when ER_DB_CREATE_EXISTS
|
|
843
884
|
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
@@ -859,6 +900,8 @@ module ActiveRecord
|
|
|
859
900
|
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
860
901
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
|
861
902
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
903
|
+
when ER_CHECK_CONSTRAINT_VIOLATED, ER_CONSTRAINT_FAILED
|
|
904
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
862
905
|
when ER_LOCK_DEADLOCK
|
|
863
906
|
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
864
907
|
when ER_LOCK_WAIT_TIMEOUT
|
|
@@ -973,12 +1016,36 @@ module ActiveRecord
|
|
|
973
1016
|
end.join(", ")
|
|
974
1017
|
|
|
975
1018
|
# ...and send them all in one query
|
|
976
|
-
|
|
1019
|
+
raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
|
977
1020
|
end
|
|
978
1021
|
|
|
979
1022
|
def column_definitions(table_name) # :nodoc:
|
|
980
|
-
|
|
981
|
-
|
|
1023
|
+
fields = internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA", allow_retry: true)
|
|
1024
|
+
|
|
1025
|
+
update_fields_for_mariadb(table_name, fields) if mariadb?
|
|
1026
|
+
|
|
1027
|
+
fields
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
def update_fields_for_mariadb(table_name, fields)
|
|
1031
|
+
has_function_default_candidate = fields.any? do |field|
|
|
1032
|
+
default = field["Default"]
|
|
1033
|
+
default&.match?(/[a-zA-Z_]\w*\(/) && !/\ACURRENT_TIMESTAMP/i.match?(default)
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
if has_function_default_candidate
|
|
1037
|
+
table_info = create_table_info(table_name)
|
|
1038
|
+
fields.each do |field|
|
|
1039
|
+
default = field["Default"]
|
|
1040
|
+
next unless default&.match?(/[a-zA-Z_]\w*\(/)
|
|
1041
|
+
next if /\ACURRENT_TIMESTAMP/i.match?(default)
|
|
1042
|
+
|
|
1043
|
+
field_name = field["Field"]
|
|
1044
|
+
match = table_info&.match(/`#{field_name}` .+ DEFAULT ('|\d+|[A-z]+)/)
|
|
1045
|
+
if match && match[1].match?(/\A[A-z]/)
|
|
1046
|
+
field["Extra"] = "DEFAULT_GENERATED"
|
|
1047
|
+
end
|
|
1048
|
+
end
|
|
982
1049
|
end
|
|
983
1050
|
end
|
|
984
1051
|
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
class Column
|
|
8
8
|
include Deduplicable
|
|
9
9
|
|
|
10
|
-
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
|
|
10
|
+
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment, :cast_type
|
|
11
11
|
|
|
12
12
|
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
|
|
13
13
|
|
|
@@ -17,8 +17,9 @@ module ActiveRecord
|
|
|
17
17
|
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
|
18
18
|
# +sql_type_metadata+ is various information about the type of the column
|
|
19
19
|
# +null+ determines if this column allows +NULL+ values.
|
|
20
|
-
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
|
|
20
|
+
def initialize(name, cast_type, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
|
|
21
21
|
@name = name.freeze
|
|
22
|
+
@cast_type = cast_type
|
|
22
23
|
@sql_type_metadata = sql_type_metadata
|
|
23
24
|
@null = null
|
|
24
25
|
@default = default
|
|
@@ -27,6 +28,11 @@ module ActiveRecord
|
|
|
27
28
|
@comment = comment
|
|
28
29
|
end
|
|
29
30
|
|
|
31
|
+
def fetch_cast_type(connection) # :nodoc:
|
|
32
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
33
|
+
@cast_type || connection.lookup_cast_type(sql_type)
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def has_default?
|
|
31
37
|
!default.nil? || default_function
|
|
32
38
|
end
|
|
@@ -45,6 +51,7 @@ module ActiveRecord
|
|
|
45
51
|
|
|
46
52
|
def init_with(coder)
|
|
47
53
|
@name = coder["name"]
|
|
54
|
+
@cast_type = coder["cast_type"]
|
|
48
55
|
@sql_type_metadata = coder["sql_type_metadata"]
|
|
49
56
|
@null = coder["null"]
|
|
50
57
|
@default = coder["default"]
|
|
@@ -55,6 +62,7 @@ module ActiveRecord
|
|
|
55
62
|
|
|
56
63
|
def encode_with(coder)
|
|
57
64
|
coder["name"] = @name
|
|
65
|
+
coder["cast_type"] = @cast_type
|
|
58
66
|
coder["sql_type_metadata"] = @sql_type_metadata
|
|
59
67
|
coder["null"] = @null
|
|
60
68
|
coder["default"] = @default
|
|
@@ -75,6 +83,7 @@ module ActiveRecord
|
|
|
75
83
|
def ==(other)
|
|
76
84
|
other.is_a?(Column) &&
|
|
77
85
|
name == other.name &&
|
|
86
|
+
cast_type == other.cast_type &&
|
|
78
87
|
default == other.default &&
|
|
79
88
|
sql_type_metadata == other.sql_type_metadata &&
|
|
80
89
|
null == other.null &&
|
|
@@ -88,6 +97,7 @@ module ActiveRecord
|
|
|
88
97
|
Column.hash ^
|
|
89
98
|
name.hash ^
|
|
90
99
|
name.encoding.hash ^
|
|
100
|
+
cast_type.hash ^
|
|
91
101
|
default.hash ^
|
|
92
102
|
sql_type_metadata.hash ^
|
|
93
103
|
null.hash ^
|
|
@@ -104,7 +114,7 @@ module ActiveRecord
|
|
|
104
114
|
def deduplicated
|
|
105
115
|
@name = -name
|
|
106
116
|
@sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
|
|
107
|
-
@default = -default if default
|
|
117
|
+
@default = -default if String === default
|
|
108
118
|
@default_function = -default_function if default_function
|
|
109
119
|
@collation = -collation if collation
|
|
110
120
|
@comment = -comment if comment
|
|
@@ -114,7 +124,7 @@ module ActiveRecord
|
|
|
114
124
|
|
|
115
125
|
class NullColumn < Column
|
|
116
126
|
def initialize(name, **)
|
|
117
|
-
super(name, nil)
|
|
127
|
+
super(name, nil, nil)
|
|
118
128
|
end
|
|
119
129
|
end
|
|
120
130
|
end
|
|
@@ -45,16 +45,16 @@ module ActiveRecord
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def default_insert_value(column) # :nodoc:
|
|
49
|
+
super unless column.auto_increment?
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
private
|
|
49
53
|
# https://mariadb.com/kb/en/analyze-statement/
|
|
50
54
|
def analyze_without_explain?
|
|
51
55
|
mariadb? && database_version >= "10.1.0"
|
|
52
56
|
end
|
|
53
57
|
|
|
54
|
-
def default_insert_value(column)
|
|
55
|
-
super unless column.auto_increment?
|
|
56
|
-
end
|
|
57
|
-
|
|
58
58
|
def returning_column_values(result)
|
|
59
59
|
if supports_insert_returning?
|
|
60
60
|
result.rows.first
|
|
@@ -49,6 +49,8 @@ module ActiveRecord
|
|
|
49
49
|
sql << "USING #{o.using}" if o.using
|
|
50
50
|
sql << "ON #{quote_table_name(o.table)}" if create
|
|
51
51
|
sql << "(#{quoted_columns(o)})"
|
|
52
|
+
sql << "INVISIBLE" if o.disabled? && !mariadb?
|
|
53
|
+
sql << "IGNORED" if o.disabled? && mariadb?
|
|
52
54
|
|
|
53
55
|
add_sql_comment!(sql.join(" "), o.comment)
|
|
54
56
|
end
|
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
|
5
5
|
module MySQL
|
|
6
6
|
module ColumnMethods
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
|
+
extend ConnectionAdapters::ColumnMethods::ClassMethods
|
|
8
9
|
|
|
9
10
|
##
|
|
10
11
|
# :method: blob
|
|
@@ -42,18 +43,26 @@ module ActiveRecord
|
|
|
42
43
|
# :method: unsigned_bigint
|
|
43
44
|
# :call-seq: unsigned_bigint(*names, **options)
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
|
|
47
|
+
:tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint
|
|
48
|
+
end
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
# = Active Record MySQL Adapter \Index Definition
|
|
51
|
+
class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition # :nodoc:
|
|
52
|
+
attr_accessor :enabled
|
|
53
|
+
|
|
54
|
+
def initialize(*args, **kwargs)
|
|
55
|
+
@enabled = kwargs.key?(:enabled) ? kwargs.delete(:enabled) : true
|
|
56
|
+
super
|
|
57
|
+
end
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, enabled: nil, **options)
|
|
60
|
+
super(columns, name:, unique:, valid:, include:, nulls_not_distinct:, **options) &&
|
|
61
|
+
(enabled.nil? || self.enabled == enabled)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def disabled?
|
|
65
|
+
!@enabled
|
|
57
66
|
end
|
|
58
67
|
end
|
|
59
68
|
|
|
@@ -106,6 +115,28 @@ module ActiveRecord
|
|
|
106
115
|
# = Active Record MySQL Adapter \Table
|
|
107
116
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
|
108
117
|
include ColumnMethods
|
|
118
|
+
|
|
119
|
+
# Enables an index to be used by query optimizers.
|
|
120
|
+
#
|
|
121
|
+
# t.enable_index(:email)
|
|
122
|
+
#
|
|
123
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
|
124
|
+
#
|
|
125
|
+
# See {connection.enable_index}[rdoc-ref:SchemaStatements#enable_index]
|
|
126
|
+
def enable_index(index_name)
|
|
127
|
+
@base.enable_index(name, index_name)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Disables an index not to be used by query optimizers.
|
|
131
|
+
#
|
|
132
|
+
# t.disable_index(:email)
|
|
133
|
+
#
|
|
134
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
|
135
|
+
#
|
|
136
|
+
# See {connection.disable_index}[rdoc-ref:SchemaStatements#disable_index]
|
|
137
|
+
def disable_index(index_name)
|
|
138
|
+
@base.disable_index(name, index_name)
|
|
139
|
+
end
|
|
109
140
|
end
|
|
110
141
|
end
|
|
111
142
|
end
|