activerecord 7.2.1.1 → 8.0.0.rc1
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 +220 -756
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -4
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -12
- data/lib/active_record/attributes.rb +1 -2
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -42
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +54 -14
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +28 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +15 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +54 -75
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- data/lib/active_record/model_schema.rb +4 -6
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +0 -4
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +9 -38
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +23 -23
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +41 -40
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +14 -1
- data/lib/active_record/relation/query_methods.rb +122 -71
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +40 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +9 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +10 -11
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -345,6 +345,15 @@ module ActiveRecord
|
|
345
345
|
# # Creates a table called 'assemblies_parts' with no id.
|
346
346
|
# create_join_table(:assemblies, :parts)
|
347
347
|
#
|
348
|
+
# # Creates a table called 'paper_boxes_papers' with no id.
|
349
|
+
# create_join_table('papers', 'paper_boxes')
|
350
|
+
#
|
351
|
+
# A duplicate prefix is combined into a single prefix. This is useful for
|
352
|
+
# namespaced models like Music::Artist and Music::Record:
|
353
|
+
#
|
354
|
+
# # Creates a table called 'music_artists_records' with no id.
|
355
|
+
# create_join_table('music_artists', 'music_records')
|
356
|
+
#
|
348
357
|
# You can pass an +options+ hash which can include the following keys:
|
349
358
|
# [<tt>:table_name</tt>]
|
350
359
|
# Sets the table name, overriding the default.
|
@@ -516,7 +525,7 @@ module ActiveRecord
|
|
516
525
|
raise NotImplementedError, "rename_table is not implemented"
|
517
526
|
end
|
518
527
|
|
519
|
-
# Drops a table from the database.
|
528
|
+
# Drops a table or tables from the database.
|
520
529
|
#
|
521
530
|
# [<tt>:force</tt>]
|
522
531
|
# Set to +:cascade+ to drop dependent objects as well.
|
@@ -527,10 +536,12 @@ module ActiveRecord
|
|
527
536
|
#
|
528
537
|
# Although this command ignores most +options+ and the block if one is given,
|
529
538
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
530
|
-
# In that case, +options+ and the block will be used by #create_table.
|
531
|
-
def drop_table(
|
532
|
-
|
533
|
-
|
539
|
+
# 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.
|
540
|
+
def drop_table(*table_names, **options)
|
541
|
+
table_names.each do |table_name|
|
542
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
543
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
544
|
+
end
|
534
545
|
end
|
535
546
|
|
536
547
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
@@ -682,7 +693,7 @@ module ActiveRecord
|
|
682
693
|
#
|
683
694
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
684
695
|
# column does not exist. This will silently ignore the migration rather than raising
|
685
|
-
# if the column was already
|
696
|
+
# if the column was already removed.
|
686
697
|
#
|
687
698
|
# remove_column(:suppliers, :qualification, if_exists: true)
|
688
699
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -844,6 +855,16 @@ module ActiveRecord
|
|
844
855
|
#
|
845
856
|
# Note: only supported by PostgreSQL.
|
846
857
|
#
|
858
|
+
# ====== Creating an index where NULLs are treated equally
|
859
|
+
#
|
860
|
+
# add_index(:people, :last_name, nulls_not_distinct: true)
|
861
|
+
#
|
862
|
+
# generates:
|
863
|
+
#
|
864
|
+
# CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
|
865
|
+
#
|
866
|
+
# Note: only supported by PostgreSQL version 15.0.0 and greater.
|
867
|
+
#
|
847
868
|
# ====== Creating an index with a specific method
|
848
869
|
#
|
849
870
|
# add_index(:developers, :name, using: 'btree')
|
@@ -1313,7 +1334,6 @@ module ActiveRecord
|
|
1313
1334
|
execute schema_creation.accept(at)
|
1314
1335
|
end
|
1315
1336
|
|
1316
|
-
|
1317
1337
|
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
1318
1338
|
#
|
1319
1339
|
# check_constraint_exists?(:products, name: "price_check")
|
@@ -1325,6 +1345,13 @@ module ActiveRecord
|
|
1325
1345
|
check_constraint_for(table_name, **options).present?
|
1326
1346
|
end
|
1327
1347
|
|
1348
|
+
def remove_constraint(table_name, constraint_name) # :nodoc:
|
1349
|
+
at = create_alter_table(table_name)
|
1350
|
+
at.drop_constraint(constraint_name)
|
1351
|
+
|
1352
|
+
execute schema_creation.accept(at)
|
1353
|
+
end
|
1354
|
+
|
1328
1355
|
def dump_schema_information # :nodoc:
|
1329
1356
|
versions = pool.schema_migration.versions
|
1330
1357
|
insert_versions_sql(versions) if versions.any?
|
@@ -448,10 +448,14 @@ module ActiveRecord
|
|
448
448
|
# = Active Record Real \Transaction
|
449
449
|
class RealTransaction < Transaction
|
450
450
|
def materialize!
|
451
|
-
if
|
452
|
-
|
451
|
+
if joinable?
|
452
|
+
if isolation_level
|
453
|
+
connection.begin_isolated_db_transaction(isolation_level)
|
454
|
+
else
|
455
|
+
connection.begin_db_transaction
|
456
|
+
end
|
453
457
|
else
|
454
|
-
connection.
|
458
|
+
connection.begin_deferred_transaction(isolation_level)
|
455
459
|
end
|
456
460
|
|
457
461
|
super
|
@@ -472,13 +476,19 @@ module ActiveRecord
|
|
472
476
|
end
|
473
477
|
|
474
478
|
def rollback
|
475
|
-
|
479
|
+
if materialized?
|
480
|
+
connection.rollback_db_transaction
|
481
|
+
connection.reset_isolation_level if isolation_level
|
482
|
+
end
|
476
483
|
@state.full_rollback!
|
477
484
|
@instrumenter.finish(:rollback) if materialized?
|
478
485
|
end
|
479
486
|
|
480
487
|
def commit
|
481
|
-
|
488
|
+
if materialized?
|
489
|
+
connection.commit_db_transaction
|
490
|
+
connection.reset_isolation_level if isolation_level
|
491
|
+
end
|
482
492
|
@state.full_commit!
|
483
493
|
@instrumenter.finish(:commit) if materialized?
|
484
494
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
3
|
require "active_record/connection_adapters/sql_type_metadata"
|
5
4
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
6
5
|
require "active_record/connection_adapters/abstract/schema_creation"
|
@@ -576,23 +575,31 @@ module ActiveRecord
|
|
576
575
|
end
|
577
576
|
|
578
577
|
# This is meant to be implemented by the adapters that support custom enum types
|
579
|
-
def create_enum(
|
578
|
+
def create_enum(...) # :nodoc:
|
580
579
|
end
|
581
580
|
|
582
581
|
# This is meant to be implemented by the adapters that support custom enum types
|
583
|
-
def drop_enum(
|
582
|
+
def drop_enum(...) # :nodoc:
|
584
583
|
end
|
585
584
|
|
586
585
|
# This is meant to be implemented by the adapters that support custom enum types
|
587
|
-
def rename_enum(
|
586
|
+
def rename_enum(...) # :nodoc:
|
588
587
|
end
|
589
588
|
|
590
589
|
# This is meant to be implemented by the adapters that support custom enum types
|
591
|
-
def add_enum_value(
|
590
|
+
def add_enum_value(...) # :nodoc:
|
592
591
|
end
|
593
592
|
|
594
593
|
# This is meant to be implemented by the adapters that support custom enum types
|
595
|
-
def rename_enum_value(
|
594
|
+
def rename_enum_value(...) # :nodoc:
|
595
|
+
end
|
596
|
+
|
597
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
598
|
+
def create_virtual_table(*) # :nodoc:
|
599
|
+
end
|
600
|
+
|
601
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
602
|
+
def drop_virtual_table(*) # :nodoc:
|
596
603
|
end
|
597
604
|
|
598
605
|
def advisory_locks_enabled? # :nodoc:
|
@@ -1044,7 +1051,8 @@ module ActiveRecord
|
|
1044
1051
|
end
|
1045
1052
|
|
1046
1053
|
def retryable_connection_error?(exception)
|
1047
|
-
exception.is_a?(ConnectionNotEstablished)
|
1054
|
+
(exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
|
1055
|
+
exception.is_a?(ConnectionFailed)
|
1048
1056
|
end
|
1049
1057
|
|
1050
1058
|
def invalidate_transaction(exception)
|
@@ -1105,24 +1113,25 @@ module ActiveRecord
|
|
1105
1113
|
end
|
1106
1114
|
end
|
1107
1115
|
|
1108
|
-
def translate_exception_class(
|
1109
|
-
|
1116
|
+
def translate_exception_class(native_error, sql, binds)
|
1117
|
+
return native_error if native_error.is_a?(ActiveRecordError)
|
1118
|
+
|
1119
|
+
message = "#{native_error.class.name}: #{native_error.message}"
|
1110
1120
|
|
1111
|
-
|
1112
|
-
|
1121
|
+
active_record_error = translate_exception(
|
1122
|
+
native_error, message: message, sql: sql, binds: binds
|
1113
1123
|
)
|
1114
|
-
|
1115
|
-
|
1124
|
+
active_record_error.set_backtrace(native_error.backtrace)
|
1125
|
+
active_record_error
|
1116
1126
|
end
|
1117
1127
|
|
1118
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [],
|
1128
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
|
1119
1129
|
@instrumenter.instrument(
|
1120
1130
|
"sql.active_record",
|
1121
1131
|
sql: sql,
|
1122
1132
|
name: name,
|
1123
1133
|
binds: binds,
|
1124
1134
|
type_casted_binds: type_casted_binds,
|
1125
|
-
statement_name: statement_name,
|
1126
1135
|
async: async,
|
1127
1136
|
connection: self,
|
1128
1137
|
transaction: current_transaction.user_transaction.presence,
|
@@ -1133,13 +1142,6 @@ module ActiveRecord
|
|
1133
1142
|
raise ex.set_query(sql, binds)
|
1134
1143
|
end
|
1135
1144
|
|
1136
|
-
def transform_query(sql)
|
1137
|
-
ActiveRecord.query_transformers.each do |transformer|
|
1138
|
-
sql = transformer.call(sql, self)
|
1139
|
-
end
|
1140
|
-
sql
|
1141
|
-
end
|
1142
|
-
|
1143
1145
|
def translate_exception(exception, message:, sql:, binds:)
|
1144
1146
|
# override in derived class
|
1145
1147
|
case exception
|
@@ -1150,10 +1152,6 @@ module ActiveRecord
|
|
1150
1152
|
end
|
1151
1153
|
end
|
1152
1154
|
|
1153
|
-
def without_prepared_statement?(binds)
|
1154
|
-
!prepared_statements || binds.empty?
|
1155
|
-
end
|
1156
|
-
|
1157
1155
|
def column_for(table_name, column_name)
|
1158
1156
|
column_name = column_name.to_s
|
1159
1157
|
columns(table_name).detect { |c| c.name == column_name } ||
|
@@ -79,7 +79,7 @@ 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
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -98,7 +98,7 @@ module ActiveRecord
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def supports_index_sort_order?
|
101
|
-
|
101
|
+
mariadb? ? database_version >= "10.8.1" : database_version >= "8.0.1"
|
102
102
|
end
|
103
103
|
|
104
104
|
def supports_expression_index?
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def supports_datetime_with_precision?
|
141
|
-
|
141
|
+
true
|
142
142
|
end
|
143
143
|
|
144
144
|
def supports_virtual_columns?
|
@@ -197,12 +197,6 @@ module ActiveRecord
|
|
197
197
|
|
198
198
|
# HELPER METHODS ===========================================
|
199
199
|
|
200
|
-
# The two drivers have slightly different ways of yielding hashes of results, so
|
201
|
-
# this method must be implemented to provide a uniform interface.
|
202
|
-
def each_hash(result) # :nodoc:
|
203
|
-
raise NotImplementedError
|
204
|
-
end
|
205
|
-
|
206
200
|
# Must return the MySQL error number from the exception, if the exception has an
|
207
201
|
# error number.
|
208
202
|
def error_number(exception) # :nodoc:
|
@@ -226,24 +220,19 @@ module ActiveRecord
|
|
226
220
|
# DATABASE STATEMENTS ======================================
|
227
221
|
#++
|
228
222
|
|
229
|
-
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
230
|
-
# to write stuff in an abstract way without concerning ourselves about whether it
|
231
|
-
# needs to be explicitly freed or not.
|
232
|
-
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
233
|
-
sql = transform_query(sql)
|
234
|
-
check_if_write_query(sql)
|
235
|
-
|
236
|
-
mark_transaction_written_if_write(sql)
|
237
|
-
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
238
|
-
end
|
239
|
-
|
240
223
|
def begin_db_transaction # :nodoc:
|
241
224
|
internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
|
242
225
|
end
|
243
226
|
|
244
227
|
def begin_isolated_db_transaction(isolation) # :nodoc:
|
245
|
-
|
246
|
-
|
228
|
+
# From MySQL manual: The [SET TRANSACTION] statement applies only to the next single transaction performed within the session.
|
229
|
+
# So we don't need to implement #reset_isolation_level
|
230
|
+
execute_batch(
|
231
|
+
["SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "BEGIN"],
|
232
|
+
"TRANSACTION",
|
233
|
+
allow_retry: true,
|
234
|
+
materialize_transactions: false,
|
235
|
+
)
|
247
236
|
end
|
248
237
|
|
249
238
|
def commit_db_transaction # :nodoc:
|
@@ -343,7 +332,7 @@ module ActiveRecord
|
|
343
332
|
rename_table_indexes(table_name, new_name, **options)
|
344
333
|
end
|
345
334
|
|
346
|
-
# Drops a table from the database.
|
335
|
+
# Drops a table or tables from the database.
|
347
336
|
#
|
348
337
|
# [<tt>:force</tt>]
|
349
338
|
# Set to +:cascade+ to drop dependent objects as well.
|
@@ -357,10 +346,10 @@ module ActiveRecord
|
|
357
346
|
#
|
358
347
|
# Although this command ignores most +options+ and the block if one is given,
|
359
348
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
360
|
-
# In that case, +options+ and the block will be used by create_table.
|
361
|
-
def drop_table(
|
362
|
-
schema_cache.clear_data_source_cache!(table_name.to_s)
|
363
|
-
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
349
|
+
# 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.
|
350
|
+
def drop_table(*table_names, **options)
|
351
|
+
table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
|
352
|
+
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}"
|
364
353
|
end
|
365
354
|
|
366
355
|
def rename_index(table_name, old_name, new_name)
|
@@ -580,7 +569,7 @@ module ActiveRecord
|
|
580
569
|
|
581
570
|
# SHOW VARIABLES LIKE 'name'
|
582
571
|
def show_variable(name)
|
583
|
-
query_value("SELECT @@#{name}", "SCHEMA")
|
572
|
+
query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
|
584
573
|
rescue ActiveRecord::StatementInvalid
|
585
574
|
nil
|
586
575
|
end
|
@@ -639,7 +628,7 @@ module ActiveRecord
|
|
639
628
|
end
|
640
629
|
|
641
630
|
def build_insert_sql(insert) # :nodoc:
|
642
|
-
no_op_column = quote_column_name(insert.keys.first)
|
631
|
+
no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
|
643
632
|
|
644
633
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
634
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
@@ -648,7 +637,9 @@ module ActiveRecord
|
|
648
637
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
638
|
|
650
639
|
if insert.skip_duplicates?
|
651
|
-
|
640
|
+
if no_op_column
|
641
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
642
|
+
end
|
652
643
|
elsif insert.update_duplicates?
|
653
644
|
if insert.raw_update_sql?
|
654
645
|
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
@@ -662,7 +653,9 @@ module ActiveRecord
|
|
662
653
|
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
663
654
|
|
664
655
|
if insert.skip_duplicates?
|
665
|
-
|
656
|
+
if no_op_column
|
657
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
658
|
+
end
|
666
659
|
elsif insert.update_duplicates?
|
667
660
|
sql << " ON DUPLICATE KEY UPDATE "
|
668
661
|
if insert.raw_update_sql?
|
@@ -679,8 +672,8 @@ module ActiveRecord
|
|
679
672
|
end
|
680
673
|
|
681
674
|
def check_version # :nodoc:
|
682
|
-
if database_version < "5.
|
683
|
-
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.
|
675
|
+
if database_version < "5.6.4"
|
676
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
|
684
677
|
end
|
685
678
|
end
|
686
679
|
|
@@ -787,11 +780,6 @@ module ActiveRecord
|
|
787
780
|
warning.level == "Note" || super
|
788
781
|
end
|
789
782
|
|
790
|
-
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
791
|
-
# made since we established the connection
|
792
|
-
def sync_timezone_changes(raw_connection)
|
793
|
-
end
|
794
|
-
|
795
783
|
# See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
|
796
784
|
ER_DB_CREATE_EXISTS = 1007
|
797
785
|
ER_FILSORT_ABORT = 1028
|
@@ -961,13 +949,11 @@ module ActiveRecord
|
|
961
949
|
end.join(", ")
|
962
950
|
|
963
951
|
# ...and send them all in one query
|
964
|
-
|
952
|
+
raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
965
953
|
end
|
966
954
|
|
967
955
|
def column_definitions(table_name) # :nodoc:
|
968
|
-
|
969
|
-
each_hash(result)
|
970
|
-
end
|
956
|
+
internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
|
971
957
|
end
|
972
958
|
|
973
959
|
def create_table_info(table_name) # :nodoc:
|
@@ -42,18 +42,12 @@ module ActiveRecord
|
|
42
42
|
# :method: unsigned_bigint
|
43
43
|
# :call-seq: unsigned_bigint(*names, **options)
|
44
44
|
|
45
|
-
##
|
46
|
-
# :method: unsigned_float
|
47
|
-
# :call-seq: unsigned_float(*names, **options)
|
48
|
-
|
49
|
-
##
|
50
|
-
# :method: unsigned_decimal
|
51
|
-
# :call-seq: unsigned_decimal(*names, **options)
|
52
|
-
|
53
45
|
included do
|
54
46
|
define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
|
55
47
|
:tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint,
|
56
48
|
:unsigned_float, :unsigned_decimal
|
49
|
+
|
50
|
+
deprecate :unsigned_float, :unsigned_decimal, deprecator: ActiveRecord.deprecator
|
57
51
|
end
|
58
52
|
end
|
59
53
|
|
@@ -8,45 +8,43 @@ 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
|
-
|
49
|
-
|
24
|
+
indexes << [
|
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
|
+
end
|
36
|
+
|
37
|
+
if expression = row["Expression"]
|
38
|
+
expression = expression.gsub("\\'", "'")
|
39
|
+
expression = +"(#{expression})" unless expression.start_with?("(")
|
40
|
+
indexes.last[-2] << expression
|
41
|
+
indexes.last[-1][:expressions] ||= {}
|
42
|
+
indexes.last[-1][:expressions][expression] = expression
|
43
|
+
indexes.last[-1][:orders][expression] = :desc if row["Collation"] == "D"
|
44
|
+
else
|
45
|
+
indexes.last[-2] << row["Column_name"]
|
46
|
+
indexes.last[-1][:lengths][row["Column_name"]] = row["Sub_part"].to_i if row["Sub_part"]
|
47
|
+
indexes.last[-1][:orders][row["Column_name"]] = :desc if row["Collation"] == "D"
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -182,12 +180,12 @@ module ActiveRecord
|
|
182
180
|
end
|
183
181
|
|
184
182
|
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[
|
183
|
+
field_name = field.fetch("Field")
|
184
|
+
type_metadata = fetch_type_metadata(field["Type"], field["Extra"])
|
185
|
+
default, default_function = field["Default"], nil
|
188
186
|
|
189
187
|
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[
|
188
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field["Extra"])
|
191
189
|
default, default_function = nil, default
|
192
190
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
193
191
|
default = +"(#{default})" unless default.start_with?("(")
|
@@ -203,13 +201,13 @@ module ActiveRecord
|
|
203
201
|
end
|
204
202
|
|
205
203
|
MySQL::Column.new(
|
206
|
-
field[
|
204
|
+
field["Field"],
|
207
205
|
default,
|
208
206
|
type_metadata,
|
209
|
-
field[
|
207
|
+
field["Null"] == "YES",
|
210
208
|
default_function,
|
211
|
-
collation: field[
|
212
|
-
comment: field[
|
209
|
+
collation: field["Collation"],
|
210
|
+
comment: field["Comment"].presence
|
213
211
|
)
|
214
212
|
end
|
215
213
|
|