You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We are big users of CTEs and have been using vlado/activerecord-cte for many years now. While trying to upgrade to Rails 7.1, we came across this situation which previously worked in Vlado's extension (stripped down code follows):
With the extension, this would produce SQL like the following:
WITH tasks AS (
SELECT tasks.*FROM tasks
WHERE active
)
SELECT projects.*FROM projects
JOIN project_resources ONproject_resources.resource_type='Task'ANDproject_resources.project_id=projects.idJOIN tasks ONtasks.id=project_resources.resource_id
With the Rails 7.1 CTE joins, this produces the following:
WITH tasks AS (
SELECT tasks.*FROM tasks
WHERE active
)
SELECT projects.*FROM projects
JOIN tasks ONtasks.project_id=projects.id
Which is an invalid SQL query, as project_id doesn't exist on tasks but rather exists on the project_resources relation which sits in between Task and Project. This only happens when the CTE name is the same as an association. For instance, this does not change the join conditions:
WITH filtered_tasks AS (
SELECT tasks.*FROM tasks
WHERE active
)
SELECT projects.*FROM projects
JOIN project_resources ONproject_resources.resource_type='Task'ANDproject_resources.project_id=projects.idJOINtasks.id=project_resources.resource_id
Where the join conditions are retained as expected. (The filtered_tasks relation isn't joined here, of course, but the point is is that the :tasks join itself is unaffected by the #with call.)
Masking a table name in a CTE is a very useful capability, but it's not really possible if masking the table impacts the joins.
Expected behavior
I would expect that the joins would not be affected if the relation alias in the WITH query is masking an actual relation.
Actual behavior
The join condition should remain unaffected in the case of the CTE alias being the same as a relation name.
System configuration
Rails version: 7.1.3.2
Ruby version: 3.3.0
Potential Fix
After playing with it a bit, the following appears to satisfy all of our unit tests for our CTEs:
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 5d63709c3a..f17ed6b562 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb+++ b/activerecord/lib/active_record/relation/query_methods.rb@@ -1625,7 +1625,7 @@ def build_from
def select_named_joins(join_names, stashed_joins = nil, &block)
cte_joins, associations = join_names.partition do |join_name|
- Symbol === join_name && with_values.any? { _1.key?(join_name) }+ Symbol === join_name && with_values.any? { _1.key?(join_name) } && !reflections[join_name.to_s]
end
cte_joins.each do |cte_name|
This does not appear to break any existing ActiveRecord tests, so if there is interest in using this I can write up a patch that includes tests.
The text was updated successfully, but these errors were encountered:
This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 7-2-stable branch or on main, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.
Steps to reproduce
We are big users of CTEs and have been using vlado/activerecord-cte for many years now. While trying to upgrade to Rails 7.1, we came across this situation which previously worked in Vlado's extension (stripped down code follows):
With the extension, this would produce SQL like the following:
With the Rails 7.1 CTE joins, this produces the following:
Which is an invalid SQL query, as
project_id
doesn't exist ontasks
but rather exists on theproject_resources
relation which sits in betweenTask
andProject
. This only happens when the CTE name is the same as an association. For instance, this does not change the join conditions:Produces
Where the join conditions are retained as expected. (The
filtered_tasks
relation isn't joined here, of course, but the point is is that the:tasks
join itself is unaffected by the#with
call.)Masking a table name in a CTE is a very useful capability, but it's not really possible if masking the table impacts the joins.
Expected behavior
I would expect that the joins would not be affected if the relation alias in the
WITH
query is masking an actual relation.Actual behavior
The join condition should remain unaffected in the case of the CTE alias being the same as a relation name.
System configuration
Rails version: 7.1.3.2
Ruby version: 3.3.0
Potential Fix
After playing with it a bit, the following appears to satisfy all of our unit tests for our CTEs:
This does not appear to break any existing ActiveRecord tests, so if there is interest in using this I can write up a patch that includes tests.
The text was updated successfully, but these errors were encountered: