activerecord 6.1.7 → 7.2.0
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 +520 -1385
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- 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/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +404 -509
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +2 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +51 -49
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- 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 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -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 +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- 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 +377 -142
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
- 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 +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- 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 +114 -54
- 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/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/interval.rb +1 -1
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- 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 +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +276 -251
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- 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 +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -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 +170 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +163 -63
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- 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 +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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 +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -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 +374 -177
- data/lib/active_record/model_schema.rb +143 -159
- data/lib/active_record/nested_attributes.rb +48 -21
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +234 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- 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 +325 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +300 -111
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- 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 +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +842 -150
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- 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 +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +277 -149
- 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 +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- 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 -7
- 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/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +64 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- 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/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- 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/templates/create_table_migration.rb.tt +4 -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 +100 -14
- 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
|
#
|
@@ -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,23 +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
|
-
raise ArgumentError, "Column name argument is not supported when a block is passed."
|
90
|
-
end
|
91
|
-
|
92
|
-
super()
|
174
|
+
map(&block).sum(initial_value_or_column)
|
93
175
|
else
|
94
|
-
calculate(:sum,
|
176
|
+
calculate(:sum, initial_value_or_column)
|
95
177
|
end
|
96
178
|
end
|
97
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
|
+
|
98
186
|
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
99
187
|
# #minimum, and #maximum have been added as shortcuts.
|
100
188
|
#
|
@@ -127,13 +215,26 @@ module ActiveRecord
|
|
127
215
|
# ...
|
128
216
|
# end
|
129
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
|
+
|
130
231
|
if has_include?(column_name)
|
131
232
|
relation = apply_join_dependency
|
132
233
|
|
133
|
-
if operation
|
234
|
+
if operation == "count"
|
134
235
|
unless distinct_value || distinct_select?(column_name || select_for_count)
|
135
236
|
relation.distinct!
|
136
|
-
relation.select_values =
|
237
|
+
relation.select_values = Array(klass.primary_key || table[Arel.star])
|
137
238
|
end
|
138
239
|
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
139
240
|
relation.order_values = [] if group_values.empty?
|
@@ -146,7 +247,7 @@ module ActiveRecord
|
|
146
247
|
end
|
147
248
|
|
148
249
|
# Use #pluck as a shortcut to select one or more attributes without
|
149
|
-
# loading
|
250
|
+
# loading an entire record object per row.
|
150
251
|
#
|
151
252
|
# Person.pluck(:name)
|
152
253
|
#
|
@@ -174,36 +275,62 @@ module ActiveRecord
|
|
174
275
|
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
175
276
|
# # => [2, 3]
|
176
277
|
#
|
278
|
+
# Comment.joins(:person).pluck(:id, person: [:id])
|
279
|
+
# # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
|
280
|
+
# # => [[1, 2], [2, 2]]
|
281
|
+
#
|
177
282
|
# Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
|
178
283
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
179
284
|
# # => ['0', '27761', '173']
|
180
285
|
#
|
181
286
|
# See also #ids.
|
182
|
-
#
|
183
287
|
def pluck(*column_names)
|
288
|
+
if @none
|
289
|
+
if @async
|
290
|
+
return Promise::Complete.new([])
|
291
|
+
else
|
292
|
+
return []
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
184
296
|
if loaded? && all_attributes?(column_names)
|
185
|
-
|
297
|
+
result = records.pluck(*column_names)
|
298
|
+
if @async
|
299
|
+
return Promise::Complete.new(result)
|
300
|
+
else
|
301
|
+
return result
|
302
|
+
end
|
186
303
|
end
|
187
304
|
|
188
305
|
if has_include?(column_names.first)
|
189
306
|
relation = apply_join_dependency
|
190
307
|
relation.pluck(*column_names)
|
191
308
|
else
|
192
|
-
klass.disallow_raw_sql!(column_names)
|
309
|
+
klass.disallow_raw_sql!(flattened_args(column_names))
|
193
310
|
columns = arel_columns(column_names)
|
194
311
|
relation = spawn
|
195
312
|
relation.select_values = columns
|
196
313
|
result = skip_query_cache_if_necessary do
|
197
314
|
if where_clause.contradiction?
|
198
|
-
ActiveRecord::Result.
|
315
|
+
ActiveRecord::Result.empty(async: @async)
|
199
316
|
else
|
200
|
-
klass.
|
317
|
+
klass.with_connection do |c|
|
318
|
+
c.select_all(relation.arel, "#{klass.name} Pluck", async: @async)
|
319
|
+
end
|
201
320
|
end
|
202
321
|
end
|
203
|
-
|
322
|
+
result.then do |result|
|
323
|
+
type_cast_pluck_values(result, columns)
|
324
|
+
end
|
204
325
|
end
|
205
326
|
end
|
206
327
|
|
328
|
+
# Same as #pluck, but performs the query asynchronously and returns an
|
329
|
+
# ActiveRecord::Promise.
|
330
|
+
def async_pluck(*column_names)
|
331
|
+
async.pluck(*column_names)
|
332
|
+
end
|
333
|
+
|
207
334
|
# Pick the value(s) from the named column(s) in the current relation.
|
208
335
|
# This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
|
209
336
|
# when you have a relation that's already narrowed down to a single row.
|
@@ -220,18 +347,63 @@ module ActiveRecord
|
|
220
347
|
# # => [ 'David', 'david@loudthinking.com' ]
|
221
348
|
def pick(*column_names)
|
222
349
|
if loaded? && all_attributes?(column_names)
|
223
|
-
|
350
|
+
result = records.pick(*column_names)
|
351
|
+
return @async ? Promise::Complete.new(result) : result
|
224
352
|
end
|
225
353
|
|
226
|
-
limit(1).pluck(*column_names).first
|
354
|
+
limit(1).pluck(*column_names).then(&:first)
|
355
|
+
end
|
356
|
+
|
357
|
+
# Same as #pick, but performs the query asynchronously and returns an
|
358
|
+
# ActiveRecord::Promise.
|
359
|
+
def async_pick(*column_names)
|
360
|
+
async.pick(*column_names)
|
227
361
|
end
|
228
362
|
|
229
|
-
#
|
363
|
+
# Returns the base model's ID's for the relation using the table's primary key
|
230
364
|
#
|
231
365
|
# Person.ids # SELECT people.id FROM people
|
232
|
-
# Person.joins(:
|
366
|
+
# Person.joins(:company).ids # SELECT people.id FROM people INNER JOIN companies ON companies.id = people.company_id
|
233
367
|
def ids
|
234
|
-
|
368
|
+
primary_key_array = Array(primary_key)
|
369
|
+
|
370
|
+
if loaded?
|
371
|
+
result = records.map do |record|
|
372
|
+
if primary_key_array.one?
|
373
|
+
record._read_attribute(primary_key_array.first)
|
374
|
+
else
|
375
|
+
primary_key_array.map { |column| record._read_attribute(column) }
|
376
|
+
end
|
377
|
+
end
|
378
|
+
return @async ? Promise::Complete.new(result) : result
|
379
|
+
end
|
380
|
+
|
381
|
+
if has_include?(primary_key)
|
382
|
+
relation = apply_join_dependency.group(*primary_key_array)
|
383
|
+
return relation.ids
|
384
|
+
end
|
385
|
+
|
386
|
+
columns = arel_columns(primary_key_array)
|
387
|
+
relation = spawn
|
388
|
+
relation.select_values = columns
|
389
|
+
|
390
|
+
result = if relation.where_clause.contradiction?
|
391
|
+
ActiveRecord::Result.empty
|
392
|
+
else
|
393
|
+
skip_query_cache_if_necessary do
|
394
|
+
klass.with_connection do |c|
|
395
|
+
c.select_all(relation, "#{klass.name} Ids", async: @async)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
result.then { |result| type_cast_pluck_values(result, columns) }
|
401
|
+
end
|
402
|
+
|
403
|
+
# Same as #ids, but performs the query asynchronously and returns an
|
404
|
+
# ActiveRecord::Promise.
|
405
|
+
def async_ids
|
406
|
+
async.ids
|
235
407
|
end
|
236
408
|
|
237
409
|
private
|
@@ -278,7 +450,7 @@ module ActiveRecord
|
|
278
450
|
return column_name if Arel::Expressions === column_name
|
279
451
|
|
280
452
|
arel_column(column_name.to_s) do |name|
|
281
|
-
|
453
|
+
column_name == :all ? Arel.sql("*", retryable: true) : Arel.sql(name)
|
282
454
|
end
|
283
455
|
end
|
284
456
|
|
@@ -286,11 +458,12 @@ module ActiveRecord
|
|
286
458
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
459
|
end
|
288
460
|
|
289
|
-
def execute_simple_calculation(operation, column_name, distinct)
|
290
|
-
if operation
|
461
|
+
def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
|
462
|
+
if build_count_subquery?(operation, column_name, distinct)
|
291
463
|
# Shortcut when limit is zero.
|
292
464
|
return 0 if limit_value == 0
|
293
465
|
|
466
|
+
relation = self
|
294
467
|
query_builder = build_count_subquery(spawn, column_name, distinct)
|
295
468
|
else
|
296
469
|
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
@@ -305,29 +478,31 @@ module ActiveRecord
|
|
305
478
|
query_builder = relation.arel
|
306
479
|
end
|
307
480
|
|
308
|
-
|
481
|
+
query_result = if relation.where_clause.contradiction?
|
482
|
+
ActiveRecord::Result.empty
|
483
|
+
else
|
484
|
+
skip_query_cache_if_necessary do
|
485
|
+
@klass.with_connection do |c|
|
486
|
+
c.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
309
490
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
491
|
+
query_result.then do |result|
|
492
|
+
if operation != "count"
|
493
|
+
type = column.try(:type_caster) ||
|
494
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
495
|
+
type = type.subtype if Enum::EnumType === type
|
496
|
+
end
|
497
|
+
|
498
|
+
type_cast_calculated_value(result.cast_values.first, operation, type)
|
315
499
|
end
|
316
500
|
end
|
317
501
|
|
318
|
-
def execute_grouped_calculation(operation, column_name, distinct)
|
502
|
+
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
319
503
|
group_fields = group_values
|
320
504
|
group_fields = group_fields.uniq if group_fields.size > 1
|
321
505
|
|
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
506
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
332
507
|
association = klass._reflect_on_association(group_fields.first)
|
333
508
|
associated = association && association.belongs_to? # only count belongs_to associations
|
@@ -335,86 +510,77 @@ module ActiveRecord
|
|
335
510
|
end
|
336
511
|
group_fields = arel_columns(group_fields)
|
337
512
|
|
338
|
-
|
339
|
-
|
340
|
-
column_alias_for(field.to_s.downcase)
|
341
|
-
}
|
342
|
-
group_columns = group_aliases.zip(group_fields)
|
343
|
-
|
344
|
-
column = aggregate_column(column_name)
|
345
|
-
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
|
-
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
-
select_value.as(column_alias)
|
513
|
+
@klass.with_connection do |connection|
|
514
|
+
column_alias_tracker = ColumnAliasTracker.new(connection)
|
348
515
|
|
349
|
-
|
350
|
-
|
516
|
+
group_aliases = group_fields.map { |field|
|
517
|
+
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
518
|
+
column_alias_tracker.alias_for(field.to_s.downcase)
|
519
|
+
}
|
520
|
+
group_columns = group_aliases.zip(group_fields)
|
351
521
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
"#{field} AS #{aliaz}"
|
357
|
-
end
|
358
|
-
}
|
522
|
+
column = aggregate_column(column_name)
|
523
|
+
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
524
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
525
|
+
select_value.as(adapter_class.quote_column_name(column_alias))
|
359
526
|
|
360
|
-
|
361
|
-
|
362
|
-
relation.select_values = select_values
|
527
|
+
select_values = [select_value]
|
528
|
+
select_values += self.select_values unless having_clause.empty?
|
363
529
|
|
364
|
-
|
530
|
+
select_values.concat group_columns.map { |aliaz, field|
|
531
|
+
aliaz = adapter_class.quote_column_name(aliaz)
|
532
|
+
if field.respond_to?(:as)
|
533
|
+
field.as(aliaz)
|
534
|
+
else
|
535
|
+
"#{field} AS #{aliaz}"
|
536
|
+
end
|
537
|
+
}
|
365
538
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
key_records = key_records.index_by(&:id)
|
370
|
-
end
|
539
|
+
relation = except(:group).distinct!(false)
|
540
|
+
relation.group_values = group_fields
|
541
|
+
relation.select_values = select_values
|
371
542
|
|
372
|
-
|
373
|
-
|
374
|
-
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
543
|
+
result = skip_query_cache_if_necessary do
|
544
|
+
connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}", async: @async)
|
375
545
|
end
|
376
|
-
end
|
377
546
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
547
|
+
result.then do |calculated_data|
|
548
|
+
if association
|
549
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
550
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
551
|
+
key_records = key_records.index_by(&:id)
|
552
|
+
end
|
383
553
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
554
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
555
|
+
types[aliaz] = col_name.try(:type_caster) ||
|
556
|
+
type_for(col_name) do
|
557
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
562
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
563
|
+
hash[col_name] = row[i]
|
564
|
+
end
|
565
|
+
end
|
389
566
|
|
390
|
-
|
391
|
-
unless type
|
567
|
+
if operation != "count"
|
392
568
|
type = column.try(:type_caster) ||
|
393
569
|
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
570
|
type = type.subtype if Enum::EnumType === type
|
395
571
|
end
|
396
|
-
|
572
|
+
|
573
|
+
hash_rows.each_with_object({}) do |row, result|
|
574
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
575
|
+
key = key.first if key.size == 1
|
576
|
+
key = key_records[key] if associated
|
577
|
+
|
578
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
579
|
+
end
|
397
580
|
end
|
398
581
|
end
|
399
582
|
end
|
400
583
|
|
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
584
|
def type_for(field, &block)
|
419
585
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
420
586
|
@klass.type_for_attribute(field_name, &block)
|
@@ -438,35 +604,58 @@ module ActiveRecord
|
|
438
604
|
klass.attribute_types.fetch(name = result.columns[i]) do
|
439
605
|
join_dependencies ||= build_join_dependencies
|
440
606
|
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
441
|
-
result.column_types[
|
607
|
+
result.column_types[i] || Type.default_value
|
442
608
|
end
|
443
609
|
end
|
444
610
|
end
|
445
611
|
result.cast_values(cast_types)
|
446
612
|
end
|
447
613
|
|
448
|
-
def type_cast_calculated_value(value, operation)
|
614
|
+
def type_cast_calculated_value(value, operation, type)
|
449
615
|
case operation
|
450
616
|
when "count"
|
451
617
|
value.to_i
|
452
618
|
when "sum"
|
453
|
-
|
619
|
+
type.deserialize(value || 0)
|
454
620
|
when "average"
|
455
|
-
|
621
|
+
case type.type
|
622
|
+
when :integer, :decimal
|
623
|
+
value&.to_d
|
624
|
+
else
|
625
|
+
type.deserialize(value)
|
626
|
+
end
|
456
627
|
else # "minimum", "maximum"
|
457
|
-
|
628
|
+
type.deserialize(value)
|
458
629
|
end
|
459
630
|
end
|
460
631
|
|
461
632
|
def select_for_count
|
462
633
|
if select_values.present?
|
463
634
|
return select_values.first if select_values.one?
|
464
|
-
|
635
|
+
|
636
|
+
select_values.map do |field|
|
637
|
+
column = arel_column(field.to_s) do |attr_name|
|
638
|
+
Arel.sql(attr_name)
|
639
|
+
end
|
640
|
+
|
641
|
+
if column.is_a?(Arel::Nodes::SqlLiteral)
|
642
|
+
column
|
643
|
+
else
|
644
|
+
"#{adapter_class.quote_table_name(column.relation.name)}.#{adapter_class.quote_column_name(column.name)}"
|
645
|
+
end
|
646
|
+
end.join(", ")
|
465
647
|
else
|
466
648
|
:all
|
467
649
|
end
|
468
650
|
end
|
469
651
|
|
652
|
+
def build_count_subquery?(operation, column_name, distinct)
|
653
|
+
# SQLite and older MySQL does not support `COUNT DISTINCT` with `*` or
|
654
|
+
# multiple columns, so we need to use subquery for this.
|
655
|
+
operation == "count" &&
|
656
|
+
(((column_name == :all || select_values.many?) && distinct) || has_limit_or_offset?)
|
657
|
+
end
|
658
|
+
|
470
659
|
def build_count_subquery(relation, column_name, distinct)
|
471
660
|
if column_name == :all
|
472
661
|
column_alias = Arel.star
|
@@ -476,7 +665,7 @@ module ActiveRecord
|
|
476
665
|
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
477
666
|
end
|
478
667
|
|
479
|
-
subquery_alias = Arel.sql("subquery_for_count")
|
668
|
+
subquery_alias = Arel.sql("subquery_for_count", retryable: true)
|
480
669
|
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
481
670
|
|
482
671
|
relation.build_subquery(subquery_alias, select_value)
|