기여 목록 / 8521677
[iOS] Use ColumnTime() and BindTime() in DownloadRecordDatabase
iOS 다운로드 기록 DB(DownloadRecordDatabase)가 시간 값을 읽고 쓸 때 손으로 하던 변환을 sql::Statement::ColumnTime()·BindTime()으로 바꿨습니다. sql::Statement 시간 접근자 이행 시리즈(crbug.com/40176243, OSSCA #416)의 여섯 번째 CL입니다. 1파일 +10/−27, 저장 형식은 바뀌지 않습니다. ios/chrome/browser/download/model OWNER sdefresne@가 먼저 승인한 뒤 같은 OWNERS의 gambard@를 더해 두 번째 승인을 받았고, gambard@가 CQ를 돌려 tryjob 42개를 한 번에 통과해 2026-10-06에 머지됐습니다(cb8028e6d1253).
문제 설명
- issue url: https://crbug.com/40176243
sql/statement.h는 시간 컬럼을ColumnTime()·BindTime()으로 다루고 손 변환을 옮기라는 TODO를 남겨 두었습니다. CL 1~5(8382856, 8410045, 8474691, 8479611, 8505857)에 이어 iOS 쪽에 남은 곳을 옮깁니다.DownloadRecordDatabase는 페이징 커서 시각을ToDeltaSinceWindowsEpoch().InMicroseconds()로 두 번 바인딩하고,created_time·completed_time을 같은 방식으로 쓰고, 읽을 때는base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(...))로 되돌리고 있었습니다.completed_time이 비어 있으면 0을 쓰고 읽을 때는 0보다 큰 값만 시각으로 되돌리는 분기가 insert·update·read 세 곳에 반복돼 있었습니다.
해결 내용
- 커서 바인딩 2곳과
created_time쓰기 1곳,completed_time쓰기 2곳(insert·update)을BindTime()으로, 읽기 2곳을ColumnTime()으로 바꿨습니다. completed_time의 «없음 = 0» 처리는 분기 없이 그대로 유지됩니다.BindTime()은 nullbase::Time을 0으로 쓰고ColumnTime()은 0을 null로 읽기 때문입니다. 옛 읽기 코드는 음수도 버렸지만 이 클래스는 음수를 쓰지 않으므로 저장된 모든 행이 같은 값으로 읽힙니다.
- int64_t completed_time_microseconds = 0;
- if (!record.completed_time.is_null()) {
- completed_time_microseconds =
- record.completed_time.ToDeltaSinceWindowsEpoch().InMicroseconds();
- }
- statement.BindInt64(16, completed_time_microseconds);
+ // A null `completed_time` is stored as 0.
+ statement.BindTime(16, record.completed_time);
...
- int64_t completed_time_microseconds = statement.ColumnInt64(16);
- if (completed_time_microseconds > 0) {
- record.completed_time = base::Time::FromDeltaSinceWindowsEpoch(
- base::Microseconds(completed_time_microseconds));
- }
+ // A stored 0 reads back as a null `completed_time`.
+ record.completed_time = statement.ColumnTime(16);발굴 과정
CL 5 착수 전 전수 재검색에서 iOS 전용 파일에 7곳이 더 나와 별도 CL로 나눴습니다. iOS 코드는 Mac에서만 빌드할 수 있어 이 CL을 위해 처음으로 iOS 시뮬레이터 빌드 환경(.gclient의 target_os에 ios 추가, out/ios-sim)을 만들었습니다.
리뷰어는 ios/chrome/browser/download/model/OWNERS 중 이 파일의 최신 변경을 승인했고 최근에도 활동 중인 sdefresne@에게 먼저 받고, 이어서 gambard@에게 받았습니다. 처음 후보로 적어 둔 사람은 이 파일을 리뷰한 적은 있지만 OWNERS가 아니었고, OWNERS 한 명은 80일째 활동이 없어 제외했습니다.
테스트 방법
동작 불변 리팩토링이라 새 테스트는 없고 기존 테스트 통과가 검증입니다.
- 등가성:
sql/statement.cc에서BindTime()은time.ToDeltaSinceWindowsEpoch().InMicroseconds()를 바인딩하고(null은 0),ColumnTime()은base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(value))를 돌려줍니다(0은 null). 손으로 쓴 변환·분기와 같은 값입니다. - 첫 iOS 시뮬레이터 빌드
ios_chrome_unittests27,964스텝 48분 46초 - iossim으로
DownloadRecordDatabaseTest.*(29) ·DownloadRecordServiceImpl*(31) ·DownloadRecordStore*(17) 77/77 통과 — 경계값 테스트DataTypeBoundaryValues와 커서 페이징 테스트 포함 - presubmit 0 경고
배운 점
BindTime(null) = 0,ColumnTime(0) = null이라는 접근자 규약을 확인하고 나서야 «없음 = 0» 분기 세 곳을 지워도 된다고 판단할 수 있었습니다. 접근자의 null 처리까지 원문에서 읽어야 합니다.- iOS 전용 코드는 별도 빌드 환경이 필요하지만 한 번 만들면 증분으로 돌릴 수 있어, 시리즈에서 플랫폼별 파일을 따로 CL로 나누는 편이 리뷰어와 빌드 양쪽에서 낫습니다.
- 리뷰어 후보가 «최근에 이 파일을 리뷰한 사람»이어도 OWNERS 파일에서 등재 여부를 반드시 확인합니다. 자주 보이는 리뷰어가 OWNER가 아닐 수 있습니다.
- 파일 하나짜리 CL은 같은 OWNERS 파일에서 두 명을 차례로 받으면 됩니다. 첫 승인이 오자 바로 두 번째를 붙이고 승인이 모이자 바로 제출을 요청해, 업로드부터 머지까지 15시간이 걸렸습니다.