activerecord 4.2.0 → 5.2.8.1
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 +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- data/lib/active_record/gem_version.rb +5 -3
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Calculations
|
3
5
|
# Count the records.
|
@@ -14,121 +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
|
-
|
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
|
48
|
+
|
49
|
+
return super()
|
50
|
+
end
|
51
|
+
|
52
|
+
calculate(:count, column_name)
|
43
53
|
end
|
44
54
|
|
45
55
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
46
|
-
# no row. See
|
56
|
+
# no row. See #calculate for examples with options.
|
47
57
|
#
|
48
58
|
# Person.average(:age) # => 35.8
|
49
|
-
def average(column_name
|
50
|
-
|
51
|
-
# activerecord-deprecated_finders.
|
52
|
-
calculate(:average, column_name, options)
|
59
|
+
def average(column_name)
|
60
|
+
calculate(:average, column_name)
|
53
61
|
end
|
54
62
|
|
55
63
|
# Calculates the minimum value on a given column. The value is returned
|
56
64
|
# with the same data type of the column, or +nil+ if there's no row. See
|
57
|
-
#
|
65
|
+
# #calculate for examples with options.
|
58
66
|
#
|
59
67
|
# Person.minimum(:age) # => 7
|
60
|
-
def minimum(column_name
|
61
|
-
|
62
|
-
# activerecord-deprecated_finders.
|
63
|
-
calculate(:minimum, column_name, options)
|
68
|
+
def minimum(column_name)
|
69
|
+
calculate(:minimum, column_name)
|
64
70
|
end
|
65
71
|
|
66
72
|
# Calculates the maximum value on a given column. The value is returned
|
67
73
|
# with the same data type of the column, or +nil+ if there's no row. See
|
68
|
-
#
|
74
|
+
# #calculate for examples with options.
|
69
75
|
#
|
70
76
|
# Person.maximum(:age) # => 93
|
71
|
-
def maximum(column_name
|
72
|
-
|
73
|
-
# activerecord-deprecated_finders.
|
74
|
-
calculate(:maximum, column_name, options)
|
77
|
+
def maximum(column_name)
|
78
|
+
calculate(:maximum, column_name)
|
75
79
|
end
|
76
80
|
|
77
81
|
# Calculates the sum of values on a given column. The value is returned
|
78
|
-
# with the same data type of the column, 0 if there's no row. See
|
79
|
-
#
|
82
|
+
# with the same data type of the column, +0+ if there's no row. See
|
83
|
+
# #calculate for examples with options.
|
80
84
|
#
|
81
85
|
# Person.sum(:age) # => 4562
|
82
|
-
def sum(
|
83
|
-
|
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)
|
84
98
|
end
|
85
99
|
|
86
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
87
|
-
# 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.
|
88
102
|
#
|
89
|
-
#
|
103
|
+
# Person.calculate(:count, :all) # The same as Person.count
|
104
|
+
# Person.average(:age) # SELECT AVG(age) FROM people...
|
90
105
|
#
|
91
|
-
#
|
92
|
-
#
|
106
|
+
# # Selects the minimum age for any family without any minors
|
107
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
93
108
|
#
|
94
|
-
# *
|
95
|
-
# takes either a column name, or the name of a belongs_to association.
|
109
|
+
# Person.sum("2 * age")
|
96
110
|
#
|
97
|
-
#
|
98
|
-
# puts values["Drake"]
|
99
|
-
# # => 43
|
111
|
+
# There are two basic forms of output:
|
100
112
|
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# puts values[drake]
|
104
|
-
# # => 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.
|
105
115
|
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
# 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.
|
109
118
|
#
|
110
|
-
#
|
111
|
-
#
|
119
|
+
# values = Person.group('last_name').maximum(:age)
|
120
|
+
# puts values["Drake"]
|
121
|
+
# # => 43
|
112
122
|
#
|
113
|
-
#
|
114
|
-
#
|
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
|
115
127
|
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
121
|
-
column_name = attribute_alias(column_name)
|
122
|
-
end
|
123
|
-
|
128
|
+
# values.each do |family, max_age|
|
129
|
+
# ...
|
130
|
+
# end
|
131
|
+
def calculate(operation, column_name)
|
124
132
|
if has_include?(column_name)
|
125
|
-
|
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)
|
126
145
|
else
|
127
|
-
perform_calculation(operation, column_name
|
146
|
+
perform_calculation(operation, column_name)
|
128
147
|
end
|
129
148
|
end
|
130
149
|
|
131
|
-
# Use
|
150
|
+
# Use #pluck as a shortcut to select one or more attributes without
|
132
151
|
# loading a bunch of records just to grab the attributes you want.
|
133
152
|
#
|
134
153
|
# Person.pluck(:name)
|
@@ -137,19 +156,19 @@ module ActiveRecord
|
|
137
156
|
#
|
138
157
|
# Person.all.map(&:name)
|
139
158
|
#
|
140
|
-
# Pluck returns an
|
159
|
+
# Pluck returns an Array of attribute values type-casted to match
|
141
160
|
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
142
161
|
# returns String values by default.
|
143
162
|
#
|
144
|
-
# Person.pluck(:
|
145
|
-
# # SELECT people.
|
146
|
-
# # => [
|
163
|
+
# Person.pluck(:name)
|
164
|
+
# # SELECT people.name FROM people
|
165
|
+
# # => ['David', 'Jeremy', 'Jose']
|
147
166
|
#
|
148
167
|
# Person.pluck(:id, :name)
|
149
168
|
# # SELECT people.id, people.name FROM people
|
150
169
|
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
151
170
|
#
|
152
|
-
# Person.pluck(
|
171
|
+
# Person.distinct.pluck(:role)
|
153
172
|
# # SELECT DISTINCT role FROM people
|
154
173
|
# # => ['admin', 'member', 'guest']
|
155
174
|
#
|
@@ -161,24 +180,22 @@ module ActiveRecord
|
|
161
180
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
162
181
|
# # => ['0', '27761', '173']
|
163
182
|
#
|
183
|
+
# See also #ids.
|
184
|
+
#
|
164
185
|
def pluck(*column_names)
|
165
|
-
column_names.map
|
166
|
-
|
167
|
-
attribute_alias(column_name)
|
168
|
-
else
|
169
|
-
column_name.to_s
|
170
|
-
end
|
186
|
+
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
187
|
+
return records.pluck(*column_names)
|
171
188
|
end
|
172
189
|
|
173
190
|
if has_include?(column_names.first)
|
174
|
-
|
191
|
+
relation = apply_join_dependency
|
192
|
+
relation.pluck(*column_names)
|
175
193
|
else
|
194
|
+
klass.enforce_raw_sql_whitelist(column_names)
|
176
195
|
relation = spawn
|
177
|
-
relation.select_values = column_names
|
178
|
-
|
179
|
-
|
180
|
-
result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
|
181
|
-
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)
|
182
199
|
end
|
183
200
|
end
|
184
201
|
|
@@ -191,213 +208,211 @@ module ActiveRecord
|
|
191
208
|
end
|
192
209
|
|
193
210
|
private
|
211
|
+
def has_include?(column_name)
|
212
|
+
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
213
|
+
end
|
194
214
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
209
234
|
|
210
|
-
|
211
|
-
|
235
|
+
if group_values.any?
|
236
|
+
execute_grouped_calculation(operation, column_name, distinct)
|
237
|
+
else
|
238
|
+
execute_simple_calculation(operation, column_name, distinct)
|
212
239
|
end
|
240
|
+
end
|
213
241
|
|
214
|
-
|
215
|
-
|
242
|
+
def distinct_select?(column_name)
|
243
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
216
244
|
end
|
217
245
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
246
|
+
def aggregate_column(column_name)
|
247
|
+
return column_name if Arel::Expressions === column_name
|
248
|
+
|
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)
|
253
|
+
end
|
222
254
|
end
|
223
|
-
end
|
224
255
|
|
225
|
-
|
226
|
-
|
227
|
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
|
228
|
-
else
|
229
|
-
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
256
|
+
def operation_over_aggregate_column(column, operation, distinct)
|
257
|
+
operation == "count" ? column.count(distinct) : column.send(operation)
|
230
258
|
end
|
231
|
-
end
|
232
259
|
|
233
|
-
|
234
|
-
|
235
|
-
end
|
260
|
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
261
|
+
column_alias = column_name
|
236
262
|
|
237
|
-
|
238
|
-
|
239
|
-
|
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
|
240
266
|
|
241
|
-
|
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)
|
242
271
|
|
243
|
-
|
272
|
+
column = aggregate_column(column_name)
|
244
273
|
|
245
|
-
|
246
|
-
|
247
|
-
|
274
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
275
|
+
if operation == "sum" && distinct
|
276
|
+
select_value.distinct = true
|
277
|
+
end
|
248
278
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
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]
|
253
282
|
|
254
|
-
|
283
|
+
query_builder = relation.arel
|
284
|
+
end
|
255
285
|
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
259
292
|
|
260
|
-
|
261
|
-
bind_values = query_builder.bind_values + relation.bind_values
|
293
|
+
type_cast_calculated_value(value, type, operation)
|
262
294
|
end
|
263
295
|
|
264
|
-
|
265
|
-
|
266
|
-
value = row && row.values.first
|
267
|
-
column = result.column_types.fetch(column_alias) do
|
268
|
-
type_for(column_name)
|
269
|
-
end
|
296
|
+
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
297
|
+
group_attrs = group_values
|
270
298
|
|
271
|
-
|
272
|
-
|
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)
|
273
307
|
|
274
|
-
|
275
|
-
|
308
|
+
group_aliases = group_fields.map { |field| column_alias_for(field) }
|
309
|
+
group_columns = group_aliases.zip(group_fields)
|
276
310
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
group_fields = group_attrs
|
283
|
-
end
|
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
|
284
316
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
+
}
|
291
332
|
|
292
|
-
|
333
|
+
relation = except(:group).distinct!(false)
|
334
|
+
relation.group_values = group_fields
|
335
|
+
relation.select_values = select_values
|
293
336
|
|
294
|
-
|
295
|
-
aggregate_alias = 'count_all'
|
296
|
-
else
|
297
|
-
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
298
|
-
end
|
337
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
299
338
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
distinct).as(aggregate_alias)
|
305
|
-
]
|
306
|
-
select_values += select_values unless having_values.empty?
|
307
|
-
|
308
|
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
309
|
-
if field.respond_to?(:as)
|
310
|
-
field.as(aliaz)
|
311
|
-
else
|
312
|
-
"#{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] }]
|
313
343
|
end
|
314
|
-
}
|
315
|
-
|
316
|
-
relation = except(:group)
|
317
|
-
relation.group_values = group
|
318
|
-
relation.select_values = select_values
|
319
|
-
|
320
|
-
calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
|
321
344
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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]
|
326
358
|
end
|
327
359
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
337
371
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
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!(/ +/, "_")
|
342
377
|
|
343
|
-
|
344
|
-
# a usable column name:
|
345
|
-
#
|
346
|
-
# column_alias_for("users.id") # => "users_id"
|
347
|
-
# column_alias_for("sum(id)") # => "sum_id"
|
348
|
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
349
|
-
# column_alias_for("count(*)") # => "count_all"
|
350
|
-
# column_alias_for("count", "id") # => "count_id"
|
351
|
-
def column_alias_for(keys)
|
352
|
-
if keys.respond_to? :name
|
353
|
-
keys = "#{keys.relation.name}.#{keys.name}"
|
378
|
+
@klass.connection.table_alias_for(table_name)
|
354
379
|
end
|
355
380
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
table_name.gsub!(/ +/, '_')
|
361
|
-
|
362
|
-
@klass.connection.table_alias_for(table_name)
|
363
|
-
end
|
364
|
-
|
365
|
-
def type_for(field)
|
366
|
-
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
367
|
-
@klass.type_for_attribute(field_name)
|
368
|
-
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
|
369
385
|
|
370
|
-
|
371
|
-
|
372
|
-
when
|
373
|
-
when
|
374
|
-
when
|
375
|
-
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
|
376
393
|
end
|
377
|
-
end
|
378
394
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
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
|
385
402
|
end
|
386
|
-
end
|
387
403
|
|
388
|
-
|
389
|
-
|
390
|
-
|
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
|
391
411
|
|
392
|
-
|
393
|
-
|
394
|
-
arel = relation.arel
|
395
|
-
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)
|
396
414
|
|
397
|
-
|
398
|
-
|
399
|
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
400
|
-
sm.project(select_value).from(subquery)
|
401
|
-
end
|
415
|
+
Arel::SelectManager.new(subquery).project(select_value)
|
416
|
+
end
|
402
417
|
end
|
403
418
|
end
|