activerecord 7.2.3 → 8.1.3
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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- 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 +1 -1
- data/lib/active_record/associations/collection_proxy.rb +22 -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/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- 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 +16 -3
- 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/coders/json.rb +14 -5
- 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 +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- 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 +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- data/lib/active_record/connection_adapters/column.rb +17 -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 +73 -46
- 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 +76 -45
- 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/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 +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- 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 +13 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -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 +33 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -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/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 +73 -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 +1 -1
- 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 +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +45 -12
- 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 +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -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 +8 -8
- 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 +53 -21
- 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 +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- data/lib/active_record/relation/merger.rb +8 -8
- 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 +22 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- 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 +107 -75
- 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_dumper.rb +18 -11
- 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 +43 -15
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -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 +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- 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 +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- 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 +37 -16
- 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 +84 -49
- 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 +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- 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
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
|
42
42
|
date: { name: "date" },
|
|
43
43
|
binary: { name: "blob" },
|
|
44
44
|
blob: { name: "blob" },
|
|
45
|
-
boolean: { name: "
|
|
45
|
+
boolean: { name: "boolean" },
|
|
46
46
|
json: { name: "json" },
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -79,7 +79,11 @@ module ActiveRecord
|
|
|
79
79
|
|
|
80
80
|
args << config.database
|
|
81
81
|
|
|
82
|
-
find_cmd_and_exec([
|
|
82
|
+
find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def native_database_types # :nodoc:
|
|
86
|
+
NATIVE_DATABASE_TYPES
|
|
83
87
|
end
|
|
84
88
|
end
|
|
85
89
|
|
|
@@ -98,7 +102,7 @@ module ActiveRecord
|
|
|
98
102
|
end
|
|
99
103
|
|
|
100
104
|
def supports_index_sort_order?
|
|
101
|
-
|
|
105
|
+
mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
|
|
102
106
|
end
|
|
103
107
|
|
|
104
108
|
def supports_expression_index?
|
|
@@ -138,7 +142,7 @@ module ActiveRecord
|
|
|
138
142
|
end
|
|
139
143
|
|
|
140
144
|
def supports_datetime_with_precision?
|
|
141
|
-
|
|
145
|
+
true
|
|
142
146
|
end
|
|
143
147
|
|
|
144
148
|
def supports_virtual_columns?
|
|
@@ -178,6 +182,16 @@ module ActiveRecord
|
|
|
178
182
|
supports_insert_returning? ? column.auto_populated? : column.auto_increment?
|
|
179
183
|
end
|
|
180
184
|
|
|
185
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/invisible-indexes.html for more details on MySQL feature.
|
|
186
|
+
# See https://mariadb.com/kb/en/ignored-indexes/ for more details on the MariaDB feature.
|
|
187
|
+
def supports_disabling_indexes?
|
|
188
|
+
if mariadb?
|
|
189
|
+
database_version >= "10.6.0"
|
|
190
|
+
else
|
|
191
|
+
database_version >= "8.0.0"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
181
195
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
|
182
196
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
|
183
197
|
end
|
|
@@ -186,10 +200,6 @@ module ActiveRecord
|
|
|
186
200
|
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
|
187
201
|
end
|
|
188
202
|
|
|
189
|
-
def native_database_types
|
|
190
|
-
NATIVE_DATABASE_TYPES
|
|
191
|
-
end
|
|
192
|
-
|
|
193
203
|
def index_algorithms
|
|
194
204
|
{
|
|
195
205
|
default: "ALGORITHM = DEFAULT",
|
|
@@ -199,14 +209,6 @@ module ActiveRecord
|
|
|
199
209
|
}
|
|
200
210
|
end
|
|
201
211
|
|
|
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
212
|
# Must return the MySQL error number from the exception, if the exception has an
|
|
211
213
|
# error number.
|
|
212
214
|
def error_number(exception) # :nodoc:
|
|
@@ -230,24 +232,19 @@ module ActiveRecord
|
|
|
230
232
|
# DATABASE STATEMENTS ======================================
|
|
231
233
|
#++
|
|
232
234
|
|
|
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
235
|
def begin_db_transaction # :nodoc:
|
|
245
236
|
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
|
246
237
|
end
|
|
247
238
|
|
|
248
239
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
|
249
|
-
|
|
250
|
-
|
|
240
|
+
# From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
|
|
241
|
+
# So we don't need to implement #reset_isolation_level
|
|
242
|
+
execute_batch(
|
|
243
|
+
["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
|
|
244
|
+
"TRANSACTION",
|
|
245
|
+
allow_retry: true,
|
|
246
|
+
materialize_transactions: false,
|
|
247
|
+
)
|
|
251
248
|
end
|
|
252
249
|
|
|
253
250
|
def commit_db_transaction # :nodoc:
|
|
@@ -347,7 +344,7 @@ module ActiveRecord
|
|
|
347
344
|
rename_table_indexes(table_name, new_name, **options)
|
|
348
345
|
end
|
|
349
346
|
|
|
350
|
-
# Drops a table from the database.
|
|
347
|
+
# Drops a table or tables from the database.
|
|
351
348
|
#
|
|
352
349
|
# [<tt>:force</tt>]
|
|
353
350
|
# Set to +:cascade+ to drop dependent objects as well.
|
|
@@ -361,10 +358,10 @@ module ActiveRecord
|
|
|
361
358
|
#
|
|
362
359
|
# Although this command ignores most +options+ and the block if one is given,
|
|
363
360
|
# 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}"
|
|
361
|
+
# 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.
|
|
362
|
+
def drop_table(*table_names, **options)
|
|
363
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
|
364
|
+
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
365
|
end
|
|
369
366
|
|
|
370
367
|
def rename_index(table_name, old_name, new_name)
|
|
@@ -468,6 +465,24 @@ module ActiveRecord
|
|
|
468
465
|
CreateIndexDefinition.new(index, algorithm)
|
|
469
466
|
end
|
|
470
467
|
|
|
468
|
+
def enable_index(table_name, index_name) # :nodoc:
|
|
469
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
|
470
|
+
|
|
471
|
+
query = <<~SQL
|
|
472
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
|
|
473
|
+
SQL
|
|
474
|
+
execute(query)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def disable_index(table_name, index_name) # :nodoc:
|
|
478
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
|
479
|
+
|
|
480
|
+
query = <<~SQL
|
|
481
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "IGNORED" : "INVISIBLE"}
|
|
482
|
+
SQL
|
|
483
|
+
execute(query)
|
|
484
|
+
end
|
|
485
|
+
|
|
471
486
|
def add_sql_comment!(sql, comment) # :nodoc:
|
|
472
487
|
sql << " COMMENT #{quote(comment)}" if comment.present?
|
|
473
488
|
sql
|
|
@@ -588,7 +603,7 @@ module ActiveRecord
|
|
|
588
603
|
|
|
589
604
|
# SHOW VARIABLES LIKE 'name'
|
|
590
605
|
def show_variable(name)
|
|
591
|
-
query_value("SELECT @@#{name}", "SCHEMA")
|
|
606
|
+
query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
|
|
592
607
|
rescue ActiveRecord::StatementInvalid
|
|
593
608
|
nil
|
|
594
609
|
end
|
|
@@ -693,8 +708,8 @@ module ActiveRecord
|
|
|
693
708
|
end
|
|
694
709
|
|
|
695
710
|
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.
|
|
711
|
+
if database_version < "5.6.4"
|
|
712
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
|
|
698
713
|
end
|
|
699
714
|
end
|
|
700
715
|
|
|
@@ -743,12 +758,12 @@ module ActiveRecord
|
|
|
743
758
|
m.alias_type %r(bit)i, "binary"
|
|
744
759
|
end
|
|
745
760
|
|
|
746
|
-
def register_integer_type(mapping, key,
|
|
761
|
+
def register_integer_type(mapping, key, limit:)
|
|
747
762
|
mapping.register_type(key) do |sql_type|
|
|
748
763
|
if /\bunsigned\b/.match?(sql_type)
|
|
749
|
-
Type::UnsignedInteger.new(
|
|
764
|
+
Type::UnsignedInteger.new(limit: limit)
|
|
750
765
|
else
|
|
751
|
-
Type::Integer.new(
|
|
766
|
+
Type::Integer.new(limit: limit)
|
|
752
767
|
end
|
|
753
768
|
end
|
|
754
769
|
end
|
|
@@ -778,14 +793,13 @@ module ActiveRecord
|
|
|
778
793
|
end
|
|
779
794
|
end
|
|
780
795
|
|
|
781
|
-
def handle_warnings(sql)
|
|
796
|
+
def handle_warnings(_initial_result, sql)
|
|
782
797
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
|
783
798
|
|
|
784
|
-
@affected_rows_before_warnings = @raw_connection.affected_rows
|
|
785
799
|
warning_count = @raw_connection.warning_count
|
|
786
800
|
result = @raw_connection.query("SHOW WARNINGS")
|
|
787
801
|
result = [
|
|
788
|
-
["Warning", nil, "Query had warning_count=#{warning_count} but
|
|
802
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but `SHOW WARNINGS` did not return the warnings. Check MySQL logs or database configuration."],
|
|
789
803
|
] if result.count == 0
|
|
790
804
|
result.each do |level, code, message|
|
|
791
805
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
|
@@ -799,11 +813,6 @@ module ActiveRecord
|
|
|
799
813
|
warning.level == "Note" || super
|
|
800
814
|
end
|
|
801
815
|
|
|
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
816
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
|
808
817
|
ER_DB_CREATE_EXISTS = 1007
|
|
809
818
|
ER_FILSORT_ABORT = 1028
|
|
@@ -827,6 +836,8 @@ module ActiveRecord
|
|
|
827
836
|
CR_SERVER_LOST = 2013
|
|
828
837
|
ER_QUERY_TIMEOUT = 3024
|
|
829
838
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
|
839
|
+
ER_CHECK_CONSTRAINT_VIOLATED = 3819
|
|
840
|
+
ER_CONSTRAINT_FAILED = 4025
|
|
830
841
|
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
|
831
842
|
|
|
832
843
|
def translate_exception(exception, message:, sql:, binds:)
|
|
@@ -859,6 +870,8 @@ module ActiveRecord
|
|
|
859
870
|
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
860
871
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
|
861
872
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
873
|
+
when ER_CHECK_CONSTRAINT_VIOLATED, ER_CONSTRAINT_FAILED
|
|
874
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
862
875
|
when ER_LOCK_DEADLOCK
|
|
863
876
|
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
864
877
|
when ER_LOCK_WAIT_TIMEOUT
|
|
@@ -973,13 +986,11 @@ module ActiveRecord
|
|
|
973
986
|
end.join(", ")
|
|
974
987
|
|
|
975
988
|
# ...and send them all in one query
|
|
976
|
-
|
|
989
|
+
raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
|
977
990
|
end
|
|
978
991
|
|
|
979
992
|
def column_definitions(table_name) # :nodoc:
|
|
980
|
-
|
|
981
|
-
each_hash(result)
|
|
982
|
-
end
|
|
993
|
+
internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA", allow_retry: true)
|
|
983
994
|
end
|
|
984
995
|
|
|
985
996
|
def create_table_info(table_name) # :nodoc:
|
|
@@ -17,16 +17,22 @@ 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
|
-
@default = default
|
|
25
|
+
@default = default.nil? || cast_type.mutable? ? default : cast_type.deserialize(default)
|
|
25
26
|
@default_function = default_function
|
|
26
27
|
@collation = collation
|
|
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 ^
|
|
@@ -100,11 +110,14 @@ module ActiveRecord
|
|
|
100
110
|
false
|
|
101
111
|
end
|
|
102
112
|
|
|
113
|
+
protected
|
|
114
|
+
attr_reader :cast_type
|
|
115
|
+
|
|
103
116
|
private
|
|
104
117
|
def deduplicated
|
|
105
118
|
@name = -name
|
|
106
119
|
@sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
|
|
107
|
-
@default = -default if default
|
|
120
|
+
@default = -default if String === default
|
|
108
121
|
@default_function = -default_function if default_function
|
|
109
122
|
@collation = -collation if collation
|
|
110
123
|
@comment = -comment if comment
|
|
@@ -114,7 +127,7 @@ module ActiveRecord
|
|
|
114
127
|
|
|
115
128
|
class NullColumn < Column
|
|
116
129
|
def initialize(name, **)
|
|
117
|
-
super(name, nil)
|
|
130
|
+
super(name, nil, nil)
|
|
118
131
|
end
|
|
119
132
|
end
|
|
120
133
|
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
|
|
@@ -8,45 +8,49 @@ module ActiveRecord
|
|
|
8
8
|
def indexes(table_name)
|
|
9
9
|
indexes = []
|
|
10
10
|
current_index = nil
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
index_using = mysql_index_type
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
indexes << [
|
|
26
|
-
row[:Table],
|
|
27
|
-
row[:Key_name],
|
|
28
|
-
row[:Non_unique].to_i == 0,
|
|
29
|
-
[],
|
|
30
|
-
lengths: {},
|
|
31
|
-
orders: {},
|
|
32
|
-
type: index_type,
|
|
33
|
-
using: index_using,
|
|
34
|
-
comment: row[:Index_comment].presence
|
|
35
|
-
]
|
|
11
|
+
internal_exec_query("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA").each do |row|
|
|
12
|
+
if current_index != row["Key_name"]
|
|
13
|
+
next if row["Key_name"] == "PRIMARY" # skip the primary key
|
|
14
|
+
current_index = row["Key_name"]
|
|
15
|
+
|
|
16
|
+
mysql_index_type = row["Index_type"].downcase.to_sym
|
|
17
|
+
case mysql_index_type
|
|
18
|
+
when :fulltext, :spatial
|
|
19
|
+
index_type = mysql_index_type
|
|
20
|
+
when :btree, :hash
|
|
21
|
+
index_using = mysql_index_type
|
|
36
22
|
end
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
index = [
|
|
25
|
+
row["Table"],
|
|
26
|
+
row["Key_name"],
|
|
27
|
+
row["Non_unique"].to_i == 0,
|
|
28
|
+
[],
|
|
29
|
+
lengths: {},
|
|
30
|
+
orders: {},
|
|
31
|
+
type: index_type,
|
|
32
|
+
using: index_using,
|
|
33
|
+
comment: row["Index_comment"].presence,
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
if supports_disabling_indexes?
|
|
37
|
+
index[-1][:enabled] = mariadb? ? row["Ignored"] == "NO" : row["Visible"] == "YES"
|
|
49
38
|
end
|
|
39
|
+
|
|
40
|
+
indexes << index
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if expression = row["Expression"]
|
|
44
|
+
expression = expression.gsub("\\'", "'")
|
|
45
|
+
expression = +"(#{expression})" unless expression.start_with?("(")
|
|
46
|
+
indexes.last[-2] << expression
|
|
47
|
+
indexes.last[-1][:expressions] ||= {}
|
|
48
|
+
indexes.last[-1][:expressions][expression] = expression
|
|
49
|
+
indexes.last[-1][:orders][expression] = :desc if row["Collation"] == "D"
|
|
50
|
+
else
|
|
51
|
+
indexes.last[-2] << row["Column_name"]
|
|
52
|
+
indexes.last[-1][:lengths][row["Column_name"]] = row["Sub_part"].to_i if row["Sub_part"]
|
|
53
|
+
indexes.last[-1][:orders][row["Column_name"]] = :desc if row["Collation"] == "D"
|
|
50
54
|
end
|
|
51
55
|
end
|
|
52
56
|
|
|
@@ -65,8 +69,7 @@ module ActiveRecord
|
|
|
65
69
|
columns, order: orders, length: lengths
|
|
66
70
|
).values.join(", ")
|
|
67
71
|
end
|
|
68
|
-
|
|
69
|
-
IndexDefinition.new(*index, **options)
|
|
72
|
+
MySQL::IndexDefinition.new(*index, **options)
|
|
70
73
|
end
|
|
71
74
|
rescue StatementInvalid => e
|
|
72
75
|
if e.message.match?(/Table '.+' doesn't exist/)
|
|
@@ -76,6 +79,16 @@ module ActiveRecord
|
|
|
76
79
|
end
|
|
77
80
|
end
|
|
78
81
|
|
|
82
|
+
def create_index_definition(table_name, name, unique, columns, **options)
|
|
83
|
+
MySQL::IndexDefinition.new(table_name, name, unique, columns, **options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
|
|
87
|
+
index, algorithm, if_not_exists = super
|
|
88
|
+
index.enabled = options[:enabled] unless options[:enabled].nil?
|
|
89
|
+
[index, algorithm, if_not_exists]
|
|
90
|
+
end
|
|
91
|
+
|
|
79
92
|
def remove_column(table_name, column_name, type = nil, **options)
|
|
80
93
|
if foreign_key_exists?(table_name, column: column_name)
|
|
81
94
|
remove_foreign_key(table_name, column: column_name)
|
|
@@ -87,6 +100,13 @@ module ActiveRecord
|
|
|
87
100
|
super
|
|
88
101
|
end
|
|
89
102
|
|
|
103
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
|
104
|
+
# RESTRICT is by default in MySQL.
|
|
105
|
+
options.delete(:on_update) if options[:on_update] == :restrict
|
|
106
|
+
options.delete(:on_delete) if options[:on_delete] == :restrict
|
|
107
|
+
super
|
|
108
|
+
end
|
|
109
|
+
|
|
90
110
|
def internal_string_options_for_primary_key
|
|
91
111
|
super.tap do |options|
|
|
92
112
|
if !row_format_dynamic_by_default? && CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
|
@@ -182,12 +202,12 @@ module ActiveRecord
|
|
|
182
202
|
end
|
|
183
203
|
|
|
184
204
|
def new_column_from_field(table_name, field, _definitions)
|
|
185
|
-
field_name = field.fetch(
|
|
186
|
-
type_metadata = fetch_type_metadata(field[
|
|
187
|
-
default, default_function = field[
|
|
205
|
+
field_name = field.fetch("Field")
|
|
206
|
+
type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
|
|
207
|
+
default, default_function = field["Default"], nil
|
|
188
208
|
|
|
189
209
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
|
190
|
-
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[
|
|
210
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
|
|
191
211
|
default, default_function = nil, default
|
|
192
212
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
|
193
213
|
default = +"(#{default})" unless default.start_with?("(")
|
|
@@ -203,13 +223,14 @@ module ActiveRecord
|
|
|
203
223
|
end
|
|
204
224
|
|
|
205
225
|
MySQL::Column.new(
|
|
206
|
-
field[
|
|
226
|
+
field["Field"],
|
|
227
|
+
lookup_cast_type(type_metadata.sql_type),
|
|
207
228
|
default,
|
|
208
229
|
type_metadata,
|
|
209
|
-
field[
|
|
230
|
+
field["Null"] == "YES",
|
|
210
231
|
default_function,
|
|
211
|
-
collation: field[
|
|
212
|
-
comment: field[
|
|
232
|
+
collation: field["Collation"],
|
|
233
|
+
comment: field["Comment"].presence
|
|
213
234
|
)
|
|
214
235
|
end
|
|
215
236
|
|
|
@@ -228,6 +249,12 @@ module ActiveRecord
|
|
|
228
249
|
end
|
|
229
250
|
end
|
|
230
251
|
|
|
252
|
+
def valid_index_options
|
|
253
|
+
index_options = super
|
|
254
|
+
index_options << :enabled if supports_disabling_indexes?
|
|
255
|
+
index_options
|
|
256
|
+
end
|
|
257
|
+
|
|
231
258
|
def add_options_for_index_columns(quoted_columns, **options)
|
|
232
259
|
quoted_columns = add_index_length(quoted_columns, **options)
|
|
233
260
|
super
|