activerecord 6.1.3.2 → 7.0.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +734 -1058
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +35 -7
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +16 -6
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +24 -25
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -49
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +11 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
- data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +8 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +129 -117
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +18 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +80 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +44 -46
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +39 -6
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +46 -32
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +83 -58
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +42 -25
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +32 -23
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +233 -50
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +22 -15
- data/lib/active_record/relation.rb +170 -87
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +62 -15
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +107 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +45 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/composite.rb +3 -3
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/homogeneous_in.rb +4 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +3 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +44 -3
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +55 -16
@@ -33,6 +33,10 @@ module ActiveRecord
|
|
33
33
|
@state == :fully_rolledback
|
34
34
|
end
|
35
35
|
|
36
|
+
def invalidated?
|
37
|
+
@state == :invalidated
|
38
|
+
end
|
39
|
+
|
36
40
|
def fully_completed?
|
37
41
|
completed?
|
38
42
|
end
|
@@ -51,6 +55,11 @@ module ActiveRecord
|
|
51
55
|
@state = :fully_rolledback
|
52
56
|
end
|
53
57
|
|
58
|
+
def invalidate!
|
59
|
+
@children&.each { |c| c.invalidate! }
|
60
|
+
@state = :invalidated
|
61
|
+
end
|
62
|
+
|
54
63
|
def commit!
|
55
64
|
@state = :committed
|
56
65
|
end
|
@@ -64,7 +73,7 @@ module ActiveRecord
|
|
64
73
|
end
|
65
74
|
end
|
66
75
|
|
67
|
-
class NullTransaction
|
76
|
+
class NullTransaction # :nodoc:
|
68
77
|
def initialize; end
|
69
78
|
def state; end
|
70
79
|
def closed?; true; end
|
@@ -73,7 +82,7 @@ module ActiveRecord
|
|
73
82
|
def add_record(record, _ = true); end
|
74
83
|
end
|
75
84
|
|
76
|
-
class Transaction
|
85
|
+
class Transaction # :nodoc:
|
77
86
|
attr_reader :connection, :state, :savepoint_name, :isolation_level
|
78
87
|
attr_accessor :written
|
79
88
|
|
@@ -212,7 +221,7 @@ module ActiveRecord
|
|
212
221
|
end
|
213
222
|
end
|
214
223
|
|
215
|
-
class TransactionManager
|
224
|
+
class TransactionManager # :nodoc:
|
216
225
|
def initialize(connection)
|
217
226
|
@stack = []
|
218
227
|
@connection = connection
|
@@ -299,7 +308,7 @@ module ActiveRecord
|
|
299
308
|
def rollback_transaction(transaction = nil)
|
300
309
|
@connection.lock.synchronize do
|
301
310
|
transaction ||= @stack.pop
|
302
|
-
transaction.rollback
|
311
|
+
transaction.rollback unless transaction.state.invalidated?
|
303
312
|
transaction.rollback_records
|
304
313
|
end
|
305
314
|
end
|
@@ -312,15 +321,17 @@ module ActiveRecord
|
|
312
321
|
ret
|
313
322
|
rescue Exception => error
|
314
323
|
if transaction
|
324
|
+
transaction.state.invalidate! if error.is_a? ActiveRecord::TransactionRollbackError
|
315
325
|
rollback_transaction
|
316
326
|
after_failure_actions(transaction, error)
|
317
327
|
end
|
328
|
+
|
318
329
|
raise
|
319
330
|
ensure
|
320
331
|
if transaction
|
321
332
|
if error
|
322
|
-
# @connection still holds an open transaction, so we must not
|
323
|
-
# put it back in the pool for reuse
|
333
|
+
# @connection still holds an open or invalid transaction, so we must not
|
334
|
+
# put it back in the pool for reuse.
|
324
335
|
@connection.throw_away! unless transaction.state.rolledback?
|
325
336
|
else
|
326
337
|
if Thread.current.status == "aborting"
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require "active_record/connection_adapters/schema_cache"
|
5
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
6
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
7
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
@@ -37,7 +36,7 @@ module ActiveRecord
|
|
37
36
|
include Savepoints
|
38
37
|
|
39
38
|
SIMPLE_INT = /\A\d+\z/
|
40
|
-
COMMENT_REGEX = %r{(
|
39
|
+
COMMENT_REGEX = %r{(?:--.*\n)*|/\*(?:[^*]|\*[^/])*\*/}m
|
41
40
|
|
42
41
|
attr_accessor :pool
|
43
42
|
attr_reader :visitor, :owner, :logger, :lock
|
@@ -69,7 +68,7 @@ module ActiveRecord
|
|
69
68
|
def self.build_read_query_regexp(*parts) # :nodoc:
|
70
69
|
parts += DEFAULT_READ_QUERY
|
71
70
|
parts = parts.map { |part| /#{part}/i }
|
72
|
-
/\A(?:[
|
71
|
+
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
73
72
|
end
|
74
73
|
|
75
74
|
def self.quoted_column_names # :nodoc:
|
@@ -103,6 +102,25 @@ module ActiveRecord
|
|
103
102
|
)
|
104
103
|
end
|
105
104
|
|
105
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
106
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
|
107
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
108
|
+
def with_instrumenter(instrumenter, &block) # :nodoc:
|
109
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
110
|
+
previous_instrumenter = @instrumenter
|
111
|
+
@instrumenter = instrumenter
|
112
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
113
|
+
ensure
|
114
|
+
@instrumenter = previous_instrumenter
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_if_write_query(sql) # :nodoc:
|
119
|
+
if preventing_writes? && write_query?(sql)
|
120
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
106
124
|
def replica?
|
107
125
|
@config[:replica] || false
|
108
126
|
end
|
@@ -122,7 +140,7 @@ module ActiveRecord
|
|
122
140
|
# will return true based on +current_preventing_writes+.
|
123
141
|
def preventing_writes?
|
124
142
|
return true if replica?
|
125
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord
|
143
|
+
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
126
144
|
return false if connection_klass.nil?
|
127
145
|
|
128
146
|
connection_klass.current_preventing_writes
|
@@ -154,9 +172,10 @@ module ActiveRecord
|
|
154
172
|
end
|
155
173
|
end
|
156
174
|
|
157
|
-
def prepared_statements
|
175
|
+
def prepared_statements?
|
158
176
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
159
177
|
end
|
178
|
+
alias :prepared_statements :prepared_statements?
|
160
179
|
|
161
180
|
def prepared_statements_disabled_cache # :nodoc:
|
162
181
|
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
@@ -250,7 +269,7 @@ module ActiveRecord
|
|
250
269
|
end
|
251
270
|
|
252
271
|
def unprepared_statement
|
253
|
-
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
272
|
+
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
254
273
|
yield
|
255
274
|
ensure
|
256
275
|
cache&.delete(object_id)
|
@@ -418,6 +437,15 @@ module ActiveRecord
|
|
418
437
|
false
|
419
438
|
end
|
420
439
|
|
440
|
+
def supports_concurrent_connections?
|
441
|
+
true
|
442
|
+
end
|
443
|
+
|
444
|
+
def async_enabled? # :nodoc:
|
445
|
+
supports_concurrent_connections? &&
|
446
|
+
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
447
|
+
end
|
448
|
+
|
421
449
|
# This is meant to be implemented by the adapters that support extensions
|
422
450
|
def disable_extension(name)
|
423
451
|
end
|
@@ -461,6 +489,11 @@ module ActiveRecord
|
|
461
489
|
yield
|
462
490
|
end
|
463
491
|
|
492
|
+
# Override to check all foreign key constraints in a database.
|
493
|
+
def all_foreign_keys_valid?
|
494
|
+
true
|
495
|
+
end
|
496
|
+
|
464
497
|
# CONNECTION MANAGEMENT ====================================
|
465
498
|
|
466
499
|
# Checks whether the connection to the database is still active. This includes
|
@@ -599,78 +632,85 @@ module ActiveRecord
|
|
599
632
|
def check_version # :nodoc:
|
600
633
|
end
|
601
634
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
end
|
635
|
+
def field_ordered_value(column, values) # :nodoc:
|
636
|
+
node = Arel::Nodes::Case.new(column)
|
637
|
+
values.each.with_index(1) do |value, order|
|
638
|
+
node.when(value).then(order)
|
607
639
|
end
|
608
640
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
641
|
+
Arel::Nodes::Ascending.new(node.else(values.length + 1))
|
642
|
+
end
|
643
|
+
|
644
|
+
class << self
|
645
|
+
private
|
646
|
+
def initialize_type_map(m)
|
647
|
+
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
648
|
+
register_class_with_limit m, %r(char)i, Type::String
|
649
|
+
register_class_with_limit m, %r(binary)i, Type::Binary
|
650
|
+
register_class_with_limit m, %r(text)i, Type::Text
|
651
|
+
register_class_with_precision m, %r(date)i, Type::Date
|
652
|
+
register_class_with_precision m, %r(time)i, Type::Time
|
653
|
+
register_class_with_precision m, %r(datetime)i, Type::DateTime
|
654
|
+
register_class_with_limit m, %r(float)i, Type::Float
|
655
|
+
register_class_with_limit m, %r(int)i, Type::Integer
|
656
|
+
|
657
|
+
m.alias_type %r(blob)i, "binary"
|
658
|
+
m.alias_type %r(clob)i, "text"
|
659
|
+
m.alias_type %r(timestamp)i, "datetime"
|
660
|
+
m.alias_type %r(numeric)i, "decimal"
|
661
|
+
m.alias_type %r(number)i, "decimal"
|
662
|
+
m.alias_type %r(double)i, "float"
|
663
|
+
|
664
|
+
m.register_type %r(^json)i, Type::Json.new
|
665
|
+
|
666
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
667
|
+
scale = extract_scale(sql_type)
|
668
|
+
precision = extract_precision(sql_type)
|
669
|
+
|
670
|
+
if scale == 0
|
671
|
+
# FIXME: Remove this class as well
|
672
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
673
|
+
else
|
674
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
675
|
+
end
|
638
676
|
end
|
639
677
|
end
|
640
|
-
end
|
641
678
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
679
|
+
def register_class_with_limit(mapping, key, klass)
|
680
|
+
mapping.register_type(key) do |*args|
|
681
|
+
limit = extract_limit(args.last)
|
682
|
+
klass.new(limit: limit)
|
683
|
+
end
|
684
|
+
end
|
646
685
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
686
|
+
def register_class_with_precision(mapping, key, klass)
|
687
|
+
mapping.register_type(key) do |*args|
|
688
|
+
precision = extract_precision(args.last)
|
689
|
+
klass.new(precision: precision)
|
690
|
+
end
|
651
691
|
end
|
652
|
-
end
|
653
692
|
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
693
|
+
def extract_scale(sql_type)
|
694
|
+
case sql_type
|
695
|
+
when /\((\d+)\)/ then 0
|
696
|
+
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
697
|
+
end
|
658
698
|
end
|
659
|
-
end
|
660
699
|
|
661
|
-
|
662
|
-
|
663
|
-
when /\((\d+)\)/ then 0
|
664
|
-
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
700
|
+
def extract_precision(sql_type)
|
701
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
665
702
|
end
|
666
|
-
end
|
667
703
|
|
668
|
-
|
669
|
-
|
670
|
-
|
704
|
+
def extract_limit(sql_type)
|
705
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
671
710
|
|
672
|
-
|
673
|
-
|
711
|
+
private
|
712
|
+
def type_map
|
713
|
+
TYPE_MAP
|
674
714
|
end
|
675
715
|
|
676
716
|
def translate_exception_class(e, sql, binds)
|
@@ -683,7 +723,7 @@ module ActiveRecord
|
|
683
723
|
exception
|
684
724
|
end
|
685
725
|
|
686
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
726
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
|
687
727
|
@instrumenter.instrument(
|
688
728
|
"sql.active_record",
|
689
729
|
sql: sql,
|
@@ -691,15 +731,21 @@ module ActiveRecord
|
|
691
731
|
binds: binds,
|
692
732
|
type_casted_binds: type_casted_binds,
|
693
733
|
statement_name: statement_name,
|
734
|
+
async: async,
|
694
735
|
connection: self) do
|
695
|
-
@lock.synchronize
|
696
|
-
yield
|
697
|
-
end
|
736
|
+
@lock.synchronize(&block)
|
698
737
|
rescue => e
|
699
738
|
raise translate_exception_class(e, sql, binds)
|
700
739
|
end
|
701
740
|
end
|
702
741
|
|
742
|
+
def transform_query(sql)
|
743
|
+
ActiveRecord.query_transformers.each do |transformer|
|
744
|
+
sql = transformer.call(sql)
|
745
|
+
end
|
746
|
+
sql
|
747
|
+
end
|
748
|
+
|
703
749
|
def translate_exception(exception, message:, sql:, binds:)
|
704
750
|
# override in derived class
|
705
751
|
case exception
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
super(connection, logger, config)
|
55
55
|
end
|
56
56
|
|
57
|
-
def get_database_version
|
57
|
+
def get_database_version # :nodoc:
|
58
58
|
full_version_string = get_full_version
|
59
59
|
version_string = version_string(full_version_string)
|
60
60
|
Version.new(version_string, full_version_string)
|
@@ -137,6 +137,11 @@ module ActiveRecord
|
|
137
137
|
true
|
138
138
|
end
|
139
139
|
|
140
|
+
def field_ordered_value(column, values) # :nodoc:
|
141
|
+
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse])
|
142
|
+
Arel::Nodes::Descending.new(field)
|
143
|
+
end
|
144
|
+
|
140
145
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
141
146
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
142
147
|
end
|
@@ -174,7 +179,7 @@ module ActiveRecord
|
|
174
179
|
|
175
180
|
# REFERENTIAL INTEGRITY ====================================
|
176
181
|
|
177
|
-
def disable_referential_integrity
|
182
|
+
def disable_referential_integrity # :nodoc:
|
178
183
|
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")
|
179
184
|
|
180
185
|
begin
|
@@ -185,54 +190,40 @@ module ActiveRecord
|
|
185
190
|
end
|
186
191
|
end
|
187
192
|
|
188
|
-
# CONNECTION MANAGEMENT ====================================
|
189
|
-
|
190
|
-
def clear_cache! # :nodoc:
|
191
|
-
reload_type_map
|
192
|
-
super
|
193
|
-
end
|
194
|
-
|
195
193
|
#--
|
196
194
|
# DATABASE STATEMENTS ======================================
|
197
195
|
#++
|
198
196
|
|
199
197
|
# Executes the SQL statement in the context of this connection.
|
200
|
-
def execute(sql, name = nil)
|
201
|
-
|
202
|
-
mark_transaction_written_if_write(sql)
|
203
|
-
|
204
|
-
log(sql, name) do
|
205
|
-
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
206
|
-
@connection.query(sql)
|
207
|
-
end
|
208
|
-
end
|
198
|
+
def execute(sql, name = nil, async: false)
|
199
|
+
raw_execute(sql, name, async)
|
209
200
|
end
|
210
201
|
|
211
202
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
212
203
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
213
204
|
# needs to be explicitly freed or not.
|
214
|
-
def execute_and_free(sql, name = nil) # :nodoc:
|
215
|
-
yield execute(sql, name)
|
205
|
+
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
206
|
+
yield execute(sql, name, async: async)
|
216
207
|
end
|
217
208
|
|
218
|
-
def begin_db_transaction
|
209
|
+
def begin_db_transaction # :nodoc:
|
219
210
|
execute("BEGIN", "TRANSACTION")
|
220
211
|
end
|
221
212
|
|
222
|
-
def begin_isolated_db_transaction(isolation)
|
213
|
+
def begin_isolated_db_transaction(isolation) # :nodoc:
|
223
214
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
224
215
|
begin_db_transaction
|
225
216
|
end
|
226
217
|
|
227
|
-
def commit_db_transaction
|
218
|
+
def commit_db_transaction # :nodoc:
|
228
219
|
execute("COMMIT", "TRANSACTION")
|
229
220
|
end
|
230
221
|
|
231
|
-
def exec_rollback_db_transaction
|
222
|
+
def exec_rollback_db_transaction # :nodoc:
|
232
223
|
execute("ROLLBACK", "TRANSACTION")
|
233
224
|
end
|
234
225
|
|
235
|
-
def empty_insert_statement_value(primary_key = nil)
|
226
|
+
def empty_insert_statement_value(primary_key = nil) # :nodoc:
|
236
227
|
"VALUES ()"
|
237
228
|
end
|
238
229
|
|
@@ -270,7 +261,7 @@ module ActiveRecord
|
|
270
261
|
#
|
271
262
|
# Example:
|
272
263
|
# drop_database('sebastian_development')
|
273
|
-
def drop_database(name)
|
264
|
+
def drop_database(name) # :nodoc:
|
274
265
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
275
266
|
end
|
276
267
|
|
@@ -346,12 +337,12 @@ module ActiveRecord
|
|
346
337
|
end
|
347
338
|
end
|
348
339
|
|
349
|
-
def change_column_default(table_name, column_name, default_or_changes)
|
340
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
350
341
|
default = extract_new_default_value(default_or_changes)
|
351
342
|
change_column table_name, column_name, nil, default: default
|
352
343
|
end
|
353
344
|
|
354
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
345
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
355
346
|
unless null || default.nil?
|
356
347
|
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
357
348
|
end
|
@@ -364,16 +355,16 @@ module ActiveRecord
|
|
364
355
|
change_column table_name, column_name, nil, comment: comment
|
365
356
|
end
|
366
357
|
|
367
|
-
def change_column(table_name, column_name, type, **options)
|
358
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
368
359
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, **options)}")
|
369
360
|
end
|
370
361
|
|
371
|
-
def rename_column(table_name, column_name, new_column_name)
|
362
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
372
363
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_for_alter(table_name, column_name, new_column_name)}")
|
373
364
|
rename_column_indexes(table_name, column_name, new_column_name)
|
374
365
|
end
|
375
366
|
|
376
|
-
def add_index(table_name, column_name, **options)
|
367
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
377
368
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
378
369
|
|
379
370
|
return if if_not_exists && index_exists?(table_name, column_name, name: index.name)
|
@@ -427,7 +418,7 @@ module ActiveRecord
|
|
427
418
|
if supports_check_constraints?
|
428
419
|
scope = quoted_scope(table_name)
|
429
420
|
|
430
|
-
|
421
|
+
sql = <<~SQL
|
431
422
|
SELECT cc.constraint_name AS 'name',
|
432
423
|
cc.check_clause AS 'expression'
|
433
424
|
FROM information_schema.check_constraints cc
|
@@ -437,6 +428,9 @@ module ActiveRecord
|
|
437
428
|
AND tc.table_name = #{scope[:name]}
|
438
429
|
AND cc.constraint_schema = #{scope[:schema]}
|
439
430
|
SQL
|
431
|
+
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
|
432
|
+
|
433
|
+
chk_info = exec_query(sql, "SCHEMA")
|
440
434
|
|
441
435
|
chk_info.map do |row|
|
442
436
|
options = {
|
@@ -548,8 +542,12 @@ module ActiveRecord
|
|
548
542
|
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
549
543
|
elsif insert.update_duplicates?
|
550
544
|
sql << " ON DUPLICATE KEY UPDATE "
|
551
|
-
|
552
|
-
|
545
|
+
if insert.raw_update_sql?
|
546
|
+
sql << insert.raw_update_sql
|
547
|
+
else
|
548
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
549
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
550
|
+
end
|
553
551
|
end
|
554
552
|
|
555
553
|
sql
|
@@ -561,55 +559,77 @@ module ActiveRecord
|
|
561
559
|
end
|
562
560
|
end
|
563
561
|
|
564
|
-
|
565
|
-
|
566
|
-
|
562
|
+
class << self
|
563
|
+
private
|
564
|
+
def initialize_type_map(m)
|
565
|
+
super
|
567
566
|
|
568
|
-
|
569
|
-
|
570
|
-
|
567
|
+
m.register_type(%r(char)i) do |sql_type|
|
568
|
+
limit = extract_limit(sql_type)
|
569
|
+
Type.lookup(:string, adapter: :mysql2, limit: limit)
|
570
|
+
end
|
571
|
+
|
572
|
+
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
|
573
|
+
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
|
574
|
+
m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
|
575
|
+
m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
|
576
|
+
m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
|
577
|
+
m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
|
578
|
+
m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
|
579
|
+
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
|
580
|
+
m.register_type %r(^float)i, Type::Float.new(limit: 24)
|
581
|
+
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
582
|
+
|
583
|
+
register_integer_type m, %r(^bigint)i, limit: 8
|
584
|
+
register_integer_type m, %r(^int)i, limit: 4
|
585
|
+
register_integer_type m, %r(^mediumint)i, limit: 3
|
586
|
+
register_integer_type m, %r(^smallint)i, limit: 2
|
587
|
+
register_integer_type m, %r(^tinyint)i, limit: 1
|
588
|
+
|
589
|
+
m.alias_type %r(year)i, "integer"
|
590
|
+
m.alias_type %r(bit)i, "binary"
|
591
|
+
|
592
|
+
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
593
|
+
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
571
594
|
end
|
572
595
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
m.register_type %r(^double)i, Type::Float.new(limit: 53)
|
583
|
-
|
584
|
-
register_integer_type m, %r(^bigint)i, limit: 8
|
585
|
-
register_integer_type m, %r(^int)i, limit: 4
|
586
|
-
register_integer_type m, %r(^mediumint)i, limit: 3
|
587
|
-
register_integer_type m, %r(^smallint)i, limit: 2
|
588
|
-
register_integer_type m, %r(^tinyint)i, limit: 1
|
589
|
-
|
590
|
-
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
|
591
|
-
m.alias_type %r(year)i, "integer"
|
592
|
-
m.alias_type %r(bit)i, "binary"
|
593
|
-
|
594
|
-
m.register_type %r(^enum)i, Type.lookup(:string, adapter: :mysql2)
|
595
|
-
m.register_type %r(^set)i, Type.lookup(:string, adapter: :mysql2)
|
596
|
-
end
|
596
|
+
def register_integer_type(mapping, key, **options)
|
597
|
+
mapping.register_type(key) do |sql_type|
|
598
|
+
if /\bunsigned\b/.match?(sql_type)
|
599
|
+
Type::UnsignedInteger.new(**options)
|
600
|
+
else
|
601
|
+
Type::Integer.new(**options)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
597
605
|
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
Type::UnsignedInteger.new(**options)
|
606
|
+
def extract_precision(sql_type)
|
607
|
+
if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
|
608
|
+
super || 0
|
602
609
|
else
|
603
|
-
|
610
|
+
super
|
604
611
|
end
|
605
612
|
end
|
613
|
+
end
|
614
|
+
|
615
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
616
|
+
TYPE_MAP_WITH_BOOLEAN = Type::TypeMap.new(TYPE_MAP).tap do |m|
|
617
|
+
m.register_type %r(^tinyint\(1\))i, Type::Boolean.new
|
618
|
+
end
|
619
|
+
|
620
|
+
private
|
621
|
+
def type_map
|
622
|
+
emulate_booleans ? TYPE_MAP_WITH_BOOLEAN : TYPE_MAP
|
606
623
|
end
|
607
624
|
|
608
|
-
def
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
625
|
+
def raw_execute(sql, name, async: false)
|
626
|
+
materialize_transactions
|
627
|
+
mark_transaction_written_if_write(sql)
|
628
|
+
|
629
|
+
log(sql, name, async: async) do
|
630
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
631
|
+
@connection.query(sql)
|
632
|
+
end
|
613
633
|
end
|
614
634
|
end
|
615
635
|
|
@@ -780,14 +800,13 @@ module ActiveRecord
|
|
780
800
|
end
|
781
801
|
|
782
802
|
# Gather up all of the SET variables...
|
783
|
-
variable_assignments = variables.
|
803
|
+
variable_assignments = variables.filter_map do |k, v|
|
784
804
|
if defaults.include?(v)
|
785
805
|
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
|
786
806
|
elsif !v.nil?
|
787
807
|
"@@SESSION.#{k} = #{quote(v)}"
|
788
808
|
end
|
789
|
-
|
790
|
-
end.compact.join(", ")
|
809
|
+
end.join(", ")
|
791
810
|
|
792
811
|
# ...and send them all in one query
|
793
812
|
execute("SET #{encoding} #{sql_mode_assignment} #{variable_assignments}", "SCHEMA")
|
@@ -839,10 +858,6 @@ module ActiveRecord
|
|
839
858
|
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
|
840
859
|
end
|
841
860
|
|
842
|
-
# Alias MysqlString to work Mashal.load(File.read("legacy_record.dump")).
|
843
|
-
# TODO: Remove the constant alias once Rails 6.1 has released.
|
844
|
-
MysqlString = Type::String # :nodoc:
|
845
|
-
|
846
861
|
ActiveRecord::Type.register(:immutable_string, adapter: :mysql2) do |_, **args|
|
847
862
|
Type::ImmutableString.new(true: "1", false: "0", **args)
|
848
863
|
end
|