[Rails-core] In development mode not all types are included in the query related to type

20 views
Skip to first unread message

Neeraj Singh

unread,
May 11, 2010, 11:47:01 PM5/11/10
to Ruby on Rails: Core
class User
end

class Agent < User
end

script/console production
User.find_by_name 'john'

SELECT "people".* FROM "people" WHERE ((("people"."type" = 'User' OR
"people"."type" = 'Manager') OR "people"."type" = 'Agent')) AND
("people"."name" = 'agent') LIMIT 1


In development mode.
script/console
User.find_by_name 'john'
SELECT "people".* FROM "people" WHERE ("people"."type" = 'User') AND
("people"."name" = 'agent') LIMIT 1


I should have mentioned the ticket rather than typing all this much.
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4516-correct-type-conditions-arent-added-when-using-multiple-inheritances-wsti#ticket-4516-3

I am sure this issue must be there for a while. And probably the
consensus is to leave it the way it is and no need to try to fix it in
development mode. Just asking for confirmation.

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To post to this group, send email to rubyonra...@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-co...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.

Colin Law

unread,
May 12, 2010, 4:32:55 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 04:47, Neeraj Singh <neeraj...@gmail.com> wrote:
> class User
> end
>
> class Agent < User
> end
>
> script/console production
> User.find_by_name 'john'
>
> SELECT "people".* FROM "people" WHERE ((("people"."type" = 'User' OR
> "people"."type" = 'Manager') OR "people"."type" = 'Agent')) AND
> ("people"."name" = 'agent') LIMIT 1

That does not make sense, there is no 'john' in the query.

>
>
> In development mode.
> script/console
> User.find_by_name 'john'
> SELECT "people".* FROM "people" WHERE ("people"."type" = 'User') AND
> ("people"."name" = 'agent') LIMIT 1

I am guessing that it is due to the way files are loaded in
development vs production. I think (but may be wrong) that in
production all files are loaded so it knows about the other user
types, whereas in development it only loads files as it needs them so
does not know about the other types. Though why it is testing the
type at all in the query I am not sure. Is User derived from
ActiveRecord base? I wonder whether there is more to the class
declarations than you have shown us.

Colin

Neeraj Singh

unread,
May 12, 2010, 6:42:14 AM5/12/10
to Ruby on Rails: Core
The sql that I posted came out after testing the exact case provided
by the author of the ticket. While composing this email I did not
layout the full hierarchy.

@Colin. It is definitely due to class loading. If in development I
load Agent then query will include Agent too.

script/console
> Agent
> User.find_by_name 'john' # now the query will include agent

My question is this: This is something I am encountering now but it
must have been there since STI was introduced which was years ago. So
should this bug be fixed. For some it might not be a bug given that it
happens only in development mode. Nonetheless it could be confusing if
you are not aware of how it works (as I was initially).

Colin Law

unread,
May 12, 2010, 6:53:21 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 11:42, Neeraj Singh <neeraj...@gmail.com> wrote:
> The sql that I posted came out after testing the exact case provided
> by the author of the ticket. While composing this email I did not
> layout the full hierarchy.
>
> @Colin. It is definitely due to class loading. If in development I
> load Agent then query will include Agent too.
>
> script/console
>> Agent
>> User.find_by_name 'john' # now the query will include agent
>
> My question is this: This is something I am encountering now but it
> must have been there since STI was introduced which was years ago. So
> should this bug be fixed. For some it might not be a bug given that it
> happens only in development mode. Nonetheless it could be confusing if
> you are not aware of how it works (as I was initially).

Since you have deleted all of the original post and my comments the
above now makes no sense. No-one reading this will know what your
problem is without going back to the previous mail, which is something
one should not normally be forced to do.

Colin

Neeraj Singh

unread,
May 12, 2010, 7:56:36 AM5/12/10
to Ruby on Rails: Core
> Since you have deleted all of the original post and my comments the
> above now makes no sense.  No-one reading this will know what your
> problem is without going back to the previous mail, which is something
> one should not normally be forced to do.

Extremely sorry about that. I have created a gist which explains the
problem much more succintly.
http://gist.github.com/398486

Colin Law

