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.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -6
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/associations.rb +3 -2
  6. data/lib/active_record/associations/association.rb +1 -1
  7. data/lib/active_record/associations/builder/association.rb +14 -18
  8. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  9. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  10. data/lib/active_record/associations/builder/has_many.rb +2 -0
  11. data/lib/active_record/associations/builder/has_one.rb +35 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  13. data/lib/active_record/associations/collection_proxy.rb +1 -1
  14. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  15. data/lib/active_record/associations/preloader.rb +11 -6
  16. data/lib/active_record/associations/preloader/association.rb +32 -30
  17. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  18. data/lib/active_record/attribute_methods.rb +4 -3
  19. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  20. data/lib/active_record/attribute_methods/dirty.rb +42 -14
  21. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  22. data/lib/active_record/attribute_methods/query.rb +2 -3
  23. data/lib/active_record/attribute_methods/read.rb +3 -9
  24. data/lib/active_record/attribute_methods/write.rb +6 -12
  25. data/lib/active_record/attributes.rb +13 -0
  26. data/lib/active_record/autosave_association.rb +13 -3
  27. data/lib/active_record/base.rb +0 -1
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
  29. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  30. data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
  31. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  32. data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
  35. data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
  36. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
  37. data/lib/active_record/connection_adapters/column.rb +17 -13
  38. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  39. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
  41. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  42. data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
  43. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  44. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  45. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  46. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  47. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
  48. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  49. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  50. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
  53. data/lib/active_record/connection_handling.rb +17 -10
  54. data/lib/active_record/core.rb +15 -20
  55. data/lib/active_record/database_configurations.rb +14 -14
  56. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  57. data/lib/active_record/database_configurations/url_config.rb +12 -12
  58. data/lib/active_record/dynamic_matchers.rb +1 -1
  59. data/lib/active_record/enum.rb +6 -0
  60. data/lib/active_record/errors.rb +1 -1
  61. data/lib/active_record/gem_version.rb +1 -1
  62. data/lib/active_record/insert_all.rb +180 -0
  63. data/lib/active_record/integration.rb +13 -1
  64. data/lib/active_record/internal_metadata.rb +5 -1
  65. data/lib/active_record/locking/optimistic.rb +3 -4
  66. data/lib/active_record/log_subscriber.rb +1 -1
  67. data/lib/active_record/migration.rb +25 -18
  68. data/lib/active_record/migration/command_recorder.rb +28 -14
  69. data/lib/active_record/migration/compatibility.rb +10 -0
  70. data/lib/active_record/persistence.rb +206 -13
  71. data/lib/active_record/querying.rb +17 -12
  72. data/lib/active_record/railties/databases.rake +68 -6
  73. data/lib/active_record/reflection.rb +2 -2
  74. data/lib/active_record/relation.rb +98 -20
  75. data/lib/active_record/relation/calculations.rb +39 -39
  76. data/lib/active_record/relation/delegation.rb +22 -30
  77. data/lib/active_record/relation/finder_methods.rb +3 -9
  78. data/lib/active_record/relation/merger.rb +7 -16
  79. data/lib/active_record/relation/query_methods.rb +153 -38
  80. data/lib/active_record/relation/where_clause.rb +9 -5
  81. data/lib/active_record/sanitization.rb +3 -2
  82. data/lib/active_record/schema_dumper.rb +5 -0
  83. data/lib/active_record/schema_migration.rb +1 -1
  84. data/lib/active_record/scoping/default.rb +6 -7
  85. data/lib/active_record/scoping/named.rb +1 -1
  86. data/lib/active_record/statement_cache.rb +2 -2
  87. data/lib/active_record/store.rb +48 -0
  88. data/lib/active_record/table_metadata.rb +3 -3
  89. data/lib/active_record/tasks/database_tasks.rb +36 -1
  90. data/lib/active_record/touch_later.rb +2 -2
  91. data/lib/active_record/transactions.rb +52 -41
  92. data/lib/active_record/validations/uniqueness.rb +3 -5
  93. data/lib/arel/insert_manager.rb +3 -3
  94. data/lib/arel/nodes.rb +2 -1
  95. data/lib/arel/nodes/comment.rb +29 -0
  96. data/lib/arel/nodes/select_core.rb +16 -12
  97. data/lib/arel/nodes/unary.rb +1 -0
  98. data/lib/arel/nodes/values_list.rb +2 -17
  99. data/lib/arel/select_manager.rb +10 -10
  100. data/lib/arel/visitors/depth_first.rb +6 -1
  101. data/lib/arel/visitors/dot.rb +7 -2
  102. data/lib/arel/visitors/ibm_db.rb +13 -0
  103. data/lib/arel/visitors/informix.rb +6 -0
  104. data/lib/arel/visitors/mssql.rb +15 -1
  105. data/lib/arel/visitors/oracle12.rb +4 -5
  106. data/lib/arel/visitors/postgresql.rb +4 -10
  107. data/lib/arel/visitors/to_sql.rb +87 -108
  108. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  109. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  110. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  111. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  112. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  113. metadata +12 -11
  114. data/lib/active_record/collection_cache_key.rb +0 -53
  115. 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, :rollback_to_savepoint, :rollback_db_transaction
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 type_casted_binds(binds) # :nodoc:
142
- if binds.first.is_a?(Array)
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?(to_table_ord = nil, to_table: nil, **options)
109
- if to_table_ord
110
- self.to_table == to_table_ord.to_s
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
- # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
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, options_or_to_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, options_or_to_table).name
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, options_or_to_table = {})
1028
- foreign_key_for(from_table, options_or_to_table).present?
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(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
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
- def change_table_comment(table_name, comment)
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
- def change_column_comment(table_name, column_name, comment)
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, options_or_to_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? options_or_to_table }
1376
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1347
1377
  end
