activerecord 7.0.4 → 7.1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1971 -1243
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +20 -14
- data/lib/active_record/associations/collection_proxy.rb +20 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +333 -222
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +21 -8
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -26
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +59 -10
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -7
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +297 -127
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -103
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +254 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +365 -61
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +213 -85
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +181 -154
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +28 -14
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +15 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +40 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +33 -19
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +9 -11
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +105 -7
- data/lib/active_record/migration/compatibility.rb +163 -58
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +271 -114
- data/lib/active_record/model_schema.rb +69 -44
- data/lib/active_record/nested_attributes.rb +37 -8
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +195 -42
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +14 -9
- data/lib/active_record/railties/databases.rake +144 -150
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +189 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +232 -81
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +408 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +103 -37
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +50 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +9 -9
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +152 -108
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +114 -96
- data/lib/active_record/timestamp.rb +30 -16
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +130 -17
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +83 -18
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +51 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -3,7 +3,49 @@
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
|
+
# = Active Record \Calculations
|
6
7
|
module Calculations
|
8
|
+
class ColumnAliasTracker # :nodoc:
|
9
|
+
def initialize(connection)
|
10
|
+
@connection = connection
|
11
|
+
@aliases = Hash.new(0)
|
12
|
+
end
|
13
|
+
|
14
|
+
def alias_for(field)
|
15
|
+
aliased_name = column_alias_for(field)
|
16
|
+
|
17
|
+
if @aliases[aliased_name] == 0
|
18
|
+
@aliases[aliased_name] = 1
|
19
|
+
aliased_name
|
20
|
+
else
|
21
|
+
# Update the count
|
22
|
+
count = @aliases[aliased_name] += 1
|
23
|
+
"#{truncate(aliased_name)}_#{count}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
# Converts the given field to the value that the database adapter returns as
|
29
|
+
# a usable column name:
|
30
|
+
#
|
31
|
+
# column_alias_for("users.id") # => "users_id"
|
32
|
+
# column_alias_for("sum(id)") # => "sum_id"
|
33
|
+
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
34
|
+
# column_alias_for("count(*)") # => "count_all"
|
35
|
+
def column_alias_for(field)
|
36
|
+
column_alias = +field
|
37
|
+
column_alias.gsub!(/\*/, "all")
|
38
|
+
column_alias.gsub!(/\W+/, " ")
|
39
|
+
column_alias.strip!
|
40
|
+
column_alias.gsub!(/ +/, "_")
|
41
|
+
@connection.table_alias_for(column_alias)
|
42
|
+
end
|
43
|
+
|
44
|
+
def truncate(name)
|
45
|
+
name.slice(0, @connection.table_alias_length - 2)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
7
49
|
# Count the records.
|
8
50
|
#
|
9
51
|
# Person.count
|
@@ -30,8 +72,7 @@ module ActiveRecord
|
|
30
72
|
# of each key would be the #count.
|
31
73
|
#
|
32
74
|
# Article.group(:status, :category).count
|
33
|
-
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
34
|
-
# # ["published", "business"]=>0, ["published", "technology"]=>2}
|
75
|
+
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4, ["published", "technology"]=>2}
|
35
76
|
#
|
36
77
|
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
37
78
|
#
|
@@ -40,6 +81,16 @@ module ActiveRecord
|
|
40
81
|
#
|
41
82
|
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
42
83
|
# between databases. In invalid cases, an error from the database is thrown.
|
84
|
+
#
|
85
|
+
# When given a block, loads all records in the relation, if the relation
|
86
|
+
# hasn't been loaded yet. Calls the block with each record in the relation.
|
87
|
+
# Returns the number of records for which the block returns a truthy value.
|
88
|
+
#
|
89
|
+
# Person.count { |person| person.age > 21 }
|
90
|
+
# # => counts the number of people older that 21
|
91
|
+
#
|
92
|
+
# Note: If there are a lot of records in the relation, loading all records
|
93
|
+
# could result in performance issues.
|
43
94
|
def count(column_name = nil)
|
44
95
|
if block_given?
|
45
96
|
unless column_name.nil?
|
@@ -52,6 +103,12 @@ module ActiveRecord
|
|
52
103
|
end
|
53
104
|
end
|
54
105
|
|
106
|
+
# Same as #count, but performs the query asynchronously and returns an
|
107
|
+
# ActiveRecord::Promise.
|
108
|
+
def async_count(column_name = nil)
|
109
|
+
async.count(column_name)
|
110
|
+
end
|
111
|
+
|
55
112
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
56
113
|
# no row. See #calculate for examples with options.
|
57
114
|
#
|
@@ -60,6 +117,12 @@ module ActiveRecord
|
|
60
117
|
calculate(:average, column_name)
|
61
118
|
end
|
62
119
|
|
120
|
+
# Same as #average, but performs the query asynchronously and returns an
|
121
|
+
# ActiveRecord::Promise.
|
122
|
+
def async_average(column_name)
|
123
|
+
async.average(column_name)
|
124
|
+
end
|
125
|
+
|
63
126
|
# Calculates the minimum value on a given column. The value is returned
|
64
127
|
# with the same data type of the column, or +nil+ if there's no row. See
|
65
128
|
# #calculate for examples with options.
|
@@ -69,6 +132,12 @@ module ActiveRecord
|
|
69
132
|
calculate(:minimum, column_name)
|
70
133
|
end
|
71
134
|
|
135
|
+
# Same as #minimum, but performs the query asynchronously and returns an
|
136
|
+
# ActiveRecord::Promise.
|
137
|
+
def async_minimum(column_name)
|
138
|
+
async.minimum(column_name)
|
139
|
+
end
|
140
|
+
|
72
141
|
# Calculates the maximum value on a given column. The value is returned
|
73
142
|
# with the same data type of the column, or +nil+ if there's no row. See
|
74
143
|
# #calculate for examples with options.
|
@@ -78,32 +147,42 @@ module ActiveRecord
|
|
78
147
|
calculate(:maximum, column_name)
|
79
148
|
end
|
80
149
|
|
150
|
+
# Same as #maximum, but performs the query asynchronously and returns an
|
151
|
+
# ActiveRecord::Promise.
|
152
|
+
def async_maximum(column_name)
|
153
|
+
async.maximum(column_name)
|
154
|
+
end
|
155
|
+
|
81
156
|
# Calculates the sum of values on a given column. The value is returned
|
82
157
|
# with the same data type of the column, +0+ if there's no row. See
|
83
158
|
# #calculate for examples with options.
|
84
159
|
#
|
85
160
|
# Person.sum(:age) # => 4562
|
86
|
-
|
161
|
+
#
|
162
|
+
# When given a block, loads all records in the relation, if the relation
|
163
|
+
# hasn't been loaded yet. Calls the block with each record in the relation.
|
164
|
+
# Returns the sum of +initial_value_or_column+ and the block return
|
165
|
+
# values:
|
166
|
+
#
|
167
|
+
# Person.sum { |person| person.age } # => 4562
|
168
|
+
# Person.sum(1000) { |person| person.age } # => 5562
|
169
|
+
#
|
170
|
+
# Note: If there are a lot of records in the relation, loading all records
|
171
|
+
# could result in performance issues.
|
172
|
+
def sum(initial_value_or_column = 0, &block)
|
87
173
|
if block_given?
|
88
|
-
|
89
|
-
if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
|
90
|
-
identity_or_column = 0
|
91
|
-
end
|
92
|
-
|
93
|
-
if identity_or_column.nil?
|
94
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
95
|
-
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
96
|
-
Sum of non-numeric elements requires an initial argument.
|
97
|
-
MSG
|
98
|
-
values.inject(:+) || 0
|
99
|
-
else
|
100
|
-
values.sum(identity_or_column)
|
101
|
-
end
|
174
|
+
map(&block).sum(initial_value_or_column)
|
102
175
|
else
|
103
|
-
calculate(:sum,
|
176
|
+
calculate(:sum, initial_value_or_column)
|
104
177
|
end
|
105
178
|
end
|
106
179
|
|
180
|
+
# Same as #sum, but performs the query asynchronously and returns an
|
181
|
+
# ActiveRecord::Promise.
|
182
|
+
def async_sum(identity_or_column = nil)
|
183
|
+
async.sum(identity_or_column)
|
184
|
+
end
|
185
|
+
|
107
186
|
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
108
187
|
# #minimum, and #maximum have been added as shortcuts.
|
109
188
|
#
|
@@ -136,10 +215,23 @@ module ActiveRecord
|
|
136
215
|
# ...
|
137
216
|
# end
|
138
217
|
def calculate(operation, column_name)
|
218
|
+
operation = operation.to_s.downcase
|
219
|
+
|
220
|
+
if @none
|
221
|
+
case operation
|
222
|
+
when "count", "sum"
|
223
|
+
result = group_values.any? ? Hash.new : 0
|
224
|
+
return @async ? Promise::Complete.new(result) : result
|
225
|
+
when "average", "minimum", "maximum"
|
226
|
+
result = group_values.any? ? Hash.new : nil
|
227
|
+
return @async ? Promise::Complete.new(result) : result
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
139
231
|
if has_include?(column_name)
|
140
232
|
relation = apply_join_dependency
|
141
233
|
|
142
|
-
if operation
|
234
|
+
if operation == "count"
|
143
235
|
unless distinct_value || distinct_select?(column_name || select_for_count)
|
144
236
|
relation.distinct!
|
145
237
|
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
@@ -188,31 +280,51 @@ module ActiveRecord
|
|
188
280
|
# # => ['0', '27761', '173']
|
189
281
|
#
|
190
282
|
# See also #ids.
|
191
|
-
#
|
192
283
|
def pluck(*column_names)
|
284
|
+
if @none
|
285
|
+
if @async
|
286
|
+
return Promise::Complete.new([])
|
287
|
+
else
|
288
|
+
return []
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
193
292
|
if loaded? && all_attributes?(column_names)
|
194
|
-
|
293
|
+
result = records.pluck(*column_names)
|
294
|
+
if @async
|
295
|
+
return Promise::Complete.new(result)
|
296
|
+
else
|
297
|
+
return result
|
298
|
+
end
|
195
299
|
end
|
196
300
|
|
197
301
|
if has_include?(column_names.first)
|
198
302
|
relation = apply_join_dependency
|
199
303
|
relation.pluck(*column_names)
|
200
304
|
else
|
201
|
-
klass.disallow_raw_sql!(column_names)
|
305
|
+
klass.disallow_raw_sql!(column_names.flatten)
|
202
306
|
columns = arel_columns(column_names)
|
203
307
|
relation = spawn
|
204
308
|
relation.select_values = columns
|
205
309
|
result = skip_query_cache_if_necessary do
|
206
310
|
if where_clause.contradiction?
|
207
|
-
ActiveRecord::Result.empty
|
311
|
+
ActiveRecord::Result.empty(async: @async)
|
208
312
|
else
|
209
|
-
klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
|
313
|
+
klass.connection.select_all(relation.arel, "#{klass.name} Pluck", async: @async)
|
210
314
|
end
|
211
315
|
end
|
212
|
-
|
316
|
+
result.then do |result|
|
317
|
+
type_cast_pluck_values(result, columns)
|
318
|
+
end
|
213
319
|
end
|
214
320
|
end
|
215
321
|
|
322
|
+
# Same as #pluck, but performs the query asynchronously and returns an
|
323
|
+
# ActiveRecord::Promise.
|
324
|
+
def async_pluck(*column_names)
|
325
|
+
async.pluck(*column_names)
|
326
|
+
end
|
327
|
+
|
216
328
|
# Pick the value(s) from the named column(s) in the current relation.
|
217
329
|
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
218
330
|
# when you have a relation that's already narrowed down to a single row.
|
@@ -229,18 +341,61 @@ module ActiveRecord
|
|
229
341
|
# # => [ 'David', 'david@loudthinking.com' ]
|
230
342
|
def pick(*column_names)
|
231
343
|
if loaded? && all_attributes?(column_names)
|
232
|
-
|
344
|
+
result = records.pick(*column_names)
|
345
|
+
return @async ? Promise::Complete.new(result) : result
|
233
346
|
end
|
234
347
|
|
235
|
-
limit(1).pluck(*column_names).first
|
348
|
+
limit(1).pluck(*column_names).then(&:first)
|
236
349
|
end
|
237
350
|
|
238
|
-
#
|
351
|
+
# Same as #pick, but performs the query asynchronously and returns an
|
352
|
+
# ActiveRecord::Promise.
|
353
|
+
def async_pick(*column_names)
|
354
|
+
async.pick(*column_names)
|
355
|
+
end
|
356
|
+
|
357
|
+
# Returns the base model's ID's for the relation using the table's primary key
|
239
358
|
#
|
240
359
|
# Person.ids # SELECT people.id FROM people
|
241
|
-
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.
|
360
|
+
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.id = people.company_id
|
242
361
|
def ids
|
243
|
-
|
362
|
+
primary_key_array = Array(primary_key)
|
363
|
+
|
364
|
+
if loaded?
|
365
|
+
result = records.map do |record|
|
366
|
+
if primary_key_array.one?
|
367
|
+
record._read_attribute(primary_key_array.first)
|
368
|
+
else
|
369
|
+
primary_key_array.map { |column| record._read_attribute(column) }
|
370
|
+
end
|
371
|
+
end
|
372
|
+
return @async ? Promise::Complete.new(result) : result
|
373
|
+
end
|
374
|
+
|
375
|
+
if has_include?(primary_key)
|
376
|
+
relation = apply_join_dependency.group(*primary_key_array)
|
377
|
+
return relation.ids
|
378
|
+
end
|
379
|
+
|
380
|
+
columns = arel_columns(primary_key_array)
|
381
|
+
relation = spawn
|
382
|
+
relation.select_values = columns
|
383
|
+
|
384
|
+
result = if relation.where_clause.contradiction?
|
385
|
+
ActiveRecord::Result.empty
|
386
|
+
else
|
387
|
+
skip_query_cache_if_necessary do
|
388
|
+
klass.connection.select_all(relation, "#{klass.name} Ids", async: @async)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
result.then { |result| type_cast_pluck_values(result, columns) }
|
393
|
+
end
|
394
|
+
|
395
|
+
# Same as #ids, but performs the query asynchronously and returns an
|
396
|
+
# ActiveRecord::Promise.
|
397
|
+
def async_ids
|
398
|
+
async.ids
|
244
399
|
end
|
245
400
|
|
246
401
|
private
|
@@ -300,6 +455,7 @@ module ActiveRecord
|
|
300
455
|
# Shortcut when limit is zero.
|
301
456
|
return 0 if limit_value == 0
|
302
457
|
|
458
|
+
relation = self
|
303
459
|
query_builder = build_count_subquery(spawn, column_name, distinct)
|
304
460
|
else
|
305
461
|
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
@@ -314,15 +470,23 @@ module ActiveRecord
|
|
314
470
|
query_builder = relation.arel
|
315
471
|
end
|
316
472
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
473
|
+
query_result = if relation.where_clause.contradiction?
|
474
|
+
ActiveRecord::Result.empty
|
475
|
+
else
|
476
|
+
skip_query_cache_if_necessary do
|
477
|
+
@klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async)
|
478
|
+
end
|
323
479
|
end
|
324
480
|
|
325
|
-
|
481
|
+
query_result.then do |result|
|
482
|
+
if operation != "count"
|
483
|
+
type = column.try(:type_caster) ||
|
484
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
485
|
+
type = type.subtype if Enum::EnumType === type
|
486
|
+
end
|
487
|
+
|
488
|
+
type_cast_calculated_value(result.cast_values.first, operation, type)
|
489
|
+
end
|
326
490
|
end
|
327
491
|
|
328
492
|
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
@@ -336,14 +500,16 @@ module ActiveRecord
|
|
336
500
|
end
|
337
501
|
group_fields = arel_columns(group_fields)
|
338
502
|
|
503
|
+
column_alias_tracker = ColumnAliasTracker.new(connection)
|
504
|
+
|
339
505
|
group_aliases = group_fields.map { |field|
|
340
506
|
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
341
|
-
|
507
|
+
column_alias_tracker.alias_for(field.to_s.downcase)
|
342
508
|
}
|
343
509
|
group_columns = group_aliases.zip(group_fields)
|
344
510
|
|
345
511
|
column = aggregate_column(column_name)
|
346
|
-
column_alias =
|
512
|
+
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
347
513
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
348
514
|
select_value.as(connection.quote_column_name(column_alias))
|
349
515
|
|
@@ -363,58 +529,43 @@ module ActiveRecord
|
|
363
529
|
relation.group_values = group_fields
|
364
530
|
relation.select_values = select_values
|
365
531
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
532
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}", async: @async) }
|
533
|
+
result.then do |calculated_data|
|
534
|
+
if association
|
535
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
536
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
537
|
+
key_records = key_records.index_by(&:id)
|
538
|
+
end
|
373
539
|
|
374
|
-
|
375
|
-
|
376
|
-
|
540
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
541
|
+
types[aliaz] = col_name.try(:type_caster) ||
|
542
|
+
type_for(col_name) do
|
543
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
544
|
+
end
|
377
545
|
end
|
378
|
-
end
|
379
546
|
|
380
|
-
|
381
|
-
|
382
|
-
|
547
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
548
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
549
|
+
hash[col_name] = row[i]
|
550
|
+
end
|
383
551
|
end
|
384
|
-
end
|
385
552
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
553
|
+
if operation != "count"
|
554
|
+
type = column.try(:type_caster) ||
|
555
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
556
|
+
type = type.subtype if Enum::EnumType === type
|
557
|
+
end
|
391
558
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
559
|
+
hash_rows.each_with_object({}) do |row, result|
|
560
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
561
|
+
key = key.first if key.size == 1
|
562
|
+
key = key_records[key] if associated
|
396
563
|
|
397
|
-
|
564
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
565
|
+
end
|
398
566
|
end
|
399
567
|
end
|
400
568
|
|
401
|
-
# Converts the given field to the value that the database adapter returns as
|
402
|
-
# a usable column name:
|
403
|
-
#
|
404
|
-
# column_alias_for("users.id") # => "users_id"
|
405
|
-
# column_alias_for("sum(id)") # => "sum_id"
|
406
|
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
407
|
-
# column_alias_for("count(*)") # => "count_all"
|
408
|
-
def column_alias_for(field)
|
409
|
-
column_alias = +field
|
410
|
-
column_alias.gsub!(/\*/, "all")
|
411
|
-
column_alias.gsub!(/\W+/, " ")
|
412
|
-
column_alias.strip!
|
413
|
-
column_alias.gsub!(/ +/, "_")
|
414
|
-
|
415
|
-
connection.table_alias_for(column_alias)
|
416
|
-
end
|
417
|
-
|
418
569
|
def type_for(field, &block)
|
419
570
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
420
571
|
@klass.type_for_attribute(field_name, &block)
|
@@ -5,6 +5,23 @@ require "active_support/core_ext/module/delegation"
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module Delegation # :nodoc:
|
8
|
+
class << self
|
9
|
+
def delegated_classes
|
10
|
+
[
|
11
|
+
ActiveRecord::Relation,
|
12
|
+
ActiveRecord::Associations::CollectionProxy,
|
13
|
+
ActiveRecord::AssociationRelation,
|
14
|
+
ActiveRecord::DisableJoinsAssociationRelation,
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def uncacheable_methods
|
19
|
+
@uncacheable_methods ||= (
|
20
|
+
delegated_classes.flat_map(&:public_instance_methods) - ActiveRecord::Relation.public_instance_methods
|
21
|
+
).to_set.freeze
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
8
25
|
module DelegateCache # :nodoc:
|
9
26
|
def relation_delegate_class(klass)
|
10
27
|
@relation_delegate_cache[klass]
|
@@ -12,12 +29,7 @@ module ActiveRecord
|
|
12
29
|
|
13
30
|
def initialize_relation_delegate_cache
|
14
31
|
@relation_delegate_cache = cache = {}
|
15
|
-
|
16
|
-
ActiveRecord::Relation,
|
17
|
-
ActiveRecord::Associations::CollectionProxy,
|
18
|
-
ActiveRecord::AssociationRelation,
|
19
|
-
ActiveRecord::DisableJoinsAssociationRelation
|
20
|
-
].each do |klass|
|
32
|
+
Delegation.delegated_classes.each do |klass|
|
21
33
|
delegate = Class.new(klass) {
|
22
34
|
include ClassSpecificRelation
|
23
35
|
}
|
@@ -85,12 +97,12 @@ module ActiveRecord
|
|
85
97
|
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
86
98
|
# for each different klass, and the delegations are compiled into that subclass only.
|
87
99
|
|
88
|
-
delegate :to_xml, :encode_with, :length, :each, :join,
|
100
|
+
delegate :to_xml, :encode_with, :length, :each, :join, :intersect?,
|
89
101
|
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
90
102
|
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
91
103
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
92
104
|
|
93
|
-
delegate :primary_key, :connection, to: :klass
|
105
|
+
delegate :primary_key, :connection, :transaction, to: :klass
|
94
106
|
|
95
107
|
module ClassSpecificRelation # :nodoc:
|
96
108
|
extend ActiveSupport::Concern
|
@@ -104,7 +116,9 @@ module ActiveRecord
|
|
104
116
|
private
|
105
117
|
def method_missing(method, *args, &block)
|
106
118
|
if @klass.respond_to?(method)
|
107
|
-
|
119
|
+
unless Delegation.uncacheable_methods.include?(method)
|
120
|
+
@klass.generate_relation_method(method)
|
121
|
+
end
|
108
122
|
scoping { @klass.public_send(method, *args, &block) }
|
109
123
|
else
|
110
124
|
super
|