fix(coderd): prevent stale chat model config writes (#27968)

Chat model updates could merge PATCH fields from a row read before the
serialized transaction. Deletes could also succeed after another request
deleted the row.

Merge updates from the row read after the write lock. Make soft deletes
report missing rows, and validate provider availability and model
compatibility in the same transaction.

This PR also removes `DeleteChatModelConfigsByAIProviderID`. The query
has no production callers, including the AI provider deletion path, and
only remained in generated database wrappers and mocks. Its removal is
intentional dead-code cleanup and does not change provider deletion
behavior.

_This pull request description was generated by Coder Agents._
This commit is contained in:
Ethan
2026-08-24 09:30:00 +00:00
committed by GitHub
parent 4ce8e8ed56
commit cd1362abfd
11 changed files with 476 additions and 244 deletions
+7 -20
View File
@@ -6135,7 +6135,7 @@ func (q *sqlQuerier) InsertChatFile(ctx context.Context, arg InsertChatFileParam
return i, err
}
const deleteChatModelConfigByID = `-- name: DeleteChatModelConfigByID :exec
const deleteChatModelConfigByID = `-- name: DeleteChatModelConfigByID :one
UPDATE
chat_model_configs
SET
@@ -6144,28 +6144,15 @@ SET
updated_at = NOW()
WHERE
id = $1::uuid
`
func (q *sqlQuerier) DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteChatModelConfigByID, id)
return err
}
const deleteChatModelConfigsByAIProviderID = `-- name: DeleteChatModelConfigsByAIProviderID :exec
UPDATE
chat_model_configs
SET
deleted = TRUE,
deleted_at = NOW(),
updated_at = NOW()
WHERE
ai_provider_id = $1::uuid
AND deleted = FALSE
RETURNING id
`
func (q *sqlQuerier) DeleteChatModelConfigsByAIProviderID(ctx context.Context, aiProviderID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteChatModelConfigsByAIProviderID, aiProviderID)
return err
func (q *sqlQuerier) DeleteChatModelConfigByID(ctx context.Context, id uuid.UUID) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, deleteChatModelConfigByID, id)
var id_2 uuid.UUID
err := row.Scan(&id_2)
return id_2, err
}
const getChatModelConfigByID = `-- name: GetChatModelConfigByID :one