@@ -4,6 +4,79 @@ module Calculations
44
55 private
66
7+ def execute_grouped_calculation ( operation , column_name , distinct ) #:nodoc:
8+ group_attrs = group_values
9+
10+ if group_attrs . first . respond_to? ( :to_sym )
11+ association = @klass . _reflect_on_association ( group_attrs . first )
12+ associated = group_attrs . size == 1 && association && association . belongs_to? # only count belongs_to associations
13+ group_fields = Array ( associated ? association . foreign_key : group_attrs )
14+ else
15+ group_fields = group_attrs
16+ end
17+ group_fields = arel_columns ( group_fields )
18+
19+ group_aliases = group_fields . map { |field |
20+ column_alias_for ( field )
21+ }
22+ group_columns = group_aliases . zip ( group_fields ) . map { |aliaz , field |
23+ [ aliaz , field ]
24+ }
25+
26+ group = group_fields
27+
28+ if operation == 'count' && column_name == :all
29+ aggregate_alias = 'count_all'
30+ else
31+ aggregate_alias = column_alias_for ( [ operation , column_name ] . join ( ' ' ) )
32+ end
33+
34+ select_values = [
35+ operation_over_aggregate_column (
36+ aggregate_column ( column_name ) ,
37+ operation ,
38+ distinct ) . as ( aggregate_alias )
39+ ]
40+ select_values += select_values unless having_values . empty?
41+
42+ select_values . concat group_fields . zip ( group_aliases ) . map { |field , aliaz |
43+ if field . respond_to? ( :as )
44+ field . as ( aliaz )
45+ else
46+ "#{ field } AS #{ aliaz } "
47+ end
48+ }
49+
50+ relation = except ( :group )
51+ relation . group_values = group
52+ relation . select_values = select_values
53+
54+ # Remove our cast otherwise PSQL will insist that it be included in the GROUP
55+ relation . arel . projections . select! { |p | p . to_s != "cast(\" #{ klass . table_name } \" .\" tableoid\" ::regclass as text)" } if @klass . using_multi_table_inheritance?
56+
57+ calculated_data = @klass . connection . select_all ( relation , nil , relation . arel . bind_values + bind_values )
58+
59+ if association
60+ key_ids = calculated_data . collect { |row | row [ group_aliases . first ] }
61+ key_records = association . klass . base_class . find ( key_ids )
62+ key_records = Hash [ key_records . map { |r | [ r . id , r ] } ]
63+ end
64+
65+ Hash [ calculated_data . map do |row |
66+ key = group_columns . map { |aliaz , col_name |
67+ column = calculated_data . column_types . fetch ( aliaz ) do
68+ type_for ( col_name )
69+ end
70+ type_cast_calculated_value ( row [ aliaz ] , column )
71+ }
72+ key = key . first if key . size == 1
73+ key = key_records [ key ] if associated
74+
75+ column_type = calculated_data . column_types . fetch ( aggregate_alias ) { type_for ( column_name ) }
76+ [ key , type_cast_calculated_value ( row [ aggregate_alias ] , column_type , operation ) ]
77+ end ]
78+ end
79+
780 def execute_simple_calculation ( operation , column_name , distinct ) #:nodoc:
881 # Postgresql doesn't like ORDER BY when there are no GROUP BY
982 relation = unscope ( :order )
@@ -27,9 +100,9 @@ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
27100 column_alias ||= @klass . connection . column_name_for_operation ( operation , select_value )
28101 relation . select_values = [ select_value ]
29102
30- # Only use the last projection (probably the COUNT(*)) all others don't matter
31- # relation.arel.projections = [ relation.arel.projections.last].compact if @klass.using_multi_table_inheritance?
32- relation . arel . projections . shift if @klass . using_multi_table_inheritance?
103+ # Remove our cast otherwise PSQL will insist that it be included in the GROUP
104+ # Somewhere between line 82 and 101 relation.arel.projections gets reset :/
105+ relation . arel . projections . select! { | p | p . to_s != "cast( \" #{ klass . table_name } \" . \" tableoid \" ::regclass as text)" } if @klass . using_multi_table_inheritance?
33106
34107 query_builder = relation . arel
35108 bind_values = query_builder . bind_values + relation . bind_values
0 commit comments