activerecord 4.2.9 → 5.2.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +614 -1572
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -50
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Calculations
|
3
5
|
# Count the records.
|
@@ -14,127 +16,138 @@ module ActiveRecord
|
|
14
16
|
# Person.distinct.count(:age)
|
15
17
|
# # => counts the number of different age values
|
16
18
|
#
|
17
|
-
# If
|
19
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
|
20
|
+
# it returns a Hash whose keys represent the aggregated column,
|
18
21
|
# and the values are the respective amounts:
|
19
22
|
#
|
20
23
|
# Person.group(:city).count
|
21
24
|
# # => { 'Rome' => 5, 'Paris' => 3 }
|
22
25
|
#
|
23
|
-
# If
|
26
|
+
# If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
|
24
27
|
# keys are an array containing the individual values of each column and the value
|
25
|
-
# of each key would be the
|
28
|
+
# of each key would be the #count.
|
26
29
|
#
|
27
30
|
# Article.group(:status, :category).count
|
28
31
|
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
29
32
|
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
30
33
|
#
|
31
|
-
# If
|
34
|
+
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
32
35
|
#
|
33
36
|
# Person.select(:age).count
|
34
37
|
# # => counts the number of different age values
|
35
38
|
#
|
36
|
-
# Note: not all valid
|
39
|
+
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
37
40
|
# between databases. In invalid cases, an error from the database is thrown.
|
38
|
-
def count(column_name = nil
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def count(column_name = nil)
|
42
|
+
if block_given?
|
43
|
+
unless column_name.nil?
|
44
|
+
ActiveSupport::Deprecation.warn \
|
45
|
+
"When `count' is called with a block, it ignores other arguments. " \
|
46
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
47
|
+
end
|
43
48
|
|
49
|
+
return super()
|
44
50
|
end
|
45
51
|
|
46
|
-
|
47
|
-
# activerecord-deprecated_finders.
|
48
|
-
calculate(:count, column_name, options)
|
52
|
+
calculate(:count, column_name)
|
49
53
|
end
|
50
54
|
|
51
55
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
52
|
-
# no row. See
|
56
|
+
# no row. See #calculate for examples with options.
|
53
57
|
#
|
54
58
|
# Person.average(:age) # => 35.8
|
55
|
-
def average(column_name
|
56
|
-
|
57
|
-
# activerecord-deprecated_finders.
|
58
|
-
calculate(:average, column_name, options)
|
59
|
+
def average(column_name)
|
60
|
+
calculate(:average, column_name)
|
59
61
|
end
|
60
62
|
|
61
63
|
# Calculates the minimum value on a given column. The value is returned
|
62
64
|
# with the same data type of the column, or +nil+ if there's no row. See
|
63
|
-
#
|
65
|
+
# #calculate for examples with options.
|
64
66
|
#
|
65
67
|
# Person.minimum(:age) # => 7
|
66
|
-
def minimum(column_name
|
67
|
-
|
68
|
-
# activerecord-deprecated_finders.
|
69
|
-
calculate(:minimum, column_name, options)
|
68
|
+
def minimum(column_name)
|
69
|
+
calculate(:minimum, column_name)
|
70
70
|
end
|
71
71
|
|
72
72
|
# Calculates the maximum value on a given column. The value is returned
|
73
73
|
# with the same data type of the column, or +nil+ if there's no row. See
|
74
|
-
#
|
74
|
+
# #calculate for examples with options.
|
75
75
|
#
|
76
76
|
# Person.maximum(:age) # => 93
|
77
|
-
def maximum(column_name
|
78
|
-
|
79
|
-
# activerecord-deprecated_finders.
|
80
|
-
calculate(:maximum, column_name, options)
|
77
|
+
def maximum(column_name)
|
78
|
+
calculate(:maximum, column_name)
|
81
79
|
end
|
82
80
|
|
83
81
|
# Calculates the sum of values on a given column. The value is returned
|
84
|
-
# with the same data type of the column, 0 if there's no row. See
|
85
|
-
#
|
82
|
+
# with the same data type of the column, +0+ if there's no row. See
|
83
|
+
# #calculate for examples with options.
|
86
84
|
#
|
87
85
|
# Person.sum(:age) # => 4562
|
88
|
-
def sum(
|
89
|
-
|
86
|
+
def sum(column_name = nil)
|
87
|
+
if block_given?
|
88
|
+
unless column_name.nil?
|
89
|
+
ActiveSupport::Deprecation.warn \
|
90
|
+
"When `sum' is called with a block, it ignores other arguments. " \
|
91
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
92
|
+
end
|
93
|
+
|
94
|
+
return super()
|
95
|
+
end
|
96
|
+
|
97
|
+
calculate(:sum, column_name)
|
90
98
|
end
|
91
99
|
|
92
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
93
|
-
# minimum, and maximum have been added as shortcuts.
|
100
|
+
# This calculates aggregate values in the given column. Methods for #count, #sum, #average,
|
101
|
+
# #minimum, and #maximum have been added as shortcuts.
|
94
102
|
#
|
95
|
-
#
|
103
|
+
# Person.calculate(:count, :all) # The same as Person.count
|
104
|
+
# Person.average(:age) # SELECT AVG(age) FROM people...
|
96
105
|
#
|
97
|
-
#
|
98
|
-
#
|
106
|
+
# # Selects the minimum age for any family without any minors
|
107
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
99
108
|
#
|
100
|
-
# *
|
101
|
-
# takes either a column name, or the name of a belongs_to association.
|
109
|
+
# Person.sum("2 * age")
|
102
110
|
#
|
103
|
-
#
|
104
|
-
# puts values["Drake"]
|
105
|
-
# # => 43
|
111
|
+
# There are two basic forms of output:
|
106
112
|
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
# puts values[drake]
|
110
|
-
# # => 43
|
113
|
+
# * Single aggregate value: The single value is type cast to Integer for COUNT, Float
|
114
|
+
# for AVG, and the given column's type for everything else.
|
111
115
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# end
|
116
|
+
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
117
|
+
# takes either a column name, or the name of a belongs_to association.
|
115
118
|
#
|
116
|
-
#
|
117
|
-
#
|
119
|
+
# values = Person.group('last_name').maximum(:age)
|
120
|
+
# puts values["Drake"]
|
121
|
+
# # => 43
|
118
122
|
#
|
119
|
-
#
|
120
|
-
#
|
123
|
+
# drake = Family.find_by(last_name: 'Drake')
|
124
|
+
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
|
125
|
+
# puts values[drake]
|
126
|
+
# # => 43
|
121
127
|
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
127
|
-
column_name = attribute_alias(column_name)
|
128
|
-
end
|
129
|
-
|
128
|
+
# values.each do |family, max_age|
|
129
|
+
# ...
|
130
|
+
# end
|
131
|
+
def calculate(operation, column_name)
|
130
132
|
if has_include?(column_name)
|
131
|
-
|
133
|
+
relation = apply_join_dependency
|
134
|
+
|
135
|
+
if operation.to_s.downcase == "count"
|
136
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
137
|
+
relation.distinct!
|
138
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
139
|
+
end
|
140
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
141
|
+
relation.order_values = []
|
142
|
+
end
|
143
|
+
|
144
|
+
relation.calculate(operation, column_name)
|
132
145
|
else
|
133
|
-
perform_calculation(operation, column_name
|
146
|
+
perform_calculation(operation, column_name)
|
134
147
|
end
|
135
148
|
end
|
136
149
|
|
137
|
-
# Use
|
150
|
+
# Use #pluck as a shortcut to select one or more attributes without
|
138
151
|
# loading a bunch of records just to grab the attributes you want.
|
139
152
|
#
|
140
153
|
# Person.pluck(:name)
|
@@ -143,19 +156,19 @@ module ActiveRecord
|
|
143
156
|
#
|
144
157
|
# Person.all.map(&:name)
|
145
158
|
#
|
146
|
-
# Pluck returns an
|
159
|
+
# Pluck returns an Array of attribute values type-casted to match
|
147
160
|
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
148
161
|
# returns String values by default.
|
149
162
|
#
|
150
|
-
# Person.pluck(:
|
151
|
-
# # SELECT people.
|
152
|
-
# # => [
|
163
|
+
# Person.pluck(:name)
|
164
|
+
# # SELECT people.name FROM people
|
165
|
+
# # => ['David', 'Jeremy', 'Jose']
|
153
166
|
#
|
154
167
|
# Person.pluck(:id, :name)
|
155
168
|
# # SELECT people.id, people.name FROM people
|
156
169
|
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
157
170
|
#
|
158
|
-
# Person.pluck(
|
171
|
+
# Person.distinct.pluck(:role)
|
159
172
|
# # SELECT DISTINCT role FROM people
|
160
173
|
# # => ['admin', 'member', 'guest']
|
161
174
|
#
|
@@ -167,24 +180,22 @@ module ActiveRecord
|
|
167
180
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
168
181
|
# # => ['0', '27761', '173']
|
169
182
|
#
|
183
|
+
# See also #ids.
|
184
|
+
#
|
170
185
|
def pluck(*column_names)
|
171
|
-
column_names.map
|
172
|
-
|
173
|
-
attribute_alias(column_name)
|
174
|
-
else
|
175
|
-
column_name.to_s
|
176
|
-
end
|
186
|
+
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
187
|
+
return records.pluck(*column_names)
|
177
188
|
end
|
178
189
|
|
179
190
|
if has_include?(column_names.first)
|
180
|
-
|
191
|
+
relation = apply_join_dependency
|
192
|
+
relation.pluck(*column_names)
|
181
193
|
else
|
194
|
+
klass.enforce_raw_sql_whitelist(column_names)
|
182
195
|
relation = spawn
|
183
|
-
relation.select_values = column_names
|
184
|
-
|
185
|
-
|
186
|
-
result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
|
187
|
-
result.cast_values(klass.column_types)
|
196
|
+
relation.select_values = column_names
|
197
|
+
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
198
|
+
result.cast_values(klass.attribute_types)
|
188
199
|
end
|
189
200
|
end
|
190
201
|
|
@@ -197,214 +208,211 @@ module ActiveRecord
|
|
197
208
|
end
|
198
209
|
|
199
210
|
private
|
211
|
+
def has_include?(column_name)
|
212
|
+
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
213
|
+
end
|
200
214
|
|
201
|
-
|
202
|
-
|
203
|
-
|
215
|
+
def perform_calculation(operation, column_name)
|
216
|
+
operation = operation.to_s.downcase
|
217
|
+
|
218
|
+
# If #count is used with #distinct (i.e. `relation.distinct.count`) it is
|
219
|
+
# considered distinct.
|
220
|
+
distinct = distinct_value
|
221
|
+
|
222
|
+
if operation == "count"
|
223
|
+
column_name ||= select_for_count
|
224
|
+
if column_name == :all
|
225
|
+
if !distinct
|
226
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
227
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
228
|
+
column_name = primary_key
|
229
|
+
end
|
230
|
+
elsif distinct_select?(column_name)
|
231
|
+
distinct = nil
|
232
|
+
end
|
233
|
+
end
|
204
234
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
235
|
+
if group_values.any?
|
236
|
+
execute_grouped_calculation(operation, column_name, distinct)
|
237
|
+
else
|
238
|
+
execute_simple_calculation(operation, column_name, distinct)
|
239
|
+
end
|
240
|
+
end
|
209
241
|
|
210
|
-
|
211
|
-
|
242
|
+
def distinct_select?(column_name)
|
243
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
244
|
+
end
|
212
245
|
|
213
|
-
|
214
|
-
column_name
|
246
|
+
def aggregate_column(column_name)
|
247
|
+
return column_name if Arel::Expressions === column_name
|
215
248
|
|
216
|
-
|
217
|
-
|
249
|
+
if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
|
250
|
+
@klass.arel_attribute(column_name)
|
251
|
+
else
|
252
|
+
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
218
253
|
end
|
219
|
-
|
220
|
-
column_name = primary_key if column_name == :all && distinct
|
221
|
-
distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
|
222
254
|
end
|
223
255
|
|
224
|
-
|
225
|
-
|
226
|
-
else
|
227
|
-
execute_simple_calculation(operation, column_name, distinct)
|
256
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
257
|
+
operation == "count" ? column.count(distinct) : column.send(operation)
|
228
258
|
end
|
229
|
-
end
|
230
259
|
|
231
|
-
|
232
|
-
|
233
|
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
234
|
-
else
|
235
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def operation_over_aggregate_column(column, operation, distinct)
|
240
|
-
operation == 'count' ? column.count(distinct) : column.send(operation)
|
241
|
-
end
|
260
|
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
261
|
+
column_alias = column_name
|
242
262
|
|
243
|
-
|
244
|
-
|
245
|
-
|
263
|
+
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
264
|
+
# Shortcut when limit is zero.
|
265
|
+
return 0 if limit_value == 0
|
246
266
|
|
247
|
-
|
267
|
+
query_builder = build_count_subquery(spawn, column_name, distinct)
|
268
|
+
else
|
269
|
+
# PostgreSQL doesn't like ORDER BY when there are no GROUP BY
|
270
|
+
relation = unscope(:order).distinct!(false)
|
248
271
|
|
249
|
-
|
272
|
+
column = aggregate_column(column_name)
|
250
273
|
|
251
|
-
|
252
|
-
|
253
|
-
|
274
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
275
|
+
if operation == "sum" && distinct
|
276
|
+
select_value.distinct = true
|
277
|
+
end
|
254
278
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
column = aggregate_column(column_name)
|
279
|
+
column_alias = select_value.alias
|
280
|
+
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
281
|
+
relation.select_values = [select_value]
|
259
282
|
|
260
|
-
|
283
|
+
query_builder = relation.arel
|
284
|
+
end
|
261
285
|
|
262
|
-
|
263
|
-
|
264
|
-
|
286
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
|
287
|
+
row = result.first
|
288
|
+
value = row && row.values.first
|
289
|
+
type = result.column_types.fetch(column_alias) do
|
290
|
+
type_for(column_name)
|
291
|
+
end
|
265
292
|
|
266
|
-
|
267
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
293
|
+
type_cast_calculated_value(value, type, operation)
|
268
294
|
end
|
269
295
|
|
270
|
-
|
271
|
-
|
272
|
-
value = row && row.values.first
|
273
|
-
column = result.column_types.fetch(column_alias) do
|
274
|
-
type_for(column_name)
|
275
|
-
end
|
296
|
+
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
297
|
+
group_attrs = group_values
|
276
298
|
|
277
|
-
|
278
|
-
|
299
|
+
if group_attrs.first.respond_to?(:to_sym)
|
300
|
+
association = @klass._reflect_on_association(group_attrs.first)
|
301
|
+
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
|
302
|
+
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
303
|
+
else
|
304
|
+
group_fields = group_attrs
|
305
|
+
end
|
306
|
+
group_fields = arel_columns(group_fields)
|
279
307
|
|
280
|
-
|
281
|
-
|
308
|
+
group_aliases = group_fields.map { |field| column_alias_for(field) }
|
309
|
+
group_columns = group_aliases.zip(group_fields)
|
282
310
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
group_fields = group_attrs
|
289
|
-
end
|
290
|
-
group_fields = arel_columns(group_fields)
|
311
|
+
if operation == "count" && column_name == :all
|
312
|
+
aggregate_alias = "count_all"
|
313
|
+
else
|
314
|
+
aggregate_alias = column_alias_for([operation, column_name].join(" "))
|
315
|
+
end
|
291
316
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
317
|
+
select_values = [
|
318
|
+
operation_over_aggregate_column(
|
319
|
+
aggregate_column(column_name),
|
320
|
+
operation,
|
321
|
+
distinct).as(aggregate_alias)
|
322
|
+
]
|
323
|
+
select_values += self.select_values unless having_clause.empty?
|
324
|
+
|
325
|
+
select_values.concat group_columns.map { |aliaz, field|
|
326
|
+
if field.respond_to?(:as)
|
327
|
+
field.as(aliaz)
|
328
|
+
else
|
329
|
+
"#{field} AS #{aliaz}"
|
330
|
+
end
|
331
|
+
}
|
298
332
|
|
299
|
-
|
333
|
+
relation = except(:group).distinct!(false)
|
334
|
+
relation.group_values = group_fields
|
335
|
+
relation.select_values = select_values
|
300
336
|
|
301
|
-
|
302
|
-
aggregate_alias = 'count_all'
|
303
|
-
else
|
304
|
-
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
305
|
-
end
|
337
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
306
338
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
distinct).as(aggregate_alias)
|
312
|
-
]
|
313
|
-
select_values += self.select_values unless having_values.empty?
|
314
|
-
|
315
|
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
316
|
-
if field.respond_to?(:as)
|
317
|
-
field.as(aliaz)
|
318
|
-
else
|
319
|
-
"#{field} AS #{aliaz}"
|
339
|
+
if association
|
340
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
341
|
+
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
342
|
+
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
320
343
|
end
|
321
|
-
}
|
322
344
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
345
|
+
Hash[calculated_data.map do |row|
|
346
|
+
key = group_columns.map { |aliaz, col_name|
|
347
|
+
type = type_for(col_name) do
|
348
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
349
|
+
end
|
350
|
+
type_cast_calculated_value(row[aliaz], type)
|
351
|
+
}
|
352
|
+
key = key.first if key.size == 1
|
353
|
+
key = key_records[key] if associated
|
354
|
+
|
355
|
+
type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
356
|
+
[key, type_cast_calculated_value(row[aggregate_alias], type, operation)]
|
357
|
+
end]
|
333
358
|
end
|
334
359
|
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
360
|
+
# Converts the given keys to the value that the database adapter returns as
|
361
|
+
# a usable column name:
|
362
|
+
#
|
363
|
+
# column_alias_for("users.id") # => "users_id"
|
364
|
+
# column_alias_for("sum(id)") # => "sum_id"
|
365
|
+
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
366
|
+
# column_alias_for("count(*)") # => "count_all"
|
367
|
+
def column_alias_for(keys)
|
368
|
+
if keys.respond_to? :name
|
369
|
+
keys = "#{keys.relation.name}.#{keys.name}"
|
370
|
+
end
|
344
371
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
372
|
+
table_name = keys.to_s.downcase
|
373
|
+
table_name.gsub!(/\*/, "all")
|
374
|
+
table_name.gsub!(/\W+/, " ")
|
375
|
+
table_name.strip!
|
376
|
+
table_name.gsub!(/ +/, "_")
|
349
377
|
|
350
|
-
|
351
|
-
# a usable column name:
|
352
|
-
#
|
353
|
-
# column_alias_for("users.id") # => "users_id"
|
354
|
-
# column_alias_for("sum(id)") # => "sum_id"
|
355
|
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
356
|
-
# column_alias_for("count(*)") # => "count_all"
|
357
|
-
# column_alias_for("count", "id") # => "count_id"
|
358
|
-
def column_alias_for(keys)
|
359
|
-
if keys.respond_to? :name
|
360
|
-
keys = "#{keys.relation.name}.#{keys.name}"
|
378
|
+
@klass.connection.table_alias_for(table_name)
|
361
379
|
end
|
362
380
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
table_name.gsub!(/ +/, '_')
|
368
|
-
|
369
|
-
@klass.connection.table_alias_for(table_name)
|
370
|
-
end
|
371
|
-
|
372
|
-
def type_for(field)
|
373
|
-
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
374
|
-
@klass.type_for_attribute(field_name)
|
375
|
-
end
|
381
|
+
def type_for(field, &block)
|
382
|
+
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
383
|
+
@klass.type_for_attribute(field_name, &block)
|
384
|
+
end
|
376
385
|
|
377
|
-
|
378
|
-
|
379
|
-
when
|
380
|
-
when
|
381
|
-
when
|
382
|
-
else type.
|
386
|
+
def type_cast_calculated_value(value, type, operation = nil)
|
387
|
+
case operation
|
388
|
+
when "count" then value.to_i
|
389
|
+
when "sum" then type.deserialize(value || 0)
|
390
|
+
when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
|
391
|
+
else type.deserialize(value)
|
392
|
+
end
|
383
393
|
end
|
384
|
-
end
|
385
394
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
395
|
+
def select_for_count
|
396
|
+
if select_values.present?
|
397
|
+
return select_values.first if select_values.one?
|
398
|
+
select_values.join(", ")
|
399
|
+
else
|
400
|
+
:all
|
401
|
+
end
|
392
402
|
end
|
393
|
-
end
|
394
403
|
|
395
|
-
|
396
|
-
|
397
|
-
|
404
|
+
def build_count_subquery(relation, column_name, distinct)
|
405
|
+
if column_name == :all
|
406
|
+
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
407
|
+
else
|
408
|
+
column_alias = Arel.sql("count_column")
|
409
|
+
relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
|
410
|
+
end
|
398
411
|
|
399
|
-
|
400
|
-
|
401
|
-
arel = relation.arel
|
402
|
-
subquery = arel.as(subquery_alias)
|
412
|
+
subquery = relation.arel.as(Arel.sql("subquery_for_count"))
|
413
|
+
select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false)
|
403
414
|
|
404
|
-
|
405
|
-
|
406
|
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
407
|
-
sm.project(select_value).from(subquery)
|
408
|
-
end
|
415
|
+
Arel::SelectManager.new(subquery).project(select_value)
|
416
|
+
end
|
409
417
|
end
|
410
418
|
end
|