From d5994ee48af14d67f0eec7d23863d4b19211b078 Mon Sep 17 00:00:00 2001 From: John Mileham Date: Thu, 3 Mar 2011 23:26:45 -0500 Subject: Change behavior of count(:limit => x, :offset => y) to limit/offset before counting. --- .../lib/active_record/relation/calculations.rb | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'activerecord/lib/active_record/relation/calculations.rb') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c1842b1a96..7bfeb20a2b 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -183,10 +183,13 @@ module ActiveRecord end end - def aggregate_column(column_name) + def aggregate_column(column_name, subquery_alias = nil) if @klass.column_names.include?(column_name.to_s) - Arel::Attribute.new(@klass.unscoped.table, column_name) + Arel::Attribute.new(subquery_alias || @klass.unscoped.table, column_name) else + if subquery_alias && (split_name = column_name.to_s.split(".")).length > 1 + column_name = split_name.last + end Arel.sql(column_name == :all ? "*" : column_name.to_s) end end @@ -196,24 +199,22 @@ module ActiveRecord end def execute_simple_calculation(operation, column_name, distinct) #:nodoc: - column = aggregate_column(column_name) - # Postgresql doesn't like ORDER BY when there are no GROUP BY relation = except(:order) - select_value = operation_over_aggregate_column(column, operation, distinct) - relation.select_values = [select_value] + if operation == "count" && (relation.limit_value || relation.offset_value) + # Shortcut when limit is zero. + return 0 if relation.limit_value == 0 - query_builder = relation.arel + query_builder = build_count_subquery(relation, column_name, distinct) + else + column = aggregate_column(column_name) - if operation == "count" - limit = relation.limit_value - offset = relation.offset_value + select_value = operation_over_aggregate_column(column, operation, distinct) - unless limit && offset - query_builder.limit = nil - query_builder.offset = nil - end + relation.select_values = [select_value] + + query_builder = relation.arel end type_cast_calculated_value(@klass.connection.select_value(query_builder.to_sql), column_for(column_name), operation) @@ -312,5 +313,16 @@ module ActiveRecord select if select !~ /(,|\*)/ end end + + def build_count_subquery(relation, column_name, distinct) + # Arel doesn't do subqueries + subquery_alias = arel_table.alias("subquery_for_count") + aliased_column = aggregate_column(column_name, subquery_alias) + select_value = operation_over_aggregate_column(aliased_column, 'count', distinct) + + relation.select_values = [(column_name == :all ? 1 : aggregate_column(column_name))] + subquery_sql = "(#{relation.arel.to_sql}) #{subquery_alias.name}" + subquery_alias.relation.select_manager.project(select_value).from(subquery_sql) + end end end -- cgit v1.2.3 From 28c73f012328c8386acfc608f0dfb1a459dbf170 Mon Sep 17 00:00:00 2001 From: John Mileham Date: Thu, 24 Mar 2011 15:09:24 -0400 Subject: Use Arel to build subquery. Adapt tests to changed fixtures. --- .../lib/active_record/relation/calculations.rb | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record/relation/calculations.rb') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 7bfeb20a2b..869eebfa34 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -183,13 +183,10 @@ module ActiveRecord end end - def aggregate_column(column_name, subquery_alias = nil) + def aggregate_column(column_name) if @klass.column_names.include?(column_name.to_s) - Arel::Attribute.new(subquery_alias || @klass.unscoped.table, column_name) + Arel::Attribute.new(@klass.unscoped.table, column_name) else - if subquery_alias && (split_name = column_name.to_s.split(".")).length > 1 - column_name = split_name.last - end Arel.sql(column_name == :all ? "*" : column_name.to_s) end end @@ -315,14 +312,16 @@ module ActiveRecord end def build_count_subquery(relation, column_name, distinct) - # Arel doesn't do subqueries - subquery_alias = arel_table.alias("subquery_for_count") - aliased_column = aggregate_column(column_name, subquery_alias) - select_value = operation_over_aggregate_column(aliased_column, 'count', distinct) - - relation.select_values = [(column_name == :all ? 1 : aggregate_column(column_name))] - subquery_sql = "(#{relation.arel.to_sql}) #{subquery_alias.name}" - subquery_alias.relation.select_manager.project(select_value).from(subquery_sql) + column_alias = Arel.sql('count_column') + subquery_alias = Arel.sql('subquery_for_count') + + aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias) + relation.select_values = [aliased_column] + subquery = relation.arel.as(subquery_alias) + + sm = Arel::SelectManager.new relation.engine + select_value = operation_over_aggregate_column(column_alias, 'count', distinct) + sm.project(select_value).from(subquery) end end end -- cgit v1.2.3