activerecord 7.2.3 → 8.0.0.beta1

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.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +192 -1261
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/associations/alias_tracker.rb +4 -6
  5. data/lib/active_record/associations/association.rb +25 -5
  6. data/lib/active_record/associations/belongs_to_association.rb +2 -18
  7. data/lib/active_record/associations/builder/association.rb +7 -6
  8. data/lib/active_record/associations/collection_association.rb +4 -4
  9. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  10. data/lib/active_record/associations/has_many_through_association.rb +4 -9
  11. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  12. data/lib/active_record/associations/preloader/association.rb +2 -2
  13. data/lib/active_record/associations/singular_association.rb +8 -3
  14. data/lib/active_record/associations.rb +50 -32
  15. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  16. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  17. data/lib/active_record/attribute_methods.rb +19 -24
  18. data/lib/active_record/attributes.rb +26 -37
  19. data/lib/active_record/autosave_association.rb +81 -49
  20. data/lib/active_record/base.rb +2 -2
  21. data/lib/active_record/callbacks.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  23. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  24. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  25. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -75
  26. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  27. data/lib/active_record/connection_adapters/abstract/query_cache.rb +14 -19
  28. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  29. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -6
  30. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +27 -9
  31. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  32. data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -57
  33. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -58
  34. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -15
  35. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  36. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
  37. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  38. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -16
  39. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  40. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  41. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
  42. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +12 -14
  43. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  44. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +51 -9
  45. data/lib/active_record/connection_adapters/postgresql_adapter.rb +44 -101
  46. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  47. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  48. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -13
  49. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  50. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  51. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -2
  52. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +60 -22
  53. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  54. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  55. data/lib/active_record/connection_handling.rb +29 -11
  56. data/lib/active_record/core.rb +15 -60
  57. data/lib/active_record/counter_cache.rb +1 -1
  58. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -3
  59. data/lib/active_record/delegated_type.rb +18 -18
  60. data/lib/active_record/encryption/config.rb +3 -1
  61. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  62. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  63. data/lib/active_record/encryption/encryptor.rb +35 -29
  64. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  65. data/lib/active_record/encryption/scheme.rb +8 -1
  66. data/lib/active_record/enum.rb +12 -13
  67. data/lib/active_record/errors.rb +16 -8
  68. data/lib/active_record/fixture_set/table_row.rb +2 -19
  69. data/lib/active_record/fixtures.rb +0 -1
  70. data/lib/active_record/future_result.rb +14 -10
  71. data/lib/active_record/gem_version.rb +4 -4
  72. data/lib/active_record/insert_all.rb +1 -1
  73. data/lib/active_record/marshalling.rb +1 -4
  74. data/lib/active_record/migration/command_recorder.rb +22 -5
  75. data/lib/active_record/migration/compatibility.rb +5 -2
  76. data/lib/active_record/migration.rb +36 -35
  77. data/lib/active_record/model_schema.rb +1 -1
  78. data/lib/active_record/nested_attributes.rb +4 -6
  79. data/lib/active_record/persistence.rb +128 -130
  80. data/lib/active_record/query_cache.rb +5 -4
  81. data/lib/active_record/query_logs.rb +98 -44
  82. data/lib/active_record/query_logs_formatter.rb +17 -28
  83. data/lib/active_record/querying.rb +10 -10
  84. data/lib/active_record/railtie.rb +5 -6
  85. data/lib/active_record/railties/databases.rake +1 -2
  86. data/lib/active_record/reflection.rb +9 -7
  87. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  88. data/lib/active_record/relation/batches.rb +132 -72
  89. data/lib/active_record/relation/calculations.rb +55 -55
  90. data/lib/active_record/relation/delegation.rb +25 -14
  91. data/lib/active_record/relation/finder_methods.rb +31 -32
  92. data/lib/active_record/relation/merger.rb +8 -8
  93. data/lib/active_record/relation/predicate_builder/association_query_value.rb +0 -2
  94. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  95. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  96. data/lib/active_record/relation/predicate_builder.rb +5 -0
  97. data/lib/active_record/relation/query_attribute.rb +1 -1
  98. data/lib/active_record/relation/query_methods.rb +90 -91
  99. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  100. data/lib/active_record/relation/spawn_methods.rb +1 -1
  101. data/lib/active_record/relation/where_clause.rb +2 -8
  102. data/lib/active_record/relation.rb +77 -76
  103. data/lib/active_record/result.rb +68 -7
  104. data/lib/active_record/sanitization.rb +7 -6
  105. data/lib/active_record/schema_dumper.rb +16 -29
  106. data/lib/active_record/schema_migration.rb +2 -1
  107. data/lib/active_record/scoping/named.rb +5 -2
  108. data/lib/active_record/secure_token.rb +3 -3
  109. data/lib/active_record/signed_id.rb +6 -7
  110. data/lib/active_record/statement_cache.rb +12 -12
  111. data/lib/active_record/store.rb +7 -3
  112. data/lib/active_record/tasks/database_tasks.rb +24 -15
  113. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  114. data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -7
  115. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  116. data/lib/active_record/test_fixtures.rb +12 -0
  117. data/lib/active_record/testing/query_assertions.rb +2 -2
  118. data/lib/active_record/token_for.rb +1 -1
  119. data/lib/active_record/transactions.rb +1 -3
  120. data/lib/active_record/validations/uniqueness.rb +8 -8
  121. data/lib/active_record.rb +16 -1
  122. data/lib/arel/collectors/bind.rb +1 -1
  123. data/lib/arel/crud.rb +0 -2
  124. data/lib/arel/delete_manager.rb +0 -5
  125. data/lib/arel/nodes/delete_statement.rb +2 -4
  126. data/lib/arel/nodes/update_statement.rb +2 -4
  127. data/lib/arel/select_manager.rb +2 -6
  128. data/lib/arel/update_manager.rb +0 -5
  129. data/lib/arel/visitors/dot.rb +0 -2
  130. data/lib/arel/visitors/sqlite.rb +0 -25
  131. data/lib/arel/visitors/to_sql.rb +1 -3
  132. metadata +14 -11
