activerecord 4.2.11.3 → 5.0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +1638 -1132
 - data/MIT-LICENSE +2 -2
 - data/README.rdoc +7 -8
 - data/examples/performance.rb +2 -3
 - data/examples/simple.rb +0 -1
 - data/lib/active_record.rb +7 -2
 - data/lib/active_record/aggregations.rb +34 -21
 - data/lib/active_record/association_relation.rb +7 -4
 - data/lib/active_record/associations.rb +347 -218
 - data/lib/active_record/associations/alias_tracker.rb +19 -16
 - data/lib/active_record/associations/association.rb +22 -10
 - data/lib/active_record/associations/association_scope.rb +75 -104
 - data/lib/active_record/associations/belongs_to_association.rb +21 -32
 - data/lib/active_record/associations/builder/association.rb +28 -34
 - data/lib/active_record/associations/builder/belongs_to.rb +43 -18
 - data/lib/active_record/associations/builder/collection_association.rb +7 -19
 - data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
 - data/lib/active_record/associations/builder/has_many.rb +4 -4
 - data/lib/active_record/associations/builder/has_one.rb +11 -6
 - data/lib/active_record/associations/builder/singular_association.rb +13 -11
 - data/lib/active_record/associations/collection_association.rb +85 -69
 - data/lib/active_record/associations/collection_proxy.rb +104 -46
 - data/lib/active_record/associations/foreign_association.rb +1 -1
 - data/lib/active_record/associations/has_many_association.rb +21 -78
 - data/lib/active_record/associations/has_many_through_association.rb +6 -47
 - data/lib/active_record/associations/has_one_association.rb +12 -5
 - data/lib/active_record/associations/join_dependency.rb +38 -22
 - data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
 - data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
 - data/lib/active_record/associations/preloader.rb +14 -4
 - data/lib/active_record/associations/preloader/association.rb +52 -71
 - data/lib/active_record/associations/preloader/collection_association.rb +0 -7
 - data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
 - data/lib/active_record/associations/preloader/has_one.rb +0 -8
 - data/lib/active_record/associations/preloader/singular_association.rb +0 -1
 - data/lib/active_record/associations/preloader/through_association.rb +36 -17
 - data/lib/active_record/associations/singular_association.rb +13 -1
 - data/lib/active_record/associations/through_association.rb +12 -4
 - data/lib/active_record/attribute.rb +69 -19
 - data/lib/active_record/attribute/user_provided_default.rb +28 -0
 - data/lib/active_record/attribute_assignment.rb +19 -140
 - data/lib/active_record/attribute_decorators.rb +6 -5
 - data/lib/active_record/attribute_methods.rb +69 -44
 - data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
 - data/lib/active_record/attribute_methods/dirty.rb +46 -86
 - data/lib/active_record/attribute_methods/primary_key.rb +16 -3
 - data/lib/active_record/attribute_methods/query.rb +2 -2
 - data/lib/active_record/attribute_methods/read.rb +31 -59
 - data/lib/active_record/attribute_methods/serialization.rb +13 -16
 - data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
 - data/lib/active_record/attribute_methods/write.rb +13 -37
 - data/lib/active_record/attribute_mutation_tracker.rb +70 -0
 - data/lib/active_record/attribute_set.rb +32 -3
 - data/lib/active_record/attribute_set/builder.rb +42 -16
 - data/lib/active_record/attributes.rb +199 -81
 - data/lib/active_record/autosave_association.rb +54 -17
 - data/lib/active_record/base.rb +32 -23
 - data/lib/active_record/callbacks.rb +39 -43
 - data/lib/active_record/coders/json.rb +1 -1
 - data/lib/active_record/coders/yaml_column.rb +20 -8
 - data/lib/active_record/collection_cache_key.rb +50 -0
 - data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
 - data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
 - data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
 - data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
 - data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
 - data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
 - data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
 - data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
 - data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
 - data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
 - data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
 - data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
 - data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
 - data/lib/active_record/connection_adapters/column.rb +28 -43
 - data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
 - data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
 - data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
 - data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
 - data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
 - data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
 - data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
 - data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
 - data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
 - data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
 - data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
 - data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
 - data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
 - data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
 - data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
 - data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
 - data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
 - data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
 - data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
 - data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
 - data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
 - data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
 - data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
 - data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
 - data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
 - data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
 - data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
 - data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
 - data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
 - data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
 - data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
 - data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
 - data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
 - data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
 - data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
 - data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
 - data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
 - data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
 - data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
 - data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
 - data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
 - data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
 - data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
 - data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
 - data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
 - data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
 - data/lib/active_record/connection_handling.rb +37 -14
 - data/lib/active_record/core.rb +92 -108
 - data/lib/active_record/counter_cache.rb +13 -24
 - data/lib/active_record/dynamic_matchers.rb +1 -20
 - data/lib/active_record/enum.rb +116 -76
 - data/lib/active_record/errors.rb +87 -48
 - data/lib/active_record/explain.rb +20 -9
 - data/lib/active_record/explain_registry.rb +1 -1
 - data/lib/active_record/explain_subscriber.rb +1 -1
 - data/lib/active_record/fixture_set/file.rb +26 -5
 - data/lib/active_record/fixtures.rb +77 -41
 - data/lib/active_record/gem_version.rb +4 -4
 - data/lib/active_record/inheritance.rb +32 -40
 - data/lib/active_record/integration.rb +17 -14
 - data/lib/active_record/internal_metadata.rb +56 -0
 - data/lib/active_record/legacy_yaml_adapter.rb +18 -2
 - data/lib/active_record/locale/en.yml +3 -2
 - data/lib/active_record/locking/optimistic.rb +15 -15
 - data/lib/active_record/locking/pessimistic.rb +1 -1
 - data/lib/active_record/log_subscriber.rb +48 -24
 - data/lib/active_record/migration.rb +362 -111
 - data/lib/active_record/migration/command_recorder.rb +59 -18
 - data/lib/active_record/migration/compatibility.rb +126 -0
 - data/lib/active_record/model_schema.rb +270 -73
 - data/lib/active_record/nested_attributes.rb +58 -29
 - data/lib/active_record/no_touching.rb +4 -0
 - data/lib/active_record/null_relation.rb +16 -8
 - data/lib/active_record/persistence.rb +152 -90
 - data/lib/active_record/query_cache.rb +18 -23
 - data/lib/active_record/querying.rb +12 -11
 - data/lib/active_record/railtie.rb +23 -16
 - data/lib/active_record/railties/controller_runtime.rb +1 -1
 - data/lib/active_record/railties/databases.rake +52 -41
 - data/lib/active_record/readonly_attributes.rb +1 -1
 - data/lib/active_record/reflection.rb +302 -115
 - data/lib/active_record/relation.rb +187 -120
 - data/lib/active_record/relation/batches.rb +141 -36
 - data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
 - data/lib/active_record/relation/calculations.rb +92 -117
 - data/lib/active_record/relation/delegation.rb +8 -20
 - data/lib/active_record/relation/finder_methods.rb +173 -89
 - data/lib/active_record/relation/from_clause.rb +32 -0
 - data/lib/active_record/relation/merger.rb +16 -42
 - data/lib/active_record/relation/predicate_builder.rb +120 -107
 - data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
 - data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
 - data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
 - data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
 - data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
 - data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
 - data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
 - data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
 - data/lib/active_record/relation/query_attribute.rb +19 -0
 - data/lib/active_record/relation/query_methods.rb +308 -244
 - data/lib/active_record/relation/record_fetch_warning.rb +49 -0
 - data/lib/active_record/relation/spawn_methods.rb +4 -7
 - data/lib/active_record/relation/where_clause.rb +174 -0
 - data/lib/active_record/relation/where_clause_factory.rb +38 -0
 - data/lib/active_record/result.rb +11 -4
 - data/lib/active_record/runtime_registry.rb +1 -1
 - data/lib/active_record/sanitization.rb +105 -66
 - data/lib/active_record/schema.rb +26 -22
 - data/lib/active_record/schema_dumper.rb +54 -37
 - data/lib/active_record/schema_migration.rb +11 -14
 - data/lib/active_record/scoping.rb +34 -16
 - data/lib/active_record/scoping/default.rb +28 -10
 - data/lib/active_record/scoping/named.rb +59 -26
 - data/lib/active_record/secure_token.rb +38 -0
 - data/lib/active_record/serialization.rb +3 -5
 - data/lib/active_record/statement_cache.rb +17 -15
 - data/lib/active_record/store.rb +8 -3
 - data/lib/active_record/suppressor.rb +58 -0
 - data/lib/active_record/table_metadata.rb +69 -0
 - data/lib/active_record/tasks/database_tasks.rb +66 -49
 - data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
 - data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
 - data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
 - data/lib/active_record/timestamp.rb +20 -9
 - data/lib/active_record/touch_later.rb +63 -0
 - data/lib/active_record/transactions.rb +139 -57
 - data/lib/active_record/type.rb +66 -17
 - data/lib/active_record/type/adapter_specific_registry.rb +130 -0
 - data/lib/active_record/type/date.rb +2 -45
 - data/lib/active_record/type/date_time.rb +2 -49
 - data/lib/active_record/type/internal/abstract_json.rb +33 -0
 - data/lib/active_record/type/internal/timezone.rb +15 -0
 - data/lib/active_record/type/serialized.rb +15 -14
 - data/lib/active_record/type/time.rb +10 -16
 - data/lib/active_record/type/type_map.rb +4 -4
 - data/lib/active_record/type_caster.rb +7 -0
 - data/lib/active_record/type_caster/connection.rb +29 -0
 - data/lib/active_record/type_caster/map.rb +19 -0
 - data/lib/active_record/validations.rb +33 -32
 - data/lib/active_record/validations/absence.rb +23 -0
 - data/lib/active_record/validations/associated.rb +10 -3
 - data/lib/active_record/validations/length.rb +24 -0
 - data/lib/active_record/validations/presence.rb +11 -12
 - data/lib/active_record/validations/uniqueness.rb +33 -33
 - data/lib/rails/generators/active_record/migration.rb +15 -0
 - data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
 - data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
 - data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
 - data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
 - data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
 - data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
 - metadata +58 -34
 - data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
 - data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
 - data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
 - 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/integer.rb +0 -11
 - data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
 - 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/decimal_without_scale.rb +0 -11
 - 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/text.rb +0 -11
 - data/lib/active_record/type/time_value.rb +0 -38
 - data/lib/active_record/type/unsigned_integer.rb +0 -15
 - data/lib/active_record/type/value.rb +0 -110
 
