activerecord 7.2.1.1 → 8.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|