Skip to content

Ensure custom PK types are casted in through reflection queries#36847

Merged
gmcgibbon merged 1 commit intorails:masterfrom
gmcgibbon:fix_custom_pk_through_reflect
Aug 7, 2019
Merged

Ensure custom PK types are casted in through reflection queries#36847
gmcgibbon merged 1 commit intorails:masterfrom
gmcgibbon:fix_custom_pk_through_reflect

Conversation

@gmcgibbon
Copy link
Member

@gmcgibbon gmcgibbon commented Aug 2, 2019

Summary

Make sure primary keys with non-integer types (eg. binary) can be correctly type casted in queries using through reflections.

Fixes #35839.

I deferred this check to happen when the through reflection computes its class and memoizes the result. Still, I'm curious if this one time runtime check is too expensive. Thoughts?

@gmcgibbon gmcgibbon force-pushed the fix_custom_pk_through_reflect branch from 07f918e to 530e382 Compare August 7, 2019 20:24
Make sure primary keys with non-integer types can be correctly type
casted in through reflections.
@gmcgibbon gmcgibbon force-pushed the fix_custom_pk_through_reflect branch from 530e382 to 5ec2f35 Compare August 7, 2019 20:27
@gmcgibbon gmcgibbon merged commit dacfa5b into rails:master Aug 7, 2019
@gmcgibbon gmcgibbon deleted the fix_custom_pk_through_reflect branch August 7, 2019 20:48
@ekampp
Copy link
Contributor

ekampp commented Aug 16, 2019

This breaks ActiveStorage associations for UUID primary keys. It also breaks has_many with the through option with UUID primary keys.

This correctly worked in 5.2.3, and all of the intermediate classes correctly define their associations. So this seems like a regression.

@gmcgibbon
Copy link
Member Author

@ekampp Can you open an issue and reproduce the problem in the bug report template? Thanks!

@ekampp
Copy link
Contributor

ekampp commented Aug 17, 2019

I guess I can, but wouldn't it be better to have some more thorough tests rather than everybody having to open individual bug reports on every ID type?

@ekampp
Copy link
Contributor

ekampp commented Aug 17, 2019

I see this in the code for this PR: active_record.type_for_attribute(active_record.primary_key).type != :integer

This will always trigger the NotImplementedError for anything other than integers, right?

@gmcgibbon
Copy link
Member Author

I guess I can, but wouldn't it be better to have some more thorough tests rather than everybody having to open individual bug reports on every ID type?

That isn't really the point. I want to see why this change is causing you trouble first before we try and fix anything. Also, we can only guess at how to solve a problem without reproduction steps.

This will always trigger the NotImplementedError for anything other than integers, right?

If you don't have the correct associations in place to properly type cast IDs, yes. Please read the issue this PR fixes for context.

@ekampp
Copy link
Contributor

ekampp commented Aug 19, 2019

If you don't have the correct associations in place to properly type cast IDs, yes.

I don't actually see any tests in this PR for any PK types other than integers? So how are you even sure that your PR correctly handles anything other than Integers?

This is a fairly complex guard for you to be that confident that you're not accidentally breaking non-integer PK types.

@ekampp
Copy link
Contributor

ekampp commented Aug 19, 2019

In any case. Here is the demo project that illustrates the problem.

@ekampp
Copy link
Contributor

ekampp commented Aug 19, 2019

The error message I'm getting is:

In order to correctly type cast [sic] Structure.id, Account needs to define a :project association.

But the account doesn't have a single project association and nor should it have a single one. It has multiple project associations which is entirely correct and by design. So the error message seems to also be incorrect.

@ryenski
Copy link

ryenski commented Mar 13, 2020

This is still a problem with has_many :through when using UUIDs for primary keys:

# Table name: documents
#
#  id          :uuid             not null, primary key
class Document < ApplicationRecord
  has_many :sections
  has_many :paragraphs, through: :sections
end

# Table name: sections
#
#  id                      :uuid             not null, primary key
#  document_id             :uuid
class Section < ApplicationRecord
  belongs_to :document
  has_many :paragraphs
end

# Table name: paragraphs
#  id            :uuid             not null, primary key
#  section_id    :uuid             not null
class Paragraph < ApplicationRecord
  belongs_to :section
end
irb(main)> document.paragraphs
Traceback (most recent call last):
        1: from (irb):5
NotImplementedError (In order to correctly type cast Document.id, Payment needs to define a :sections association.)

@mattes
Copy link

mattes commented Mar 13, 2020

Not just :uuids, custom ::ActiveRecord::Type's don't work either. See https://stackoverflow.com/questions/60242514/rails-custom-activerecordtype-fails-when-using-class-name-in-has-many-throu

@jeremy
Copy link
Member

jeremy commented Mar 13, 2020

Updated #38340 to reflect that @crispinheneise and @mattes. Thank you.

@ryenski
Copy link

ryenski commented Mar 13, 2020

I added a test script to reproduce this error in #38340 (comment)

@klass ||= delegate_reflection.compute_class(class_name).tap do |klass|
if !parent_reflection.is_a?(HasAndBelongsToManyReflection) &&
!klass.reflections.include?(options[:through].to_s) &&
active_record.type_for_attribute(active_record.primary_key).type != :integer
Copy link
Contributor

@josh-m-sharpe josh-m-sharpe Jun 13, 2020

Choose a reason for hiding this comment

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

re: #38340 I updated this line to be:

![:integer, :uuid].include?(active_record.type_for_attribute(active_record.primary_key).type)

This "Works For Me" in the sense that it allows my hacking around with the new ActiveStorage proxy feature to continue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"has_many through" association doesn't behave consistently when using a custom-typed PK column

7 participants