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

Refactor query constraints feature set #47378

Merged
merged 1 commit into from Feb 13, 2023

Conversation

eileencodes
Copy link
Member

I worked on this last week and shared with @nvasilevski as well. The main thing I'm doing here is walking the composite keys / query constraints feature back a bit so that we know when we have query constraints vs a primary rather than trying to make primary key into query constraints. Eventually we'll want to remove the branching but that's not required to implement the feature and we can't change the meaning of primary_key in the process (we can do it later as a separate implementation).


Commit message:

This PR refactors the query constraints feature to be more contained and have less implicit behavior. Eventually we'll want primary_key to flow through query constraints and return ["id"] when we have a single column and internally Rails will use an array for all primary_key and foreign_key calls, falling back to query constraints. At the moment however, this is a large change in Rails and one that could break current expected behavior. In order to implement the feature and not break compatibility I think we should walk back the feature a little bit. The changes are:

  • Only return query_constraints_list if query_constraints was set by the model, otherwise return nil.
  • Re-implement primary_key calls where we only have a single ID
  • Update the foreign_key option on associations to use a query_constraints option instead so we don't need to check is_a?(Array).

These changes will ensure that the changes are contained to query_constraints rather than having to make decisions about whether we want to treat primary_key as an array. For now we will call everything query_constraints which makes it easy to see the line when we want an array vs single column. Later we can change the meaning of primary_key if necessary.

elsif query_constraints = record.class.query_constraints_list
query_constraints
else
:id
Copy link
Member Author

Choose a reason for hiding this comment

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

This :id is a reimplementation of the previous code here

association_id = record.public_send(reflection.options[:primary_key] || :id)
.

I am surprised we were hardcoding :id like this and after we merge this I think we should consider getting this from elsewhere. I feel like there's an old bug here since it's not using primary key from the record or reflection.

Copy link
Contributor

Choose a reason for hiding this comment

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

I had the same concern and even tried to switch to reflection.active_record_primary_key, which technically was supposed to be equal to calling record.id but it didn't work because of at least one test failing so I decided not to dig into this to avoid unnecessary breaking things - #47230 (comment)

Though design-wise I still see reflection to be a more appropriate source of the primary key columns instead of a hardcoded :id method call, which in Rails refers to model's @primary_key

order_columns = implicit_order_column.nil? ? query_constraints_list : ([implicit_order_column] | query_constraints_list)
order(*order_columns.map { |column| table[column].asc })
if order_values.empty? && (implicit_order_column || !query_constraints_list.nil? || primary_key)
order(*Array(_order_columns).map { |column| table[column].asc })
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like _order_columns is always an array so wrap is not neccessary

Suggested change
order(*Array(_order_columns).map { |column| table[column].asc })
order(*_order_columns.map { |column| table[column].asc })

Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops, thank you!

This PR refactors the query constraints feature to be more contained and
have less implicit behavior. Eventually we'll want `primary_key` to flow
through query constraints and return `["id"]` when we have a single
column and internally Rails will use an array for all `primary_key` and
`foreign_key` calls, falling back to query constraints. At the moment
however, this is a large change in Rails and one that could break
current expected behavior. In order to implenent the feature and not
break compatibility I think we should walk back the feature a little
bit. The changes are:

* Only return `query_constraints_list` if `query_constraints` was set by
the model, otherwise return nil.
* Re-implement `primary_key` calls where we only have a single ID
* Update the `foreign_key` option on associations to use a
`query_constraints` option instead so we don't need to check
`is_a?(Array)`.

These changes will ensure that the changes are contained to
`query_constraints` rather than having to make decisions about whether
we want to treat `primary_key` as an array. For now we will call
everything `query_constraints` which makes it easy to see the line when
we want an array vs single column. Later we can change the meaning of
`primary_key` if necessary.
Copy link
Contributor

@nvasilevski nvasilevski left a comment

Choose a reason for hiding this comment

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

👍

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.

None yet

2 participants