@@ -43,7 +43,6 @@ module ActiveRecord
43
43
 
44
44
  attr_reader :pool
45
45
  attr_reader :visitor, :owner, :logger, :lock
46
- attr_accessor :pinned # :nodoc:
47
46
  alias :in_use? :owner
48
47
 
49
48
  def pool=(value)
@@ -152,7 +151,6 @@ module ActiveRecord
152
151
  end
153
152
 
154
153
  @owner = nil
155
- @pinned = false
156
154
  @instrumenter = ActiveSupport::Notifications.instrumenter
157
155
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
158
156
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -171,7 +169,6 @@ module ActiveRecord
171
169
  @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
172
170
 
173
171
  @raw_connection_dirty = false
174
- @last_activity = nil
175
172
  @verified = false
176
173
  end
177
174
 
@@ -221,10 +218,6 @@ module ActiveRecord
221
218
  (@config[:connection_retries] || 1).to_i
222
219
  end
223
220
 
224
- def verify_timeout
225
- (@config[:verify_timeout] || 2).to_i
226
- end
227
-
228
221
  def retry_deadline
229
222
  if @config[:retry_deadline]
230
223
  @config[:retry_deadline].to_f
@@ -351,13 +344,6 @@ module ActiveRecord
351
344
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
352
345
  end
353
346
 
354
- # Seconds since this connection last communicated with the server
355
- def seconds_since_last_activity # :nodoc:
356
- if @raw_connection && @last_activity
357
- Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
358
- end
359
- end
360
-
361
347
  def unprepared_statement
362
348
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
363
349
  yield
@@ -590,23 +576,31 @@ module ActiveRecord
590
576
  end
591
577
 
592
578
  # This is meant to be implemented by the adapters that support custom enum types
593
- def create_enum(*) # :nodoc:
579
+ def create_enum(...) # :nodoc:
594
580
  end
595
581
 
596
582
  # This is meant to be implemented by the adapters that support custom enum types
