activerecord 8.0.3 → 8.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +427 -522
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +382 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +66 -14
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -30
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/core.rb +5 -4
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +50 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +24 -8
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +1 -5
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +26 -16
- data/lib/active_record/model_schema.rb +10 -7
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +3 -7
- data/lib/active_record/railtie.rb +32 -3
- data/lib/active_record/railties/databases.rake +16 -4
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +20 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -11
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +39 -29
- data/lib/active_record/relation/where_clause.rb +1 -10
- data/lib/active_record/relation.rb +25 -13
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +2 -21
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +10 -2
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +32 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +65 -3
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- metadata +13 -9
- data/lib/active_record/normalization.rb +0 -163
@@ -112,6 +112,7 @@ module ActiveRecord
|
|
112
112
|
def closed?; true; end
|
113
113
|
def open?; false; end
|
114
114
|
def joinable?; false; end
|
115
|
+
def isolation; nil; end
|
115
116
|
def add_record(record, _ = true); end
|
116
117
|
def restartable?; false; end
|
117
118
|
def dirty?; false; end
|
@@ -150,6 +151,11 @@ module ActiveRecord
|
|
150
151
|
|
151
152
|
delegate :invalidate!, :invalidated?, to: :@state
|
152
153
|
|
154
|
+
# Returns the isolation level if it was explicitly set, nil otherwise
|
155
|
+
def isolation
|
156
|
+
@isolation_level
|
157
|
+
end
|
158
|
+
|
153
159
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
154
160
|
super()
|
155
161
|
@connection = connection
|
@@ -175,11 +181,11 @@ module ActiveRecord
|
|
175
181
|
end
|
176
182
|
|
177
183
|
def open?
|
178
|
-
|
184
|
+
!closed?
|
179
185
|
end
|
180
186
|
|
181
187
|
def closed?
|
182
|
-
|
188
|
+
@state.finalized?
|
183
189
|
end
|
184
190
|
|
185
191
|
def add_record(record, ensure_finalize = true)
|
@@ -386,7 +392,7 @@ module ActiveRecord
|
|
386
392
|
@parent.state.add_child(@state)
|
387
393
|
end
|
388
394
|
|
389
|
-
delegate :materialize!, :materialized?, :restart, to: :@parent
|
395
|
+
delegate :materialize!, :materialized?, :restart, :isolation, to: :@parent
|
390
396
|
|
391
397
|
def rollback
|
392
398
|
@state.rollback!
|
@@ -405,6 +411,7 @@ module ActiveRecord
|
|
405
411
|
def initialize(connection, savepoint_name, parent_transaction, **options)
|
406
412
|
super(connection, **options)
|
407
413
|
|
414
|
+
@parent_transaction = parent_transaction
|
408
415
|
parent_transaction.state.add_child(@state)
|
409
416
|
|
410
417
|
if isolation_level
|
@@ -414,6 +421,11 @@ module ActiveRecord
|
|
414
421
|
@savepoint_name = savepoint_name
|
415
422
|
end
|
416
423
|
|
424
|
+
# Delegates to parent transaction's isolation level
|
425
|
+
def isolation
|
426
|
+
@parent_transaction.isolation
|
427
|
+
end
|
428
|
+
|
417
429
|
def materialize!
|
418
430
|
connection.create_savepoint(savepoint_name)
|
419
431
|
super
|
@@ -620,6 +632,7 @@ module ActiveRecord
|
|
620
632
|
end
|
621
633
|
|
622
634
|
def within_new_transaction(isolation: nil, joinable: true)
|
635
|
+
isolation ||= @connection.pool.pool_transaction_isolation_level
|
623
636
|
@connection.lock.synchronize do
|
624
637
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
625
638
|
begin
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
attr_reader :pool
|
44
44
|
attr_reader :visitor, :owner, :logger, :lock
|
45
|
-
attr_accessor :
|
45
|
+
attr_accessor :allow_preconnect
|
46
46
|
alias :in_use? :owner
|
47
47
|
|
48
48
|
def pool=(value)
|
@@ -120,7 +120,7 @@ module ActiveRecord
|
|
120
120
|
|
121
121
|
# Opens a database console session.
|
122
122
|
def self.dbconsole(config, options = {})
|
123
|
-
raise NotImplementedError
|
123
|
+
raise NotImplementedError.new("#{self.class} should define `dbconsole` that accepts a db config and options to implement connecting to the db console")
|
124
124
|
end
|
125
125
|
|
126
126
|
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
@@ -128,6 +128,7 @@ module ActiveRecord
|
|
128
128
|
|
129
129
|
@raw_connection = nil
|
130
130
|
@unconfigured_connection = nil
|
131
|
+
@connected_since = nil
|
131
132
|
|
132
133
|
if config_or_deprecated_connection.is_a?(Hash)
|
133
134
|
@config = config_or_deprecated_connection.symbolize_keys
|
@@ -140,6 +141,7 @@ module ActiveRecord
|
|
140
141
|
# Soft-deprecated for now; we'll probably warn in future.
|
141
142
|
|
142
143
|
@unconfigured_connection = config_or_deprecated_connection
|
144
|
+
@connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
143
145
|
@logger = deprecated_logger || ActiveRecord::Base.logger
|
144
146
|
if deprecated_config
|
145
147
|
@config = (deprecated_config || {}).symbolize_keys
|
@@ -151,9 +153,9 @@ module ActiveRecord
|
|
151
153
|
end
|
152
154
|
|
153
155
|
@owner = nil
|
154
|
-
@pinned = false
|
155
156
|
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
156
157
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
158
|
+
@allow_preconnect = true
|
157
159
|
@visitor = arel_visitor
|
158
160
|
@statements = build_statement_pool
|
159
161
|
self.lock_thread = nil
|
@@ -171,6 +173,8 @@ module ActiveRecord
|
|
171
173
|
@raw_connection_dirty = false
|
172
174
|
@last_activity = nil
|
173
175
|
@verified = false
|
176
|
+
|
177
|
+
@pool_jitter = rand * max_jitter
|
174
178
|
end
|
175
179
|
|
176
180
|
def inspect # :nodoc:
|
@@ -198,6 +202,11 @@ module ActiveRecord
|
|
198
202
|
end
|
199
203
|
end
|
200
204
|
|
205
|
+
MAX_JITTER = 0.0..1.0 # :nodoc:
|
206
|
+
def max_jitter
|
207
|
+
(@config[:pool_jitter] || 0.2).to_f.clamp(MAX_JITTER)
|
208
|
+
end
|
209
|
+
|
201
210
|
def replica?
|
202
211
|
@config[:replica] || false
|
203
212
|
end
|
@@ -262,7 +271,11 @@ module ActiveRecord
|
|
262
271
|
end
|
263
272
|
|
264
273
|
def valid_type?(type) # :nodoc:
|
265
|
-
|
274
|
+
self.class.valid_type?(type)
|
275
|
+
end
|
276
|
+
|
277
|
+
def native_database_types # :nodoc:
|
278
|
+
self.class.native_database_types
|
266
279
|
end
|
267
280
|
|
268
281
|
# this method must only be called while holding connection pool's mutex
|
@@ -301,8 +314,12 @@ module ActiveRecord
|
|
301
314
|
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
302
315
|
end
|
303
316
|
|
317
|
+
def pool_jitter(duration)
|
318
|
+
duration * (1.0 - @pool_jitter)
|
319
|
+
end
|
320
|
+
|
304
321
|
# this method must only be called while holding connection pool's mutex
|
305
|
-
def expire
|
322
|
+
def expire(update_idle = true) # :nodoc:
|
306
323
|
if in_use?
|
307
324
|
if @owner != ActiveSupport::IsolatedExecutionState.context
|
308
325
|
raise ActiveRecordError, "Cannot expire connection, " \
|
@@ -310,7 +327,7 @@ module ActiveRecord
|
|
310
327
|
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
311
328
|
end
|
312
329
|
|
313
|
-
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
330
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) if update_idle
|
314
331
|
@owner = nil
|
315
332
|
else
|
316
333
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
@@ -343,6 +360,21 @@ module ActiveRecord
|
|
343
360
|
end
|
344
361
|
end
|
345
362
|
|
363
|
+
# Seconds since this connection was established. nil if not
|
364
|
+
# connected; infinity if the connection has been explicitly
|
365
|
+
# retired.
|
366
|
+
def connection_age # :nodoc:
|
367
|
+
if @raw_connection && @connected_since
|
368
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @connected_since
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Mark the connection as needing to be retired, as if the age has
|
373
|
+
# exceeded the maximum allowed.
|
374
|
+
def force_retirement # :nodoc:
|
375
|
+
@connected_since &&= -Float::INFINITY
|
376
|
+
end
|
377
|
+
|
346
378
|
def unprepared_statement
|
347
379
|
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
348
380
|
yield
|
@@ -557,6 +589,10 @@ module ActiveRecord
|
|
557
589
|
false
|
558
590
|
end
|
559
591
|
|
592
|
+
def supports_disabling_indexes?
|
593
|
+
false
|
594
|
+
end
|
595
|
+
|
560
596
|
def return_value_after_insert?(column) # :nodoc:
|
561
597
|
column.auto_populated?
|
562
598
|
end
|
@@ -666,12 +702,15 @@ module ActiveRecord
|
|
666
702
|
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
667
703
|
|
668
704
|
@lock.synchronize do
|
705
|
+
@allow_preconnect = false
|
706
|
+
|
669
707
|
reconnect
|
670
708
|
|
671
709
|
enable_lazy_transactions!
|
672
710
|
@raw_connection_dirty = false
|
673
|
-
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
711
|
+
@last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
674
712
|
@verified = true
|
713
|
+
@allow_preconnect = true
|
675
714
|
|
676
715
|
reset_transaction(restore: restore_transactions) do
|
677
716
|
clear_cache!(new_connection: true)
|
@@ -704,6 +743,7 @@ module ActiveRecord
|
|
704
743
|
clear_cache!(new_connection: true)
|
705
744
|
reset_transaction
|
706
745
|
@raw_connection_dirty = false
|
746
|
+
@connected_since = nil
|
707
747
|
end
|
708
748
|
end
|
709
749
|
|
@@ -767,6 +807,7 @@ module ActiveRecord
|
|
767
807
|
attempt_configure_connection
|
768
808
|
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
769
809
|
@verified = true
|
810
|
+
@allow_preconnect = true
|
770
811
|
return
|
771
812
|
end
|
772
813
|
|
@@ -774,6 +815,7 @@ module ActiveRecord
|
|
774
815
|
end
|
775
816
|
end
|
776
817
|
|
818
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
777
819
|
@verified = true
|
778
820
|
end
|
779
821
|
|
@@ -787,6 +829,10 @@ module ActiveRecord
|
|
787
829
|
@verified = nil
|
788
830
|
end
|
789
831
|
|
832
|
+
def verified? # :nodoc:
|
833
|
+
@verified
|
834
|
+
end
|
835
|
+
|
790
836
|
# Provides access to the underlying database driver for this adapter. For
|
791
837
|
# example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
|
792
838
|
# and a PG::Connection object in case of PostgreSQLAdapter.
|
@@ -872,7 +918,7 @@ module ActiveRecord
|
|
872
918
|
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
873
919
|
mapping.register_type(key) do |*args|
|
874
920
|
precision = extract_precision(args.last)
|
875
|
-
klass.new(precision: precision, **kwargs)
|
921
|
+
klass.new(precision: precision, **kwargs).freeze
|
876
922
|
end
|
877
923
|
end
|
878
924
|
|
@@ -884,6 +930,10 @@ module ActiveRecord
|
|
884
930
|
end
|
885
931
|
end
|
886
932
|
|
933
|
+
def valid_type?(type) # :nodoc:
|
934
|
+
!native_database_types[type].nil?
|
935
|
+
end
|
936
|
+
|
887
937
|
private
|
888
938
|
def initialize_type_map(m)
|
889
939
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
@@ -903,7 +953,7 @@ module ActiveRecord
|
|
903
953
|
m.alias_type %r(number)i, "decimal"
|
904
954
|
m.alias_type %r(double)i, "float"
|
905
955
|
|
906
|
-
m.register_type %r(^json)i, Type::Json.new
|
956
|
+
m.register_type %r(^json)i, Type::Json.new.freeze
|
907
957
|
|
908
958
|
m.register_type(%r(decimal)i) do |sql_type|
|
909
959
|
scale = extract_scale(sql_type)
|
@@ -911,9 +961,9 @@ module ActiveRecord
|
|
911
961
|
|
912
962
|
if scale == 0
|
913
963
|
# FIXME: Remove this class as well
|
914
|
-
Type::DecimalWithoutScale.new(precision: precision)
|
964
|
+
Type::DecimalWithoutScale.new(precision: precision).freeze
|
915
965
|
else
|
916
|
-
Type::Decimal.new(precision: precision, scale: scale)
|
966
|
+
Type::Decimal.new(precision: precision, scale: scale).freeze
|
917
967
|
end
|
918
968
|
end
|
919
969
|
end
|
@@ -921,7 +971,7 @@ module ActiveRecord
|
|
921
971
|
def register_class_with_limit(mapping, key, klass)
|
922
972
|
mapping.register_type(key) do |*args|
|
923
973
|
limit = extract_limit(args.last)
|
924
|
-
klass.new(limit: limit)
|
974
|
+
klass.new(limit: limit).freeze
|
925
975
|
end
|
926
976
|
end
|
927
977
|
|
@@ -1082,7 +1132,7 @@ module ActiveRecord
|
|
1082
1132
|
end
|
1083
1133
|
|
1084
1134
|
def reconnect
|
1085
|
-
raise NotImplementedError
|
1135
|
+
raise NotImplementedError.new("#{self.class} should define `reconnect` to implement adapter-specific logic for reconnecting to the database")
|
1086
1136
|
end
|
1087
1137
|
|
1088
1138
|
# Returns a raw connection for internal use with methods that are known
|
@@ -1133,7 +1183,7 @@ module ActiveRecord
|
|
1133
1183
|
active_record_error
|
1134
1184
|
end
|
1135
1185
|
|
1136
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
|
1186
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, allow_retry: false, &block) # :doc:
|
1137
1187
|
instrumenter.instrument(
|
1138
1188
|
"sql.active_record",
|
1139
1189
|
sql: sql,
|
@@ -1141,8 +1191,10 @@ module ActiveRecord
|
|
1141
1191
|
binds: binds,
|
1142
1192
|
type_casted_binds: type_casted_binds,
|
1143
1193
|
async: async,
|
1194
|
+
allow_retry: allow_retry,
|
1144
1195
|
connection: self,
|
1145
1196
|
transaction: current_transaction.user_transaction.presence,
|
1197
|
+
affected_rows: 0,
|
1146
1198
|
row_count: 0,
|
1147
1199
|
&block
|
1148
1200
|
)
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
date: { name: "date" },
|
43
43
|
binary: { name: "blob" },
|
44
44
|
blob: { name: "blob" },
|
45
|
-
boolean: { name: "
|
45
|
+
boolean: { name: "boolean" },
|
46
46
|
json: { name: "json" },
|
47
47
|
}
|
48
48
|
|
@@ -81,6 +81,10 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
find_cmd_and_exec(ActiveRecord.database_cli[:mysql], *args)
|
83
83
|
end
|
84
|
+
|
85
|
+
def native_database_types # :nodoc:
|
86
|
+
NATIVE_DATABASE_TYPES
|
87
|
+
end
|
84
88
|
end
|
85
89
|
|
86
90
|
def get_database_version # :nodoc:
|
@@ -178,6 +182,16 @@ module ActiveRecord
|
|
178
182
|
supports_insert_returning? ? column.auto_populated? : column.auto_increment?
|
179
183
|
end
|
180
184
|
|
185
|
+
# See https://dev.mysql.com/doc/refman/8.0/en/invisible-indexes.html for more details on MySQL feature.
|
186
|
+
# See https://mariadb.com/kb/en/ignored-indexes/ for more details on the MariaDB feature.
|
187
|
+
def supports_disabling_indexes?
|
188
|
+
if mariadb?
|
189
|
+
database_version >= "10.6.0"
|
190
|
+
else
|
191
|
+
database_version >= "8.0.0"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
181
195
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
182
196
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
183
197
|
end
|
@@ -186,10 +200,6 @@ module ActiveRecord
|
|
186
200
|
query_value("SELECT RELEASE_LOCK(#{quote(lock_name.to_s)})") == 1
|
187
201
|
end
|
188
202
|
|
189
|
-
def native_database_types
|
190
|
-
NATIVE_DATABASE_TYPES
|
191
|
-
end
|
192
|
-
|
193
203
|
def index_algorithms
|
194
204
|
{
|
195
205
|
default: "ALGORITHM = DEFAULT",
|
@@ -457,6 +467,24 @@ module ActiveRecord
|
|
457
467
|
CreateIndexDefinition.new(index, algorithm)
|
458
468
|
end
|
459
469
|
|
470
|
+
def enable_index(table_name, index_name) # :nodoc:
|
471
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
472
|
+
|
473
|
+
query = <<~SQL
|
474
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
|
475
|
+
SQL
|
476
|
+
execute(query)
|
477
|
+
end
|
478
|
+
|
479
|
+
def disable_index(table_name, index_name) # :nodoc:
|
480
|
+
raise NotImplementedError unless supports_disabling_indexes?
|
481
|
+
|
482
|
+
query = <<~SQL
|
483
|
+
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "IGNORED" : "INVISIBLE"}
|
484
|
+
SQL
|
485
|
+
execute(query)
|
486
|
+
end
|
487
|
+
|
460
488
|
def add_sql_comment!(sql, comment) # :nodoc:
|
461
489
|
sql << " COMMENT #{quote(comment)}" if comment.present?
|
462
490
|
sql
|
@@ -732,12 +760,12 @@ module ActiveRecord
|
|
732
760
|
m.alias_type %r(bit)i, "binary"
|
733
761
|
end
|
734
762
|
|
735
|
-
def register_integer_type(mapping, key,
|
763
|
+
def register_integer_type(mapping, key, limit:)
|
736
764
|
mapping.register_type(key) do |sql_type|
|
737
765
|
if /\bunsigned\b/.match?(sql_type)
|
738
|
-
Type::UnsignedInteger.new(
|
766
|
+
Type::UnsignedInteger.new(limit: limit)
|
739
767
|
else
|
740
|
-
Type::Integer.new(
|
768
|
+
Type::Integer.new(limit: limit)
|
741
769
|
end
|
742
770
|
end
|
743
771
|
end
|
@@ -767,13 +795,13 @@ module ActiveRecord
|
|
767
795
|
end
|
768
796
|
end
|
769
797
|
|
770
|
-
def handle_warnings(sql)
|
798
|
+
def handle_warnings(_initial_result, sql)
|
771
799
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
772
800
|
|
773
801
|
warning_count = @raw_connection.warning_count
|
774
802
|
result = @raw_connection.query("SHOW WARNINGS")
|
775
803
|
result = [
|
776
|
-
["Warning", nil, "Query had warning_count=#{warning_count} but
|
804
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but `SHOW WARNINGS` did not return the warnings. Check MySQL logs or database configuration."],
|
777
805
|
] if result.count == 0
|
778
806
|
result.each do |level, code, message|
|
779
807
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
@@ -810,6 +838,8 @@ module ActiveRecord
|
|
810
838
|
CR_SERVER_LOST = 2013
|
811
839
|
ER_QUERY_TIMEOUT = 3024
|
812
840
|
ER_FK_INCOMPATIBLE_COLUMNS = 3780
|
841
|
+
ER_CHECK_CONSTRAINT_VIOLATED = 3819
|
842
|
+
ER_CONSTRAINT_FAILED = 4025
|
813
843
|
ER_CLIENT_INTERACTION_TIMEOUT = 4031
|
814
844
|
|
815
845
|
def translate_exception(exception, message:, sql:, binds:)
|
@@ -842,6 +872,8 @@ module ActiveRecord
|
|
842
872
|
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
843
873
|
when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
|
844
874
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
875
|
+
when ER_CHECK_CONSTRAINT_VIOLATED, ER_CONSTRAINT_FAILED
|
876
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
845
877
|
when ER_LOCK_DEADLOCK
|
846
878
|
Deadlocked.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
847
879
|
when ER_LOCK_WAIT_TIMEOUT
|
@@ -960,7 +992,7 @@ module ActiveRecord
|
|
960
992
|
end
|
961
993
|
|
962
994
|
def column_definitions(table_name) # :nodoc:
|
963
|
-
internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA")
|
995
|
+
internal_exec_query("SHOW FULL FIELDS FROM #{quote_table_name(table_name)}", "SCHEMA", allow_retry: true)
|
964
996
|
end
|
965
997
|
|
966
998
|
def create_table_info(table_name) # :nodoc:
|
@@ -17,16 +17,22 @@ module ActiveRecord
|
|
17
17
|
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
18
18
|
# +sql_type_metadata+ is various information about the type of the column
|
19
19
|
# +null+ determines if this column allows +NULL+ values.
|
20
|
-
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
|
20
|
+
def initialize(name, cast_type, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
|
21
21
|
@name = name.freeze
|
22
|
+
@cast_type = cast_type
|
22
23
|
@sql_type_metadata = sql_type_metadata
|
23
24
|
@null = null
|
24
|
-
@default = default
|
25
|
+
@default = default.nil? || cast_type.mutable? ? default : cast_type.deserialize(default)
|
25
26
|
@default_function = default_function
|
26
27
|
@collation = collation
|
27
28
|
@comment = comment
|
28
29
|
end
|
29
30
|
|
31
|
+
def fetch_cast_type(connection) # :nodoc:
|
32
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
33
|
+
@cast_type || connection.lookup_cast_type(sql_type)
|
34
|
+
end
|
35
|
+
|
30
36
|
def has_default?
|
31
37
|
!default.nil? || default_function
|
32
38
|
end
|
@@ -45,6 +51,7 @@ module ActiveRecord
|
|
45
51
|
|
46
52
|
def init_with(coder)
|
47
53
|
@name = coder["name"]
|
54
|
+
@cast_type = coder["cast_type"]
|
48
55
|
@sql_type_metadata = coder["sql_type_metadata"]
|
49
56
|
@null = coder["null"]
|
50
57
|
@default = coder["default"]
|
@@ -55,6 +62,7 @@ module ActiveRecord
|
|
55
62
|
|
56
63
|
def encode_with(coder)
|
57
64
|
coder["name"] = @name
|
65
|
+
coder["cast_type"] = @cast_type
|
58
66
|
coder["sql_type_metadata"] = @sql_type_metadata
|
59
67
|
coder["null"] = @null
|
60
68
|
coder["default"] = @default
|
@@ -75,6 +83,7 @@ module ActiveRecord
|
|
75
83
|
def ==(other)
|
76
84
|
other.is_a?(Column) &&
|
77
85
|
name == other.name &&
|
86
|
+
cast_type == other.cast_type &&
|
78
87
|
default == other.default &&
|
79
88
|
sql_type_metadata == other.sql_type_metadata &&
|
80
89
|
null == other.null &&
|
@@ -88,6 +97,7 @@ module ActiveRecord
|
|
88
97
|
Column.hash ^
|
89
98
|
name.hash ^
|
90
99
|
name.encoding.hash ^
|
100
|
+
cast_type.hash ^
|
91
101
|
default.hash ^
|
92
102
|
sql_type_metadata.hash ^
|
93
103
|
null.hash ^
|
@@ -100,11 +110,14 @@ module ActiveRecord
|
|
100
110
|
false
|
101
111
|
end
|
102
112
|
|
113
|
+
protected
|
114
|
+
attr_reader :cast_type
|
115
|
+
|
103
116
|
private
|
104
117
|
def deduplicated
|
105
118
|
@name = -name
|
106
119
|
@sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
|
107
|
-
@default = -default if default
|
120
|
+
@default = -default if String === default
|
108
121
|
@default_function = -default_function if default_function
|
109
122
|
@collation = -collation if collation
|
110
123
|
@comment = -comment if comment
|
@@ -114,7 +127,7 @@ module ActiveRecord
|
|
114
127
|
|
115
128
|
class NullColumn < Column
|
116
129
|
def initialize(name, **)
|
117
|
-
super(name, nil)
|
130
|
+
super(name, nil, nil)
|
118
131
|
end
|
119
132
|
end
|
120
133
|
end
|
@@ -45,16 +45,16 @@ module ActiveRecord
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
def default_insert_value(column) # :nodoc:
|
49
|
+
super unless column.auto_increment?
|
50
|
+
end
|
51
|
+
|
48
52
|
private
|
49
53
|
# https://mariadb.com/kb/en/analyze-statement/
|
50
54
|
def analyze_without_explain?
|
51
55
|
mariadb? && database_version >= "10.1.0"
|
52
56
|
end
|
53
57
|
|
54
|
-
def default_insert_value(column)
|
55
|
-
super unless column.auto_increment?
|
56
|
-
end
|
57
|
-
|
58
58
|
def returning_column_values(result)
|
59
59
|
if supports_insert_returning?
|
60
60
|
result.rows.first
|
@@ -49,6 +49,8 @@ module ActiveRecord
|
|
49
49
|
sql << "USING #{o.using}" if o.using
|
50
50
|
sql << "ON #{quote_table_name(o.table)}" if create
|
51
51
|
sql << "(#{quoted_columns(o)})"
|
52
|
+
sql << "INVISIBLE" if o.disabled? && !mariadb?
|
53
|
+
sql << "IGNORED" if o.disabled? && mariadb?
|
52
54
|
|
53
55
|
add_sql_comment!(sql.join(" "), o.comment)
|
54
56
|
end
|
@@ -5,6 +5,7 @@ module ActiveRecord
|
|
5
5
|
module MySQL
|
6
6
|
module ColumnMethods
|
7
7
|
extend ActiveSupport::Concern
|
8
|
+
extend ConnectionAdapters::ColumnMethods::ClassMethods
|
8
9
|
|
9
10
|
##
|
10
11
|
# :method: blob
|
@@ -42,12 +43,26 @@ module ActiveRecord
|
|
42
43
|
# :method: unsigned_bigint
|
43
44
|
# :call-seq: unsigned_bigint(*names, **options)
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
define_column_methods :blob, :tinyblob, :mediumblob, :longblob,
|
47
|
+
:tinytext, :mediumtext, :longtext, :unsigned_integer, :unsigned_bigint
|
48
|
+
end
|
49
|
+
|
50
|
+
# = Active Record MySQL Adapter \Index Definition
|
51
|
+
class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
|
52
|
+
attr_accessor :enabled
|
53
|
+
|
54
|
+
def initialize(*args, **kwargs)
|
55
|
+
@enabled = kwargs.key?(:enabled) ? kwargs.delete(:enabled) : true
|
56
|
+
super
|
57
|
+
end
|
49
58
|
|
50
|
-
|
59
|
+
def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, enabled: nil, **options)
|
60
|
+
super(columns, name:, unique:, valid:, include:, nulls_not_distinct:, **options) &&
|
61
|
+
(enabled.nil? || self.enabled == enabled)
|
62
|
+
end
|
63
|
+
|
64
|
+
def disabled?
|
65
|
+
!@enabled
|
51
66
|
end
|
52
67
|
end
|
53
68
|
|
@@ -100,6 +115,28 @@ module ActiveRecord
|
|
100
115
|
# = Active Record MySQL Adapter \Table
|
101
116
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
102
117
|
include ColumnMethods
|
118
|
+
|
119
|
+
# Enables an index to be used by query optimizers.
|
120
|
+
#
|
121
|
+
# t.enable_index(:email)
|
122
|
+
#
|
123
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
124
|
+
#
|
125
|
+
# See {connection.enable_index}[rdoc-ref:SchemaStatements#enable_index]
|
126
|
+
def enable_index(index_name)
|
127
|
+
@base.enable_index(name, index_name)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Disables an index not to be used by query optimizers.
|
131
|
+
#
|
132
|
+
# t.disable_index(:email)
|
133
|
+
#
|
134
|
+
# Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
|
135
|
+
#
|
136
|
+
# See {connection.disable_index}[rdoc-ref:SchemaStatements#disable_index]
|
137
|
+
def disable_index(index_name)
|
138
|
+
@base.disable_index(name, index_name)
|
139
|
+
end
|
103
140
|
end
|
104
141
|
end
|
105
142
|
end
|