unread,
May 12, 2010, 8:33:29 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 12:56, Neeraj Singh <neeraj...@gmail.com> wrote:
>> Since you have deleted all of the original post and my comments the
>> above now makes no sense.  No-one reading this will know what your
>> problem is without going back to the previous mail, which is something
>> one should not normally be forced to do.
>
> Extremely sorry about that. I have created a gist which explains the
> problem much more succintly.
> http://gist.github.com/398486

What are the names of the rb files containing the class definitions?

Colin

Colin Law

unread,
May 12, 2010, 8:34:31 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 13:33, Colin Law <cla...@googlemail.com> wrote:
> On 12 May 2010 12:56, Neeraj Singh <neeraj...@gmail.com> wrote:
>>> Since you have deleted all of the original post and my comments the
>>> above now makes no sense.  No-one reading this will know what your
>>> problem is without going back to the previous mail, which is something
>>> one should not normally be forced to do.
>>
>> Extremely sorry about that. I have created a gist which explains the
>> problem much more succintly.
>> http://gist.github.com/398486
>
> What are the names of the rb files containing the class definitions?

Also what version of rails?

Mislav Marohnić

unread,
May 12, 2010, 8:50:54 AM5/12/10
to rubyonra...@googlegroups.com
Isn't the problem obvious? ActiveRecord cannot list all subclasses under STI until it loads all modes in the app. In development mode it relies on autoloading and never preloads all models.

The local solution: require statements in the end of "user.rb":

  require 'manager'
  require 'agent'

So when User is preloaded, all subclasses will get loaded and be known to ActiveRecord too.

The global solution: ActiveRecord shouldn't rely on obtaining a list of all subclasses. Instead, when you query on a parent model, it shouldn't specify any classes at all. Thus:

  User.all
  # SELECT * FROM users

However, this is not adequate with complex STI schemes where there are grandchildren. If we're querying on a child that has subclasses, we still have to know what they are:

  Manager.all
  # SELECT * from users WHERE users.type = 'Manager'
  #   OR users.type = 'EvilManager' OR users.type = 'GoodManager'

Colin Law

unread,
May 12, 2010, 8:57:22 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 13:50, Mislav Marohnić <mislav....@gmail.com> wrote:
> Isn't the problem obvious? ActiveRecord cannot list all subclasses under STI
> until it loads all modes in the app. In development mode it relies on
> autoloading and never preloads all models.
> The local solution: require statements in the end of "user.rb":
>   require 'manager'
>   require 'agent'
> So when User is preloaded, all subclasses will get loaded and be known to
> ActiveRecord too.
> The global solution: ActiveRecord shouldn't rely on obtaining a list of all
> subclasses. Instead, when you query on a parent model, it shouldn't specify
> any classes at all. Thus:
>   User.all
>   # SELECT * FROM users
> However, this is not adequate with complex STI schemes where there are
> grandchildren. If we're querying on a child that has subclasses, we still
> have to know what they are:
>   Manager.all
>   # SELECT * from users WHERE users.type = 'Manager'
>   #   OR users.type = 'EvilManager' OR users.type = 'GoodManager'

Can you explain why when the OP does
Agent.find_by_name 'agent'
it generates
SELECT "users".* FROM "users" WHERE (("users"."type" = 'Agent' OR
"users"."type" = 'SuperAgent')) AND ("users"."name" = 'agent') LIMIT 1

I would have expected only type Agent to be allowed, it appears to be
returning SuperAgents also. If the user of name 'agent' is actually a
SuperAgent I would not expect Agent.find_by_name to find it. Note
that both Agent and SuperAgent are directly derived from User. Unless
the OP is leading us astray.

Colin

Neeraj Singh

unread,
May 12, 2010, 9:50:02 AM5/12/10
to Ruby on Rails: Core
User.find_by_name 'agent' generates a sql that does not use Agent or
SuperAgent even if they are loaded.

However the behavior of Agent.find_by_name 'agent' is different if
SuperAgent is loaded.

@Colin: SuperAgent should be a subclass of Agent. I have updated the
gist with this info.

@Mislav: Yes the problem is obvious. That's why I am asking if it is
something that needs to be fixed or it is okay the way it is. If it is
something that needs no fixing then ticket #4516 can be marked as
wontfix.



On May 12, 8:57 am, Colin Law <clan...@googlemail.com> wrote:
> For more options, visit this group athttp://groups.google.com/group/rubyonrails-core?hl=en.

Xavier Noria