| 
         @@ -1,8 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "active_record/relation/batches/batch_enumerator"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ActiveRecord
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Batches
         
     | 
| 
      
 5 
     | 
    
         
            +
                ORDER_OR_LIMIT_IGNORED_MESSAGE = "Scoped order and limit are ignored, it's forced to be batch order and batch size."
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       3 
7 
     | 
    
         
             
                # Looping through a collection of records from the database
         
     | 
| 
       4 
     | 
    
         
            -
                # (using the  
     | 
| 
       5 
     | 
    
         
            -
                # since it will try to instantiate all the objects at once.
         
     | 
| 
      
 8 
     | 
    
         
            +
                # (using the Scoping::Named::ClassMethods.all method, for example)
         
     | 
| 
      
 9 
     | 
    
         
            +
                # is very inefficient since it will try to instantiate all the objects at once.
         
     | 
| 
       6 
10 
     | 
    
         
             
                #
         
     | 
| 
       7 
11 
     | 
    
         
             
                # In that case, batch processing methods allow you to work
         
     | 
| 
       8 
12 
     | 
    
         
             
                # with the records in batches, thereby greatly reducing memory consumption.
         
     | 
| 
         @@ -26,12 +30,16 @@ module ActiveRecord 
     | 
|
| 
       26 
30 
     | 
    
         
             
                #   end
         
     | 
| 
       27 
31 
     | 
    
         
             
                #
         
     | 
| 
       28 
32 
     | 
    
         
             
                # ==== Options
         
     | 
| 
       29 
     | 
    
         
            -
                # * <tt>:batch_size</tt> - Specifies the size of the batch.  
     | 
| 
       30 
     | 
    
         
            -
                # * <tt>:start</tt> - Specifies the  
     | 
| 
      
 33 
     | 
    
         
            +
                # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
         
     | 
| 
      
 34 
     | 
    
         
            +
                # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
         
     | 
| 
      
 35 
     | 
    
         
            +
                # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
         
     | 
| 
      
 36 
     | 
    
         
            +
                # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
         
     | 
| 
      
 37 
     | 
    
         
            +
                #   the order and limit have to be ignored due to batching.
         
     | 
| 
      
 38 
     | 
    
         
            +
                #
         
     | 
| 
       31 
39 
     | 
    
         
             
                # This is especially useful if you want multiple workers dealing with
         
     | 
| 
       32 
40 
     | 
    
         
             
                # the same processing queue. You can make worker 1 handle all the records
         
     | 
| 
       33 
41 
     | 
    
         
             
                # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
         
     | 
| 
       34 
     | 
    
         
            -
                # (by setting the +:start+ option on  
     | 
| 
      
 42 
     | 
    
         
            +
                # (by setting the +:start+ and +:finish+ option on each worker).
         
     | 
| 
       35 
43 
     | 
    
         
             
                #
         
     | 
| 
       36 
44 
     | 
    
         
             
                #   # Let's process for a batch of 2000 records, skipping the first 2000 rows
         
     | 
| 
       37 
45 
     | 
    
         
             
                #   Person.find_each(start: 2000, batch_size: 2000) do |person|
         
     | 
| 
         @@ -40,24 +48,25 @@ module ActiveRecord 
     | 
|
| 
       40 
48 
     | 
    
         
             
                #
         
     | 
| 
       41 
49 
     | 
    
         
             
                # NOTE: It's not possible to set the order. That is automatically set to
         
     | 
| 
       42 
50 
     | 
    
         
             
                # ascending on the primary key ("id ASC") to make the batch ordering
         
     | 
| 
       43 
     | 
    
         
            -
                # work. This also means that this method only works  
     | 
| 
       44 
     | 
    
         
            -
                #  
     | 
| 
      
 51 
     | 
    
         
            +
                # work. This also means that this method only works when the primary key is
         
     | 
| 
      
 52 
     | 
    
         
            +
                # orderable (e.g. an integer or string).
         
     | 
| 
       45 
53 
     | 
    
         
             
                #
         
     | 
| 
       46 
54 
     | 
    
         
             
                # NOTE: You can't set the limit either, that's used to control
         
     | 
| 
       47 
55 
     | 
    
         
             
                # the batch sizes.
         
     | 