597
- def drop_enum(*) # :nodoc:
583
+ def drop_enum(...) # :nodoc:
598
584
  end
599
585
 
600
586
  # This is meant to be implemented by the adapters that support custom enum types
601
- def rename_enum(*) # :nodoc:
587
+ def rename_enum(...) # :nodoc:
602
588
  end
603
589
 
604
590
  # This is meant to be implemented by the adapters that support custom enum types
605
- def add_enum_value(*) # :nodoc:
591
+ def add_enum_value(...) # :nodoc:
606
592
  end
607
593
 
608
594
  # This is meant to be implemented by the adapters that support custom enum types
609
- def rename_enum_value(*) # :nodoc:
595
+ def rename_enum_value(...) # :nodoc:
596
+ end
597
+
598
+ # This is meant to be implemented by the adapters that support virtual tables
599
+ def create_virtual_table(*) # :nodoc:
600
+ end
601
+
602
+ # This is meant to be implemented by the adapters that support virtual tables
603
+ def drop_virtual_table(*) # :nodoc:
610
604
  end
611
605
 
612
606
  def advisory_locks_enabled? # :nodoc:
@@ -677,12 +671,11 @@ module ActiveRecord
677
671
 
678
672
  enable_lazy_transactions!
679
673
  @raw_connection_dirty = false
680
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
681
674
  @verified = true
682
675
 
683
676
  reset_transaction(restore: restore_transactions) do
684
677
  clear_cache!(new_connection: true)
685
- attempt_configure_connection
678
+ configure_connection
686
679
  end
687
680
  rescue => original_exception
688
681
  translated_exception = translate_exception_class(original_exception, nil, nil)
@@ -697,7 +690,6 @@ module ActiveRecord
697
690
  end
698
691
  end
699
692
 
700
- @last_activity = nil
701
693
  @verified = false
702
694
 
703
695
  raise translated_exception
@@ -735,7 +727,7 @@ module ActiveRecord
735
727
  def reset!
736
728
  clear_cache!(new_connection: true)
737
729
  reset_transaction
738
- attempt_configure_connection
730
+ configure_connection
739
731
  end
740
732
 
741
733
  # Removes the connection from the pool and disconnect it.
@@ -771,8 +763,7 @@ module ActiveRecord
771
763
  if @unconfigured_connection
772
764
  @raw_connection = @unconfigured_connection
773
765
  @unconfigured_connection = nil
774
- attempt_configure_connection
775
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
766
+ configure_connection
776
767
  @verified = true
777
768
  return
778
769
  end
@@ -1002,9 +993,6 @@ module ActiveRecord
1002
993
  if @verified
1003
994
  # Cool, we're confident the connection's ready to use. (Note this might have
1004
995
  # become true during the above #materialize_transactions.)
1005
- elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
1006
- # We haven't actually verified the connection since we acquired it, but it
1007
- # has been used very recently. We're going to assume it's still okay.
1008
996
  elsif reconnectable
1009
997
  if allow_retry
1010
998
  # Not sure about the connection yet, but if anything goes wrong we can
@@ -1046,7 +1034,6 @@ module ActiveRecord
1046
1034
  # Barring a known-retryable error inside the query (regardless of
1047
1035
  # whether we were in a _position_ to retry it), we should infer that
1048
1036
  # there's likely a real problem with the connection.
1049
- @last_activity = nil
1050
1037
  @verified = false
1051
1038
  end
1052
1039
 
@@ -1061,12 +1048,12 @@ module ActiveRecord
1061
1048
  # `with_raw_connection` block only when the block is guaranteed to
1062
1049
  # exercise the raw connection.
1063
1050
  def verified!
1064
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1065
1051
  @verified = true
1066
1052
  end
1067
1053
 
1068
1054
  def retryable_connection_error?(exception)