unread,
May 12, 2010, 9:54:33 AM5/12/10
to rubyonra...@googlegroups.com
In general, if you have grandchildren you need to load them to ensure
they are known. I compute them this way:

http://gist.github.com/274219

The bottom line is that Active Record needs to know the STI hierarchy
that is relevant to any given query. That just does not play nice with
autoloading, it is a trade-off.

Colin Law

unread,
May 12, 2010, 10:12:25 AM5/12/10
to rubyonra...@googlegroups.com
On 12 May 2010 14:50, Neeraj Singh <neeraj...@gmail.com> wrote:
> User.find_by_name 'agent' generates a sql that does not use Agent or
> SuperAgent even if they are loaded.
>
> However the behavior of Agent.find_by_name 'agent' is different if
> SuperAgent is loaded.
>
> @Colin: SuperAgent should be a subclass of Agent. I have updated the
> gist with this info.

So you _were_ leading us astray. It is not surprising that I was
confused by the results you were seeing. I think Mislav's reply
covers it in that case.

Matt Jones

unread,
May 12, 2010, 10:29:48 AM5/12/10
to rubyonra...@googlegroups.com
On Wed, May 12, 2010 at 8:50 AM, Mislav Marohnić
<mislav....@gmail.com> wrote:
> Isn't the problem obvious? ActiveRecord cannot list all subclasses under STI
> until it loads all modes in the app. In development mode it relies on
> autoloading and never preloads all models.
> The local solution: require statements in the end of "user.rb":
>   require 'manager'
>   require 'agent'
> So when User is preloaded, all subclasses will get loaded and be known to
> ActiveRecord too.

I've run across this before too, but I used something like this:

%w(Admin Manager Candidate).each { |c| c.constantize }

As I can never remember which one of require / require_dependency
sometimes jams up (or used to back in the day) the autoloader.

--Matt Jones

Mislav Marohnić

unread,
May 12, 2010, 10:36:08 AM5/12/10
to rubyonra...@googlegroups.com
When you're querying a parent that doesn't have any non-abstract parent (meaning he's the top of the STI hierarchy), then theoretically you really don't have to know the list of descendants. AR could just make a query without conditions. I think it would be a good addition to AR.

Or am I missing some edge case? Are explicit conditions with list of classnames always required for some reason?

Xavier Noria

unread,
May 12, 2010, 10:51:10 AM5/12/10
to rubyonra...@googlegroups.com
On Wed, May 12, 2010 at 4:36 PM, Mislav Marohnić
<mislav....@gmail.com> wrote:

> When you're querying a parent that doesn't have any non-abstract parent
> (meaning he's the top of the STI hierarchy), then theoretically you really
> don't have to know the list of descendants. AR could just make a query
> without conditions.

Exactly, that is why in the last example in Neeraj's gist there's no
conditions on the type column even if you load the subclasses:

http://gist.github.com/398486

I added a couple of comments at the bottom.

Neeraj these are just two techniques that do not match
transparently. Do you see what is going on now?

I think the ticket could be closed, but a warn about
this gotcha could be helpful both in the API and guides. Santiago
Pastorino is about to add it.

Neeraj Singh

unread,
May 12, 2010, 11:21:55 AM5/12/10
to Ruby on Rails: Core
Thanks everyone for the input. I understand what's going on now.

I will mark ticket #4516 as invalid.


On May 12, 10:51 am, Xavier Noria <f...@hashref.com> wrote:
> On Wed, May 12, 2010 at 4:36 PM, Mislav Marohnić
>

matthewr...@gmail.com

unread,
May 13, 2010, 1:33:31 AM5/13/10
to Ruby on Rails: Core

On May 12, 10:29 pm, Matt Jones <al2o...@gmail.com> wrote:

> I've run across this before too, but I used something like this:
>
> %w(Admin Manager Candidate).each { |c| c.constantize }
>
> As I can never remember which one of require / require_dependency
> sometimes jams up (or used to back in the day) the autoloader.

I always use "require_dependency"
I guess it's more explicit that it will be reloaded in the future.
https://gist.github.com/4db76a1dbce03f3e4435

But its really annoying when you forget to require one of these.

I guess a simple approach would be doing the following on load

CtiModel.all(:select => "type", :distinct => true).each{|model|
model.type.constantize}

thereby ensuring that all the types in the db were loaded
(there may be some other subclass missing... but that wouldnt matter)
Reply all
Reply to author
Forward
0 new messages