| 
       48 
     | 
    
         
            -
                def find_each( 
     | 
| 
      
 56 
     | 
    
         
            +
                def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
         
     | 
| 
       49 
57 
     | 
    
         
             
                  if block_given?
         
     | 
| 
       50 
     | 
    
         
            -
                    find_in_batches( 
     | 
| 
      
 58 
     | 
    
         
            +
                    find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
         
     | 
| 
       51 
59 
     | 
    
         
             
                      records.each { |record| yield record }
         
     | 
| 
       52 
60 
     | 
    
         
             
                    end
         
     | 
| 
       53 
61 
     | 
    
         
             
                  else
         
     | 
| 
       54 
     | 
    
         
            -
                    enum_for 
     | 
| 
       55 
     | 
    
         
            -
                       
     | 
| 
      
 62 
     | 
    
         
            +
                    enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
         
     | 
| 
      
 63 
     | 
    
         
            +
                      relation = self
         
     | 
| 
      
 64 
     | 
    
         
            +
                      apply_limits(relation, start, finish).size
         
     | 
| 
       56 
65 
     | 
    
         
             
                    end
         
     | 
| 
       57 
66 
     | 
    
         
             
                  end
         
     | 
| 
       58 
67 
     | 
    
         
             
                end
         
     | 
| 
       59 
68 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                # Yields each batch of records that was found by the find  
     | 
| 
      
 69 
     | 
    
         
            +
                # Yields each batch of records that was found by the find options as
         
     | 
| 
       61 
70 
     | 
    
         
             
                # an array.
         
     | 
| 
       62 
71 
     | 
    
         
             
                #
         
     | 
| 
       63 
72 
     | 
    
         
             
                #   Person.where("age > 21").find_in_batches do |group|
         
     | 
| 
         @@ -76,12 +85,16 @@ module ActiveRecord 
     | 
|
| 
       76 
85 
     | 
    
         
             
                # To be yielded each record one by one, use #find_each instead.
         
     | 
| 
       77 
86 
     | 
    
         
             
                #
         
     | 
| 
       78 
87 
     | 
    
         
             
                # ==== Options
         
     | 
| 
       79 
     | 
    
         
            -
                # * <tt>:batch_size</tt> - Specifies the size of the batch.  
     | 
| 
       80 
     | 
    
         
            -
                # * <tt>:start</tt> - Specifies the  
     | 
| 
      
 88 
     | 
    
         
            +
                # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
         
     | 
| 
      
 89 
     | 
    
         
            +
                # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
         
     | 
| 
      
 90 
     | 
    
         
            +
                # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
         
     | 
| 
      
 91 
     | 
    
         
            +
                # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
         
     | 
| 
      
 92 
     | 
    
         
            +
                #   the order and limit have to be ignored due to batching.
         
     | 
| 
      
 93 
     | 
    
         
            +
                #
         
     | 
| 
       81 
94 
     | 
    
         
             
                # This is especially useful if you want multiple workers dealing with
         
     | 
| 
       82 
95 
     | 
    
         
             
                # the same processing queue. You can make worker 1 handle all the records
         
     | 
| 
       83 
96 
     | 
    
         
             
                # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
         
     | 
| 
       84 
     | 
    
         
            -
                # (by setting the +:start+ option on  
     | 
| 
      
 97 
     | 
    
         
            +
                # (by setting the +:start+ and +:finish+ option on each worker).
         
     | 
| 
       85 
98 
     | 
    
         
             
                #
         
     | 
| 
       86 
99 
     | 
    
         
             
                #   # Let's process the next 2000 records
         
     | 
| 
       87 
100 
     | 
    
         
             
                #   Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
         
     | 
| 
         @@ -90,49 +103,141 @@ module ActiveRecord 
     | 
|
| 
       90 
103 
     | 
    
         
             
                #
         
     | 
| 
       91 
104 
     | 
    
         
             
                # NOTE: It's not possible to set the order. That is automatically set to
         
     | 
| 
       92 
105 
     | 
    
         
             
                # ascending on the primary key ("id ASC") to make the batch ordering
         
     | 
| 
       93 
     | 
    
         
            -
                # work. This also means that this method only works  
     | 
| 
       94 
     | 
    
         
            -
                #  
     | 
| 
      
 106 
     | 
    
         
            +
                # work. This also means that this method only works when the primary key is
         
     | 
| 
      
 107 
     | 
    
         
            +
                # orderable (e.g. an integer or string).
         
     | 
| 
       95 
108 
     | 
    
         
             
                #
         
     | 
| 
       96 
109 
     | 
    
         
             
                # NOTE: You can't set the limit either, that's used to control
         
     | 
| 
       97 
110 
     | 
    
         
             
                # the batch sizes.
         
     | 
| 
       98 
     | 
    
         
            -
                def find_in_batches( 
     | 
| 
       99 
     | 
    
         
            -
                  options.assert_valid_keys(:start, :batch_size)
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
      
 111 
     | 
    
         
            +
                def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
         
     | 
| 
       101 
112 
     | 
    
         
             
                  relation = self
         
     | 
| 
       102 
     | 
    
         
            -
                  start = options[:start]
         
     | 
| 
       103 
     | 
    
         
            -
                  batch_size = options[:batch_size] || 1000
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
113 
     | 
    
         
             
                  unless block_given?
         
     | 
| 
       106 
     | 
    
         
            -
                    return to_enum(:find_in_batches,  
     | 
| 
       107 
     | 
    
         
            -
                      total = start  
     | 
| 
      
 114 
     | 
    
         
            +
                    return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
         
     | 
| 
      
 115 
     | 
    
         
            +
                      total = apply_limits(relation, start, finish).size
         
     | 
| 
       108 
116 
     | 
    
         
             
                      (total - 1).div(batch_size) + 1
         
     | 
| 
       109 
117 
     | 
    
         
             
                    end
         
     | 
| 
       110 
118 
     | 
    
         
             
                  end
         
     | 
| 
       111 
119 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
                   
     | 
| 
       113 
     | 
    
         
            -
                     
     | 
| 
      
 120 
     | 
    
         
            +
                  in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
         
     | 
| 
      
 121 
     | 
    
         
            +
                    yield batch.to_a
         
     | 
| 
       114 
122 
     | 
    
         
             
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
       115 
124 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
      
 125 
     | 
    
         
            +
                # Yields ActiveRecord::Relation objects to work with a batch of records.
         
     | 
| 
      
 126 
     | 
    
         
            +
                #
         
     | 
| 
      
 127 
     | 
    
         
            +
                #   Person.where("age > 21").in_batches do |relation|
         
     | 
| 
      
 128 
     | 
    
         
            +
                #     relation.delete_all
         
     | 
| 
      
 129 
     | 
    
         
            +
                #     sleep(10) # Throttle the delete queries
         
     | 
| 
      
 130 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 131 
     | 
    
         
            +
                #
         
     | 
| 
      
 132 
     | 
    
         
            +
                # If you do not provide a block to #in_batches, it will return a
         
     | 
| 
      
 133 
     | 
    
         
            +
                # BatchEnumerator which is enumerable.
         
     | 
| 
      
 134 
     | 
    
         
            +
                #
         
     | 
| 
      
 135 
     | 
    
         
            +
                #   Person.in_batches.each_with_index do |relation, batch_index|
         
     | 
| 
      
 136 
     | 
    
         
            +
                #     puts "Processing relation ##{batch_index}"
         
     | 
| 
      
 137 
     | 
    
         
            +
                #     relation.delete_all
         
     | 
| 
      
 138 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 139 
     | 
    
         
            +
                #
         
     | 
| 
      
 140 
     | 
    
         
            +
                # Examples of calling methods on the returned BatchEnumerator object:
         
     | 
| 
      
 141 
     | 
    
         
            +
                #
         
     | 
| 
      
 142 
     | 
    
         
            +
                #   Person.in_batches.delete_all
         
     | 
| 
      
 143 
     | 
    
         
            +
                #   Person.in_batches.update_all(awesome: true)
         
     | 
| 
      
 144 
     | 
    
         
            +
                #   Person.in_batches.each_record(&:party_all_night!)
         
     | 
| 
      
 145 
     | 
    
         
            +
                #
         
     | 
| 
      
 146 
     | 
    
         
            +
                # ==== Options
         
     | 
| 
      
 147 
     | 
    
         
            +
                # * <tt>:of</tt> - Specifies the size of the batch. Defaults to 1000.
         
     | 
| 
      
 148 
     | 
    
         
            +
                # * <tt>:load</tt> - Specifies if the relation should be loaded. Defaults to false.
         
     | 
| 
      
 149 
     | 
    
         
            +
                # * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
         
     | 
| 
      
 150 
     | 
    
         
            +
                # * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
         
     | 
| 
      
 151 
     | 
    
         
            +
                # * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
         
     | 
| 
      
 152 
     | 
    
         
            +
                #   the order and limit have to be ignored due to batching.
         
     | 
| 
      
 153 
     | 
    
         
            +
                #
         
     | 
| 
      
 154 
     | 
    
         
            +
                # This is especially useful if you want to work with the
         
     | 
| 
      
 155 
     | 
    
         
            +
                # ActiveRecord::Relation object instead of the array of records, or if
         
     | 
| 
      
 156 
     | 
    
         
            +
                # you want multiple workers dealing with the same processing queue. You can
         
     | 
| 
      
 157 
     | 
    
         
            +
                # make worker 1 handle all the records between id 0 and 10,000 and worker 2
         
     | 
| 
      
 158 
     | 
    
         
            +
                # handle from 10,000 and beyond (by setting the +:start+ and +:finish+
         
     | 
| 
      
 159 
     | 
    
         
            +
                # option on each worker).
         
     | 
| 
      
 160 
     | 
    
         
            +
                #
         
     | 
| 
      
 161 
     | 
    
         
            +
                #   # Let's process the next 2000 records
         
     | 
| 
      
 162 
     | 
    
         
            +
                #   Person.in_batches(of: 2000, start: 2000).update_all(awesome: true)
         
     | 
| 
      
 163 
     | 
    
         
            +
                #
         
     | 
| 
      
 164 
     | 
    
         
            +
                # An example of calling where query method on the relation:
         
     | 
| 
      
 165 
     | 
    
         
            +
                #
         
     | 
| 
      
 166 
     | 
    
         
            +
                #   Person.in_batches.each do |relation|
         
     | 
| 
      
 167 
     | 
    
         
            +
                #     relation.update_all('age = age + 1')
         
     | 
| 
      
 168 
     | 
    
         
            +
                #     relation.where('age > 21').update_all(should_party: true)
         
     | 
| 
      
 169 
     | 
    
         
            +
                #     relation.where('age <= 21').delete_all
         
     | 
| 
      
 170 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 171 
     | 
    
         
            +
                #
         
     | 
| 
      
 172 
     | 
    
         
            +
                # NOTE: If you are going to iterate through each record, you should call
         
     | 
| 
      
 173 
     | 
    
         
            +
                # #each_record on the yielded BatchEnumerator:
         
     | 
| 
      
 174 
     | 
    
         
            +
                #
         
     | 
| 
      
 175 
     | 
    
         
            +
                #   Person.in_batches.each_record(&:party_all_night!)
         
     | 
| 
      
 176 
     | 
    
         
            +
                #
         
     | 
| 
      
 177 
     | 
    
         
            +
                # NOTE: It's not possible to set the order. That is automatically set to
         
     | 
| 
      
 178 
     | 
    
         
            +
                # ascending on the primary key ("id ASC") to make the batch ordering
         
     | 
| 
      
 179 
     | 
    
         
            +
                # consistent. Therefore the primary key must be orderable, e.g. an integer
         
     | 
| 
      
 180 
     | 
    
         
            +
                # or a string.
         
     | 
| 
      
 181 
     | 
    
         
            +
                #
         
     | 
| 
      
 182 
     | 
    
         
            +
                # NOTE: You can't set the limit either, that's used to control the batch
         
     | 
| 
      
 183 
     | 
    
         
            +
                # sizes.
         
     | 
| 
      
 184 
     | 
    
         
            +
                def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  relation = self
         
     | 
| 
      
 186 
     | 
    
         
            +
                  unless block_given?
         
     | 
| 
      
 187 
     | 
    
         
            +
                    return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  end
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                  if arel.orders.present? || arel.taken.present?
         
     | 
| 
      
 191 
     | 
    
         
            +
                    act_on_order_or_limit_ignored(error_on_ignore)
         
     | 
| 
      
 192 
     | 
    
         
            +
                  end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                  relation = relation.reorder(batch_order).limit(of)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  relation = apply_limits(relation, start, finish)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  batch_relation = relation
         
     | 
| 
       118 
197 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                   
     | 
| 
       120 
     | 
    
         
            -
                     
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
      
 198 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 199 
     | 
    
         
            +
                    if load
         
     | 
| 
      
 200 
     | 
    
         
            +
                      records = batch_relation.records
         
     | 
| 
      
 201 
     | 
    
         
            +
                      ids = records.map(&:id)
         
     | 
| 
      
 202 
     | 
    
         
            +
                      yielded_relation = self.where(primary_key => ids)
         
     | 
| 
      
 203 
     | 
    
         
            +
                      yielded_relation.load_records(records)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    else
         
     | 
| 
      
 205 
     | 
    
         
            +
                      ids = batch_relation.pluck(primary_key)
         
     | 
| 
      
 206 
     | 
    
         
            +
                      yielded_relation = self.where(primary_key => ids)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    end
         
     | 
| 
       123 
208 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                     
     | 
| 
      
 209 
     | 
    
         
            +
                    break if ids.empty?
         
     | 
| 
       125 
210 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                     
     | 
| 
      
 211 
     | 
    
         
            +
                    primary_key_offset = ids.last
         
     | 
| 
      
 212 
     | 
    
         
            +
                    raise ArgumentError.new("Primary key not included in the custom select clause") unless primary_key_offset
         
     | 
| 
       127 
213 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                     
     | 
| 
      
 214 
     | 
    
         
            +
                    yield yielded_relation
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                    break if ids.length < of
         
     | 
| 
      
 217 
     | 
    
         
            +
                    batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
         
     | 
| 
       129 
218 
     | 
    
         
             
                  end
         
     | 
| 
       130 
219 
     | 
    
         
             
                end
         
     | 
| 
       131 
220 
     | 
    
         | 
| 
       132 
221 
     | 
    
         
             
                private
         
     | 
| 
       133 
222 
     | 
    
         | 
| 
      
 223 
     | 
    
         
            +
                def apply_limits(relation, start, finish)
         
     | 
| 
      
 224 
     | 
    
         
            +
                  relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
         
     | 
| 
      
 225 
     | 
    
         
            +
                  relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
         
     | 
| 
      
 226 
     | 
    
         
            +
                  relation
         
     | 
| 
      
 227 
     | 
    
         
            +
                end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
       134 
229 
     | 
    
         
             
                def batch_order
         
     | 
| 
       135 
230 
     | 
    
         
             
                  "#{quoted_table_name}.#{quoted_primary_key} ASC"
         
     | 
| 
       136 
231 
     | 
    
         
             
                end
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                def act_on_order_or_limit_ignored(error_on_ignore)
         
     | 
| 
      
 234 
     | 
    
         
            +
                  raise_error = (error_on_ignore.nil? ? self.klass.error_on_ignored_order_or_limit : error_on_ignore)
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                  if raise_error
         
     | 
| 
      
 237 
     | 
    
         
            +
                    raise ArgumentError.new(ORDER_OR_LIMIT_IGNORED_MESSAGE)
         
     | 
| 
      
 238 
     | 
    
         
            +
                  elsif logger
         
     | 
| 
      
 239 
     | 
    
         
            +
                    logger.warn(ORDER_OR_LIMIT_IGNORED_MESSAGE)
         
     | 
| 
      
 240 
     | 
    
         
            +
                  end
         
     | 
| 
      
 241 
     | 
    
         
            +
                end
         
     | 
| 
       137 
242 
     | 
    
         
             
              end
         
     | 
| 
       138 
243 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ActiveRecord
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Batches
         
     | 
| 
      
 3 
     | 
    
         
            +
                class BatchEnumerator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include Enumerable
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @of       = of
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @relation = relation
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @start = start
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @finish   = finish
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # Looping through a collection of records from the database (using the
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # +all+ method, for example) is very inefficient since it will try to
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # instantiate all the objects at once.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # In that case, batch processing methods allow you to work with the
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # records in batches, thereby greatly reducing memory consumption.
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   Person.in_batches.each_record do |person|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     person.do_awesome_stuff
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   Person.where("age > 21").in_batches(of: 10).each_record do |person|
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #     person.party_all_night!
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # If you do not provide a block to #each_record, it will return an Enumerator
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # for chaining with other methods:
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #   Person.in_batches.each_record.with_index do |person, index|
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #     person.award_trophy(index + 1)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def each_record
         
     | 
| 
      
 35 
     | 
    
         
            +
                    return to_enum(:each_record) unless block_given?
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
         
     | 
| 
      
 38 
     | 
    
         
            +
                      relation.records.each { |record| yield record }
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # Delegates #delete_all, #update_all, #destroy_all methods to each batch.
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #   People.in_batches.delete_all
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #   People.where('age < 10').in_batches.destroy_all
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #   People.in_batches.update_all('age = age + 1')
         
     | 
| 
      
 47 
     | 
    
         
            +
                  [:delete_all, :update_all, :destroy_all].each do |method|
         
     | 
| 
      
 48 
     | 
    
         
            +
                    define_method(method) do |*args, &block|
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
         
     | 
| 
      
 50 
     | 
    
         
            +
                        relation.send(method, *args, &block)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  # Yields an ActiveRecord::Relation object for each batch of records.
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #   Person.in_batches.each do |relation|
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #     relation.update_all(awesome: true)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 60 
     | 
    
         
            +
                  def each
         
     | 
| 
      
 61 
     | 
    
         
            +
                    enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    return enum.each { |relation| yield relation } if block_given?
         
     | 
| 
      
 63 
     | 
    
         
            +
                    enum
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -14,127 +14,115 @@ module ActiveRecord 
     | 
|
| 
       14 
14 
     | 
    
         
             
                #   Person.distinct.count(:age)
         
     | 
| 
       15 
15 
     | 
    
         
             
                #   # => counts the number of different age values
         
     | 
| 
       16 
16 
     | 
    
         
             
                #
         
     | 
| 
       17 
     | 
    
         
            -
                # If  
     | 
| 
      
 17 
     | 
    
         
            +
                # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
         
     | 
| 
      
 18 
     | 
    
         
            +
                # it returns a Hash whose keys represent the aggregated column,
         
     | 
| 
       18 
19 
     | 
    
         
             
                # and the values are the respective amounts:
         
     | 
| 
       19 
20 
     | 
    
         
             
                #
         
     | 
| 
       20 
21 
     | 
    
         
             
                #   Person.group(:city).count
         
     | 
| 
       21 
22 
     | 
    
         
             
                #   # => { 'Rome' => 5, 'Paris' => 3 }
         
     | 
| 
       22 
23 
     | 
    
         
             
                #
         
     | 
| 
       23 
     | 
    
         
            -
                # If  
     | 
| 
      
 24 
     | 
    
         
            +
                # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
         
     | 
| 
       24 
25 
     | 
    
         
             
                # keys are an array containing the individual values of each column and the value
         
     | 
| 
       25 
     | 
    
         
            -
                # of each key would be the  
     | 
| 
      
 26 
     | 
    
         
            +
                # of each key would be the #count.
         
     | 
| 
       26 
27 
     | 
    
         
             
                #
         
     | 
| 
       27 
28 
     | 
    
         
             
                #   Article.group(:status, :category).count
         
     | 
| 
       28 
29 
     | 
    
         
             
                #   # =>  {["draft", "business"]=>10, ["draft", "technology"]=>4,
         
     | 
| 
       29 
30 
     | 
    
         
             
                #          ["published", "business"]=>0, ["published", "technology"]=>2}
         
     | 
| 
       30 
31 
     | 
    
         
             
                #
         
     | 
| 
       31 
     | 
    
         
            -
                # If  
     | 
| 
      
 32 
     | 
    
         
            +
                # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
         
     | 
| 
       32 
33 
     | 
    
         
             
                #
         
     | 
| 
       33 
34 
     | 
    
         
             
                #   Person.select(:age).count
         
     | 
| 
       34 
35 
     | 
    
         
             
                #   # => counts the number of different age values
         
     | 
| 
       35 
36 
     | 
    
         
             
                #
         
     | 
| 
       36 
     | 
    
         
            -
                # Note: not all valid  
     | 
| 
      
 37 
     | 
    
         
            +
                # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
         
     | 
| 
       37 
38 
     | 
    
         
             
                # between databases. In invalid cases, an error from the database is thrown.
         
     | 
| 
       38 
     | 
    
         
            -
                def count(column_name = nil 
     | 
| 
       39 
     | 
    
         
            -
                   
     | 
| 
       40 
     | 
    
         
            -
                    raise ArgumentError, "Relation#count does not support finder options anymore. " \
         
     | 
| 
       41 
     | 
    
         
            -
                                         "Please build a scope and then call count on it or use the " \
         
     | 
| 
       42 
     | 
    
         
            -
                                         "activerecord-deprecated_finders gem to enable this functionality."
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                  end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                  # TODO: Remove options argument as soon we remove support to
         
     | 
| 
       47 
     | 
    
         
            -
                  # activerecord-deprecated_finders.
         
     | 
| 
       48 
     | 
    
         
            -
                  calculate(:count, column_name, options)
         
     | 
| 
      
 39 
     | 
    
         
            +
                def count(column_name = nil)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  calculate(:count, column_name)
         
     | 
| 
       49 
41 
     | 
    
         
             
                end
         
     | 
| 
       50 
42 
     | 
    
         | 
| 
       51 
43 
     | 
    
         
             
                # Calculates the average value on a given column. Returns +nil+ if there's
         
     | 
| 
       52 
     | 
    
         
            -
                # no row. See  
     | 
| 
      
 44 
     | 
    
         
            +
                # no row. See #calculate for examples with options.
         
     | 
| 
       53 
45 
     | 
    
         
             
                #
         
     | 
| 
       54 
46 
     | 
    
         
             
                #   Person.average(:age) # => 35.8
         
     | 
| 
       55 
     | 
    
         
            -
                def average(column_name 
     | 
| 
       56 
     | 
    
         
            -
                   
     | 
| 
       57 
     | 
    
         
            -
                  # activerecord-deprecated_finders.
         
     | 
| 
       58 
     | 
    
         
            -
                  calculate(:average, column_name, options)
         
     | 
| 
      
 47 
     | 
    
         
            +
                def average(column_name)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  calculate(:average, column_name)
         
     | 
| 
       59 
49 
     | 
    
         
             
                end
         
     | 
| 
       60 
50 
     | 
    
         | 
| 
       61 
51 
     | 
    
         
             
                # Calculates the minimum value on a given column. The value is returned
         
     | 
| 
       62 
52 
     | 
    
         
             
                # with the same data type of the column, or +nil+ if there's no row. See
         
     | 
| 
       63 
     | 
    
         
            -
                #  
     | 
| 
      
 53 
     | 
    
         
            +
                # #calculate for examples with options.
         
     | 
| 
       64 
54 
     | 
    
         
             
                #
         
     | 
| 
       65 
55 
     | 
    
         
             
                #   Person.minimum(:age) # => 7
         
     | 
| 
       66 
     | 
    
         
            -
                def minimum(column_name 
     | 
| 
       67 
     | 
    
         
            -
                   
     | 
| 
       68 
     | 
    
         
            -
                  # activerecord-deprecated_finders.
         
     | 
| 
       69 
     | 
    
         
            -
                  calculate(:minimum, column_name, options)
         
     | 
| 
      
 56 
     | 
    
         
            +
                def minimum(column_name)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  calculate(:minimum, column_name)
         
     | 
| 
       70 
58 
     | 
    
         
             
                end
         
     | 
| 
       71 
59 
     | 
    
         | 
| 
       72 
60 
     | 
    
         
             
                # Calculates the maximum value on a given column. The value is returned
         
     | 
| 
       73 
61 
     | 
    
         
             
                # with the same data type of the column, or +nil+ if there's no row. See
         
     | 
| 
       74 
     | 
    
         
            -
                #  
     | 
| 
      
 62 
     | 
    
         
            +
                # #calculate for examples with options.
         
     | 
| 
       75 
63 
     | 
    
         
             
                #
         
     | 
| 
       76 
64 
     | 
    
         
             
                #   Person.maximum(:age) # => 93
         
     | 
| 
       77 
     | 
    
         
            -
                def maximum(column_name 
     | 
| 
       78 
     | 
    
         
            -
                   
     | 
| 
       79 
     | 
    
         
            -
                  # activerecord-deprecated_finders.
         
     | 
| 
       80 
     | 
    
         
            -
                  calculate(:maximum, column_name, options)
         
     | 
| 
      
 65 
     | 
    
         
            +
                def maximum(column_name)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  calculate(:maximum, column_name)
         
     | 
| 
       81 
67 
     | 
    
         
             
                end
         
     | 
| 
       82 
68 
     | 
    
         | 
| 
       83 
69 
     | 
    
         
             
                # 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 
     | 
    
         
            -
                #  
     | 
| 
      
 70 
     | 
    
         
            +
                # with the same data type of the column, +0+ if there's no row. See
         
     | 
| 
      
 71 
     | 
    
         
            +
                # #calculate for examples with options.
         
     | 
| 
       86 
72 
     | 
    
         
             
                #
         
     | 
| 
       87 
73 
     | 
    
         
             
                #   Person.sum(:age) # => 4562
         
     | 
| 
       88 
     | 
    
         
            -
                def sum( 
     | 
| 
       89 
     | 
    
         
            -
                   
     | 
| 
      
 74 
     | 
    
         
            +
                def sum(column_name = nil, &block)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  return super(&block) if block_given?
         
     | 
| 
      
 76 
     | 
    
         
            +
                  calculate(:sum, column_name)
         
     | 
| 
       90 
77 
     | 
    
         
             
                end
         
     | 
| 
       91 
78 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
                # This calculates aggregate values in the given column. Methods for count, sum, average,
         
     | 
| 
       93 
     | 
    
         
            -
                # minimum, and maximum have been added as shortcuts.
         
     | 
| 
      
 79 
     | 
    
         
            +
                # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
         
     | 
| 
      
 80 
     | 
    
         
            +
                # #minimum, and #maximum have been added as shortcuts.
         
     | 
| 
       94 
81 
     | 
    
         
             
                #
         
     | 
| 
       95 
     | 
    
         
            -
                #  
     | 
| 
      
 82 
     | 
    
         
            +
                #   Person.calculate(:count, :all) # The same as Person.count
         
     | 
| 
      
 83 
     | 
    
         
            +
                #   Person.average(:age) # SELECT AVG(age) FROM people...
         
     | 
| 
       96 
84 
     | 
    
         
             
                #
         
     | 
| 
       97 
     | 
    
         
            -
                #    
     | 
| 
       98 
     | 
    
         
            -
                # 
     | 
| 
      
 85 
     | 
    
         
            +
                #   # Selects the minimum age for any family without any minors
         
     | 
| 
      
 86 
     | 
    
         
            +
                #   Person.group(:last_name).having("min(age) > 17").minimum(:age)
         
     | 
| 
       99 
87 
     | 
    
         
             
                #
         
     | 
| 
       100 
     | 
    
         
            -
                #   *  
     | 
| 
       101 
     | 
    
         
            -
                #     takes either a column name, or the name of a belongs_to association.
         
     | 
| 
      
 88 
     | 
    
         
            +
                #   Person.sum("2 * age")
         
     | 
| 
       102 
89 
     | 
    
         
             
                #
         
     | 
| 
       103 
     | 
    
         
            -
                # 
     | 
| 
       104 
     | 
    
         
            -
                #       puts values["Drake"]
         
     | 
| 
       105 
     | 
    
         
            -
                #       # => 43
         
     | 
| 
      
 90 
     | 
    
         
            +
                # There are two basic forms of output:
         
     | 
| 
       106 
91 
     | 
    
         
             
                #
         
     | 
| 
       107 
     | 
    
         
            -
                # 
     | 
| 
       108 
     | 
    
         
            -
                # 
     | 
| 
       109 
     | 
    
         
            -
                #       puts values[drake]
         
     | 
| 
       110 
     | 
    
         
            -
                #       # => 43
         
     | 
| 
      
 92 
     | 
    
         
            +
                # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
         
     | 
| 
      
 93 
     | 
    
         
            +
                #   for AVG, and the given column's type for everything else.
         
     | 
| 
       111 
94 
     | 
    
         
             
                #
         
     | 
| 
       112 
     | 
    
         
            -
                # 
     | 
| 
       113 
     | 
    
         
            -
                # 
     | 
| 
       114 
     | 
    
         
            -
                #       end
         
     | 
| 
      
 95 
     | 
    
         
            +
                # * Grouped values: This returns an ordered hash of the values and groups them. It
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   takes either a column name, or the name of a belongs_to association.
         
     | 
| 
       115 
97 
     | 
    
         
             
                #
         
     | 
| 
       116 
     | 
    
         
            -
                # 
     | 
| 
       117 
     | 
    
         
            -
                # 
     | 
| 
      
 98 
     | 
    
         
            +
                #      values = Person.group('last_name').maximum(:age)
         
     | 
| 
      
 99 
     | 
    
         
            +
                #      puts values["Drake"]
         
     | 
| 
      
 100 
     | 
    
         
            +
                #      # => 43
         
     | 
| 
       118 
101 
     | 
    
         
             
                #
         
     | 
| 
       119 
     | 
    
         
            -
                # 
     | 
| 
       120 
     | 
    
         
            -
                # 
     | 
| 
      
 102 
     | 
    
         
            +
                #      drake  = Family.find_by(last_name: 'Drake')
         
     | 
| 
      
 103 
     | 
    
         
            +
                #      values = Person.group(:family).maximum(:age) # Person belongs_to :family
         
     | 
| 
      
 104 
     | 
    
         
            +
                #      puts values[drake]
         
     | 
| 
      
 105 
     | 
    
         
            +
                #      # => 43
         
     | 
| 
       121 
106 
     | 
    
         
             
                #
         
     | 
| 
       122 
     | 
    
         
            -
                # 
     | 
| 
       123 
     | 
    
         
            -
                 
     | 
| 
       124 
     | 
    
         
            -
                   
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
      
 107 
     | 
    
         
            +
                #      values.each do |family, max_age|
         
     | 
| 
      
 108 
     | 
    
         
            +
                #        ...
         
     | 
| 
      
 109 
     | 
    
         
            +
                #      end
         
     | 
| 
      
 110 
     | 
    
         
            +
                def calculate(operation, column_name)
         
     | 
| 
       126 
111 
     | 
    
         
             
                  if column_name.is_a?(Symbol) && attribute_alias?(column_name)
         
     | 
| 
       127 
112 
     | 
    
         
             
                    column_name = attribute_alias(column_name)
         
     | 
| 
       128 
113 
     | 
    
         
             
                  end
         
     | 
| 
       129 
114 
     | 
    
         | 
| 
       130 
115 
     | 
    
         
             
                  if has_include?(column_name)
         
     | 
| 
       131 
     | 
    
         
            -
                     
     | 
| 
      
 116 
     | 
    
         
            +
                    relation = construct_relation_for_association_calculations
         
     | 
| 
      
 117 
     | 
    
         
            +
                    relation = relation.distinct if operation.to_s.downcase == "count"
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                    relation.calculate(operation, column_name)
         
     | 
| 
       132 
120 
     | 
    
         
             
                  else
         
     | 
| 
       133 
     | 
    
         
            -
                    perform_calculation(operation, column_name 
     | 
| 
      
 121 
     | 
    
         
            +
                    perform_calculation(operation, column_name)
         
     | 
| 
       134 
122 
     | 
    
         
             
                  end
         
     | 
| 
       135 
123 
     | 
    
         
             
                end
         
     | 
| 
       136 
124 
     | 
    
         | 
| 
       137 
     | 
    
         
            -
                # Use  
     | 
| 
      
 125 
     | 
    
         
            +
                # Use #pluck as a shortcut to select one or more attributes without
         
     | 
| 
       138 
126 
     | 
    
         
             
                # loading a bunch of records just to grab the attributes you want.
         
     | 
| 
       139 
127 
     | 
    
         
             
                #
         
     | 
| 
       140 
128 
     | 
    
         
             
                #   Person.pluck(:name)
         
     | 
| 
         @@ -143,19 +131,19 @@ module ActiveRecord 
     | 
|
| 
       143 
131 
     | 
    
         
             
                #
         
     | 
| 
       144 
132 
     | 
    
         
             
                #   Person.all.map(&:name)
         
     | 
| 
       145 
133 
     | 
    
         
             
                #
         
     | 
| 
       146 
     | 
    
         
            -
                # Pluck returns an  
     | 
| 
      
 134 
     | 
    
         
            +
                # Pluck returns an Array of attribute values type-casted to match
         
     | 
| 
       147 
135 
     | 
    
         
             
                # the plucked column names, if they can be deduced. Plucking an SQL fragment
         
     | 
| 
       148 
136 
     | 
    
         
             
                # returns String values by default.
         
     | 
| 
       149 
137 
     | 
    
         
             
                #
         
     | 
| 
       150 
     | 
    
         
            -
                #   Person.pluck(: 
     | 
| 
       151 
     | 
    
         
            -
                #   # SELECT people. 
     | 
| 
       152 
     | 
    
         
            -
                #   # => [ 
     | 
| 
      
 138 
     | 
    
         
            +
                #   Person.pluck(:name)
         
     | 
| 
      
 139 
     | 
    
         
            +
                #   # SELECT people.name FROM people
         
     | 
| 
      
 140 
     | 
    
         
            +
                #   # => ['David', 'Jeremy', 'Jose']
         
     | 
| 
       153 
141 
     | 
    
         
             
                #
         
     | 
| 
       154 
142 
     | 
    
         
             
                #   Person.pluck(:id, :name)
         
     | 
| 
       155 
143 
     | 
    
         
             
                #   # SELECT people.id, people.name FROM people
         
     | 
| 
       156 
144 
     | 
    
         
             
                #   # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
         
     | 
| 
       157 
145 
     | 
    
         
             
                #
         
     | 
| 
       158 
     | 
    
         
            -
                #   Person.pluck( 
     | 
| 
      
 146 
     | 
    
         
            +
                #   Person.distinct.pluck(:role)
         
     | 
| 
       159 
147 
     | 
    
         
             
                #   # SELECT DISTINCT role FROM people
         
     | 
| 
       160 
148 
     | 
    
         
             
                #   # => ['admin', 'member', 'guest']
         
     | 
| 
       161 
149 
     | 
    
         
             
                #
         
     | 
| 
         @@ -167,13 +155,11 @@ module ActiveRecord 
     | 
|
| 
       167 
155 
     | 
    
         
             
                #   # SELECT DATEDIFF(updated_at, created_at) FROM people
         
     | 
| 
       168 
156 
     | 
    
         
             
                #   # => ['0', '27761', '173']
         
     | 
| 
       169 
157 
     | 
    
         
             
                #
         
     | 
| 
      
 158 
     | 
    
         
            +
                # See also #ids.
         
     | 
| 
      
 159 
     | 
    
         
            +
                #
         
     | 
| 
       170 
160 
     | 
    
         
             
                def pluck(*column_names)
         
     | 
| 
       171 
     | 
    
         
            -
                  column_names.map 
     | 
| 
       172 
     | 
    
         
            -
                     
     | 
| 
       173 
     | 
    
         
            -
                      attribute_alias(column_name)
         
     | 
| 
       174 
     | 
    
         
            -
                    else
         
     | 
| 
       175 
     | 
    
         
            -
                      column_name.to_s
         
     | 
| 
       176 
     | 
    
         
            -
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
         
     | 
| 
      
 162 
     | 
    
         
            +
                    return records.pluck(*column_names)
         
     | 
| 
       177 
163 
     | 
    
         
             
                  end
         
     | 
| 
       178 
164 
     | 
    
         | 
| 
       179 
165 
     | 
    
         
             
                  if has_include?(column_names.first)
         
     | 
| 
         @@ -181,10 +167,10 @@ module ActiveRecord 
     | 
|
| 
       181 
167 
     | 
    
         
             
                  else
         
     | 
| 
       182 
168 
     | 
    
         
             
                    relation = spawn
         
     | 
| 
       183 
169 
     | 
    
         
             
                    relation.select_values = column_names.map { |cn|
         
     | 
| 
       184 
     | 
    
         
            -
                       
     | 
| 
      
 170 
     | 
    
         
            +
                      @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
         
     | 
| 
       185 
171 
     | 
    
         
             
                    }
         
     | 
| 
       186 
     | 
    
         
            -
                    result = klass.connection.select_all(relation.arel, nil,  
     | 
| 
       187 
     | 
    
         
            -
                    result.cast_values(klass. 
     | 
| 
      
 172 
     | 
    
         
            +
                    result = klass.connection.select_all(relation.arel, nil, bound_attributes)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    result.cast_values(klass.attribute_types)
         
     | 
| 
       188 
174 
     | 
    
         
             
                  end
         
     | 
| 
       189 
175 
     | 
    
         
             
                end
         
     | 
| 
       190 
176 
     | 
    
         | 
| 
         @@ -199,24 +185,18 @@ module ActiveRecord 
     | 
|
| 
       199 
185 
     | 
    
         
             
                private
         
     | 
| 
       200 
186 
     | 
    
         | 
| 
       201 
187 
     | 
    
         
             
                def has_include?(column_name)
         
     | 
| 
       202 
     | 
    
         
            -
                  eager_loading? || (includes_values.present? &&  
     | 
| 
      
 188 
     | 
    
         
            +
                  eager_loading? || (includes_values.present? && column_name && column_name != :all)
         
     | 
| 
       203 
189 
     | 
    
         
             
                end
         
     | 
| 
       204 
190 
     | 
    
         | 
| 
       205 
     | 
    
         
            -
                def perform_calculation(operation, column_name 
     | 
| 
       206 
     | 
    
         
            -
                  # TODO: Remove options argument as soon we remove support to
         
     | 
| 
       207 
     | 
    
         
            -
                  # activerecord-deprecated_finders.
         
     | 
| 
      
 191 
     | 
    
         
            +
                def perform_calculation(operation, column_name)
         
     | 
| 
       208 
192 
     | 
    
         
             
                  operation = operation.to_s.downcase
         
     | 
| 
       209 
193 
     | 
    
         | 
| 
       210 
     | 
    
         
            -
                  # If #count is used with #distinct  
     | 
| 
      
 194 
     | 
    
         
            +
                  # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
         
     | 
| 
      
 195 
     | 
    
         
            +
                  # considered distinct.
         
     | 
| 
       211 
196 
     | 
    
         
             
                  distinct = self.distinct_value
         
     | 
| 
       212 
197 
     | 
    
         | 
| 
       213 
198 
     | 
    
         
             
                  if operation == "count"
         
     | 
| 
       214 
199 
     | 
    
         
             
                    column_name ||= select_for_count
         
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
                    unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
         
     | 
| 
       217 
     | 
    
         
            -
                      distinct = true
         
     | 
| 
       218 
     | 
    
         
            -
                    end
         
     | 
| 
       219 
     | 
    
         
            -
             
     | 
| 
       220 
200 
     | 
    
         
             
                    column_name = primary_key if column_name == :all && distinct
         
     | 
| 
       221 
201 
     | 
    
         
             
                    distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
         
     | 
| 
       222 
202 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -229,6 +209,8 @@ module ActiveRecord 
     | 
|
| 
       229 
209 
     | 
    
         
             
                end
         
     | 
| 
       230 
210 
     | 
    
         | 
| 
       231 
211 
     | 
    
         
             
                def aggregate_column(column_name)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  return column_name if Arel::Expressions === column_name
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
       232 
214 
     | 
    
         
             
                  if @klass.column_names.include?(column_name.to_s)
         
     | 
| 
       233 
215 
     | 
    
         
             
                    Arel::Attribute.new(@klass.unscoped.table, column_name)
         
     | 
| 
       234 
216 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -241,33 +223,33 @@ module ActiveRecord 
     | 
|
| 
       241 
223 
     | 
    
         
             
                end
         
     | 
| 
       242 
224 
     | 
    
         | 
| 
       243 
225 
     | 
    
         
             
                def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
         
     | 
| 
       244 
     | 
    
         
            -
                  #  
     | 
| 
      
 226 
     | 
    
         
            +
                  # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
         
     | 
| 
       245 
227 
     | 
    
         
             
                  relation = unscope(:order)
         
     | 
| 
       246 
228 
     | 
    
         | 
| 
       247 
229 
     | 
    
         
             
                  column_alias = column_name
         
     | 
| 
       248 
230 
     | 
    
         | 
| 
       249 
     | 
    
         
            -
                  bind_values = nil
         
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
231 
     | 
    
         
             
                  if operation == "count" && (relation.limit_value || relation.offset_value)
         
     | 
| 
       252 
232 
     | 
    
         
             
                    # Shortcut when limit is zero.
         
     | 
| 
       253 
233 
     | 
    
         
             
                    return 0 if relation.limit_value == 0
         
     | 
| 
       254 
234 
     | 
    
         | 
| 
       255 
235 
     | 
    
         
             
                    query_builder = build_count_subquery(relation, column_name, distinct)
         
     | 
| 
       256 
     | 
    
         
            -
                    bind_values = query_builder.bind_values + relation.bind_values
         
     | 
| 
       257 
236 
     | 
    
         
             
                  else
         
     | 
| 
       258 
237 
     | 
    
         
             
                    column = aggregate_column(column_name)
         
     | 
| 
       259 
238 
     | 
    
         | 
| 
       260 
239 
     | 
    
         
             
                    select_value = operation_over_aggregate_column(column, operation, distinct)
         
     | 
| 
       261 
240 
     | 
    
         | 
| 
      
 241 
     | 
    
         
            +
                    if operation == "sum" && distinct
         
     | 
| 
      
 242 
     | 
    
         
            +
                      select_value.distinct = true
         
     | 
| 
      
 243 
     | 
    
         
            +
                    end
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
       262 
245 
     | 
    
         
             
                    column_alias = select_value.alias
         
     | 
| 
       263 
246 
     | 
    
         
             
                    column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
         
     | 
| 
       264 
247 
     | 
    
         
             
                    relation.select_values = [select_value]
         
     | 
| 
       265 
248 
     | 
    
         | 
| 
       266 
249 
     | 
    
         
             
                    query_builder = relation.arel
         
     | 
| 
       267 
     | 
    
         
            -
                    bind_values = query_builder.bind_values + relation.bind_values
         
     | 
| 
       268 
250 
     | 
    
         
             
                  end
         
     | 
| 
       269 
251 
     | 
    
         | 
| 
       270 
     | 
    
         
            -
                  result = @klass.connection.select_all(query_builder, nil,  
     | 
| 
      
 252 
     | 
    
         
            +
                  result = @klass.connection.select_all(query_builder, nil, bound_attributes)
         
     | 
| 
       271 
253 
     | 
    
         
             
                  row    = result.first
         
     | 
| 
       272 
254 
     | 
    
         
             
                  value  = row && row.values.first
         
     | 
| 
       273 
255 
     | 
    
         
             
                  column = result.column_types.fetch(column_alias) do
         
     | 
| 
         @@ -289,14 +271,8 @@ module ActiveRecord 
     | 
|
| 
       289 
271 
     | 
    
         
             
                  end
         
     | 
| 
       290 
272 
     | 
    
         
             
                  group_fields = arel_columns(group_fields)
         
     | 
| 
       291 
273 
     | 
    
         | 
| 
       292 
     | 
    
         
            -
                  group_aliases = group_fields.map { |field|
         
     | 
| 
       293 
     | 
    
         
            -
             
     | 
| 
       294 
     | 
    
         
            -
                  }
         
     | 
| 
       295 
     | 
    
         
            -
                  group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
         
     | 
| 
       296 
     | 
    
         
            -
                    [aliaz, field]
         
     | 
| 
       297 
     | 
    
         
            -
                  }
         
     | 
| 
       298 
     | 
    
         
            -
             
     | 
| 
       299 
     | 
    
         
            -
                  group = group_fields
         
     | 
| 
      
 274 
     | 
    
         
            +
                  group_aliases = group_fields.map { |field| column_alias_for(field) }
         
     | 
| 
      
 275 
     | 
    
         
            +
                  group_columns = group_aliases.zip(group_fields)
         
     | 
| 
       300 
276 
     | 
    
         | 
| 
       301 
277 
     | 
    
         
             
                  if operation == 'count' && column_name == :all
         
     | 
| 
       302 
278 
     | 
    
         
             
                    aggregate_alias = 'count_all'
         
     | 
| 
         @@ -310,9 +286,9 @@ module ActiveRecord 
     | 
|
| 
       310 
286 
     | 
    
         
             
                      operation,
         
     | 
| 
       311 
287 
     | 
    
         
             
                      distinct).as(aggregate_alias)
         
     | 
| 
       312 
288 
     | 
    
         
             
                  ]
         
     | 
| 
       313 
     | 
    
         
            -
                  select_values += self.select_values unless  
     | 
| 
      
 289 
     | 
    
         
            +
                  select_values += self.select_values unless having_clause.empty?
         
     | 
| 
       314 
290 
     | 
    
         | 
| 
       315 
     | 
    
         
            -
                  select_values.concat  
     | 
| 
      
 291 
     | 
    
         
            +
                  select_values.concat group_columns.map { |aliaz, field|
         
     | 
| 
       316 
292 
     | 
    
         
             
                    if field.respond_to?(:as)
         
     | 
| 
       317 
293 
     | 
    
         
             
                      field.as(aliaz)
         
     | 
| 
       318 
294 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -321,21 +297,23 @@ module ActiveRecord 
     | 
|
| 
       321 
297 
     | 
    
         
             
                  }
         
     | 
| 
       322 
298 
     | 
    
         | 
| 
       323 
299 
     | 
    
         
             
                  relation = except(:group)
         
     | 
| 
       324 
     | 
    
         
            -
                  relation.group_values  =  
     | 
| 
      
 300 
     | 
    
         
            +
                  relation.group_values  = group_fields
         
     | 
| 
       325 
301 
     | 
    
         
             
                  relation.select_values = select_values
         
     | 
| 
       326 
302 
     | 
    
         | 
| 
       327 
     | 
    
         
            -
                  calculated_data = @klass.connection.select_all(relation, nil, relation. 
     | 
| 
      
 303 
     | 
    
         
            +
                  calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
         
     | 
| 
       328 
304 
     | 
    
         | 
| 
       329 
305 
     | 
    
         
             
                  if association
         
     | 
| 
       330 
306 
     | 
    
         
             
                    key_ids     = calculated_data.collect { |row| row[group_aliases.first] }
         
     | 
| 
       331 
     | 
    
         
            -
                    key_records = association.klass.base_class. 
     | 
| 
      
 307 
     | 
    
         
            +
                    key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
         
     | 
| 
       332 
308 
     | 
    
         
             
                    key_records = Hash[key_records.map { |r| [r.id, r] }]
         
     | 
| 
       333 
309 
     | 
    
         
             
                  end
         
     | 
| 
       334 
310 
     | 
    
         | 
| 
       335 
311 
     | 
    
         
             
                  Hash[calculated_data.map do |row|
         
     | 
| 
       336 
312 
     | 
    
         
             
                    key = group_columns.map { |aliaz, col_name|
         
     | 
| 
       337 
     | 
    
         
            -
                      column =  
     | 
| 
       338 
     | 
    
         
            -
                         
     | 
| 
      
 313 
     | 
    
         
            +
                      column = type_for(col_name) do
         
     | 
| 
      
 314 
     | 
    
         
            +
                        calculated_data.column_types.fetch(aliaz) do
         
     | 
| 
      
 315 
     | 
    
         
            +
                          Type::Value.new
         
     | 
| 
      
 316 
     | 
    
         
            +
                        end
         
     | 
| 
       339 
317 
     | 
    
         
             
                      end
         
     | 
| 
       340 
318 
     | 
    
         
             
                      type_cast_calculated_value(row[aliaz], column)
         
     | 
| 
       341 
319 
     | 
    
         
             
                    }
         
     | 
| 
         @@ -354,7 +332,6 @@ module ActiveRecord 
     | 
|
| 
       354 
332 
     | 
    
         
             
                #   column_alias_for("sum(id)")                  # => "sum_id"
         
     | 
| 
       355 
333 
     | 
    
         
             
                #   column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
         
     | 
| 
       356 
334 
     | 
    
         
             
                #   column_alias_for("count(*)")                 # => "count_all"
         
     | 
| 
       357 
     | 
    
         
            -
                #   column_alias_for("count", "id")              # => "count_id"
         
     | 
| 
       358 
335 
     | 
    
         
             
                def column_alias_for(keys)
         
     | 
| 
       359 
336 
     | 
    
         
             
                  if keys.respond_to? :name
         
     | 
| 
       360 
337 
     | 
    
         
             
                    keys = "#{keys.relation.name}.#{keys.name}"
         
     | 
| 
         @@ -369,23 +346,23 @@ module ActiveRecord 
     | 
|
| 
       369 
346 
     | 
    
         
             
                  @klass.connection.table_alias_for(table_name)
         
     | 
| 
       370 
347 
     | 
    
         
             
                end
         
     | 
| 
       371 
348 
     | 
    
         | 
| 
       372 
     | 
    
         
            -
                def type_for(field)
         
     | 
| 
      
 349 
     | 
    
         
            +
                def type_for(field, &block)
         
     | 
| 
       373 
350 
     | 
    
         
             
                  field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
         
     | 
| 
       374 
     | 
    
         
            -
                  @klass.type_for_attribute(field_name)
         
     | 
| 
      
 351 
     | 
    
         
            +
                  @klass.type_for_attribute(field_name, &block)
         
     | 
| 
       375 
352 
     | 
    
         
             
                end
         
     | 
| 
       376 
353 
     | 
    
         | 
| 
       377 
354 
     | 
    
         
             
                def type_cast_calculated_value(value, type, operation = nil)
         
     | 
| 
       378 
355 
     | 
    
         
             
                  case operation
         
     | 
| 
       379 
356 
     | 
    
         
             
                    when 'count'   then value.to_i
         
     | 
| 
       380 
     | 
    
         
            -
                    when 'sum'     then type. 
     | 
| 
      
 357 
     | 
    
         
            +
                    when 'sum'     then type.deserialize(value || 0)
         
     | 
| 
       381 
358 
     | 
    
         
             
                    when 'average' then value.respond_to?(:to_d) ? value.to_d : value
         
     | 
| 
       382 
     | 
    
         
            -
                    else type. 
     | 
| 
      
 359 
     | 
    
         
            +
                    else type.deserialize(value)
         
     | 
| 
       383 
360 
     | 
    
         
             
                  end
         
     | 
| 
       384 
361 
     | 
    
         
             
                end
         
     | 
| 
       385 
362 
     | 
    
         | 
| 
       386 
     | 
    
         
            -
                # TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
         
     | 
| 
       387 
363 
     | 
    
         
             
                def select_for_count
         
     | 
| 
       388 
364 
     | 
    
         
             
                  if select_values.present?
         
     | 
| 
      
 365 
     | 
    
         
            +
                    return select_values.first if select_values.one?
         
     | 
| 
       389 
366 
     | 
    
         
             
                    select_values.join(", ")
         
     | 
| 
       390 
367 
     | 
    
         
             
                  else
         
     | 
| 
       391 
368 
     | 
    
         
             
                    :all
         
     | 
| 
         @@ -398,11 +375,9 @@ module ActiveRecord 
     | 
|
| 
       398 
375 
     | 
    
         | 
| 
       399 
376 
     | 
    
         
             
                  aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
         
     | 
| 
       400 
377 
     | 
    
         
             
                  relation.select_values = [aliased_column]
         
     | 
| 
       401 
     | 
    
         
            -
                   
     | 
| 
       402 
     | 
    
         
            -
                  subquery = arel.as(subquery_alias)
         
     | 
| 
      
 378 
     | 
    
         
            +
                  subquery = relation.arel.as(subquery_alias)
         
     | 
| 
       403 
379 
     | 
    
         | 
| 
       404 
380 
     | 
    
         
             
                  sm = Arel::SelectManager.new relation.engine
         
     | 
| 
       405 
     | 
    
         
            -
                  sm.bind_values = arel.bind_values
         
     | 
| 
       406 
381 
     | 
    
         
             
                  select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
         
     | 
| 
       407 
382 
     | 
    
         
             
                  sm.project(select_value).from(subquery)
         
     | 
| 
       408 
383 
     | 
    
         
             
                end
         
     |