openlang

에러 없이 끝난 rows.Next 루프

database/sql로 만든 작은 도구가 목록을 반만 돌려줬어. 에러 없이. 코드는 이래.

func names(ctx context.Context, db *sql.DB) ([]string, error) {
	rows, err := db.QueryContext(ctx, `SELECT name FROM users ORDER BY id`)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("users: %w", err)
	}
	defer rows.Close()

	var out []string
	for rows.Next() {
		var n string
		if err := rows.Scan(&n); err != nil {
			return nil, fmt.Errorf("scan: %w", err)
		}
		out = append(out, n)
	}
	return out, nil
}

컨텍스트에 데드라인이 있었어. 가져오는 도중에 데드라인이 지나서 rows.Next()가 false를 줬고, 루프가 끝났고, 함수는 모은 것까지만 nil이랑 같이 넘겼어. 에러는 rows.Err()에 들어 있었는데 아무도 안 물어봤지.

고치는 건 루프 뒤 두 줄. if err := rows.Err(); err != nil 그리고 %w로 감싸서 반환. 끝. 이제 비슷한 루프를 나머지 코드에서 찾는 중이야.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    Next가 false를 돌려주는 이유는 두 가지인데 그걸 bool 하나로 말해요. 행이 끝났거나 에러가 났거나고, 둘을 구별해 주는 건 Err뿐이에요. 데드라인이 끼면 세는 문제도 하나 생겨요. 취소에 따른 close가 다른 고루틴에서 일어나서, false가 나오기 전까지 모인 이름 수가 실행할 때마다 달라요. 데드라인 전에 쿼리가 마지막 행까지 가 버리면 Err는 nil이고, 그게 맞는 결과예요. 그러니 고친 걸 확인하는 테스트에는 데드라인 안에 다 못 읽을 만큼 행이 많아야 해요. 아니면 가끔 엉뚱한 이유로 통과해요.

  2. 고유 링크

    작은 도우미 하나로 피할 수도 있어요. 루프를 돌며 행마다 스캔 함수를 부르고 결과를 rows.Err()와 같이 돌려주면, 루프 뒤 확인이 한 군데에만 있게 돼요.

같은 질문의 다른 갈래

  1. 문자열로 온 count(*)와 121건

    할 일 목록 화면이에요. 상단에 총 12건이 떠 있고, 제가 할 일을 하나 추가해요. 서버 응답을 기다리지 않고 숫자부터 올리려고 total + 1을 했는데, 화면에 총 121건이 떴어요. (순간 할 일이 백 개 넘게 생긴…

  2. 기다린 뒤 WHERE를 다시 보는 UPDATE

    PostgreSQL Read Committed에서 두 세션이 있고, 잔액이 15인 계좌가 하나 있으며, 두 세션이 같은 문장을 실행합니다. 세션 A가 행을 갱신했고 아직 커밋하지 않았습니다. 세션 B는 그 행을 찾고, 갱신…

    답글 0sql
  3. 페이지 보고서에는 count(*) OVER ()일까 Repeatable Read일까

    Read Committed에서는 페이지 보고서가 자기 자신과 어긋날 수 있습니다. 보고 화면이 조건에 맞는 주문 12건을 셉니다. 다른 세션이 조건에 맞는 새 주문을 커밋합니다. 그다음 화면이 첫 페이지를 가져옵니다. SEL…

    답글 9sql