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
@@ -17,8 +17,8 @@ module ActiveRecord
|
|
17
17
|
QueryRegistry.reset
|
18
18
|
|
19
19
|
super.tap do |records|
|
20
|
-
if logger && warn_on_records_fetched_greater_than
|
21
|
-
if records.length > warn_on_records_fetched_greater_than
|
20
|
+
if logger && ActiveRecord.warn_on_records_fetched_greater_than
|
21
|
+
if records.length > ActiveRecord.warn_on_records_fetched_greater_than
|
22
22
|
logger.warn "Query fetched #{records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
|
23
23
|
end
|
24
24
|
end
|
@@ -31,17 +31,15 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
# :startdoc:
|
33
33
|
|
34
|
-
|
35
|
-
extend
|
34
|
+
module QueryRegistry # :nodoc:
|
35
|
+
extend self
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
def initialize
|
40
|
-
@queries = []
|
37
|
+
def queries
|
38
|
+
ActiveSupport::IsolatedExecutionState[:active_record_query_registry] ||= []
|
41
39
|
end
|
42
40
|
|
43
41
|
def reset
|
44
|
-
|
42
|
+
queries.clear
|
45
43
|
end
|
46
44
|
end
|
47
45
|
end
|
@@ -7,8 +7,8 @@ require "active_record/relation/merger"
|
|
7
7
|
module ActiveRecord
|
8
8
|
module SpawnMethods
|
9
9
|
# This is overridden by Associations::CollectionProxy
|
10
|
-
def spawn
|
11
|
-
already_in_scope? ? klass.all : clone
|
10
|
+
def spawn # :nodoc:
|
11
|
+
already_in_scope?(klass.scope_registry) ? klass.all : clone
|
12
12
|
end
|
13
13
|
|
14
14
|
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
@@ -76,6 +76,11 @@ module ActiveRecord
|
|
76
76
|
other.is_a?(WhereClause) &&
|
77
77
|
predicates == other.predicates
|
78
78
|
end
|
79
|
+
alias :eql? :==
|
80
|
+
|
81
|
+
def hash
|
82
|
+
[self.class, predicates].hash
|
83
|
+
end
|
79
84
|
|
80
85
|
def invert
|
81
86
|
if predicates.size == 1
|
@@ -158,21 +163,8 @@ module ActiveRecord
|
|
158
163
|
attr = extract_attribute(node) || begin
|
159
164
|
node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
|
160
165
|
end
|
161
|
-
|
162
|
-
|
163
|
-
ref = referenced_columns[attr]
|
164
|
-
next false unless ref
|
165
|
-
|
166
|
-
if equality_node?(node) && equality_node?(ref) || node == ref
|
167
|
-
true
|
168
|
-
else
|
169
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
170
|
-
Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
|
171
|
-
both conditions, and will be replaced by the latter in Rails 6.2.
|
172
|
-
To migrate to Rails 6.2's behavior, use `relation.merge(other, rewhere: true)`.
|
173
|
-
MSG
|
174
|
-
false
|
175
|
-
end
|
166
|
+
|
167
|
+
attr && referenced_columns[attr]
|
176
168
|
end
|
177
169
|
end
|
178
170
|
|
@@ -227,11 +219,10 @@ module ActiveRecord
|
|
227
219
|
end
|
228
220
|
|
229
221
|
def extract_node_value(node)
|
230
|
-
|
231
|
-
when Array
|
232
|
-
node.map { |v| extract_node_value(v) }
|
233
|
-
when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
|
222
|
+
if node.respond_to?(:value_before_type_cast)
|
234
223
|
node.value_before_type_cast
|
224
|
+
elsif Array === node
|
225
|
+
node.map { |v| extract_node_value(v) }
|
235
226
|
end
|
236
227
|
end
|
237
228
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
12
12
|
|
13
13
|
CLAUSE_METHODS = [:where, :having, :from]
|
14
|
-
INVALID_METHODS_FOR_DELETE_ALL = [:distinct
|
14
|
+
INVALID_METHODS_FOR_DELETE_ALL = [:distinct]
|
15
15
|
|
16
16
|
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
17
17
|
|
@@ -31,6 +31,9 @@ module ActiveRecord
|
|
31
31
|
@loaded = false
|
32
32
|
@predicate_builder = predicate_builder
|
33
33
|
@delegate_to_klass = false
|
34
|
+
@future_result = nil
|
35
|
+
@records = nil
|
36
|
+
@limited_count = nil
|
34
37
|
end
|
35
38
|
|
36
39
|
def initialize_copy(other)
|
@@ -38,11 +41,6 @@ module ActiveRecord
|
|
38
41
|
reset
|
39
42
|
end
|
40
43
|
|
41
|
-
def arel_attribute(name) # :nodoc:
|
42
|
-
table[name]
|
43
|
-
end
|
44
|
-
deprecate :arel_attribute
|
45
|
-
|
46
44
|
def bind_attribute(name, value) # :nodoc:
|
47
45
|
if reflection = klass._reflect_on_association(name)
|
48
46
|
name = reflection.foreign_key
|
@@ -67,8 +65,12 @@ module ActiveRecord
|
|
67
65
|
# user = users.new { |user| user.name = 'Oscar' }
|
68
66
|
# user.name # => Oscar
|
69
67
|
def new(attributes = nil, &block)
|
70
|
-
|
71
|
-
|
68
|
+
if attributes.is_a?(Array)
|
69
|
+
attributes.collect { |attr| new(attr, &block) }
|
70
|
+
else
|
71
|
+
block = current_scope_restoring_block(&block)
|
72
|
+
scoping { _new(attributes, &block) }
|
73
|
+
end
|
72
74
|
end
|
73
75
|
alias build new
|
74
76
|
|
@@ -148,7 +150,7 @@ module ActiveRecord
|
|
148
150
|
# above can be alternatively written this way:
|
149
151
|
#
|
150
152
|
# # Find the first user named "Scarlett" or create a new one with a
|
151
|
-
# #
|
153
|
+
# # particular last name.
|
152
154
|
# User.find_or_create_by(first_name: 'Scarlett') do |user|
|
153
155
|
# user.last_name = 'Johansson'
|
154
156
|
# end
|
@@ -175,7 +177,7 @@ module ActiveRecord
|
|
175
177
|
find_by(attributes) || create!(attributes, &block)
|
176
178
|
end
|
177
179
|
|
178
|
-
# Attempts to create a record with the given attributes in a table that has a unique constraint
|
180
|
+
# Attempts to create a record with the given attributes in a table that has a unique database constraint
|
179
181
|
# on one or several of its columns. If a row already exists with one or several of these
|
180
182
|
# unique constraints, the exception such an insertion would normally raise is caught,
|
181
183
|
# and the existing record with those attributes is found using #find_by!.
|
@@ -186,7 +188,7 @@ module ActiveRecord
|
|
186
188
|
#
|
187
189
|
# There are several drawbacks to #create_or_find_by, though:
|
188
190
|
#
|
189
|
-
# * The underlying table must have the relevant columns defined with unique constraints.
|
191
|
+
# * The underlying table must have the relevant columns defined with unique database constraints.
|
190
192
|
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
191
193
|
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
192
194
|
# matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
|
@@ -257,13 +259,20 @@ module ActiveRecord
|
|
257
259
|
|
258
260
|
# Returns size of the records.
|
259
261
|
def size
|
260
|
-
loaded?
|
262
|
+
if loaded?
|
263
|
+
records.length
|
264
|
+
else
|
265
|
+
count(:all)
|
266
|
+
end
|
261
267
|
end
|
262
268
|
|
263
269
|
# Returns true if there are no records.
|
264
270
|
def empty?
|
265
|
-
|
266
|
-
|
271
|
+
if loaded?
|
272
|
+
records.empty?
|
273
|
+
else
|
274
|
+
!exists?
|
275
|
+
end
|
267
276
|
end
|
268
277
|
|
269
278
|
# Returns true if there are no records.
|
@@ -281,13 +290,15 @@ module ActiveRecord
|
|
281
290
|
# Returns true if there is exactly one record.
|
282
291
|
def one?
|
283
292
|
return super if block_given?
|
284
|
-
|
293
|
+
return records.one? if loaded?
|
294
|
+
limited_count == 1
|
285
295
|
end
|
286
296
|
|
287
297
|
# Returns true if there is more than one record.
|
288
298
|
def many?
|
289
299
|
return super if block_given?
|
290
|
-
|
300
|
+
return records.many? if loaded?
|
301
|
+
limited_count > 1
|
291
302
|
end
|
292
303
|
|
293
304
|
# Returns a stable cache key that can be used to identify this query.
|
@@ -344,7 +355,7 @@ module ActiveRecord
|
|
344
355
|
def compute_cache_version(timestamp_column) # :nodoc:
|
345
356
|
timestamp_column = timestamp_column.to_s
|
346
357
|
|
347
|
-
if loaded?
|
358
|
+
if loaded?
|
348
359
|
size = records.size
|
349
360
|
if size > 0
|
350
361
|
timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
|
@@ -357,6 +368,7 @@ module ActiveRecord
|
|
357
368
|
|
358
369
|
if collection.has_limit_or_offset?
|
359
370
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
371
|
+
query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
|
360
372
|
subquery_alias = "subquery_for_cache_key"
|
361
373
|
subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
|
362
374
|
arel = query.build_subquery(subquery_alias, select_values % subquery_column)
|
@@ -377,7 +389,7 @@ module ActiveRecord
|
|
377
389
|
end
|
378
390
|
|
379
391
|
if timestamp
|
380
|
-
"#{size}-#{timestamp.utc.
|
392
|
+
"#{size}-#{timestamp.utc.to_formatted_s(cache_timestamp_format)}"
|
381
393
|
else
|
382
394
|
"#{size}"
|
383
395
|
end
|
@@ -400,15 +412,28 @@ module ActiveRecord
|
|
400
412
|
# end
|
401
413
|
# # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
|
402
414
|
#
|
415
|
+
# If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
|
416
|
+
# for the relation including +update+ and +delete+ on instances.
|
417
|
+
# Once +all_queries+ is set to true it cannot be set to false in a
|
418
|
+
# nested block.
|
419
|
+
#
|
403
420
|
# Please check unscoped if you want to remove all previous scopes (including
|
404
421
|
# the default_scope) during the execution of a block.
|
405
|
-
def scoping
|
406
|
-
|
422
|
+
def scoping(all_queries: nil, &block)
|
423
|
+
registry = klass.scope_registry
|
424
|
+
if global_scope?(registry) && all_queries == false
|
425
|
+
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
426
|
+
elsif already_in_scope?(registry)
|
427
|
+
yield
|
428
|
+
else
|
429
|
+
_scoping(self, registry, all_queries, &block)
|
430
|
+
end
|
407
431
|
end
|
408
432
|
|
409
433
|
def _exec_scope(*args, &block) # :nodoc:
|
410
434
|
@delegate_to_klass = true
|
411
|
-
|
435
|
+
registry = klass.scope_registry
|
436
|
+
_scoping(nil, registry) { instance_exec(*args, &block) || self }
|
412
437
|
ensure
|
413
438
|
@delegate_to_klass = false
|
414
439
|
end
|
@@ -440,17 +465,6 @@ module ActiveRecord
|
|
440
465
|
def update_all(updates)
|
441
466
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
442
467
|
|
443
|
-
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
444
|
-
arel.source.left = table
|
445
|
-
|
446
|
-
stmt = Arel::UpdateManager.new
|
447
|
-
stmt.table(arel.source)
|
448
|
-
stmt.key = table[primary_key]
|
449
|
-
stmt.take(arel.limit)
|
450
|
-
stmt.offset(arel.offset)
|
451
|
-
stmt.order(*arel.orders)
|
452
|
-
stmt.wheres = arel.constraints
|
453
|
-
|
454
468
|
if updates.is_a?(Hash)
|
455
469
|
if klass.locking_enabled? &&
|
456
470
|
!updates.key?(klass.locking_column) &&
|
@@ -458,11 +472,17 @@ module ActiveRecord
|
|
458
472
|
attr = table[klass.locking_column]
|
459
473
|
updates[attr.name] = _increment_attribute(attr)
|
460
474
|
end
|
461
|
-
|
475
|
+
values = _substitute_values(updates)
|
462
476
|
else
|
463
|
-
|
477
|
+
values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
464
478
|
end
|
465
479
|
|
480
|
+
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
481
|
+
arel.source.left = table
|
482
|
+
|
483
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
484
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
485
|
+
stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
|
466
486
|
klass.connection.update(stmt, "#{klass} Update All").tap { reset }
|
467
487
|
end
|
468
488
|
|
@@ -474,6 +494,14 @@ module ActiveRecord
|
|
474
494
|
end
|
475
495
|
end
|
476
496
|
|
497
|
+
def update!(id = :all, attributes) # :nodoc:
|
498
|
+
if id == :all
|
499
|
+
each { |record| record.update!(attributes) }
|
500
|
+
else
|
501
|
+
klass.update!(id, attributes)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
477
505
|
# Updates the counters of the records in the current relation.
|
478
506
|
#
|
479
507
|
# ==== Parameters
|
@@ -583,15 +611,11 @@ module ActiveRecord
|
|
583
611
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel
|
584
612
|
arel.source.left = table
|
585
613
|
|
586
|
-
|
587
|
-
|
588
|
-
stmt
|
589
|
-
stmt.take(arel.limit)
|
590
|
-
stmt.offset(arel.offset)
|
591
|
-
stmt.order(*arel.orders)
|
592
|
-
stmt.wheres = arel.constraints
|
614
|
+
group_values_arel_columns = arel_columns(group_values.uniq)
|
615
|
+
having_clause_ast = having_clause.ast unless having_clause.empty?
|
616
|
+
stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
|
593
617
|
|
594
|
-
klass.connection.delete(stmt, "#{klass}
|
618
|
+
klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
|
595
619
|
end
|
596
620
|
|
597
621
|
# Finds and destroys all records matching the specified conditions.
|
@@ -620,6 +644,32 @@ module ActiveRecord
|
|
620
644
|
where(*args).delete_all
|
621
645
|
end
|
622
646
|
|
647
|
+
# Schedule the query to be performed from a background thread pool.
|
648
|
+
#
|
649
|
+
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
650
|
+
def load_async
|
651
|
+
return load if !connection.async_enabled?
|
652
|
+
|
653
|
+
unless loaded?
|
654
|
+
result = exec_main_query(async: connection.current_transaction.closed?)
|
655
|
+
|
656
|
+
if result.is_a?(Array)
|
657
|
+
@records = result
|
658
|
+
else
|
659
|
+
@future_result = result
|
660
|
+
end
|
661
|
+
@loaded = true
|
662
|
+
end
|
663
|
+
|
664
|
+
self
|
665
|
+
end
|
666
|
+
|
667
|
+
# Returns <tt>true</tt> if the relation was scheduled on the background
|
668
|
+
# thread pool.
|
669
|
+
def scheduled?
|
670
|
+
!!@future_result
|
671
|
+
end
|
672
|
+
|
623
673
|
# Causes the records to be loaded from the database if they have not
|
624
674
|
# been loaded already. You can use this if for some reason you need
|
625
675
|
# to explicitly load some records before actually using them. The
|
@@ -627,7 +677,7 @@ module ActiveRecord
|
|
627
677
|
#
|
628
678
|
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
629
679
|
def load(&block)
|
630
|
-
|
680
|
+
if !loaded? || scheduled?
|
631
681
|
@records = exec_queries(&block)
|
632
682
|
@loaded = true
|
633
683
|
end
|
@@ -642,11 +692,14 @@ module ActiveRecord
|
|
642
692
|
end
|
643
693
|
|
644
694
|
def reset
|
695
|
+
@future_result&.cancel
|
696
|
+
@future_result = nil
|
645
697
|
@delegate_to_klass = false
|
646
698
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
647
699
|
@offsets = @take = nil
|
648
700
|
@cache_keys = nil
|
649
|
-
@records =
|
701
|
+
@records = nil
|
702
|
+
@limited_count = nil
|
650
703
|
self
|
651
704
|
end
|
652
705
|
|
@@ -655,16 +708,14 @@ module ActiveRecord
|
|
655
708
|
# User.where(name: 'Oscar').to_sql
|
656
709
|
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
657
710
|
def to_sql
|
658
|
-
@to_sql ||=
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
relation.to_sql
|
663
|
-
end
|
664
|
-
else
|
665
|
-
conn = klass.connection
|
666
|
-
conn.unprepared_statement { conn.to_sql(arel) }
|
711
|
+
@to_sql ||= if eager_loading?
|
712
|
+
apply_join_dependency do |relation, join_dependency|
|
713
|
+
relation = join_dependency.apply_column_aliases(relation)
|
714
|
+
relation.to_sql
|
667
715
|
end
|
716
|
+
else
|
717
|
+
conn = klass.connection
|
718
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
668
719
|
end
|
669
720
|
end
|
670
721
|
|
@@ -722,6 +773,10 @@ module ActiveRecord
|
|
722
773
|
@values.dup
|
723
774
|
end
|
724
775
|
|
776
|
+
def values_for_queries # :nodoc:
|
777
|
+
@values.except(:extending, :skip_query_cache, :strict_loading)
|
778
|
+
end
|
779
|
+
|
725
780
|
def inspect
|
726
781
|
subject = loaded? ? records : annotate("loading for inspect")
|
727
782
|
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
@@ -756,11 +811,9 @@ module ActiveRecord
|
|
756
811
|
def preload_associations(records) # :nodoc:
|
757
812
|
preload = preload_values
|
758
813
|
preload += includes_values unless eager_loading?
|
759
|
-
preloader = nil
|
760
814
|
scope = strict_loading_value ? StrictLoadingScope : nil
|
761
815
|
preload.each do |associations|
|
762
|
-
|
763
|
-
preloader.preload records, associations, scope
|
816
|
+
ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
|
764
817
|
end
|
765
818
|
end
|
766
819
|
|
@@ -775,8 +828,12 @@ module ActiveRecord
|
|
775
828
|
end
|
776
829
|
|
777
830
|
private
|
778
|
-
def already_in_scope?
|
779
|
-
@delegate_to_klass &&
|
831
|
+
def already_in_scope?(registry)
|
832
|
+
@delegate_to_klass && registry.current_scope(klass, true)
|
833
|
+
end
|
834
|
+
|
835
|
+
def global_scope?(registry)
|
836
|
+
registry.global_current_scope(klass, true)
|
780
837
|
end
|
781
838
|
|
782
839
|
def current_scope_restoring_block(&block)
|
@@ -799,11 +856,20 @@ module ActiveRecord
|
|
799
856
|
klass.create!(attributes, &block)
|
800
857
|
end
|
801
858
|
|
802
|
-
def _scoping(scope)
|
803
|
-
previous
|
859
|
+
def _scoping(scope, registry, all_queries = false)
|
860
|
+
previous = registry.current_scope(klass, true)
|
861
|
+
registry.set_current_scope(klass, scope)
|
862
|
+
|
863
|
+
if all_queries
|
864
|
+
previous_global = registry.global_current_scope(klass, true)
|
865
|
+
registry.set_global_current_scope(klass, scope)
|
866
|
+
end
|
804
867
|
yield
|
805
868
|
ensure
|
806
|
-
klass
|
869
|
+
registry.set_current_scope(klass, previous)
|
870
|
+
if all_queries
|
871
|
+
registry.set_global_current_scope(klass, previous_global)
|
872
|
+
end
|
807
873
|
end
|
808
874
|
|
809
875
|
def _substitute_values(values)
|
@@ -826,23 +892,15 @@ module ActiveRecord
|
|
826
892
|
|
827
893
|
def exec_queries(&block)
|
828
894
|
skip_query_cache_if_necessary do
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
else
|
837
|
-
relation = join_dependency.apply_column_aliases(relation)
|
838
|
-
rows = connection.select_all(relation.arel, "SQL")
|
839
|
-
join_dependency.instantiate(rows, strict_loading_value, &block)
|
840
|
-
end.freeze
|
841
|
-
end
|
842
|
-
else
|
843
|
-
klass.find_by_sql(arel, &block).freeze
|
844
|
-
end
|
895
|
+
rows = if scheduled?
|
896
|
+
future = @future_result
|
897
|
+
@future_result = nil
|
898
|
+
future.result
|
899
|
+
else
|
900
|
+
exec_main_query
|
901
|
+
end
|
845
902
|
|
903
|
+
records = instantiate_records(rows, &block)
|
846
904
|
preload_associations(records) unless skip_preloading_value
|
847
905
|
|
848
906
|
records.each(&:readonly!) if readonly_value
|
@@ -852,18 +910,43 @@ module ActiveRecord
|
|
852
910
|
end
|
853
911
|
end
|
854
912
|
|
855
|
-
def
|
856
|
-
|
857
|
-
|
858
|
-
|
913
|
+
def exec_main_query(async: false)
|
914
|
+
skip_query_cache_if_necessary do
|
915
|
+
if where_clause.contradiction?
|
916
|
+
[].freeze
|
917
|
+
elsif eager_loading?
|
918
|
+
apply_join_dependency do |relation, join_dependency|
|
919
|
+
if relation.null_relation?
|
920
|
+
[].freeze
|
921
|
+
else
|
922
|
+
relation = join_dependency.apply_column_aliases(relation)
|
923
|
+
@_join_dependency = join_dependency
|
924
|
+
connection.select_all(relation.arel, "SQL", async: async)
|
925
|
+
end
|
926
|
+
end
|
927
|
+
else
|
928
|
+
klass._query_by_sql(arel, async: async)
|
859
929
|
end
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
def instantiate_records(rows, &block)
|
934
|
+
return [].freeze if rows.empty?
|
935
|
+
if eager_loading?
|
936
|
+
records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
|
937
|
+
@_join_dependency = nil
|
938
|
+
records
|
860
939
|
else
|
861
|
-
|
940
|
+
klass._load_from_sql(rows, &block).freeze
|
862
941
|
end
|
863
942
|
end
|
864
943
|
|
865
|
-
def
|
866
|
-
|
944
|
+
def skip_query_cache_if_necessary(&block)
|
945
|
+
if skip_query_cache_value
|
946
|
+
uncached(&block)
|
947
|
+
else
|
948
|
+
yield
|
949
|
+
end
|
867
950
|
end
|
868
951
|
|
869
952
|
def references_eager_loaded_tables?
|
@@ -889,5 +972,9 @@ module ActiveRecord
|
|
889
972
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
890
973
|
string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
|
891
974
|
end
|
975
|
+
|
976
|
+
def limited_count
|
977
|
+
@limited_count ||= limit_value ? count : limit(2).count
|
978
|
+
end
|
892
979
|
end
|
893
980
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -36,6 +36,10 @@ module ActiveRecord
|
|
36
36
|
|
37
37
|
attr_reader :columns, :rows, :column_types
|
38
38
|
|
39
|
+
def self.empty # :nodoc:
|
40
|
+
EMPTY
|
41
|
+
end
|
42
|
+
|
39
43
|
def initialize(columns, rows, column_types = {})
|
40
44
|
@columns = columns
|
41
45
|
@rows = rows
|
@@ -43,6 +47,9 @@ module ActiveRecord
|
|
43
47
|
@column_types = column_types
|
44
48
|
end
|
45
49
|
|
50
|
+
EMPTY = new([].freeze, [].freeze, {}.freeze)
|
51
|
+
private_constant :EMPTY
|
52
|
+
|
46
53
|
# Returns true if this result set includes the column named +name+
|
47
54
|
def includes_column?(name)
|
48
55
|
@columns.include? name
|
@@ -57,19 +64,14 @@ module ActiveRecord
|
|
57
64
|
# row as parameter.
|
58
65
|
#
|
59
66
|
# Returns an +Enumerator+ if no block is given.
|
60
|
-
def each
|
67
|
+
def each(&block)
|
61
68
|
if block_given?
|
62
|
-
hash_rows.each
|
69
|
+
hash_rows.each(&block)
|
63
70
|
else
|
64
71
|
hash_rows.to_enum { @rows.size }
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
68
|
-
alias :map! :map
|
69
|
-
alias :collect! :map
|
70
|
-
deprecate "map!": :map
|
71
|
-
deprecate "collect!": :map
|
72
|
-
|
73
75
|
# Returns true if there are no records, otherwise false.
|
74
76
|
def empty?
|
75
77
|
rows.empty?
|
@@ -91,6 +93,14 @@ module ActiveRecord
|
|
91
93
|
n ? hash_rows.last(n) : hash_rows.last
|
92
94
|
end
|
93
95
|
|
96
|
+
def result # :nodoc:
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def cancel # :nodoc:
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
94
104
|
def cast_values(type_overrides = {}) # :nodoc:
|
95
105
|
if columns.one?
|
96
106
|
# Separated to avoid allocating an array per row
|
@@ -1,24 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/per_thread_registry"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
# This is a thread locals registry for Active Record. For example:
|
7
5
|
#
|
8
|
-
# ActiveRecord::RuntimeRegistry.
|
9
|
-
#
|
10
|
-
# returns the connection handler local to the current thread.
|
6
|
+
# ActiveRecord::RuntimeRegistry.sql_runtime
|
11
7
|
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
extend ActiveSupport::PerThreadRegistry
|
8
|
+
# returns the connection handler local to the current unit of execution (either thread of fiber).
|
9
|
+
module RuntimeRegistry # :nodoc:
|
10
|
+
extend self
|
16
11
|
|
17
|
-
|
12
|
+
def sql_runtime
|
13
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime]
|
14
|
+
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
|
16
|
+
def sql_runtime=(runtime)
|
17
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
|
22
18
|
end
|
23
19
|
end
|
24
20
|
end
|