activerecord 6.1.6 → 7.1.2
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 +1627 -983
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- 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 +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- 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 +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +439 -305
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- 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 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -138
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- 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 +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- 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 +30 -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 +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- 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 +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +202 -223
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- 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 +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -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 +157 -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 +53 -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 +92 -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 +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -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 +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +112 -14
- data/lib/active_record/migration/compatibility.rb +221 -48
- 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/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +358 -171
- data/lib/active_record/model_schema.rb +120 -101
- data/lib/active_record/nested_attributes.rb +37 -18
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +188 -252
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +241 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +219 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +29 -18
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- 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 +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- 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/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- 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/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- 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 +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- 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
- 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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +93 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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
|
#
|
@@ -52,6 +93,12 @@ module ActiveRecord
|
|
52
93
|
end
|
53
94
|
end
|
54
95
|
|
96
|
+
# Same as #count, but performs the query asynchronously and returns an
|
97
|
+
# ActiveRecord::Promise.
|
98
|
+
def async_count(column_name = nil)
|
99
|
+
async.count(column_name)
|
100
|
+
end
|
101
|
+
|
55
102
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
56
103
|
# no row. See #calculate for examples with options.
|
57
104
|
#
|
@@ -60,6 +107,12 @@ module ActiveRecord
|
|
60
107
|
calculate(:average, column_name)
|
61
108
|
end
|
62
109
|
|
110
|
+
# Same as #average, but performs the query asynchronously and returns an
|
111
|
+
# ActiveRecord::Promise.
|
112
|
+
def async_average(column_name)
|
113
|
+
async.average(column_name)
|
114
|
+
end
|
115
|
+
|
63
116
|
# Calculates the minimum value on a given column. The value is returned
|
64
117
|
# with the same data type of the column, or +nil+ if there's no row. See
|
65
118
|
# #calculate for examples with options.
|
@@ -69,6 +122,12 @@ module ActiveRecord
|
|
69
122
|
calculate(:minimum, column_name)
|
70
123
|
end
|
71
124
|
|
125
|
+
# Same as #minimum, but performs the query asynchronously and returns an
|
126
|
+
# ActiveRecord::Promise.
|
127
|
+
def async_minimum(column_name)
|
128
|
+
async.minimum(column_name)
|
129
|
+
end
|
130
|
+
|
72
131
|
# Calculates the maximum value on a given column. The value is returned
|
73
132
|
# with the same data type of the column, or +nil+ if there's no row. See
|
74
133
|
# #calculate for examples with options.
|
@@ -78,23 +137,31 @@ module ActiveRecord
|
|
78
137
|
calculate(:maximum, column_name)
|
79
138
|
end
|
80
139
|
|
140
|
+
# Same as #maximum, but performs the query asynchronously and returns an
|
141
|
+
# ActiveRecord::Promise.
|
142
|
+
def async_maximum(column_name)
|
143
|
+
async.maximum(column_name)
|
144
|
+
end
|
145
|
+
|
81
146
|
# Calculates the sum of values on a given column. The value is returned
|
82
147
|
# with the same data type of the column, +0+ if there's no row. See
|
83
148
|
# #calculate for examples with options.
|
84
149
|
#
|
85
150
|
# Person.sum(:age) # => 4562
|
86
|
-
def sum(
|
151
|
+
def sum(initial_value_or_column = 0, &block)
|
87
152
|
if block_given?
|
88
|
-
|
89
|
-
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
90
|
-
end
|
91
|
-
|
92
|
-
super()
|
153
|
+
map(&block).sum(initial_value_or_column)
|
93
154
|
else
|
94
|
-
calculate(:sum,
|
155
|
+
calculate(:sum, initial_value_or_column)
|
95
156
|
end
|
96
157
|
end
|
97
158
|
|
159
|
+
# Same as #sum, but performs the query asynchronously and returns an
|
160
|
+
# ActiveRecord::Promise.
|
161
|
+
def async_sum(identity_or_column = nil)
|
162
|
+
async.sum(identity_or_column)
|
163
|
+
end
|
164
|
+
|
98
165
|
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
99
166
|
# #minimum, and #maximum have been added as shortcuts.
|
100
167
|
#
|
@@ -127,10 +194,23 @@ module ActiveRecord
|
|
127
194
|
# ...
|
128
195
|
# end
|
129
196
|
def calculate(operation, column_name)
|
197
|
+
operation = operation.to_s.downcase
|
198
|
+
|
199
|
+
if @none
|
200
|
+
case operation
|
201
|
+
when "count", "sum"
|
202
|
+
result = group_values.any? ? Hash.new : 0
|
203
|
+
return @async ? Promise::Complete.new(result) : result
|
204
|
+
when "average", "minimum", "maximum"
|
205
|
+
result = group_values.any? ? Hash.new : nil
|
206
|
+
return @async ? Promise::Complete.new(result) : result
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
130
210
|
if has_include?(column_name)
|
131
211
|
relation = apply_join_dependency
|
132
212
|
|
133
|
-
if operation
|
213
|
+
if operation == "count"
|
134
214
|
unless distinct_value || distinct_select?(column_name || select_for_count)
|
135
215
|
relation.distinct!
|
136
216
|
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
@@ -146,7 +226,7 @@ module ActiveRecord
|
|
146
226
|
end
|
147
227
|
|
148
228
|
# Use #pluck as a shortcut to select one or more attributes without
|
149
|
-
# loading
|
229
|
+
# loading an entire record object per row.
|
150
230
|
#
|
151
231
|
# Person.pluck(:name)
|
152
232
|
#
|
@@ -179,31 +259,45 @@ module ActiveRecord
|
|
179
259
|
# # => ['0', '27761', '173']
|
180
260
|
#
|
181
261
|
# See also #ids.
|
182
|
-
#
|
183
262
|
def pluck(*column_names)
|
263
|
+
return [] if @none
|
264
|
+
|
184
265
|
if loaded? && all_attributes?(column_names)
|
185
|
-
|
266
|
+
result = records.pluck(*column_names)
|
267
|
+
if @async
|
268
|
+
return Promise::Complete.new(result)
|
269
|
+
else
|
270
|
+
return result
|
271
|
+
end
|
186
272
|
end
|
187
273
|
|
188
274
|
if has_include?(column_names.first)
|
189
275
|
relation = apply_join_dependency
|
190
276
|
relation.pluck(*column_names)
|
191
277
|
else
|
192
|
-
klass.disallow_raw_sql!(column_names)
|
278
|
+
klass.disallow_raw_sql!(column_names.flatten)
|
193
279
|
columns = arel_columns(column_names)
|
194
280
|
relation = spawn
|
195
281
|
relation.select_values = columns
|
196
282
|
result = skip_query_cache_if_necessary do
|
197
283
|
if where_clause.contradiction?
|
198
|
-
ActiveRecord::Result.
|
284
|
+
ActiveRecord::Result.empty(async: @async)
|
199
285
|
else
|
200
|
-
klass.connection.select_all(relation.arel,
|
286
|
+
klass.connection.select_all(relation.arel, "#{klass.name} Pluck", async: @async)
|
201
287
|
end
|
202
288
|
end
|
203
|
-
|
289
|
+
result.then do |result|
|
290
|
+
type_cast_pluck_values(result, columns)
|
291
|
+
end
|
204
292
|
end
|
205
293
|
end
|
206
294
|
|
295
|
+
# Same as #pluck, but performs the query asynchronously and returns an
|
296
|
+
# ActiveRecord::Promise.
|
297
|
+
def async_pluck(*column_names)
|
298
|
+
async.pluck(*column_names)
|
299
|
+
end
|
300
|
+
|
207
301
|
# Pick the value(s) from the named column(s) in the current relation.
|
208
302
|
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
209
303
|
# when you have a relation that's already narrowed down to a single row.
|
@@ -220,18 +314,61 @@ module ActiveRecord
|
|
220
314
|
# # => [ 'David', 'david@loudthinking.com' ]
|
221
315
|
def pick(*column_names)
|
222
316
|
if loaded? && all_attributes?(column_names)
|
223
|
-
|
317
|
+
result = records.pick(*column_names)
|
318
|
+
return @async ? Promise::Complete.new(result) : result
|
224
319
|
end
|
225
320
|
|
226
|
-
limit(1).pluck(*column_names).first
|
321
|
+
limit(1).pluck(*column_names).then(&:first)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Same as #pick, but performs the query asynchronously and returns an
|
325
|
+
# ActiveRecord::Promise.
|
326
|
+
def async_pick(*column_names)
|
327
|
+
async.pick(*column_names)
|
227
328
|
end
|
228
329
|
|
229
|
-
#
|
330
|
+
# Returns the base model's ID's for the relation using the table's primary key
|
230
331
|
#
|
231
332
|
# Person.ids # SELECT people.id FROM people
|
232
|
-
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.
|
333
|
+
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.id = people.company_id
|
233
334
|
def ids
|
234
|
-
|
335
|
+
primary_key_array = Array(primary_key)
|
336
|
+
|
337
|
+
if loaded?
|
338
|
+
result = records.map do |record|
|
339
|
+
if primary_key_array.one?
|
340
|
+
record._read_attribute(primary_key_array.first)
|
341
|
+
else
|
342
|
+
primary_key_array.map { |column| record._read_attribute(column) }
|
343
|
+
end
|
344
|
+
end
|
345
|
+
return @async ? Promise::Complete.new(result) : result
|
346
|
+
end
|
347
|
+
|
348
|
+
if has_include?(primary_key)
|
349
|
+
relation = apply_join_dependency.group(*primary_key_array)
|
350
|
+
return relation.ids
|
351
|
+
end
|
352
|
+
|
353
|
+
columns = arel_columns(primary_key_array)
|
354
|
+
relation = spawn
|
355
|
+
relation.select_values = columns
|
356
|
+
|
357
|
+
result = if relation.where_clause.contradiction?
|
358
|
+
ActiveRecord::Result.empty
|
359
|
+
else
|
360
|
+
skip_query_cache_if_necessary do
|
361
|
+
klass.connection.select_all(relation, "#{klass.name} Ids", async: @async)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
result.then { |result| type_cast_pluck_values(result, columns) }
|
366
|
+
end
|
367
|
+
|
368
|
+
# Same as #ids, but performs the query asynchronously and returns an
|
369
|
+
# ActiveRecord::Promise.
|
370
|
+
def async_ids
|
371
|
+
async.ids
|
235
372
|
end
|
236
373
|
|
237
374
|
private
|
@@ -286,11 +423,12 @@ module ActiveRecord
|
|
286
423
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
424
|
end
|
288
425
|
|
289
|
-
def execute_simple_calculation(operation, column_name, distinct)
|
426
|
+
def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
|
290
427
|
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
291
428
|
# Shortcut when limit is zero.
|
292
429
|
return 0 if limit_value == 0
|
293
430
|
|
431
|
+
relation = self
|
294
432
|
query_builder = build_count_subquery(spawn, column_name, distinct)
|
295
433
|
else
|
296
434
|
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
@@ -305,29 +443,29 @@ module ActiveRecord
|
|
305
443
|
query_builder = relation.arel
|
306
444
|
end
|
307
445
|
|
308
|
-
|
446
|
+
query_result = if relation.where_clause.contradiction?
|
447
|
+
ActiveRecord::Result.empty
|
448
|
+
else
|
449
|
+
skip_query_cache_if_necessary do
|
450
|
+
@klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async)
|
451
|
+
end
|
452
|
+
end
|
309
453
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
454
|
+
query_result.then do |result|
|
455
|
+
if operation != "count"
|
456
|
+
type = column.try(:type_caster) ||
|
457
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
458
|
+
type = type.subtype if Enum::EnumType === type
|
459
|
+
end
|
460
|
+
|
461
|
+
type_cast_calculated_value(result.cast_values.first, operation, type)
|
315
462
|
end
|
316
463
|
end
|
317
464
|
|
318
|
-
def execute_grouped_calculation(operation, column_name, distinct)
|
465
|
+
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
319
466
|
group_fields = group_values
|
320
467
|
group_fields = group_fields.uniq if group_fields.size > 1
|
321
468
|
|
322
|
-
unless group_fields == group_values
|
323
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
324
|
-
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 7.0.
|
325
|
-
To migrate to Rails 7.0's behavior, use `uniq!(:group)` to deduplicate group fields
|
326
|
-
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
327
|
-
MSG
|
328
|
-
group_fields = group_values
|
329
|
-
end
|
330
|
-
|
331
469
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
332
470
|
association = klass._reflect_on_association(group_fields.first)
|
333
471
|
associated = association && association.belongs_to? # only count belongs_to associations
|
@@ -335,21 +473,24 @@ module ActiveRecord
|
|
335
473
|
end
|
336
474
|
group_fields = arel_columns(group_fields)
|
337
475
|
|
476
|
+
column_alias_tracker = ColumnAliasTracker.new(connection)
|
477
|
+
|
338
478
|
group_aliases = group_fields.map { |field|
|
339
479
|
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
340
|
-
|
480
|
+
column_alias_tracker.alias_for(field.to_s.downcase)
|
341
481
|
}
|
342
482
|
group_columns = group_aliases.zip(group_fields)
|
343
483
|
|
344
484
|
column = aggregate_column(column_name)
|
345
|
-
column_alias =
|
485
|
+
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
486
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
-
select_value.as(column_alias)
|
487
|
+
select_value.as(connection.quote_column_name(column_alias))
|
348
488
|
|
349
489
|
select_values = [select_value]
|
350
490
|
select_values += self.select_values unless having_clause.empty?
|
351
491
|
|
352
492
|
select_values.concat group_columns.map { |aliaz, field|
|
493
|
+
aliaz = connection.quote_column_name(aliaz)
|
353
494
|
if field.respond_to?(:as)
|
354
495
|
field.as(aliaz)
|
355
496
|
else
|
@@ -361,60 +502,43 @@ module ActiveRecord
|
|
361
502
|
relation.group_values = group_fields
|
362
503
|
relation.select_values = select_values
|
363
504
|
|
364
|
-
|
505
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}", async: @async) }
|
506
|
+
result.then do |calculated_data|
|
507
|
+
if association
|
508
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
509
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
510
|
+
key_records = key_records.index_by(&:id)
|
511
|
+
end
|
365
512
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
513
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
514
|
+
types[aliaz] = col_name.try(:type_caster) ||
|
515
|
+
type_for(col_name) do
|
516
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
517
|
+
end
|
518
|
+
end
|
371
519
|
|
372
|
-
|
373
|
-
|
374
|
-
|
520
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
521
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
522
|
+
hash[col_name] = row[i]
|
523
|
+
end
|
375
524
|
end
|
376
|
-
end
|
377
525
|
|
378
|
-
|
379
|
-
|
380
|
-
|
526
|
+
if operation != "count"
|
527
|
+
type = column.try(:type_caster) ||
|
528
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
529
|
+
type = type.subtype if Enum::EnumType === type
|
381
530
|
end
|
382
|
-
end
|
383
531
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
391
|
-
unless type
|
392
|
-
type = column.try(:type_caster) ||
|
393
|
-
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
|
-
type = type.subtype if Enum::EnumType === type
|
395
|
-
end
|
396
|
-
type.deserialize(value)
|
532
|
+
hash_rows.each_with_object({}) do |row, result|
|
533
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
534
|
+
key = key.first if key.size == 1
|
535
|
+
key = key_records[key] if associated
|
536
|
+
|
537
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
397
538
|
end
|
398
539
|
end
|
399
540
|
end
|
400
541
|
|
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
542
|
def type_for(field, &block)
|
419
543
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
420
544
|
@klass.type_for_attribute(field_name, &block)
|
@@ -445,16 +569,21 @@ module ActiveRecord
|
|
445
569
|
result.cast_values(cast_types)
|
446
570
|
end
|
447
571
|
|
448
|
-
def type_cast_calculated_value(value, operation)
|
572
|
+
def type_cast_calculated_value(value, operation, type)
|
449
573
|
case operation
|
450
574
|
when "count"
|
451
575
|
value.to_i
|
452
576
|
when "sum"
|
453
|
-
|
577
|
+
type.deserialize(value || 0)
|
454
578
|
when "average"
|
455
|
-
|
579
|
+
case type.type
|
580
|
+
when :integer, :decimal
|
581
|
+
value&.to_d
|
582
|
+
else
|
583
|
+
type.deserialize(value)
|
584
|
+
end
|
456
585
|
else # "minimum", "maximum"
|
457
|
-
|
586
|
+
type.deserialize(value)
|
458
587
|
end
|
459
588
|
end
|
460
589
|
|
@@ -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,11 +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
|
-
].each do |klass|
|
32
|
+
Delegation.delegated_classes.each do |klass|
|
20
33
|
delegate = Class.new(klass) {
|
21
34
|
include ClassSpecificRelation
|
22
35
|
}
|
@@ -61,17 +74,16 @@ module ActiveRecord
|
|
61
74
|
return if method_defined?(method)
|
62
75
|
|
63
76
|
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
|
64
|
-
definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
|
65
77
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
-
def #{method}(
|
67
|
-
scoping { klass.#{method}(
|
78
|
+
def #{method}(...)
|
79
|
+
scoping { klass.#{method}(...) }
|
68
80
|
end
|
69
81
|
RUBY
|
70
82
|
else
|
71
83
|
define_method(method) do |*args, &block|
|
72
84
|
scoping { klass.public_send(method, *args, &block) }
|
73
85
|
end
|
74
|
-
ruby2_keywords(method)
|
86
|
+
ruby2_keywords(method)
|
75
87
|
end
|
76
88
|
end
|
77
89
|
end
|
@@ -85,9 +97,9 @@ 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
|
-
:to_sentence, :to_formatted_s, :as_json,
|
102
|
+
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
91
103
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
92
104
|
|
93
105
|
delegate :primary_key, :connection, to: :klass
|
@@ -104,13 +116,15 @@ 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
|
111
125
|
end
|
112
126
|
end
|
113
|
-
ruby2_keywords(:method_missing)
|
127
|
+
ruby2_keywords(:method_missing)
|
114
128
|
end
|
115
129
|
|
116
130
|
module ClassMethods # :nodoc:
|