tools/createuser: report which field collided on insert failure
ON CONFLICT (id) DO NOTHING only catches the id itself, so every other failure (duplicate username/phone/signup_email) was reported as a generic "already exists (or insert failed)" - not useful for telling apart the four distinct causes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2e60301a08
commit
7f0921c82c
1 changed files with 29 additions and 1 deletions
|
|
@ -27,11 +27,14 @@ import (
|
|||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"telesrv/internal/config"
|
||||
|
|
@ -124,7 +127,7 @@ func main() {
|
|||
|
||||
var createdID int64
|
||||
if err := row.Scan(&createdID); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "createuser: id %d already exists (or insert failed): %v\n", *id, err)
|
||||
fmt.Fprintln(os.Stderr, describeInsertFailure(*id, *username, displayPhone, signupEmail, err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +135,31 @@ func main() {
|
|||
createdID, accessHash, *firstName, *lastName, *username, displayPhone, signupEmail)
|
||||
}
|
||||
|
||||
// describeInsertFailure turns the INSERT's failure into a message naming the
|
||||
// actual thing that collided, instead of "id already exists" for every case:
|
||||
// ON CONFLICT (id) DO NOTHING only covers the id itself, so a duplicate
|
||||
// username/phone/signup_email surfaces here as a distinct unique-violation
|
||||
// error (pgx.ErrNoRows only means the id itself was the conflict).
|
||||
func describeInsertFailure(id int64, username, phone, signupEmail string, err error) string {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
|
||||
switch pgErr.ConstraintName {
|
||||
case "users_username_lower_unique_idx":
|
||||
return fmt.Sprintf("createuser: username %q is already taken", username)
|
||||
case "users_phone_unique_idx":
|
||||
return fmt.Sprintf("createuser: phone %q is already in use", phone)
|
||||
case "users_signup_email_lower_unique_idx":
|
||||
return fmt.Sprintf("createuser: email %q is already in use by another account", signupEmail)
|
||||
default:
|
||||
return fmt.Sprintf("createuser: unique constraint %q violated: %v", pgErr.ConstraintName, err)
|
||||
}
|
||||
}
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return fmt.Sprintf("createuser: id %d already exists", id)
|
||||
}
|
||||
return fmt.Sprintf("createuser: insert failed: %v", err)
|
||||
}
|
||||
|
||||
// assignEmailSignupDisplayPhone mirrors internal/app/auth/service.go's method
|
||||
// of the same name: pick a random "888"-prefixed display phone and re-roll on
|
||||
// the astronomically unlikely collision with an existing account's phone.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue