Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix merging of WITH expressions when the expressions are identical #3

Merged
merged 1 commit into from
Jun 25, 2021

Conversation

dark-panda
Copy link
Contributor

No description provided.

Copy link
Owner

@vlado vlado left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dark-panda thanks for the changes. Looks like a step in the right direction 👍

I've left few comments. Let me know what you think.

merged = most_popular1.merge(most_popular2)

assert_equal merged.size, 4
assert_equal merged[0].views_count, 456
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails for Postgres adapter. Try running rake test:matrix (I need to setup CI or Github Actions) and you will see the error. Since Postgres does not guarantee ordering by default first item is not the one with 456 views.

Maybe you can define popular_posts = Post.where("views_count >= 100") and merged = most_popular1.merge(most_popular2).from("most_popular as posts") and then test that they return equal results assert_equal most_popular.to_a, merged.to_a.

How does that sound to you?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed that arguments for assert_equal are wrong which produce wrong error message :)
It should be assert_equal expected, actual. Missed that on last PR for test cases above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I noticed that but I didn’t want to change that in this PR. I figured I’d mention that later on and it can be fixed across the board.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll rejigger the tests to make them more consistent, I hadn’t noticed the test matrix and apparently only ran the tests on SQLite.


def test_with_when_merging_relations_with_identical_with_names_and_identical_quries
most_popular1 = Post.with(most_popular: Post.where("views_count >= 100"))
most_popular2 = Post.with(most_popular: Post.where("views_count >= 100"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried what happens if different with values are provided that result with the same sql in the end?

Something like:

most_popular1 = Post.with(most_popular: Post.where("views_count = 100"))
most_popular2 = Post.with(most_popular: Post.where(views_count: 100))

or

most_popular1 = Post.with(most_popular: Post.where("views_count > 100"))
most_popular2 = Post.with(most_popular: Arel::Table.new(:posts)..project(Arel.star).where(posts_table[:views_count].gt(100))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These still fail since the equality check isn't doing any sort of conversion. This would require some more extensive rejiggering I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vlado @joelvh in the case of Rails 6.1 and using strings as #with values, this broke in Rails 6.1 due to this commit:

rails/rails@596469d

There's no detection for strings in there. Before they just slid on through, but now they need to be accounted for.

I have a patch that I'll submit upstream, just wrapping that up now. As for what to do for the gem, a monkey patch would probably be the only recourse, as the code in question is in the guts of the SQL generation visitor methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rails PR is up at rails/rails#42563, with an issue report at rails/rails#42561.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These still fail since the equality check isn't doing any sort of conversion. This would require some more extensive rejiggering I think.

I suggest we skip that then. Let's merge this one and open new PR when/if needed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a patch that I'll submit upstream, just wrapping that up now. As for what to do for the gem, a monkey patch would probably be the only recourse, as the code in question is in the guts of the SQL generation visitor methods.

Great find and great that you submitted PR to the Rails 👍 Let's see when it gets merged and released.

I would even be ok if we just add a note to the Readme that passing string for some Rails versions does not work due to the mentioned bug.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that failed in my tests w/ 6.1 and i ignored it because I'm not using strings 😬

Can we use the SQL literal suggested in your upstream PR internally here?


def test_with_when_merging_relations_with_identical_with_names_and_identical_quries
most_popular1 = Post.with(most_popular: Post.where("views_count >= 100"))
most_popular2 = Post.with(most_popular: Post.where("views_count >= 100"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These still fail since the equality check isn't doing any sort of conversion. This would require some more extensive rejiggering I think.

I suggest we skip that then. Let's merge this one and open new PR when/if needed.


def test_with_when_merging_relations_with_identical_with_names_and_identical_quries
most_popular1 = Post.with(most_popular: Post.where("views_count >= 100"))
most_popular2 = Post.with(most_popular: Post.where("views_count >= 100"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a patch that I'll submit upstream, just wrapping that up now. As for what to do for the gem, a monkey patch would probably be the only recourse, as the code in question is in the guts of the SQL generation visitor methods.

Great find and great that you submitted PR to the Rails 👍 Let's see when it gets merged and released.

I would even be ok if we just add a note to the Readme that passing string for some Rails versions does not work due to the mentioned bug.

Comment on lines 150 to 152
merged = most_popular1.merge(most_popular2).from("most_popular as posts")

assert_equal posts(:two, :three, :four), merged.to_a
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still does not guarantee the order and will fail for some adapters I suppose. Maybe you should add ordering merged = most_popular1.merge(most_popular2).from("most_popular as posts").order(:id) or try something like:

Suggested change
merged = most_popular1.merge(most_popular2).from("most_popular as posts")
assert_equal posts(:two, :three, :four), merged.to_a
expected_posts = Post.where("views_count >= 100")
merged = most_popular1.merge(most_popular2).from("most_popular as posts")
assert expected_posts.any?
assert_equal expected_posts.to_a, merged.to_a

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by doing assert_equal foo.sort, bar.sort, basically.

@@ -6,10 +6,6 @@ module Querying
end

module WithMerger
def normal_values
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dark-panda when I looked at the 6.0.x internals, this made it so Rails automatically did some things. What steps that Rails was doing are not not being performed as a result of not including :with here? Do we need to manually perform those steps?

I believe normal_values was removed in 6.1.x, so we need to consider how this affects both versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

@vlado vlado left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dark-panda I think it looks great now!

@vlado vlado merged commit bc96318 into vlado:master Jun 25, 2021
@vlado
Copy link
Owner

vlado commented Jun 27, 2021

Released in version 0.1.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants