Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Uniq has_many :through with default_scope order does not work with PostgreSQL #5701

Closed
codez opened this Issue · 20 comments
@codez

Given these models:

class Tracker < ActiveRecord::Base
  has_many :issues
  has_many :categories, :through => :issues, :uniq => true
end

class Issue < ActiveRecord::Base
  belongs_to :tracker
  belongs_to :category
  default_scope order(:number)
end

class Category < ActiveRecord::Base
  has_many :issues
  default_scope order(:name)
end

When calling tracker.categories, on PostgreSQL, this fails with the following error:

ActiveRecord::StatementInvalid: PG::Error: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list

SELECT DISTINCT "categories".* FROM "categories" INNER JOIN "issues" ON "categories"."id" = "issue"."category_id" WHERE "issue"."tracker_id" = 42 ORDER BY number, name
@oscardelben

Not necessarily related, but did you mean has_many :issues in the Category model?

Edit: You also don't show the relationship between Tracker and Comment here (you're calling tracker.comments but we don't know anything about it).

I've created a sample app though, and all calls to relationships ran fine for me (under sqlite3)

@codez

Uhm, sorry, my fault (too much editing of my example...). It should be tracker.categories of course, and has_many :issues.

I think the error is specific to PostgreSQL.

Might be related to #520

@masterkain

@codez, which version of Rails are you using?

@codez

rails 3.2.3, pg 0.13.2 on PostgreSQL 8.4.9

@thephw

Ran into the same issue, currently thinking on what a good solution is. It's not necessarily particular to PostgreSQL, more like a malformed SQL statement. No issue in my deve environment with SQLite, but I'd assume (like a jerk) that is because SQLite isn't following strictly to standards. This article provided a decent rational of why it is required:

http://weblogs.sqlteam.com/jeffs/archive/2007/12/13/select-distinct-order-by-error.aspx

@rubysolo

I can confirm this issue with Rails 3.2.2 and PostgreSQL 9.1.3.

It's a bit janky, but there is a workaround ... simply add select: "{missing column}" to your has_many declaration. In the example above:

class Tracker < ActiveRecord::Base
  has_many :categories, through: :issues, uniq: true, select: "trackers.*, issues.number"
end

This adds the missing order by column to the select statement to keep Postgres happy.

@j0ni

I'm hitting this right now. @patrickwiseman, @codez, did you ever come up with a good solution?

@thephw
@codez

Same here, I stopped using default_scope for ordering :(

@senny senny referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@senny senny referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@senny
Owner

I wrote a test-case to illustrate the issue on master: https://github.com/senny/rails/compare/5701_hmt_uniq_and_default_scope

I think there could be an additional problem on master as I had to prefix the table-name for the order call on the join-model. Otherwise It resulted in:

ActiveRecord::StatementInvalid: PG::Error: ERROR:  column posts.category_id does not exist
LINE 1: ...HERE "categorizations"."author_id" = $1  ORDER BY "posts"."c...
                                                             ^
: SELECT DISTINCT "posts".* FROM "posts" INNER JOIN "categorizations" ON "posts"."id" = "categorizations"."post_id" WHERE "categorizations"."author_id" = $1  ORDER BY "posts"."category_id" ASC, "posts"."title" ASC

/cc @jonleighton

@neerajdotname
Collaborator

Looks like it is fixed in master. This is what I got

$ Tracker.new.categories

"SELECT DISTINCT \"categories\".* FROM \"categories\" INNER JOIN \"issues\" 
ON \"categories\".\"id\" = \"issues\".\"category_id\" 
WHERE \"issues\".\"tracker_id\" = $1  
ORDER BY \"issues\".number ASC, \"categories\".name ASC"
class Tracker < ActiveRecord::Base
  has_many :issues
  has_many :categories, -> { uniq }, :through => :issues
end

class Category < ActiveRecord::Base
  has_many :issues
  default_scope order(:name)
end

class Issue < ActiveRecord::Base
  belongs_to :tracker
  belongs_to :category
  default_scope order(:number)
end

Am I missing something ?

@kulbida

I run into the same issue with rails 2.3.12 and pg 0.13.2. Did someone find a workaround?

@korobkov korobkov referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@thomasguillory thomasguillory referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@thomasguillory thomasguillory referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@laurocaetano
Collaborator

I confirm what @neerajdotname posted before. I've created this gist to reproduce this case and it seems to be fixed.

Update: I updated the gist and this issue seems to be present on master.

@rafaelfranca

The combination of order + distinct is what is causing the issue. The problem here is that PostgreSQL enforce you do add all columns you added in the ORDER BY clause in the SELECT list when you are using any aggregate functions

The way to get the valid SQL is explicitly adding the column in the select list.

I'm closing this one since we can't do anything.

@korobkov

Why do you think we can't do anything?
Why couldn't ActiveRecord just automatically add columns from ORDER BY clause to the SELECT columns list? (Possibly, for Postgresql adapter only)

@senny
Owner

@korobkov this is adding more magic than we want. Active Record can't hide everything from the underlying database. It exposes it's functionality through a convenient API. You still need to be aware of the restrictions / features of your DB.

@korobkov

Well, I agree. This could be especially complicated in case of ordering on associated tables' columns…

@nhoffmann

Removing order would fix the problem here and is also not needed at all. I ran into the same problem, and was expecting something around these lines to work:

has_many :categories, -> { reorder('').uniq }, :through => :issues

But unfortunately it did not remove the order clause. At least not in the association context, using the code from the scope block runs fine in the console. Can anybody shine some light on why that is?

@brendon

Was the fix to this problem in master ever back-ported to 3.2.x? It doesn't appear to have been.

I solved the problem by adding reorder('only the column from the table we're querying') to the end of my call to the :through relationship when using it in the application. It seems a bit hacky, but works fine.

@robin850
Collaborator

@brendon : The 3-2-stable branch is not maintained anymore for bug fixes (only security ones) but thanks for the work-around. :-)

@korobkov korobkov referenced this issue from a commit in korobkov/fremantle
@korobkov korobkov Для основных моделей сортировку можно включить
(в связующих - нельзя из-за rails/rails#5701)
5039839
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.