activerecord 6.0.0.beta3 → 6.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +286 -6
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_proxy.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations/preloader/association.rb +32 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +4 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +42 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +13 -3
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
- data/lib/active_record/connection_handling.rb +17 -10
- data/lib/active_record/core.rb +15 -20
- data/lib/active_record/database_configurations.rb +14 -14
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +6 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +180 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railties/databases.rake +68 -6
- data/lib/active_record/reflection.rb +2 -2
- data/lib/active_record/relation.rb +98 -20
- data/lib/active_record/relation/calculations.rb +39 -39
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +3 -9
- data/lib/active_record/relation/merger.rb +7 -16
- data/lib/active_record/relation/query_methods.rb +153 -38
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +3 -2
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +3 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -1
- data/lib/active_record/touch_later.rb +2 -2
- data/lib/active_record/transactions.rb +52 -41
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +6 -1
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +87 -108
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +12 -11
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -7,7 +7,8 @@ module ActiveRecord
|
|
7
7
|
module QueryCache
|
8
8
|
class << self
|
9
9
|
def included(base) #:nodoc:
|
10
|
-
dirties_query_cache base, :insert, :update, :delete, :
|
10
|
+
dirties_query_cache base, :insert, :update, :delete, :truncate, :truncate_tables,
|
11
|
+
:rollback_to_savepoint, :rollback_db_transaction
|
11
12
|
|
12
13
|
base.set_callback :checkout, :after, :configure_query_cache!
|
13
14
|
base.set_callback :checkin, :after, :disable_query_cache!
|
@@ -138,15 +138,19 @@ module ActiveRecord
|
|
138
138
|
"'#{quote_string(value.to_s)}'"
|
139
139
|
end
|
140
140
|
|
141
|
-
def
|
142
|
-
|
143
|
-
binds.map { |column, value| type_cast(value, column) }
|
144
|
-
else
|
145
|
-
binds.map { |attr| type_cast(attr.value_for_database) }
|
146
|
-
end
|
141
|
+
def sanitize_as_sql_comment(value) # :nodoc:
|
142
|
+
value.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
|
147
143
|
end
|
148
144
|
|
149
145
|
private
|
146
|
+
def type_casted_binds(binds)
|
147
|
+
if binds.first.is_a?(Array)
|
148
|
+
binds.map { |column, value| type_cast(value, column) }
|
149
|
+
else
|
150
|
+
binds.map { |attr| type_cast(attr.value_for_database) }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
150
154
|
def lookup_cast_type(sql_type)
|
151
155
|
type_map.lookup(sql_type)
|
152
156
|
end
|
@@ -105,13 +105,9 @@ module ActiveRecord
|
|
105
105
|
!ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
|
106
106
|
end
|
107
107
|
|
108
|
-
def defined_for?(
|
109
|
-
|
110
|
-
self.
|
111
|
-
else
|
112
|
-
(to_table.nil? || to_table.to_s == self.to_table) &&
|
113
|
-
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
114
|
-
end
|
108
|
+
def defined_for?(to_table: nil, **options)
|
109
|
+
(to_table.nil? || to_table.to_s == self.to_table) &&
|
110
|
+
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
115
111
|
end
|
116
112
|
|
117
113
|
private
|
@@ -420,6 +416,7 @@ module ActiveRecord
|
|
420
416
|
#
|
421
417
|
# t.references(:user)
|
422
418
|
# t.belongs_to(:supplier, foreign_key: true)
|
419
|
+
# t.belongs_to(:supplier, foreign_key: true, type: :integer)
|
423
420
|
#
|
424
421
|
# See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
|
425
422
|
def references(*args, **options)
|
@@ -770,6 +770,17 @@ module ActiveRecord
|
|
770
770
|
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
|
771
771
|
#
|
772
772
|
# Note: only supported by MySQL.
|
773
|
+
#
|
774
|
+
# ====== Creating an index with a specific algorithm
|
775
|
+
#
|
776
|
+
# add_index(:developers, :name, algorithm: :concurrently)
|
777
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
778
|
+
#
|
779
|
+
# Note: only supported by PostgreSQL.
|
780
|
+
#
|
781
|
+
# Concurrently adding an index is not supported in a transaction.
|
782
|
+
#
|
783
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
773
784
|
def add_index(table_name, column_name, options = {})
|
774
785
|
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
775
786
|
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
@@ -793,6 +804,15 @@ module ActiveRecord
|
|
793
804
|
#
|
794
805
|
# remove_index :accounts, name: :by_branch_party
|
795
806
|
#
|
807
|
+
# Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
|
808
|
+
#
|
809
|
+
# remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
|
810
|
+
#
|
811
|
+
# Note: only supported by PostgreSQL.
|
812
|
+
#
|
813
|
+
# Concurrently removing an index is not supported in a transaction.
|
814
|
+
#
|
815
|
+
# For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
|
796
816
|
def remove_index(table_name, options = {})
|
797
817
|
index_name = index_name_for_remove(table_name, options)
|
798
818
|
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
@@ -966,7 +986,7 @@ module ActiveRecord
|
|
966
986
|
# [<tt>:on_update</tt>]
|
967
987
|
# Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
|
968
988
|
# [<tt>:validate</tt>]
|
969
|
-
# (
|
989
|
+
# (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
|
970
990
|
def add_foreign_key(from_table, to_table, options = {})
|
971
991
|
return unless supports_foreign_keys?
|
972
992
|
|
@@ -1002,10 +1022,10 @@ module ActiveRecord
|
|
1002
1022
|
# with an addition of
|
1003
1023
|
# [<tt>:to_table</tt>]
|
1004
1024
|
# The name of the table that contains the referenced primary key.
|
1005
|
-
def remove_foreign_key(from_table,
|
1025
|
+
def remove_foreign_key(from_table, to_table = nil, **options)
|
1006
1026
|
return unless supports_foreign_keys?
|
1007
1027
|
|
1008
|
-
fk_name_to_delete = foreign_key_for!(from_table,
|
1028
|
+
fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
|
1009
1029
|
|
1010
1030
|
at = create_alter_table from_table
|
1011
1031
|
at.drop_foreign_key fk_name_to_delete
|
@@ -1024,8 +1044,8 @@ module ActiveRecord
|
|
1024
1044
|
# # Checks to see if a foreign key with a custom name exists.
|
1025
1045
|
# foreign_key_exists?(:accounts, name: "special_fk_name")
|
1026
1046
|
#
|
1027
|
-
def foreign_key_exists?(from_table,
|
1028
|
-
foreign_key_for(from_table,
|
1047
|
+
def foreign_key_exists?(from_table, to_table = nil, **options)
|
1048
|
+
foreign_key_for(from_table, to_table: to_table, **options).present?
|
1029
1049
|
end
|
1030
1050
|
|
1031
1051
|
def foreign_key_column_for(table_name) # :nodoc:
|
@@ -1051,7 +1071,7 @@ module ActiveRecord
|
|
1051
1071
|
|
1052
1072
|
def assume_migrated_upto_version(version, migrations_paths = nil)
|
1053
1073
|
unless migrations_paths.nil?
|
1054
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
1074
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
1055
1075
|
Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
|
1056
1076
|
MSG
|
1057
1077
|
end
|
@@ -1097,7 +1117,7 @@ module ActiveRecord
|
|
1097
1117
|
if (0..6) === precision
|
1098
1118
|
column_type_sql << "(#{precision})"
|
1099
1119
|
else
|
1100
|
-
raise
|
1120
|
+
raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
|
1101
1121
|
end
|
1102
1122
|
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
1103
1123
|
column_type_sql << "(#{limit})"
|
@@ -1185,12 +1205,22 @@ module ActiveRecord
|
|
1185
1205
|
end
|
1186
1206
|
|
1187
1207
|
# Changes the comment for a table or removes it if +nil+.
|
1188
|
-
|
1208
|
+
#
|
1209
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1210
|
+
# reversible in migration:
|
1211
|
+
#
|
1212
|
+
# change_table_comment(:posts, from: "old_comment", to: "new_comment")
|
1213
|
+
def change_table_comment(table_name, comment_or_changes)
|
1189
1214
|
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1190
1215
|
end
|
1191
1216
|
|
1192
1217
|
# Changes the comment for a column or removes it if +nil+.
|
1193
|
-
|
1218
|
+
#
|
1219
|
+
# Passing a hash containing +:from+ and +:to+ will make this change
|
1220
|
+
# reversible in migration:
|
1221
|
+
#
|
1222
|
+
# change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
|
1223
|
+
def change_column_comment(table_name, column_name, comment_or_changes)
|
1194
1224
|
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1195
1225
|
end
|
1196
1226
|
|
@@ -1341,14 +1371,14 @@ module ActiveRecord
|
|
1341
1371
|
end
|
1342
1372
|
end
|
1343
1373
|
|
1344
|
-
def foreign_key_for(from_table,
|
1374
|
+
def foreign_key_for(from_table, **options)
|
1345
1375
|
return unless supports_foreign_keys?
|
1346
|
-
foreign_keys(from_table).detect { |fk| fk.defined_for?
|
1376
|
+
foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
|
1347
1377
|
end
|
1348
1378
|
|
1349
|
-
def foreign_key_for!(from_table,
|
1350
|
-
foreign_key_for(from_table,
|
1351
|
-
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{
|
1379
|
+
def foreign_key_for!(from_table, to_table: nil, **options)
|
1380
|
+
foreign_key_for(from_table, to_table: to_table, **options) ||
|
1381
|
+
raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
1352
1382
|
end
|
1353
1383
|
|
1354
1384
|
def extract_foreign_key_action(specifier)
|
@@ -1374,11 +1404,37 @@ module ActiveRecord
|
|
1374
1404
|
default_or_changes
|
1375
1405
|
end
|
1376
1406
|
end
|
1407
|
+
alias :extract_new_comment_value :extract_new_default_value
|
1377
1408
|
|
1378
1409
|
def can_remove_index_by_name?(options)
|
1379
1410
|
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1380
1411
|
end
|
1381
1412
|
|
1413
|
+
def bulk_change_table(table_name, operations)
|
1414
|
+
sql_fragments = []
|
1415
|
+
non_combinable_operations = []
|
1416
|
+
|
1417
|
+
operations.each do |command, args|
|
1418
|
+
table, arguments = args.shift, args
|
1419
|
+
method = :"#{command}_for_alter"
|
1420
|
+
|
1421
|
+
if respond_to?(method, true)
|
1422
|
+
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
1423
|
+
sql_fragments << sqls
|
1424
|
+
non_combinable_operations.concat(procs)
|
1425
|
+
else
|
1426
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1427
|
+
non_combinable_operations.each(&:call)
|
1428
|
+
sql_fragments = []
|
1429
|
+
non_combinable_operations = []
|
1430
|
+
send(command, table, *arguments)
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
1435
|
+
non_combinable_operations.each(&:call)
|
1436
|
+
end
|
1437
|
+
|
1382
1438
|
def add_column_for_alter(table_name, column_name, type, options = {})
|
1383
1439
|
td = create_table_definition(table_name)
|
1384
1440
|
cd = td.new_column_definition(column_name, type, options)
|
@@ -120,6 +120,7 @@ module ActiveRecord
|
|
120
120
|
@quoted_column_names, @quoted_table_names = {}, {}
|
121
121
|
@prevent_writes = false
|
122
122
|
@visitor = arel_visitor
|
123
|
+
@statements = build_statement_pool
|
123
124
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
124
125
|
|
125
126
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
@@ -132,8 +133,6 @@ module ActiveRecord
|
|
132
133
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
133
134
|
config.fetch(:advisory_locks, true)
|
134
135
|
)
|
135
|
-
|
136
|
-
check_version
|
137
136
|
end
|
138
137
|
|
139
138
|
def replica?
|
@@ -171,8 +170,11 @@ module ActiveRecord
|
|
171
170
|
class Version
|
172
171
|
include Comparable
|
173
172
|
|
174
|
-
|
173
|
+
attr_reader :full_version_string
|
174
|
+
|
175
|
+
def initialize(version_string, full_version_string = nil)
|
175
176
|
@version = version_string.split(".").map(&:to_i)
|
177
|
+
@full_version_string = full_version_string
|
176
178
|
end
|
177
179
|
|
178
180
|
def <=>(version_string)
|
@@ -383,10 +385,31 @@ module ActiveRecord
|
|
383
385
|
false
|
384
386
|
end
|
385
387
|
|
388
|
+
# Does this adapter support optimizer hints?
|
389
|
+
def supports_optimizer_hints?
|
390
|
+
false
|
391
|
+
end
|
392
|
+
|
386
393
|
def supports_lazy_transactions?
|
387
394
|
false
|
388
395
|
end
|
389
396
|
|
397
|
+
def supports_insert_returning?
|
398
|
+
false
|
399
|
+
end
|
400
|
+
|
401
|
+
def supports_insert_on_duplicate_skip?
|
402
|
+
false
|
403
|
+
end
|
404
|
+
|
405
|
+
def supports_insert_on_duplicate_update?
|
406
|
+
false
|
407
|
+
end
|
408
|
+
|
409
|
+
def supports_insert_conflict_target?
|
410
|
+
false
|
411
|
+
end
|
412
|
+
|
390
413
|
# This is meant to be implemented by the adapters that support extensions
|
391
414
|
def disable_extension(name)
|
392
415
|
end
|
@@ -476,11 +499,9 @@ module ActiveRecord
|
|
476
499
|
# this should be overridden by concrete adapters
|
477
500
|
end
|
478
501
|
|
479
|
-
|
480
|
-
# Clear any caching the database adapter may be doing, for example
|
481
|
-
# clearing the prepared statement cache. This is database specific.
|
502
|
+
# Clear any caching the database adapter may be doing.
|
482
503
|
def clear_cache!
|
483
|
-
|
504
|
+
@lock.synchronize { @statements.clear } if @statements
|
484
505
|
end
|
485
506
|
|
486
507
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -506,8 +527,8 @@ module ActiveRecord
|
|
506
527
|
@connection
|
507
528
|
end
|
508
529
|
|
509
|
-
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
510
|
-
|
530
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
531
|
+
attribute.eq(value)
|
511
532
|
end
|
512
533
|
|
513
534
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
@@ -542,10 +563,31 @@ module ActiveRecord
|
|
542
563
|
index.using.nil?
|
543
564
|
end
|
544
565
|
|
545
|
-
|
546
|
-
|
566
|
+
# Called by ActiveRecord::InsertAll,
|
567
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
568
|
+
# This method implements standard bulk inserts for all databases, but
|
569
|
+
# should be overridden by adapters to implement common features with
|
570
|
+
# non-standard syntax like handling duplicates or returning values.
|
571
|
+
def build_insert_sql(insert) # :nodoc:
|
572
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
573
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
547
574
|
end
|
548
575
|
|
576
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
577
|
+
end
|
578
|
+
|
579
|
+
def get_database_version # :nodoc:
|
580
|
+
end
|
581
|
+
|
582
|
+
def database_version # :nodoc:
|
583
|
+
schema_cache.database_version
|
584
|
+
end
|
585
|
+
|
586
|
+
def check_version # :nodoc:
|
587
|
+
end
|
588
|
+
|
589
|
+
private
|
590
|
+
|
549
591
|
def type_map
|
550
592
|
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
551
593
|
initialize_type_map(mapping)
|
@@ -689,6 +731,9 @@ module ActiveRecord
|
|
689
731
|
def arel_visitor
|
690
732
|
Arel::Visitors::ToSql.new(self)
|
691
733
|
end
|
734
|
+
|
735
|
+
def build_statement_pool
|
736
|
+
end
|
692
737
|
end
|
693
738
|
end
|
694
739
|
end
|
@@ -53,28 +53,28 @@ module ActiveRecord
|
|
53
53
|
|
54
54
|
def initialize(connection, logger, connection_options, config)
|
55
55
|
super(connection, logger, config)
|
56
|
-
|
57
|
-
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
58
56
|
end
|
59
57
|
|
60
|
-
def
|
61
|
-
|
58
|
+
def get_database_version #:nodoc:
|
59
|
+
full_version_string = get_full_version
|
60
|
+
version_string = version_string(full_version_string)
|
61
|
+
Version.new(version_string, full_version_string)
|
62
62
|
end
|
63
63
|
|
64
64
|
def mariadb? # :nodoc:
|
65
65
|
/mariadb/i.match?(full_version)
|
66
66
|
end
|
67
67
|
|
68
|
-
def supports_bulk_alter?
|
68
|
+
def supports_bulk_alter?
|
69
69
|
true
|
70
70
|
end
|
71
71
|
|
72
72
|
def supports_index_sort_order?
|
73
|
-
!mariadb? &&
|
73
|
+
!mariadb? && database_version >= "8.0.1"
|
74
74
|
end
|
75
75
|
|
76
76
|
def supports_expression_index?
|
77
|
-
!mariadb? &&
|
77
|
+
!mariadb? && database_version >= "8.0.13"
|
78
78
|
end
|
79
79
|
|
80
80
|
def supports_transaction_isolation?
|
@@ -98,17 +98,30 @@ module ActiveRecord
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def supports_datetime_with_precision?
|
101
|
-
mariadb? ||
|
101
|
+
mariadb? || database_version >= "5.6.4"
|
102
102
|
end
|
103
103
|
|
104
104
|
def supports_virtual_columns?
|
105
|
-
mariadb? ||
|
105
|
+
mariadb? || database_version >= "5.7.5"
|
106
|
+
end
|
107
|
+
|
108
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
109
|
+
def supports_optimizer_hints?
|
110
|
+
!mariadb? && database_version >= "5.7.7"
|
106
111
|
end
|
107
112
|
|
108
113
|
def supports_advisory_locks?
|
109
114
|
true
|
110
115
|
end
|
111
116
|
|
117
|
+
def supports_insert_on_duplicate_skip?
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def supports_insert_on_duplicate_update?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
112
125
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
113
126
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
114
127
|
end
|
@@ -154,10 +167,9 @@ module ActiveRecord
|
|
154
167
|
|
155
168
|
# CONNECTION MANAGEMENT ====================================
|
156
169
|
|
157
|
-
|
158
|
-
def clear_cache!
|
170
|
+
def clear_cache! # :nodoc:
|
159
171
|
reload_type_map
|
160
|
-
|
172
|
+
super
|
161
173
|
end
|
162
174
|
|
163
175
|
#--
|
@@ -264,10 +276,6 @@ module ActiveRecord
|
|
264
276
|
show_variable "collation_database"
|
265
277
|
end
|
266
278
|
|
267
|
-
def truncate(table_name, name = nil)
|
268
|
-
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
269
|
-
end
|
270
|
-
|
271
279
|
def table_comment(table_name) # :nodoc:
|
272
280
|
scope = quoted_scope(table_name)
|
273
281
|
|
@@ -279,22 +287,8 @@ module ActiveRecord
|
|
279
287
|
SQL
|
280
288
|
end
|
281
289
|
|
282
|
-
def
|
283
|
-
|
284
|
-
table, arguments = args.shift, args
|
285
|
-
method = :"#{command}_for_alter"
|
286
|
-
|
287
|
-
if respond_to?(method, true)
|
288
|
-
send(method, table, *arguments)
|
289
|
-
else
|
290
|
-
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
291
|
-
end
|
292
|
-
end.join(", ")
|
293
|
-
|
294
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
295
|
-
end
|
296
|
-
|
297
|
-
def change_table_comment(table_name, comment) #:nodoc:
|
290
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
291
|
+
comment = extract_new_comment_value(comment_or_changes)
|
298
292
|
comment = "" if comment.nil?
|
299
293
|
execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
|
300
294
|
end
|
@@ -350,7 +344,8 @@ module ActiveRecord
|
|
350
344
|
change_column table_name, column_name, nil, null: null
|
351
345
|
end
|
352
346
|
|
353
|
-
def change_column_comment(table_name, column_name,
|
347
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
348
|
+
comment = extract_new_comment_value(comment_or_changes)
|
354
349
|
change_column table_name, column_name, nil, comment: comment
|
355
350
|
end
|
356
351
|
|
@@ -453,6 +448,21 @@ module ActiveRecord
|
|
453
448
|
SQL
|
454
449
|
end
|
455
450
|
|
451
|
+
def default_uniqueness_comparison(attribute, value, klass) # :nodoc:
|
452
|
+
column = column_for_attribute(attribute)
|
453
|
+
|
454
|
+
if column.collation && !column.case_sensitive? && !value.nil?
|
455
|
+
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
456
|
+
Uniqueness validator will no longer enforce case sensitive comparison in Rails 6.1.
|
457
|
+
To continue case sensitive comparison on the :#{attribute.name} attribute in #{klass} model,
|
458
|
+
pass `case_sensitive: true` option explicitly to the uniqueness validator.
|
459
|
+
MSG
|
460
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
461
|
+
else
|
462
|
+
super
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
456
466
|
def case_sensitive_comparison(attribute, value) # :nodoc:
|
457
467
|
column = column_for_attribute(attribute)
|
458
468
|
|
@@ -491,45 +501,27 @@ module ActiveRecord
|
|
491
501
|
index.using == :btree || super
|
492
502
|
end
|
493
503
|
|
494
|
-
def
|
495
|
-
|
496
|
-
super { discard_remaining_results }
|
497
|
-
end
|
498
|
-
end
|
504
|
+
def build_insert_sql(insert) # :nodoc:
|
505
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
499
506
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
507
|
+
if insert.skip_duplicates?
|
508
|
+
no_op_column = quote_column_name(insert.keys.first)
|
509
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
510
|
+
elsif insert.update_duplicates?
|
511
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
512
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
505
513
|
end
|
506
514
|
|
507
|
-
|
508
|
-
|
509
|
-
previous_packet = total_sql_chunks.last
|
510
|
-
sql << ";\n"
|
511
|
-
if max_allowed_packet_reached?(sql, previous_packet) || total_sql_chunks.empty?
|
512
|
-
total_sql_chunks << sql
|
513
|
-
else
|
514
|
-
previous_packet << sql
|
515
|
-
end
|
516
|
-
end
|
517
|
-
end
|
515
|
+
sql
|
516
|
+
end
|
518
517
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
elsif previous_packet.nil?
|
523
|
-
false
|
524
|
-
else
|
525
|
-
(current_packet.bytesize + previous_packet.bytesize) > max_allowed_packet
|
526
|
-
end
|
518
|
+
def check_version # :nodoc:
|
519
|
+
if database_version < "5.5.8"
|
520
|
+
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
527
521
|
end
|
522
|
+
end
|
528
523
|
|
529
|
-
|
530
|
-
bytes_margin = 2
|
531
|
-
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
532
|
-
end
|
524
|
+
private
|
533
525
|
|
534
526
|
def initialize_type_map(m = type_map)
|
535
527
|
super
|
@@ -700,7 +692,7 @@ module ActiveRecord
|
|
700
692
|
end
|
701
693
|
|
702
694
|
def supports_rename_index?
|
703
|
-
mariadb? ? false :
|
695
|
+
mariadb? ? false : database_version >= "5.7.6"
|
704
696
|
end
|
705
697
|
|
706
698
|
def configure_connection
|
@@ -770,6 +762,10 @@ module ActiveRecord
|
|
770
762
|
Arel::Visitors::MySQL.new(self)
|
771
763
|
end
|
772
764
|
|
765
|
+
def build_statement_pool
|
766
|
+
StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
767
|
+
end
|
768
|
+
|
773
769
|
def mismatched_foreign_key(message, sql:, binds:)
|
774
770
|
match = %r/
|
775
771
|
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
@@ -794,8 +790,8 @@ module ActiveRecord
|
|
794
790
|
MismatchedForeignKey.new(options)
|
795
791
|
end
|
796
792
|
|
797
|
-
def version_string
|
798
|
-
|
793
|
+
def version_string(full_version_string)
|
794
|
+
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
799
795
|
end
|
800
796
|
|
801
797
|
class MysqlString < Type::String # :nodoc:
|