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

Support composite foreign keys via migration helpers #47637

Merged
merged 3 commits into from
Sep 9, 2023

Conversation

fatkodima
Copy link
Member

Closes #47593.

# Assuming "carts" table has "(shop_id, id)" as a primary key.

add_foreign_key(:orders, :carts, primary_key: [:shop_id, :id])
# or
add_foreign_key(:orders, :carts, column: [:cart_shop_id, :cart_id])

remove_foreign_key(:orders, :carts, primary_key: [:shop_id, :id])
foreign_key_exists?(:orders, :carts, primary_key: [:shop_id, :id])

We discussed 3 possible ways of using add_foreign_key.

The first one (add_foreign_key :brochures, :cars) (no :column and :primary_key options) turned out to be complex to implement, as it introduced a lot of kinda ugly changes in many places because we needed to properly generate these column names (consider configured table name prefixes and suffixes, pass a connection around to be able to query a primary key for the to_table. Some classes, like ForeignKeyDefinition does not have access to the connection, and so we needed to calculate these columns at several places differently and pass there).

And the biggest problem is when we need to add a foreign key for a self referencing tables, like

@connection.create_table :testings do |t|
t.references :parent1, foreign_key: { to_table: :testing_parents }
t.references :parent2, foreign_key: { to_table: :testing_parents }
t.references :self_join, foreign_key: { to_table: :testings }
end
That would produce very not elegant code.

So I made that it is required to pass at least one of :column or :primary_key options, as it is easy to infer one from the other.

cc @eileencodes @nvasilevski

@fatkodima fatkodima force-pushed the composite-foreign-keys branch 3 times, most recently from a277972 to ceaf183 Compare March 11, 2023 01:45
Copy link
Member

@eileencodes eileencodes left a comment

Choose a reason for hiding this comment

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

Hey @fatkodima thanks for working on this! I've left some comments.

@@ -1099,6 +1099,17 @@ def foreign_keys(table_name)
#
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
#
# ====== Creating a composite foreign key
#
# # Assuming "carts" table has "(shop_id, id)" as a primary key.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# # Assuming "carts" table has "(shop_id, id)" as a primary key.
# Assuming "carts" table has "(shop_id, id)" as a primary key.

* Support composite foreign keys via migration helpers.

```ruby
# Assuming "carts" table has "(shop_id, id)" as a primary key.
Copy link
Member

Choose a reason for hiding this comment

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

I think we are actually going to discourage using id as one of the ids in the composite key when we write the docs so can we change this to something like (shop_id, oid)?

#
# ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_id") REFERENCES "carts" ("shop_id", "id")
#
# Note: At least one of +column+ or +primary_key+ options must be provided.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not super clear on why we need to support both, can we support just primary_key and simplify the code a bit?

@fatkodima
Copy link
Member Author

@eileencodes Thanks for the review, addressed the feedback.

I initially added support for both :primary_key and :column to be able to specify either one and the other will be inferred. Small convenience. But yeah, changed to require passing a :primary_key manually, if the :column is passed as an array.

@eileencodes eileencodes self-assigned this Sep 7, 2023
@eileencodes eileencodes added this to the 7.1.0 milestone Sep 7, 2023
We only set the column to a single column if the primary key is a single
value, so move that code to an explicit else.
This method is internal so we can just always pass the argument.
@rafaelfranca rafaelfranca merged commit 1559fe1 into rails:main Sep 9, 2023
3 of 4 checks passed
alpaca-tc added a commit to alpaca-tc/ridgepole that referenced this pull request Oct 11, 2023
y-yagi added a commit to y-yagi/ridgepole that referenced this pull request Oct 13, 2023
alpaca-tc added a commit to alpaca-tc/ridgepole that referenced this pull request Oct 14, 2023
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.

add_foreign_key should support composite primary_key
3 participants