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 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.
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.
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).
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.
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.
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’
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.
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.
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.
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.
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?