Foreign key reference naming and Ash.Generator
On my resource I have:
Using an invalid region_id in a seed I get this error:
Are FKs combined with seed_generate in Ash.Generator not supposed to work?
The error says that:
Is defined on the changeset, which makes it seem like the foreign key is not applied as it should.
references do
reference :region, name: "FK_dfwoyi1w884tqx68rctmbut4u"
...snip...
references do
reference :region, name: "FK_dfwoyi1w884tqx68rctmbut4u"
...snip...
** (Ash.Error.Unknown)
Unknown Error
* ** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* "FK_dfwoyi1w884tqx68rctmbut4u" (foreign_key_constraint)
** (Ash.Error.Unknown)
Unknown Error
* ** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* "FK_dfwoyi1w884tqx68rctmbut4u" (foreign_key_constraint)
* "outcrop_region_id_fkey" (foreign_key_constraint)
* "outcrop_region_id_fkey" (foreign_key_constraint)
6 Replies
🤔 not sure
I'm pretty sure that goes down the code path that should add foreign key constraints etc.
weird
Currently waiting on an output from an LLM simulating this through project_eval (it is a HUGE resource with like forty validated columns), then we'll see if it works on the Ash.create! codepath or not
I get the same error with Ash.create!()
Solution
regression perhaps? want a failing pr submitted to ash_postgres?
*a failing test PR
yup, fails on ash_postgres too
but just to align expectations here, I would expect a reference with a name: "some name here" to end up as an error with invalid attribute or something
instead of the error saying that I can add foreign_key_constraint/3
from:
Result of creating comment with invalid post_id: {:error,
%Ash.Error.Unknown{
bread_crumbs: ["Error returned from: AshPostgres.Test.Comment.create"],
changeset: "#Changeset<>",
errors: [
%Ash.Error.Unknown.UnknownError{
error: "** (Ecto.ConstraintError) constraint error when attempting to insert struct:\n\n * \"special_name_fkey\" (foreign_key_constraint)\n\nIf you would like to stop this constraint violation from raising an\nexception and instead add it as an error to your changeset, please\ncall `foreign_key_constraint/3` on your changeset with the constraint\n`:name` as an option.\n\nThe changeset defined the following constraints:\n\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_author_id_fkey\" (foreign_key_constraint)\n * \"comments_post_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_pkey\" (unique_constraint)\n",
field: nil,
value: nil,
splode: Ash.Error,
bread_crumbs: ["Error returned from: AshPostgres.Test.Comment.create"],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :unknown
}
]
}}
Result of creating comment with invalid post_id: {:error,
%Ash.Error.Unknown{
bread_crumbs: ["Error returned from: AshPostgres.Test.Comment.create"],
changeset: "#Changeset<>",
errors: [
%Ash.Error.Unknown.UnknownError{
error: "** (Ecto.ConstraintError) constraint error when attempting to insert struct:\n\n * \"special_name_fkey\" (foreign_key_constraint)\n\nIf you would like to stop this constraint violation from raising an\nexception and instead add it as an error to your changeset, please\ncall `foreign_key_constraint/3` on your changeset with the constraint\n`:name` as an option.\n\nThe changeset defined the following constraints:\n\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_author_id_fkey\" (foreign_key_constraint)\n * \"comments_post_id_fkey\" (foreign_key_constraint)\n * \"comments_id_fkey\" (foreign_key_constraint)\n * \"comments_pkey\" (unique_constraint)\n",
field: nil,
value: nil,
splode: Ash.Error,
bread_crumbs: ["Error returned from: AshPostgres.Test.Comment.create"],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :unknown
}
]
}}
test "named reference results in properly applied foreign_key_constraint/3 on the underlying changeset" do
# Create a comment with an invalid post_id
result =
AshPostgres.Test.Comment
|> Ash.Changeset.for_create(:create, %{
title: "Test Comment",
# This post doesn't exist
post_id: Ash.UUID.generate()
})
|> Ash.create()
IO.inspect(result, label: "Result of creating comment with invalid post_id")
end
test "named reference results in properly applied foreign_key_constraint/3 on the underlying changeset" do
# Create a comment with an invalid post_id
result =
AshPostgres.Test.Comment
|> Ash.Changeset.for_create(:create, %{
title: "Test Comment",
# This post doesn't exist
post_id: Ash.UUID.generate()
})
|> Ash.create()
IO.inspect(result, label: "Result of creating comment with invalid post_id")
end
Correct
ok I will submit a failing PR for now, I have to go but I'll set claude code churning on it just for fun
considering how well it went with ash_graphql 😛
crossing fingers 😄
Gotta say, this looks very promising so far:
🤯
in test file @test/references_test.exs there is a test that writes a comment with a random uuid. It tries to insert a
@test/support/resources/comment.ex from the test modules. However the reference name set on post in the comment module is
not used to put a foreign_key_constraint on comment.post for that special name. Analyze the codebase and find out where
foreign key constraints are applied based on the references + references.reference dsl. You can look for code referencing
`references` + `references.references`, probably implemented in spark dsl. Most likely there is also some code defining
foreign_key_constraint. The code to analyze lives in the lib/ folder. Documentation you might need to understand the system
better can be found in the documentation/ folder. Think really hard, then make a plan using the TodoWriter and show me an
oerview in a debugging.md file in the root folder of this project. The expected outcome of the failing test would be an
InvalidAttribute error instead of the ash unknown error it gets now.
in test file @test/references_test.exs there is a test that writes a comment with a random uuid. It tries to insert a
@test/support/resources/comment.ex from the test modules. However the reference name set on post in the comment module is
not used to put a foreign_key_constraint on comment.post for that special name. Analyze the codebase and find out where
foreign key constraints are applied based on the references + references.reference dsl. You can look for code referencing
`references` + `references.references`, probably implemented in spark dsl. Most likely there is also some code defining
foreign_key_constraint. The code to analyze lives in the lib/ folder. Documentation you might need to understand the system
better can be found in the documentation/ folder. Think really hard, then make a plan using the TodoWriter and show me an
oerview in a debugging.md file in the root folder of this project. The expected outcome of the failing test would be an
InvalidAttribute error instead of the ash unknown error it gets now.
✻ Thinking…
Perfect! This is exactly what I was looking for. This code is in add_related_foreign_key_constraints, which checks if there's
a custom reference defined and uses its name if present (line 2641-2642).
However, looking back at the add_my_foreign_key_constraints function (lines 2681-2701), it doesn't check for custom reference
names - it always uses the default pattern.
This is the bug! The add_my_foreign_key_constraints function should be checking for custom reference names just like
add_related_foreign_key_constraints does.
✻ Thinking…
Perfect! This is exactly what I was looking for. This code is in add_related_foreign_key_constraints, which checks if there's
a custom reference defined and uses its name if present (line 2641-2642).
However, looking back at the add_my_foreign_key_constraints function (lines 2681-2701), it doesn't check for custom reference
names - it always uses the default pattern.
This is the bug! The add_my_foreign_key_constraints function should be checking for custom reference names just like
add_related_foreign_key_constraints does.
⏺ Update Todos
⎿ ☒ Implement fix in add_my_foreign_key_constraints to check for custom reference names
☐ Run the test to verify the fix works
☐ Run full test suite to ensure no regressions
⏺ Bash(mix test test/references_test.exs:109 --trace)
⎿ Compiling 67 files (.ex)
Generated ash_postgres app
Running ExUnit with seed: 762839, max_cases: 1
… +36 lines (ctrl+r to expand)
⏺ Great! The fix is working. The test now shows the expected Ash.Error.Invalid with InvalidAttribute error instead of the
Ash.Error.Unknown error. The foreign key constraint with the custom name "special_name_fkey" is now being properly recognized
and handled.
⏺ Update Todos
⎿ ☒ Implement fix in add_my_foreign_key_constraints to check for custom reference names
☒ Run the test to verify the fix works
☐ Run full test suite to ensure no regressions
⏺ Bash(mix test)
⎿ Running…
⏺ Update Todos
⎿ ☒ Implement fix in add_my_foreign_key_constraints to check for custom reference names
☐ Run the test to verify the fix works
☐ Run full test suite to ensure no regressions
⏺ Bash(mix test test/references_test.exs:109 --trace)
⎿ Compiling 67 files (.ex)
Generated ash_postgres app
Running ExUnit with seed: 762839, max_cases: 1
… +36 lines (ctrl+r to expand)
⏺ Great! The fix is working. The test now shows the expected Ash.Error.Invalid with InvalidAttribute error instead of the
Ash.Error.Unknown error. The foreign key constraint with the custom name "special_name_fkey" is now being properly recognized
and handled.
⏺ Update Todos
⎿ ☒ Implement fix in add_my_foreign_key_constraints to check for custom reference names
☒ Run the test to verify the fix works
☐ Run full test suite to ensure no regressions
⏺ Bash(mix test)
⎿ Running…