1069
- exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1055
+ (exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
1056
+ exception.is_a?(ConnectionFailed)
1070
1057
  end
1071
1058
 
1072
1059
  def invalidate_transaction(exception)
@@ -1127,24 +1114,25 @@ module ActiveRecord
1127
1114
  end
1128
1115
  end
1129
1116
 
1130
- def translate_exception_class(e, sql, binds)
1131
- message = "#{e.class.name}: #{e.message}"
1117
+ def translate_exception_class(native_error, sql, binds)
1118
+ return native_error if native_error.is_a?(ActiveRecordError)
1132
1119
 
1133
- exception = translate_exception(
1134
- e, message: message, sql: sql, binds: binds
1120
+ message = "#{native_error.class.name}: #{native_error.message}"
1121
+
1122
+ active_record_error = translate_exception(
1123
+ native_error, message: message, sql: sql, binds: binds
1135
1124
  )
1136
- exception.set_backtrace e.backtrace
1137
- exception
1125
+ active_record_error.set_backtrace(native_error.backtrace)
1126
+ active_record_error
1138
1127
  end
1139
1128
 
1140
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
1129
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1141
1130
  @instrumenter.instrument(
1142
1131
  "sql.active_record",
1143
1132
  sql: sql,
1144
1133
  name: name,
1145
1134
  binds: binds,
1146
1135
  type_casted_binds: type_casted_binds,
1147
- statement_name: statement_name,
1148
1136
  async: async,
1149
1137
  connection: self,
1150
1138
  transaction: current_transaction.user_transaction.presence,
@@ -1155,13 +1143,6 @@ module ActiveRecord
1155
1143
  raise ex.set_query(sql, binds)
1156
1144
  end
1157
1145
 
1158
- def transform_query(sql)
1159
- ActiveRecord.query_transformers.each do |transformer|
1160
- sql = transformer.call(sql, self)
1161
- end
1162
- sql
1163
- end
1164
-
1165
1146
  def translate_exception(exception, message:, sql:, binds:)
1166
1147
  # override in derived class
1167
1148
  case exception
@@ -1172,10 +1153,6 @@ module ActiveRecord
1172
1153
  end
1173
1154
  end
1174
1155
 
1175
- def without_prepared_statement?(binds)
1176
- !prepared_statements || binds.empty?
1177
- end
1178
-
1179
1156
  def column_for(table_name, column_name)
1180
1157
  column_name = column_name.to_s
1181
1158
  columns(table_name).detect { |c| c.name == column_name } ||
@@ -1227,13 +1204,6 @@ module ActiveRecord
1227
1204
  check_version
1228
1205
  end
1229
1206
 
1230
- def attempt_configure_connection
1231
- configure_connection
1232
- rescue Exception # Need to handle things such as Timeout::ExitException
1233
- disconnect!
1234
- raise
1235
- end
1236
-
1237
1207
  def default_prepared_statements
1238
1208
  true
1239
1209
  end
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
 
80
80
  args << config.database
81
81
 
82
- find_cmd_and_exec(["mysql", "mysql5"], *args)
82
+ find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
83
83
  end
84
84
  end
85
85
 
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  end
139
139
 
140
140
  def supports_datetime_with_precision?
141
- mariadb? || database_version >= "5.6.4"
141
+ true
142
142
  end
143
143
 
144
144
  def supports_virtual_columns?
@@ -174,10 +174,6 @@ module ActiveRecord
174
174
  mariadb? && database_version >= "10.5.0"
175
175
  end
176
176
 
177
- def return_value_after_insert?(column) # :nodoc:
178
- supports_insert_returning? ? column.auto_populated? : column.auto_increment?
179
- end
180
-
181
177
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
182
178
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
183
179
  end
@@ -201,12 +197,6 @@ module ActiveRecord
201
197
 
202
198
  # HELPER METHODS ===========================================
203
199
 
204
- # The two drivers have slightly different ways of yielding hashes of results, so
205
- # this method must be implemented to provide a uniform interface.
206
- def each_hash(result) # :nodoc:
207
- raise NotImplementedError
208
- end
209
-
210
200
  # Must return the MySQL error number from the exception, if the exception has an
211
201
  # error number.
212
202
  def error_number(exception) # :nodoc:
@@ -230,24 +220,19 @@ module ActiveRecord
230
220
  # DATABASE STATEMENTS ======================================
231
221
  #++
232
222
 
233
- # Mysql2Adapter doesn't have to free a result after using it, but we use this method
234
- # to write stuff in an abstract way without concerning ourselves about whether it
235
- # needs to be explicitly freed or not.
236
- def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
237
- sql = transform_query(sql)
238
- check_if_write_query(sql)
239
-
240
- mark_transaction_written_if_write(sql)
241
- yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
242
- end
243
-
244
223
  def begin_db_transaction # :nodoc:
245
224
  internal_execute("BEGIN", "TRANSACTION", allow_retry: true, materialize_transactions: false)
246
225
  end
247
226
 
248
227
  def begin_isolated_db_transaction(isolation) # :nodoc:
249
- internal_execute("SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
250
- begin_db_transaction
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
+ )
251
236
  end
252
237
 
253
238
  def commit_db_transaction # :nodoc:
@@ -347,7 +332,7 @@ module ActiveRecord
347
332
  rename_table_indexes(table_name, new_name, **options)
348
333
  end
349
334
 
350
- # Drops a table from the database.
335
+ # Drops a table or tables from the database.
351
336
  #
352
337
  # [<tt>:force</tt>]
353
338
  # Set to +:cascade+ to drop dependent objects as well.
@@ -361,10 +346,10 @@ module ActiveRecord
361
346
  #
362
347
  # Although this command ignores most +options+ and the block if one is given,
363
348
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
364
- # In that case, +options+ and the block will be used by create_table.
365
- def drop_table(table_name, **options)
366
- schema_cache.clear_data_source_cache!(table_name.to_s)
367
- execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
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}"
368
353
  end
369
354
 
370
355
  def rename_index(table_name, old_name, new_name)
@@ -418,11 +403,7 @@ module ActiveRecord
418
403
  type ||= column.sql_type
419
404
 
420
405
  unless options.key?(:default)
421
- options[:default] = if column.default_function
422
- -> { column.default_function }
423
- else
424
- column.default
425
- end
406
+ options[:default] = column.default
426
407
  end
427
408
 
428
409
  unless options.key?(:null)
@@ -588,7 +569,7 @@ module ActiveRecord
588
569
 
589
570
  # SHOW VARIABLES LIKE 'name'
590
571
  def show_variable(name)
591
- query_value("SELECT @@#{name}", "SCHEMA")
572
+ query_value("SELECT @@#{name}", "SCHEMA", materialize_transactions: false, allow_retry: true)
592
573
  rescue ActiveRecord::StatementInvalid
593
574
  nil
594
575
  end
@@ -647,26 +628,22 @@ module ActiveRecord
647
628
  end
648
629
 
649
630
  def build_insert_sql(insert) # :nodoc:
650
- # Can use any column as it will be assigned to itself.
651
- no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
631
+ no_op_column = quote_column_name(insert.keys.first)
652
632
 
653
633
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
654
634
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
655
635
  if supports_insert_raw_alias_syntax?
656
- quoted_table_name = insert.model.quoted_table_name
657
636
  values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
658
637
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
659
638
 
660
639
  if insert.skip_duplicates?
661
- if no_op_column
662
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
663
- end
640
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
664
641
  elsif insert.update_duplicates?
665
642
  if insert.raw_update_sql?
666
643
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
667
644
  else
668
645
  sql << " ON DUPLICATE KEY UPDATE "
669
- sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
646
+ sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
670
647
  sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
671
648
  end
672
649
  end
@@ -674,9 +651,7 @@ module ActiveRecord
674
651
  sql = +"INSERT #{insert.into} #{insert.values_list}"
675
652
 
676
653
  if insert.skip_duplicates?
677
- if no_op_column
678
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
679
- end
654
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
680
655
  elsif insert.update_duplicates?
681
656
  sql << " ON DUPLICATE KEY UPDATE "
682
657
  if insert.raw_update_sql?
@@ -693,8 +668,8 @@ module ActiveRecord
693
668
  end
694
669
 
695
670
  def check_version # :nodoc:
696
- if database_version < "5.5.8"
697
- raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
671
+ if database_version < "5.6.4"
672
+ raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.6.4."
698
673
  end
699
674
  end
700
675
 
@@ -767,7 +742,9 @@ module ActiveRecord
767
742
 
768
743
  private
769
744
  def strip_whitespace_characters(expression)
770
- expression.gsub('\\\n', "").gsub("x0A", "").squish
745
+ expression = expression.gsub(/\\n|\\\\/, "")
746
+ expression = expression.gsub(/\s{2,}/, " ")
747
+ expression
771
748
  end
772
749
 
773
750
  def extended_type_map_key
@@ -799,11 +776,6 @@ module ActiveRecord
799
776
  warning.level == "Note" || super
800
777
  end
801
778
 
802
- # Make sure we carry over any changes to ActiveRecord.default_timezone that have been
803
- # made since we established the connection
804
- def sync_timezone_changes(raw_connection)
805
- end
806
-
807
779
  # See https://dev.mysql.com/doc/mysql-errors/en/server-error-reference.html
808
780
  ER_DB_CREATE_EXISTS = 1007
809
781
  ER_FILSORT_ABORT = 1028
@@ -973,13 +945,11 @@ module ActiveRecord
973
945
  end.join(", ")
974
946
 
975
947
  # ...and send them all in one query
976
- internal_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}")
948
+ raw_execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
977
949
  end
978
950
 
979
951
  def column_definitions(table_name) # :nodoc:
980
- execute_and_free("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
981
- each_hash(result)
982
- end
952
+ internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
983
953
  end
984
954
 
985
955
  def create_table_info(table_name) # :nodoc:
@@ -77,14 +77,6 @@ module ActiveRecord
77
77
  0
78
78
  end
79
79
 
80
- def quoted_date(value)
81
- if supports_datetime_with_precision?
82
- super
83
- else
84
- super.sub(/\.\d{6}\z/, "")
85
- end
86
- end
87
-
88
80
  def quoted_binary(value)
89
81
  "x'#{value.hex}'"
90
82
  end
@@ -110,13 +102,7 @@ module ActiveRecord
110
102
  else
111
103
  value.getlocal
112
104
  end
113
- when Time
114
- if default_timezone == :utc
115
- value.utc? ? value : value.getutc
116
- else
117
- value.utc? ? value.getlocal : value
118
- end
119
- when Date
105
+ when Date, Time
120
106
  value
121
107
  else
122
108
  super
@@ -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
- execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
12
- each_hash(result) do |row|
13
- if current_index != row[:Key_name]
14
- next if row[:Key_name] == "PRIMARY" # skip the primary key
15
- current_index = row[:Key_name]
16
-
17
- mysql_index_type = row[:Index_type].downcase.to_sym
18
- case mysql_index_type
19
- when :fulltext, :spatial
20
- index_type = mysql_index_type
21
- when :btree, :hash
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
- if row[:Expression]
39
- expression = row[:Expression].gsub("\\'", "'")
40
- expression = +"(#{expression})" unless expression.start_with?("(")
41
- indexes.last[-2] << expression
42
- indexes.last[-1][:expressions] ||= {}
43
- indexes.last[-1][:expressions][expression] = expression
44
- indexes.last[-1][:orders][expression] = :desc if row[:Collation] == "D"
45
- else
46
- indexes.last[-2] << row[:Column_name]
47
- indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
48
- indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == "D"
49
- end
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(:Field)
186
- type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
187
- default, default_function = field[:Default], nil
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[:Extra])
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[:Field],
204
+ field["Field"],
207
205
  default,
208
206
  type_metadata,
209
- field[:Null] == "YES",
207
+ field["Null"] == "YES",
210
208
  default_function,
211
- collation: field[:Collation],
212
- comment: field[:Comment].presence
209
+ collation: field["Collation"],
210
+ comment: field["Comment"].presence
213
211
  )
214
212
  end
215
213