I'm pretty sure I'm missing something here but I'm pulling my hair out over this. I've got the following schema's setup:
schema "accounts_providers" do
field :provider, :string, null: false
field :uid, :string, null: false
field :auth_token, Persistence.Encrypted.Binary, null: false
belongs_to :user, MyApp.Accounts.User
timestamps()
end
schema "accounts_users" do
field :name, :string
field :username, :string
field :nickname, :string
field :email, :string
field :avatar_url, :string
has_many :providers, Polydoc.Accounts.Provider, on_delete: :delete_all
timestamps()
end
schema "repositories_repositories" do
field :name, :string
field :description, :string
field :fork, :boolean
field :private, :boolean
field :write_permission, :boolean
field :uid, :string
field :owner_name, :string
belongs_to :provider, Polydoc.Accounts.Provider
end
I'm attempting to insert a record into the accounts_providers
table but running into a constraint error, but the error is referencing a constraint that exists in the repositories_repositories
table which I don't think I am touching
[debug] QUERY ERROR db=32.6ms queue=9.5ms
INSERT INTO "accounts_providers" ("auth_token","provider","uid","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6) ON CONFLICT ("uid","provider") DO UPDATE SET "id" = EXCLUDED."id","provider" = EXCLUDED."provider","uid" = EXCLUDED."uid","auth_token" = EXCLUDED."auth_token","user_id" = EXCLUDED."user_id","inserted_at" = EXCLUDED."inserted_at","updated_at" = EXCLUDED."updated_at" RETURNING "id" [<<1, 10, 65, 69, 83, 46, 71, 67, 77, 46, 86, 49, 217, 158, 158, 208, 9, 91, 16, 111, 110, 135, 12, 82, 201, 8, 126, 181, 141, 227, 56, 145, 148, 2, 217, 50, 202, 36, 4, 85, 228, 160, 42, 249, 38, 24, 135, 59, 235, ...>>, "github", "1657075", 1, ~N[2019-02-03 09:42:39], ~N[2019-02-03 09:42:39]]
[info] Sent 500 in 1980ms
[error] #PID<0.586.0> running PolydocWeb.Endpoint (cowboy_protocol) terminated
Server: app.lvh.me:4000 (http)
Request: GET /auth/github/callback?code=5bf8f2761b6d3892054f
** (exit) an exception was raised:
** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* repositories_repositories_provider_id_fkey (foreign_key_constraint)
If you would like to stop this constraint violation from raising an
exception and instead add it as an error to your changeset, please
call `foreign_key_constraint/3` on your changeset with the constraint
`:name` as an option.
The changeset defined the following constraints:
* accounts_providers_user_id_fkey (foreign_key_constraint)
* accounts_providers_uid_provider_index (unique_constraint)
This is triggered when I try and run an upsert query:
provider = Provider.oauth_changeset(%Provider{}, attrs)
Repo.insert(provider, on_conflict: :replace_all, conflict_target: [:uid, :provider])
Where provider
is
#Ecto.Changeset<
action: nil,
changes: %{
auth_token: "5ab9b61181eb3bc8221310eb4121a861ebb5b0a8",
provider: "github",
uid: "1657075",
user_id: 1
},
errors: [],
data: #Polydoc.Accounts.Provider<>,
valid?: true
>
Any clues why I'm hitting this error even though the constraint is on another table? I know I don't have the foreign_key_constraint
in my changeset but this to me doesn't explain the error coming from the wrong table
Edit
Here's the changeset function for the Provider
schema
def oauth_changeset(%__MODULE__{} = provider, attrs) do
provider
|> cast(attrs, [:provider, :uid, :auth_token, :user_id])
|> validate_required([:provider, :uid, :auth_token])
|> unique_constraint(:provider_uid, name: :accounts_providers_uid_provider_index)
|> foreign_key_constraint(:user_id)
end
Ok figured it out - it's related to the way Ecto handles the on_conflict
option of the insert/2
function. If set to :replace_all
as mine was, it literally replaces everything which would appear to include the primary key which in turn broke my foreign key of the other table by making the existing records' id invalid.
To fix, I've changed the insert statement to include specify which fields to replace instead:
Repo.insert(provider,
on_conflict: { :replace, [:auth_token, :updated_at]},
conflict_target: [:uid, :provider])