activerecord 6.1.4 → 7.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1049 -977
- 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 +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- 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 +10 -3
- 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 +34 -27
- 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 +187 -55
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +90 -82
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -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 +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- 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 +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +16 -32
- data/lib/active_record/delegated_type.rb +52 -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 +28 -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 +90 -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 +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- 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 +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- 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/middleware/shard_selector.rb +60 -0
- 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 +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- 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 +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- 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 +235 -61
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +171 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -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/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -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 +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -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 +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -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 +56 -13
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class FutureResult # :nodoc:
|
5
|
+
class EventBuffer
|
6
|
+
def initialize(future_result, instrumenter)
|
7
|
+
@future_result = future_result
|
8
|
+
@instrumenter = instrumenter
|
9
|
+
@events = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def instrument(name, payload = {}, &block)
|
13
|
+
event = @instrumenter.new_event(name, payload)
|
14
|
+
@events << event
|
15
|
+
event.record(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def flush
|
19
|
+
events, @events = @events, []
|
20
|
+
events.each do |event|
|
21
|
+
event.payload[:lock_wait] = @future_result.lock_wait
|
22
|
+
ActiveSupport::Notifications.publish_event(event)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Canceled = Class.new(ActiveRecordError)
|
28
|
+
|
29
|
+
delegate :empty?, :to_a, to: :result
|
30
|
+
|
31
|
+
attr_reader :lock_wait
|
32
|
+
|
33
|
+
def initialize(pool, *args, **kwargs)
|
34
|
+
@mutex = Mutex.new
|
35
|
+
|
36
|
+
@session = nil
|
37
|
+
@pool = pool
|
38
|
+
@args = args
|
39
|
+
@kwargs = kwargs
|
40
|
+
|
41
|
+
@pending = true
|
42
|
+
@error = nil
|
43
|
+
@result = nil
|
44
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
45
|
+
@event_buffer = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def schedule!(session)
|
49
|
+
@session = session
|
50
|
+
@pool.schedule_query(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute!(connection)
|
54
|
+
execute_query(connection)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cancel
|
58
|
+
@pending = false
|
59
|
+
@error = Canceled
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_or_skip
|
64
|
+
return unless pending?
|
65
|
+
|
66
|
+
@pool.with_connection do |connection|
|
67
|
+
return unless @mutex.try_lock
|
68
|
+
begin
|
69
|
+
if pending?
|
70
|
+
@event_buffer = EventBuffer.new(self, @instrumenter)
|
71
|
+
connection.with_instrumenter(@event_buffer) do
|
72
|
+
execute_query(connection, async: true)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
ensure
|
76
|
+
@mutex.unlock
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def result
|
82
|
+
execute_or_wait
|
83
|
+
@event_buffer&.flush
|
84
|
+
|
85
|
+
if canceled?
|
86
|
+
raise Canceled
|
87
|
+
elsif @error
|
88
|
+
raise @error
|
89
|
+
else
|
90
|
+
@result
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def pending?
|
95
|
+
@pending && (!@session || @session.active?)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def canceled?
|
100
|
+
@session && !@session.active?
|
101
|
+
end
|
102
|
+
|
103
|
+
def execute_or_wait
|
104
|
+
if pending?
|
105
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
106
|
+
@mutex.synchronize do
|
107
|
+
if pending?
|
108
|
+
execute_query(@pool.connection)
|
109
|
+
else
|
110
|
+
@lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@lock_wait = 0.0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def execute_query(connection, async: false)
|
119
|
+
@result = exec_query(connection, *@args, **@kwargs, async: async)
|
120
|
+
rescue => error
|
121
|
+
@error = error
|
122
|
+
ensure
|
123
|
+
@pending = false
|
124
|
+
end
|
125
|
+
|
126
|
+
def exec_query(connection, *args, **kwargs)
|
127
|
+
connection.exec_query(*args, **kwargs)
|
128
|
+
end
|
129
|
+
|
130
|
+
class SelectAll < FutureResult # :nodoc:
|
131
|
+
private
|
132
|
+
def exec_query(*, **)
|
133
|
+
super
|
134
|
+
rescue ::RangeError
|
135
|
+
ActiveRecord::Result.empty
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/inflector"
|
3
4
|
require "active_support/core_ext/hash/indifferent_access"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -43,6 +44,8 @@ module ActiveRecord
|
|
43
44
|
# Determines whether to store the full constant name including namespace when using STI.
|
44
45
|
# This is true, by default.
|
45
46
|
class_attribute :store_full_sti_class, instance_writer: false, default: true
|
47
|
+
|
48
|
+
set_base_class
|
46
49
|
end
|
47
50
|
|
48
51
|
module ClassMethods
|
@@ -85,7 +88,7 @@ module ActiveRecord
|
|
85
88
|
end
|
86
89
|
end
|
87
90
|
|
88
|
-
def finder_needs_type_condition?
|
91
|
+
def finder_needs_type_condition? # :nodoc:
|
89
92
|
# This is like this because benchmarking justifies the strange :false stuff
|
90
93
|
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
|
91
94
|
end
|
@@ -98,17 +101,7 @@ module ActiveRecord
|
|
98
101
|
#
|
99
102
|
# If B < A and C < B and if A is an abstract_class then both B.base_class
|
100
103
|
# and C.base_class would return B as the answer since A is an abstract_class.
|
101
|
-
|
102
|
-
unless self < Base
|
103
|
-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
104
|
-
end
|
105
|
-
|
106
|
-
if superclass == Base || superclass.abstract_class?
|
107
|
-
self
|
108
|
-
else
|
109
|
-
superclass.base_class
|
110
|
-
end
|
111
|
-
end
|
104
|
+
attr_reader :base_class
|
112
105
|
|
113
106
|
# Returns whether the class is a base class.
|
114
107
|
# See #base_class for more information.
|
@@ -164,6 +157,21 @@ module ActiveRecord
|
|
164
157
|
defined?(@abstract_class) && @abstract_class == true
|
165
158
|
end
|
166
159
|
|
160
|
+
# Sets the application record class for Active Record
|
161
|
+
#
|
162
|
+
# This is useful if your application uses a different class than
|
163
|
+
# ApplicationRecord for your primary abstract class. This class
|
164
|
+
# will share a database connection with Active Record. It is the class
|
165
|
+
# that connects to your primary database.
|
166
|
+
def primary_abstract_class
|
167
|
+
if ActiveRecord.application_record_class && ActiveRecord.application_record_class.name != name
|
168
|
+
raise ArgumentError, "The `primary_abstract_class` is already set to #{ActiveRecord.application_record_class.inspect}. There can only be one `primary_abstract_class` in an application."
|
169
|
+
end
|
170
|
+
|
171
|
+
self.abstract_class = true
|
172
|
+
ActiveRecord.application_record_class = self
|
173
|
+
end
|
174
|
+
|
167
175
|
# Returns the value to be stored in the inheritance column for STI.
|
168
176
|
def sti_name
|
169
177
|
store_full_sti_class && store_full_class_name ? name : name.demodulize
|
@@ -174,7 +182,7 @@ module ActiveRecord
|
|
174
182
|
# It is used to find the class correspondent to the value stored in the inheritance column.
|
175
183
|
def sti_class_for(type_name)
|
176
184
|
if store_full_sti_class && store_full_class_name
|
177
|
-
|
185
|
+
type_name.constantize
|
178
186
|
else
|
179
187
|
compute_type(type_name)
|
180
188
|
end
|
@@ -196,17 +204,31 @@ module ActiveRecord
|
|
196
204
|
# It is used to find the class correspondent to the value stored in the polymorphic type column.
|
197
205
|
def polymorphic_class_for(name)
|
198
206
|
if store_full_class_name
|
199
|
-
|
207
|
+
name.constantize
|
200
208
|
else
|
201
209
|
compute_type(name)
|
202
210
|
end
|
203
211
|
end
|
204
212
|
|
205
213
|
def inherited(subclass)
|
214
|
+
subclass.set_base_class
|
206
215
|
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
207
216
|
super
|
208
217
|
end
|
209
218
|
|
219
|
+
def dup # :nodoc:
|
220
|
+
# `initialize_dup` / `initialize_copy` don't work when defined
|
221
|
+
# in the `singleton_class`.
|
222
|
+
other = super
|
223
|
+
other.set_base_class
|
224
|
+
other
|
225
|
+
end
|
226
|
+
|
227
|
+
def initialize_clone(other) # :nodoc:
|
228
|
+
super
|
229
|
+
set_base_class
|
230
|
+
end
|
231
|
+
|
210
232
|
protected
|
211
233
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
212
234
|
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
@@ -214,10 +236,10 @@ module ActiveRecord
|
|
214
236
|
if type_name.start_with?("::")
|
215
237
|
# If the type is prefixed with a scope operator then we assume that
|
216
238
|
# the type_name is an absolute reference.
|
217
|
-
|
239
|
+
type_name.constantize
|
218
240
|
else
|
219
241
|
type_candidate = @_type_candidates_cache[type_name]
|
220
|
-
if type_candidate && type_constant =
|
242
|
+
if type_candidate && type_constant = type_candidate.safe_constantize
|
221
243
|
return type_constant
|
222
244
|
end
|
223
245
|
|
@@ -227,7 +249,7 @@ module ActiveRecord
|
|
227
249
|
candidates << type_name
|
228
250
|
|
229
251
|
candidates.each do |candidate|
|
230
|
-
constant =
|
252
|
+
constant = candidate.safe_constantize
|
231
253
|
if candidate == constant.to_s
|
232
254
|
@_type_candidates_cache[type_name] = candidate
|
233
255
|
return constant
|
@@ -238,6 +260,22 @@ module ActiveRecord
|
|
238
260
|
end
|
239
261
|
end
|
240
262
|
|
263
|
+
def set_base_class # :nodoc:
|
264
|
+
@base_class = if self == Base
|
265
|
+
self
|
266
|
+
else
|
267
|
+
unless self < Base
|
268
|
+
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
269
|
+
end
|
270
|
+
|
271
|
+
if superclass == Base || superclass.abstract_class?
|
272
|
+
self
|
273
|
+
else
|
274
|
+
superclass.base_class
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
241
279
|
private
|
242
280
|
# Called by +instantiate+ to decide which class to use for a new
|
243
281
|
# record instance. For single-table inheritance, we check the record
|
@@ -5,13 +5,19 @@ require "active_support/core_ext/enumerable"
|
|
5
5
|
module ActiveRecord
|
6
6
|
class InsertAll # :nodoc:
|
7
7
|
attr_reader :model, :connection, :inserts, :keys
|
8
|
-
attr_reader :on_duplicate, :returning, :unique_by
|
8
|
+
attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
|
9
9
|
|
10
|
-
def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
|
10
|
+
def initialize(model, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
11
11
|
raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
|
12
12
|
|
13
13
|
@model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
|
14
|
-
@on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
|
14
|
+
@on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
|
15
|
+
@record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
|
16
|
+
|
17
|
+
disallow_raw_sql!(on_duplicate)
|
18
|
+
disallow_raw_sql!(returning)
|
19
|
+
|
20
|
+
configure_on_duplicate_update_logic
|
15
21
|
|
16
22
|
if model.scope_attributes?
|
17
23
|
@scope_attributes = model.scope_attributes
|
@@ -36,7 +42,7 @@ module ActiveRecord
|
|
36
42
|
end
|
37
43
|
|
38
44
|
def updatable_columns
|
39
|
-
keys - readonly_columns - unique_by_columns
|
45
|
+
@updatable_columns ||= keys - readonly_columns - unique_by_columns
|
40
46
|
end
|
41
47
|
|
42
48
|
def primary_keys
|
@@ -56,18 +62,50 @@ module ActiveRecord
|
|
56
62
|
inserts.map do |attributes|
|
57
63
|
attributes = attributes.stringify_keys
|
58
64
|
attributes.merge!(scope_attributes) if scope_attributes
|
65
|
+
attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
|
59
66
|
|
60
67
|
verify_attributes(attributes)
|
61
68
|
|
62
|
-
|
69
|
+
keys_including_timestamps.map do |key|
|
63
70
|
yield key, attributes[key]
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
75
|
+
def record_timestamps?
|
76
|
+
@record_timestamps
|
77
|
+
end
|
78
|
+
|
79
|
+
# TODO: Consider remaining this method, as it only conditionally extends keys, not always
|
80
|
+
def keys_including_timestamps
|
81
|
+
@keys_including_timestamps ||= if record_timestamps?
|
82
|
+
keys + model.all_timestamp_attributes_in_model
|
83
|
+
else
|
84
|
+
keys
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
68
88
|
private
|
69
89
|
attr_reader :scope_attributes
|
70
90
|
|
91
|
+
def configure_on_duplicate_update_logic
|
92
|
+
if custom_update_sql_provided? && update_only.present?
|
93
|
+
raise ArgumentError, "You can't set :update_only and provide custom update SQL via :on_duplicate at the same time"
|
94
|
+
end
|
95
|
+
|
96
|
+
if update_only.present?
|
97
|
+
@updatable_columns = Array(update_only)
|
98
|
+
@on_duplicate = :update
|
99
|
+
elsif custom_update_sql_provided?
|
100
|
+
@update_sql = on_duplicate
|
101
|
+
@on_duplicate = :update
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def custom_update_sql_provided?
|
106
|
+
@custom_update_sql_provided ||= Arel.arel_node?(on_duplicate)
|
107
|
+
end
|
108
|
+
|
71
109
|
def find_unique_index_for(unique_by)
|
72
110
|
if !connection.supports_insert_conflict_target?
|
73
111
|
return if unique_by.nil?
|
@@ -126,15 +164,28 @@ module ActiveRecord
|
|
126
164
|
|
127
165
|
|
128
166
|
def verify_attributes(attributes)
|
129
|
-
if
|
167
|
+
if keys_including_timestamps != attributes.keys.to_set
|
130
168
|
raise ArgumentError, "All objects being inserted must have the same keys"
|
131
169
|
end
|
132
170
|
end
|
133
171
|
|
172
|
+
def disallow_raw_sql!(value)
|
173
|
+
return if !value.is_a?(String) || Arel.arel_node?(value)
|
174
|
+
|
175
|
+
raise ArgumentError, "Dangerous query method (method whose arguments are used as raw " \
|
176
|
+
"SQL) called: #{value}. " \
|
177
|
+
"Known-safe values can be passed " \
|
178
|
+
"by wrapping them in Arel.sql()."
|
179
|
+
end
|
180
|
+
|
181
|
+
def timestamps_for_create
|
182
|
+
model.all_timestamp_attributes_in_model.index_with(connection.high_precision_current_timestamp)
|
183
|
+
end
|
184
|
+
|
134
185
|
class Builder # :nodoc:
|
135
186
|
attr_reader :model
|
136
187
|
|
137
|
-
delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
|
188
|
+
delegate :skip_duplicates?, :update_duplicates?, :keys, :keys_including_timestamps, :record_timestamps?, to: :insert_all
|
138
189
|
|
139
190
|
def initialize(insert_all)
|
140
191
|
@insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
|
@@ -145,9 +196,10 @@ module ActiveRecord
|
|
145
196
|
end
|
146
197
|
|
147
198
|
def values_list
|
148
|
-
types = extract_types_from_columns_on(model.table_name, keys:
|
199
|
+
types = extract_types_from_columns_on(model.table_name, keys: keys_including_timestamps)
|
149
200
|
|
150
201
|
values_list = insert_all.map_key_with_value do |key, value|
|
202
|
+
next value if Arel::Nodes::SqlLiteral === value
|
151
203
|
connection.with_yaml_fallback(types[key].serialize(value))
|
152
204
|
end
|
153
205
|
|
@@ -155,7 +207,13 @@ module ActiveRecord
|
|
155
207
|
end
|
156
208
|
|
157
209
|
def returning
|
158
|
-
|
210
|
+
return unless insert_all.returning
|
211
|
+
|
212
|
+
if insert_all.returning.is_a?(String)
|
213
|
+
insert_all.returning
|
214
|
+
else
|
215
|
+
format_columns(insert_all.returning)
|
216
|
+
end
|
159
217
|
end
|
160
218
|
|
161
219
|
def conflict_target
|
@@ -173,22 +231,30 @@ module ActiveRecord
|
|
173
231
|
end
|
174
232
|
|
175
233
|
def touch_model_timestamps_unless(&block)
|
176
|
-
|
234
|
+
return "" unless update_duplicates? && record_timestamps?
|
235
|
+
|
236
|
+
model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
|
177
237
|
if touch_timestamp_attribute?(column_name)
|
178
|
-
"#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE
|
238
|
+
"#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.high_precision_current_timestamp} END),"
|
179
239
|
end
|
180
|
-
end.
|
240
|
+
end.join
|
181
241
|
end
|
182
242
|
|
243
|
+
def raw_update_sql
|
244
|
+
insert_all.update_sql
|
245
|
+
end
|
246
|
+
|
247
|
+
alias raw_update_sql? raw_update_sql
|
248
|
+
|
183
249
|
private
|
184
250
|
attr_reader :connection, :insert_all
|
185
251
|
|
186
252
|
def touch_timestamp_attribute?(column_name)
|
187
|
-
|
253
|
+
insert_all.updatable_columns.exclude?(column_name)
|
188
254
|
end
|
189
255
|
|
190
256
|
def columns_list
|
191
|
-
format_columns(insert_all.
|
257
|
+
format_columns(insert_all.keys_including_timestamps)
|
192
258
|
end
|
193
259
|
|
194
260
|
def extract_types_from_columns_on(table_name, keys:)
|
@@ -79,7 +79,7 @@ module ActiveRecord
|
|
79
79
|
timestamp = max_updated_column_timestamp
|
80
80
|
|
81
81
|
if timestamp
|
82
|
-
timestamp = timestamp.utc.
|
82
|
+
timestamp = timestamp.utc.to_formatted_s(cache_timestamp_format)
|
83
83
|
"#{model_name.cache_key}/#{id}-#{timestamp}"
|
84
84
|
else
|
85
85
|
"#{model_name.cache_key}/#{id}"
|
@@ -101,8 +101,9 @@ module ActiveRecord
|
|
101
101
|
timestamp = updated_at_before_type_cast
|
102
102
|
if can_use_fast_cache_version?(timestamp)
|
103
103
|
raw_timestamp_to_cache_version(timestamp)
|
104
|
+
|
104
105
|
elsif timestamp = updated_at
|
105
|
-
timestamp.utc.
|
106
|
+
timestamp.utc.to_formatted_s(cache_timestamp_format)
|
106
107
|
end
|
107
108
|
elsif self.class.has_attribute?("updated_at")
|
108
109
|
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
@@ -177,7 +178,7 @@ module ActiveRecord
|
|
177
178
|
def can_use_fast_cache_version?(timestamp)
|
178
179
|
timestamp.is_a?(String) &&
|
179
180
|
cache_timestamp_format == :usec &&
|
180
|
-
default_timezone == :utc &&
|
181
|
+
ActiveRecord.default_timezone == :utc &&
|
181
182
|
!updated_at_came_from_user?
|
182
183
|
end
|
183
184
|
|
@@ -10,15 +10,13 @@ module ActiveRecord
|
|
10
10
|
# This is enabled by default. To disable this functionality set
|
11
11
|
# `use_metadata_table` to false in your database configuration.
|
12
12
|
class InternalMetadata < ActiveRecord::Base # :nodoc:
|
13
|
+
self.record_timestamps = true
|
14
|
+
|
13
15
|
class << self
|
14
16
|
def enabled?
|
15
17
|
ActiveRecord::Base.connection.use_metadata_table?
|
16
18
|
end
|
17
19
|
|
18
|
-
def _internal?
|
19
|
-
true
|
20
|
-
end
|
21
|
-
|
22
20
|
def primary_key
|
23
21
|
"key"
|
24
22
|
end
|
@@ -36,7 +34,7 @@ module ActiveRecord
|
|
36
34
|
def [](key)
|
37
35
|
return unless enabled?
|
38
36
|
|
39
|
-
where(key: key).
|
37
|
+
where(key: key).pick(:value)
|
40
38
|
end
|
41
39
|
|
42
40
|
# Creates an internal metadata table with columns +key+ and +value+
|
@@ -2,50 +2,13 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module LegacyYamlAdapter # :nodoc:
|
5
|
-
def self.convert(
|
5
|
+
def self.convert(coder)
|
6
6
|
return coder unless coder.is_a?(Psych::Coder)
|
7
7
|
|
8
8
|
case coder["active_record_yaml_version"]
|
9
9
|
when 1, 2 then coder
|
10
10
|
else
|
11
|
-
|
12
|
-
YAML loading from legacy format older than Rails 5.0 is deprecated
|
13
|
-
and will be removed in Rails 6.2.
|
14
|
-
MSG
|
15
|
-
if coder["attributes"].is_a?(ActiveModel::AttributeSet)
|
16
|
-
Rails420.convert(klass, coder)
|
17
|
-
else
|
18
|
-
Rails41.convert(klass, coder)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module Rails420 # :nodoc:
|
24
|
-
def self.convert(klass, coder)
|
25
|
-
attribute_set = coder["attributes"]
|
26
|
-
|
27
|
-
klass.attribute_names.each do |attr_name|
|
28
|
-
attribute = attribute_set[attr_name]
|
29
|
-
if attribute.type.is_a?(Delegator)
|
30
|
-
type_from_klass = klass.type_for_attribute(attr_name)
|
31
|
-
attribute_set[attr_name] = attribute.with_type(type_from_klass)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
coder
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
module Rails41 # :nodoc:
|
40
|
-
def self.convert(klass, coder)
|
41
|
-
attributes = klass.attributes_builder
|
42
|
-
.build_from_database(coder["attributes"])
|
43
|
-
new_record = coder["attributes"][klass.primary_key].blank?
|
44
|
-
|
45
|
-
{
|
46
|
-
"attributes" => attributes,
|
47
|
-
"new_record" => new_record,
|
48
|
-
}
|
11
|
+
raise("Active Record doesn't know how to load YAML with this format.")
|
49
12
|
end
|
50
13
|
end
|
51
14
|
end
|
@@ -56,11 +56,11 @@ module ActiveRecord
|
|
56
56
|
class_attribute :lock_optimistically, instance_writer: false, default: true
|
57
57
|
end
|
58
58
|
|
59
|
-
def locking_enabled?
|
59
|
+
def locking_enabled? # :nodoc:
|
60
60
|
self.class.locking_enabled?
|
61
61
|
end
|
62
62
|
|
63
|
-
def increment!(*, **)
|
63
|
+
def increment!(*, **) # :nodoc:
|
64
64
|
super.tap do
|
65
65
|
if locking_enabled?
|
66
66
|
self[self.class.locking_column] += 1
|
@@ -90,7 +90,9 @@ module ActiveRecord
|
|
90
90
|
begin
|
91
91
|
locking_column = self.class.locking_column
|
92
92
|
lock_attribute_was = @attributes[locking_column]
|
93
|
-
|
93
|
+
|
94
|
+
update_constraints = _primary_key_constraints_hash
|
95
|
+
update_constraints[locking_column] = _lock_value_for_database(locking_column)
|
94
96
|
|
95
97
|
attribute_names = attribute_names.dup if attribute_names.frozen?
|
96
98
|
attribute_names << locking_column
|
@@ -99,8 +101,7 @@ module ActiveRecord
|
|
99
101
|
|
100
102
|
affected_rows = self.class._update_record(
|
101
103
|
attributes_with_values(attribute_names),
|
102
|
-
|
103
|
-
locking_column => lock_value_for_database
|
104
|
+
update_constraints
|
104
105
|
)
|
105
106
|
|
106
107
|
if affected_rows != 1
|
@@ -121,10 +122,10 @@ module ActiveRecord
|
|
121
122
|
|
122
123
|
locking_column = self.class.locking_column
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
)
|
125
|
+
delete_constraints = _primary_key_constraints_hash
|
126
|
+
delete_constraints[locking_column] = _lock_value_for_database(locking_column)
|
127
|
+
|
128
|
+
affected_rows = self.class._delete_record(delete_constraints)
|
128
129
|
|
129
130
|
if affected_rows != 1
|
130
131
|
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
@@ -81,9 +81,15 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
# Wraps the passed block in a transaction, locking the object
|
83
83
|
# before yielding. You can pass the SQL locking clause
|
84
|
-
# as argument (see <tt
|
85
|
-
|
86
|
-
|
84
|
+
# as an optional argument (see <tt>#lock!</tt>).
|
85
|
+
#
|
86
|
+
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
|
87
|
+
# and <tt>joinable:</tt> to the wrapping transaction (see
|
88
|
+
# <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
|
89
|
+
def with_lock(*args)
|
90
|
+
transaction_opts = args.extract_options!
|
91
|
+
lock = args.present? ? args.first : true
|
92
|
+
transaction(**transaction_opts) do
|
87
93
|
lock!(lock)
|
88
94
|
yield
|
89
95
|
end
|