diff options
author | Michael Koziarski <michael@koziarski.com> | 2006-02-09 09:17:40 +0000 |
---|---|---|
committer | Michael Koziarski <michael@koziarski.com> | 2006-02-09 09:17:40 +0000 |
commit | d49a5fcb4cfe90824337dc3756bae7161cea768b (patch) | |
tree | 48af0b2f4c9301be8e0816e5ac759516862db2ef /activerecord/lib/active_record/associations.rb | |
parent | 803b9a41af46210da842afdba032c34109514942 (diff) | |
download | rails-d49a5fcb4cfe90824337dc3756bae7161cea768b.tar.gz rails-d49a5fcb4cfe90824337dc3756bae7161cea768b.tar.bz2 rails-d49a5fcb4cfe90824337dc3756bae7161cea768b.zip |
* Fix pagination problems when using include
* Introduce Unit Tests for pagination
* Allow count to work with :include by using count distinct.
[Kevin Clark & Jeremy Hopple]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3553 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-x | activerecord/lib/active_record/associations.rb | 73 |
1 files changed, 59 insertions, 14 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index ea442021c5..6b461b5514 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -779,6 +779,11 @@ module ActiveRecord end end end + + def count_with_associations(options = {}) + reflections = reflect_on_included_associations(options[:include]) + return count_by_sql(construct_counter_sql_with_included_associations(options, reflections)) + end def find_with_associations(options = {}) reflections = reflect_on_included_associations(options[:include]) @@ -996,40 +1001,72 @@ module ActiveRecord "#{name} Load Including Associations" ) end + + def construct_counter_sql_with_included_associations(options, reflections) + sql = "SELECT COUNT(DISTINCT #{table_name}.#{primary_key})" + + # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT. + if !Base.connection.supports_count_distinct? + sql = "SELECT COUNT(*) FROM (SELECT DISTINCT #{table_name}.#{primary_key}" + end + + sql << " FROM #{table_name} " + sql << reflections.collect { |reflection| association_join(reflection) }.to_s + sql << "#{options[:joins]} " if options[:joins] + + add_conditions!(sql, options[:conditions]) + add_sti_conditions!(sql, reflections) + add_limited_ids_condition!(sql, options, reflections) if !using_limitable_reflections?(reflections) && options[:limit] + + add_limit!(sql, options) if using_limitable_reflections?(reflections) + + if !Base.connection.supports_count_distinct? + sql << ")" + end + + return sanitize_sql(sql) + end def construct_finder_sql_with_included_associations(options, schema_abbreviations, reflections) sql = "SELECT #{column_aliases(schema_abbreviations)} FROM #{table_name} " sql << reflections.collect { |reflection| association_join(reflection) }.to_s sql << "#{options[:joins]} " if options[:joins] - + add_conditions!(sql, options[:conditions]) add_sti_conditions!(sql, reflections) - add_limited_ids_condition!(sql, options) if !using_limitable_reflections?(reflections) && options[:limit] + add_limited_ids_condition!(sql, options, reflections) if !using_limitable_reflections?(reflections) && options[:limit] sql << "ORDER BY #{options[:order]} " if options[:order] - + add_limit!(sql, options) if using_limitable_reflections?(reflections) - + return sanitize_sql(sql) end - - def add_limited_ids_condition!(sql, options) - unless (id_list = select_limited_ids_list(options)).empty? + + def add_limited_ids_condition!(sql, options, reflections) + unless (id_list = select_limited_ids_list(options, reflections)).empty? sql << "#{condition_word(sql)} #{table_name}.#{primary_key} IN (#{id_list}) " end end - - def select_limited_ids_list(options) + + def select_limited_ids_list(options, reflections) connection.select_values( - construct_finder_sql_for_association_limiting(options), + construct_finder_sql_for_association_limiting(options, reflections), "#{name} Load IDs For Limited Eager Loading" ).collect { |id| connection.quote(id) }.join(", ") end - - def construct_finder_sql_for_association_limiting(options) - raise(ArgumentError, "Limited eager loads and conditions on the eager tables is incompatible") if include_eager_conditions?(options) + + def construct_finder_sql_for_association_limiting(options, reflections) + #sql = "SELECT DISTINCT #{table_name}.#{primary_key} FROM #{table_name} " + sql = "SELECT " + sql << "DISTINCT #{table_name}." if include_eager_conditions?(options) || include_eager_order?(options) + sql << "#{primary_key} FROM #{table_name} " + + if include_eager_conditions?(options) || include_eager_order?(options) + sql << reflections.collect { |reflection| association_join(reflection) }.to_s + sql << "#{options[:joins]} " if options[:joins] + end - sql = "SELECT #{primary_key} FROM #{table_name} " add_conditions!(sql, options[:conditions]) sql << "ORDER BY #{options[:order]} " if options[:order] add_limit!(sql, options) @@ -1044,6 +1081,14 @@ module ActiveRecord condition_table_name != table_name end end + + def include_eager_order?(options) + order = options[:order] + return false unless order + order.scan(/(\w+)\.\w+/).flatten.any? do |order_table_name| + order_table_name != table_name + end + end def using_limitable_reflections?(reflections) reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero? |