package store import ( "context" "github.com/jackc/pgx/v5" ) const userCols = "id, username, password_hash, role, created_at" func (s *Store) CountUsers(ctx context.Context) (int, error) { var n int err := s.p.QueryRow(ctx, "SELECT count(*) FROM users").Scan(&n) return n, err } func (s *Store) CreateUser(ctx context.Context, username, hash, role string) (int64, error) { var id int64 err := s.p.QueryRow(ctx, "INSERT INTO users (username, password_hash, role) VALUES ($1,$2,$3) RETURNING id", username, hash, role).Scan(&id) return id, err } func (s *Store) GetUserByName(ctx context.Context, username string) (User, error) { return scanUser(s.p.QueryRow(ctx, "SELECT "+userCols+" FROM users WHERE username=$1", username)) } func (s *Store) GetUserByID(ctx context.Context, id int64) (User, error) { return scanUser(s.p.QueryRow(ctx, "SELECT "+userCols+" FROM users WHERE id=$1", id)) } func (s *Store) ListUsers(ctx context.Context) ([]User, error) { rows, err := s.p.Query(ctx, "SELECT "+userCols+" FROM users ORDER BY id") if err != nil { return nil, err } defer rows.Close() var out []User for rows.Next() { var u User if err := rows.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &u.CreatedAt); err != nil { return nil, err } out = append(out, u) } return out, rows.Err() } // DeleteUser deletes a user with a transactional last-admin check (B5). // Returns ErrLastAdmin if the target is the last admin. func (s *Store) DeleteUser(ctx context.Context, id int64) error { tx, err := s.p.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) var role string if err := tx.QueryRow(ctx, "SELECT role FROM users WHERE id=$1 FOR UPDATE", id).Scan(&role); err != nil { return err // includes pgx.ErrNoRows } if role == "admin" { var n int if err := tx.QueryRow(ctx, "SELECT count(*) FROM users WHERE role='admin'").Scan(&n); err != nil { return err } if n <= 1 { return ErrLastAdmin } } if _, err := tx.Exec(ctx, "DELETE FROM users WHERE id=$1", id); err != nil { return err } return tx.Commit(ctx) } func scanUser(row pgx.Row) (User, error) { var u User err := row.Scan(&u.ID, &u.Username, &u.PasswordHash, &u.Role, &u.CreatedAt) return u, err }