activerecord 8.0.2.1 → 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 +459 -421
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +9 -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_association.rb +3 -3
- 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/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +17 -4
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +2 -4
- 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 +384 -49
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -1
- 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 +89 -23
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -13
- 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 -16
- 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 +12 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -27
- 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 +56 -32
- 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/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +12 -9
- 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 +56 -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 +2 -2
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +27 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- 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 +31 -21
- 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 +7 -7
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +34 -5
- data/lib/active_record/railties/databases.rake +23 -19
- 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 +35 -25
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +37 -21
- 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 +43 -33
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +7 -10
- data/lib/active_record/relation.rb +37 -15
- 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/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +46 -18
- 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 +24 -35
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +11 -3
- 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 +34 -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 +68 -5
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +5 -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,6 +42,7 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
attr_reader :pool
|
44
44
|
attr_reader :visitor, :owner, :logger, :lock
|
45
|
+
attr_accessor :allow_preconnect
|
45
46
|
alias :in_use? :owner
|
46
47
|
|
47
48
|
def pool=(value)
|
@@ -119,7 +120,7 @@ module ActiveRecord
|
|
119
120
|
|
120
121
|
# Opens a database console session.
|
121
122
|
def self.dbconsole(config, options = {})
|
122
|
-
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")
|
123
124
|
end
|
124
125
|
|
125
126
|
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
@@ -127,6 +128,7 @@ module ActiveRecord
|
|
127
128
|
|
128
129
|
@raw_connection = nil
|
129
130
|
@unconfigured_connection = nil
|
131
|
+
@connected_since = nil
|
130
132
|
|
131
133
|
if config_or_deprecated_connection.is_a?(Hash)
|
132
134
|
@config = config_or_deprecated_connection.symbolize_keys
|
@@ -139,6 +141,7 @@ module ActiveRecord
|
|
139
141
|
# Soft-deprecated for now; we'll probably warn in future.
|
140
142
|
|
141
143
|
@unconfigured_connection = config_or_deprecated_connection
|
144
|
+
@connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
142
145
|
@logger = deprecated_logger || ActiveRecord::Base.logger
|
143
146
|
if deprecated_config
|
144
147
|
@config = (deprecated_config || {}).symbolize_keys
|
@@ -152,6 +155,7 @@ module ActiveRecord
|
|
152
155
|
@owner = nil
|
153
156
|
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
154
157
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
158
|
+
@allow_preconnect = true
|
155
159
|
@visitor = arel_visitor
|
156
160
|
@statements = build_statement_pool
|
157
161
|
self.lock_thread = nil
|
@@ -169,6 +173,8 @@ module ActiveRecord
|
|
169
173
|
@raw_connection_dirty = false
|
170
174
|
@last_activity = nil
|
171
175
|
@verified = false
|
176
|
+
|
177
|
+
@pool_jitter = rand * max_jitter
|
172
178
|
end
|
173
179
|
|
174
180
|
def inspect # :nodoc:
|
@@ -196,6 +202,11 @@ module ActiveRecord
|
|
196
202
|
end
|
197
203
|
end
|
198
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
|
+
|
199
210
|
def replica?
|
200
211
|
@config[:replica] || false
|
201
212
|
end
|
@@ -260,7 +271,11 @@ module ActiveRecord
|
|
260
271
|
end
|
261
272
|
|
262
273
|
def valid_type?(type) # :nodoc:
|
263
|
-
|
274
|
+
self.class.valid_type?(type)
|
275
|
+
end
|
276
|
+
|
277
|
+
def native_database_types # :nodoc:
|
278
|
+
self.class.native_database_types
|
264
279
|
end
|
265
280
|
|
266
281
|
# this method must only be called while holding connection pool's mutex
|
@@ -299,8 +314,12 @@ module ActiveRecord
|
|
299
314
|
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
300
315
|
end
|
301
316
|
|
317
|
+
def pool_jitter(duration)
|
318
|
+
duration * (1.0 - @pool_jitter)
|
319
|
+
end
|
320
|
+
|
302
321
|
# this method must only be called while holding connection pool's mutex
|
303
|
-
def expire
|
322
|
+
def expire(update_idle = true) # :nodoc:
|
304
323
|
if in_use?
|
305
324
|
if @owner != ActiveSupport::IsolatedExecutionState.context
|
306
325
|
raise ActiveRecordError, "Cannot expire connection, " \
|
@@ -308,7 +327,7 @@ module ActiveRecord
|
|
308
327
|
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
309
328
|
end
|
310
329
|
|
311
|
-
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
330
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) if update_idle
|
312
331
|
@owner = nil
|
313
332
|
else
|
314
333
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
@@ -341,6 +360,21 @@ module ActiveRecord
|
|
341
360
|
end
|
342
361
|
end
|
343
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
|
+
|
344
378
|
def unprepared_statement
|
345
379
|
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
346
380
|
yield
|
@@ -555,6 +589,10 @@ module ActiveRecord
|
|
555
589
|
false
|
556
590
|
end
|
557
591
|
|
592
|
+
def supports_disabling_indexes?
|
593
|
+
false
|
594
|
+
end
|
595
|
+
|
558
596
|
def return_value_after_insert?(column) # :nodoc:
|
559
597
|
column.auto_populated?
|
560
598
|
end
|
@@ -664,12 +702,15 @@ module ActiveRecord
|
|
664
702
|
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
665
703
|
|
666
704
|
@lock.synchronize do
|
705
|
+
@allow_preconnect = false
|
706
|
+
|
667
707
|
reconnect
|
668
708
|
|
669
709
|
enable_lazy_transactions!
|
670
710
|
@raw_connection_dirty = false
|
671
|
-
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
711
|
+
@last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
672
712
|
@verified = true
|
713
|
+
@allow_preconnect = true
|
673
714
|
|
674
715
|
reset_transaction(restore: restore_transactions) do
|
675
716
|
clear_cache!(new_connection: true)
|
@@ -702,6 +743,7 @@ module ActiveRecord
|
|
702
743
|
clear_cache!(new_connection: true)
|
703
744
|
reset_transaction
|
704
745
|
@raw_connection_dirty = false
|
746
|
+
@connected_since = nil
|
705
747
|
end
|
706
748
|
end
|
707
749
|
|
@@ -765,6 +807,7 @@ module ActiveRecord
|
|
765
807
|
attempt_configure_connection
|
766
808
|
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
767
809
|
@verified = true
|
810
|
+
@allow_preconnect = true
|
768
811
|
return
|
769
812
|
end
|
770
813
|
|
@@ -772,6 +815,7 @@ module ActiveRecord
|
|
772
815
|
end
|
773
816
|
end
|
774
817
|
|
818
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
775
819
|
@verified = true
|
776
820
|
end
|
777
821
|
|
@@ -785,6 +829,10 @@ module ActiveRecord
|
|
785
829
|
@verified = nil
|
786
830
|
end
|
787
831
|
|
832
|
+
def verified? # :nodoc:
|
833
|
+
@verified
|
834
|
+
end
|
835
|
+
|
788
836
|
# Provides access to the underlying database driver for this adapter. For
|
789
837
|
# example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
|
790
838
|
# and a PG::Connection object in case of PostgreSQLAdapter.
|
@@ -870,7 +918,7 @@ module ActiveRecord
|
|
870
918
|
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
871
919
|
mapping.register_type(key) do |*args|
|
872
920
|
precision = extract_precision(args.last)
|
873
|
-
klass.new(precision: precision, **kwargs)
|
921
|
+
klass.new(precision: precision, **kwargs).freeze
|
874
922
|
end
|
875
923
|
end
|
876
924
|
|
@@ -882,6 +930,10 @@ module ActiveRecord
|
|
882
930
|
end
|
883
931
|
end
|
884
932
|
|
933
|
+
def valid_type?(type) # :nodoc:
|
934
|
+
!native_database_types[type].nil?
|
935
|
+
end
|
936
|
+
|
885
937
|
private
|
886
938
|
def initialize_type_map(m)
|
887
939
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
@@ -901,7 +953,7 @@ module ActiveRecord
|
|
901
953
|
m.alias_type %r(number)i, "decimal"
|
902
954
|
m.alias_type %r(double)i, "float"
|
903
955
|
|
904
|
-
m.register_type %r(^json)i, Type::Json.new
|
956
|
+
m.register_type %r(^json)i, Type::Json.new.freeze
|
905
957
|
|
906
958
|
m.register_type(%r(decimal)i) do |sql_type|
|
907
959
|
scale = extract_scale(sql_type)
|
@@ -909,9 +961,9 @@ module ActiveRecord
|
|
909
961
|
|
910
962
|
if scale == 0
|
911
963
|
# FIXME: Remove this class as well
|
912
|
-
Type::DecimalWithoutScale.new(precision: precision)
|
964
|
+
Type::DecimalWithoutScale.new(precision: precision).freeze
|
913
965
|
else
|
914
|
-
Type::Decimal.new(precision: precision, scale: scale)
|
966
|
+
Type::Decimal.new(precision: precision, scale: scale).freeze
|
915
967
|
end
|
916
968
|
end
|
917
969
|
end
|
@@ -919,7 +971,7 @@ module ActiveRecord
|
|
919
971
|
def register_class_with_limit(mapping, key, klass)
|
920
972
|
mapping.register_type(key) do |*args|
|
921
973
|
limit = extract_limit(args.last)
|
922
|
-
klass.new(limit: limit)
|
974
|
+
klass.new(limit: limit).freeze
|
923
975
|
end
|
924
976
|
end
|
925
977
|
|
@@ -1080,7 +1132,7 @@ module ActiveRecord
|
|
1080
1132
|
end
|
1081
1133
|
|
1082
1134
|
def reconnect
|
1083
|
-
raise NotImplementedError
|
1135
|
+
raise NotImplementedError.new("#{self.class} should define `reconnect` to implement adapter-specific logic for reconnecting to the database")
|
1084
1136
|
end
|
1085
1137
|
|
1086
1138
|
# Returns a raw connection for internal use with methods that are known
|
@@ -1131,7 +1183,7 @@ module ActiveRecord
|
|
1131
1183
|
active_record_error
|
1132
1184
|
end
|
1133
1185
|
|
1134
|
-
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:
|
1135
1187
|
instrumenter.instrument(
|
1136
1188
|
"sql.active_record",
|
1137
1189
|
sql: sql,
|
@@ -1139,8 +1191,10 @@ module ActiveRecord
|
|
1139
1191
|
binds: binds,
|
1140
1192
|
type_casted_binds: type_casted_binds,
|
1141
1193
|
async: async,
|
1194
|
+
allow_retry: allow_retry,
|
1142
1195
|
connection: self,
|
1143
1196
|
transaction: current_transaction.user_transaction.presence,
|
1197
|
+
affected_rows: 0,
|
1144
1198
|
row_count: 0,
|
1145
1199
|
&block
|
1146
1200
|
)
|
@@ -1215,7 +1269,7 @@ module ActiveRecord
|
|
1215
1269
|
|
1216
1270
|
def attempt_configure_connection
|
1217
1271
|
configure_connection
|
1218
|
-
rescue
|
1272
|
+
rescue Exception # Need to handle things such as Timeout::ExitException
|
1219
1273
|
disconnect!
|
1220
1274
|
raise
|
1221
1275
|
end
|
@@ -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
|