Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

PostgreSQL: use a subselect to correctly perform eager finds with :li…

…mit and :order. Closes #4668.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@5887 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit ef4ac31de3f310c6426a6d9ebc86b4810ac6325b 1 parent 3b6555a
Jeremy Kemper jeremy authored
2  activerecord/CHANGELOG
View
@@ -1,5 +1,7 @@
*SVN*
+* PostgreSQL: use a subselect to correctly perform eager finds with :limit and :order. #4668 [eventualbuddha]
+
* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [dcmanges]
Student.find(:all, :conditions => { :grade => 9..12 })
8 activerecord/lib/active_record/associations.rb
View
@@ -1205,7 +1205,13 @@ def construct_finder_sql_for_association_limiting(options, join_dependency)
end
add_conditions!(sql, options[:conditions], scope)
- sql << "ORDER BY #{options[:order]} " if options[:order]
+ if options[:order]
+ if is_distinct
+ connection.add_order_by_for_association_limiting!(sql, options)
+ else
+ sql << "ORDER BY #{options[:order]}"
+ end
+ end
add_limit!(sql, options, scope)
return sanitize_sql(sql)
end
6 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
View
@@ -287,6 +287,12 @@ def add_column_options!(sql, options) #:nodoc:
def distinct(columns, order_by)
"DISTINCT #{columns}"
end
+
+ # ORDER BY clause for the passed order option.
+ # PostgreSQL overrides this due to its stricter standards compliance.
+ def add_order_by_for_association_limiting!(sql, options)
+ sql << "ORDER BY #{options[:order]}"
+ end
end
end
end
20 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
View
@@ -383,17 +383,29 @@ def distinct(columns, order_by)
# construct a clean list of column names from the ORDER BY clause, removing
# any asc/desc modifiers
- order_columns = order_by.split(',').collect! { |s| s.split.first }
+ order_columns = order_by.split(',').collect { |s| s.split.first }
order_columns.delete_if &:blank?
-
- # add the DISTINCT columns to the start of the ORDER BY clause
- order_by.replace "#{columns}, #{order_by}"
+ order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
# return a DISTINCT ON() clause that's distinct on the columns we want but includes
# all the required columns for the ORDER BY to work properly
sql = "DISTINCT ON (#{columns}) #{columns}, "
sql << order_columns * ', '
end
+
+ # ORDER BY clause for the passed order option.
+ #
+ # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
+ # by wrapping the sql as a sub-select and ordering in that query.
+ def add_order_by_for_association_limiting!(sql, options)
+ return sql if options[:order].blank?
+
+ order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
+ order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
+ order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
+
+ sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
+ end
private
BYTEA_COLUMN_TYPE_OID = 17
10 activerecord/test/associations/eager_test.rb
View
@@ -290,6 +290,16 @@ def test_eager_with_invalid_association_reference
def find_all_ordered(className, include=nil)
className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
end
+
+ def test_limited_eager_with_order
+ assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
+ assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
+ end
+
+ def test_limited_eager_with_multiple_order_columns
+ assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
+ assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
+ end
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
Please sign in to comment.
Something went wrong with that request. Please try again.