1348
1378
 
1349
- def foreign_key_for!(from_table, options_or_to_table = {})
1350
- foreign_key_for(from_table, options_or_to_table) || \
1351
- raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
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
- def initialize(version_string)
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
- # this should be overridden by concrete adapters
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
- case_sensitive_comparison(attribute, value)
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
- private
546
- def check_version
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 version #:nodoc:
61
- @version ||= Version.new(version_string)
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? #:nodoc:
68
+ def supports_bulk_alter?
69
69
  true
70
70
  end
71
71
 
72
72
  def supports_index_sort_order?
73
- !mariadb? && version >= "8.0.1"
73
+ !mariadb? && database_version >= "8.0.1"
74
74
  end
75
75
 
76
76
  def supports_expression_index?
77
- !mariadb? && version >= "8.0.13"
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? || version >= "5.6.4"
101
+ mariadb? || database_version >= "5.6.4"
102
102
  end
103
103
 
104
104
  def supports_virtual_columns?
105
- mariadb? || version >= "5.7.5"
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
- # Clears the prepared statements cache.
158
- def clear_cache!
170
+ def clear_cache! # :nodoc:
159
171
  reload_type_map
160
- @statements.clear
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 bulk_change_table(table_name, operations) #:nodoc:
283
- sqls = operations.flat_map do |command, args|
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, comment) #:nodoc:
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 insert_fixtures_set(fixture_set, tables_to_delete = [])
495
- with_multi_statements do
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
- private
501
- def check_version
502
- if version < "5.5.8"
503
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
504
- end
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
- def combine_multi_statements(total_sql)
508
- total_sql.each_with_object([]) do |sql, total_sql_chunks|
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
- def max_allowed_packet_reached?(current_packet, previous_packet)
520
- if current_packet.bytesize > max_allowed_packet
521
- raise ActiveRecordError, "Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
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
- def max_allowed_packet
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 : version >= "5.7.6"
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
- full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
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: