package db import ( "database/sql" "errors" "fmt" ) // ErrNotFound is returned when a lookup by key matches no row. var ErrNotFound = errors.New("not found") // User is a row in the users table. type User struct { ID int64 Username string PasswordHash string } // CreateUser inserts a user and returns its id. func CreateUser(database *sql.DB, username, passwordHash string) (int64, error) { id, err := execLastID(database, `INSERT INTO users (username, password_hash) VALUES (?, ?)`, username, passwordHash) if err != nil { return 0, fmt.Errorf("create user %s: %w", username, err) } return id, nil } // scanUser reads one users row into user. func scanUser(s rowScanner, user *User) error { return s.Scan(&user.ID, &user.Username, &user.PasswordHash) } // GetUserByName looks up a user by their unique username. func GetUserByName(database *sql.DB, username string) (User, error) { var user User err := scanUser(database.QueryRow( `SELECT id, username, password_hash FROM users WHERE username = ?`, username), &user) if errors.Is(err, sql.ErrNoRows) { return User{}, fmt.Errorf("user %s: %w", username, ErrNotFound) } if err != nil { return User{}, fmt.Errorf("get user %s: %w", username, err) } return user, nil } // GetUserByID looks up a user by primary key. func GetUserByID(database *sql.DB, id int64) (User, error) { var user User err := scanUser(database.QueryRow( `SELECT id, username, password_hash FROM users WHERE id = ?`, id), &user) if errors.Is(err, sql.ErrNoRows) { return User{}, fmt.Errorf("user %d: %w", id, ErrNotFound) } if err != nil { return User{}, fmt.Errorf("get user %d: %w", id, err) } return user, nil } // UpdateUserPassword replaces a user's password hash. func UpdateUserPassword(database *sql.DB, id int64, passwordHash string) error { return execScoped(database, fmt.Sprintf("update password for user %d", id), `UPDATE users SET password_hash = ? WHERE id = ?`, passwordHash, id) }