1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/recover_module/pager.h"
9 #include "sql/recover_module/table.h"
10 #include "third_party/sqlite/sqlite3.h"
15 constexpr int DatabasePageReader::kInvalidPageId;
16 constexpr int DatabasePageReader::kMinPageSize;
17 constexpr int DatabasePageReader::kMaxPageSize;
18 constexpr int DatabasePageReader::kDatabaseHeaderSize;
19 constexpr int DatabasePageReader::kMinUsablePageSize;
20 constexpr int DatabasePageReader::kMaxPageId;
22 static_assert(DatabasePageReader::kMaxPageId <= std::numeric_limits<int>::max(),
23 "ints are not appropriate for representing page IDs");
25 DatabasePageReader::DatabasePageReader(VirtualTable* table)
26 : page_data_(std::make_unique<uint8_t[]>(table->page_size())),
28 DCHECK(table != nullptr);
29 DCHECK(IsValidPageSize(table->page_size()));
32 DatabasePageReader::~DatabasePageReader() = default;
34 int DatabasePageReader::ReadPage(int page_id) {
35 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
36 DCHECK_GT(page_id, kInvalidPageId);
37 DCHECK_LE(page_id, kMaxPageId);
39 if (page_id_ == page_id)
42 sqlite3_file* const sqlite_file = table_->SqliteFile();
43 const int page_size = table_->page_size();
44 const int page_offset = (page_id == 1) ? kDatabaseHeaderSize : 0;
45 const int read_size = page_size - page_offset;
46 static_assert(kMinPageSize >= kDatabaseHeaderSize,
47 "The |read_size| computation above may overflow");
49 page_size_ = read_size;
50 DCHECK_GE(page_size_, kMinUsablePageSize);
51 DCHECK_LE(page_size_, kMaxPageSize);
53 const int64_t read_offset =
54 static_cast<int64_t>(page_id - 1) * page_size + page_offset;
55 static_assert(static_cast<int64_t>(kMaxPageId - 1) * kMaxPageSize +
56 kDatabaseHeaderSize <=
57 std::numeric_limits<int64_t>::max(),
58 "The |read_offset| computation above may overflow");
61 RawRead(sqlite_file, read_size, read_offset, page_data_.get());
63 // |page_id_| needs to be set to kInvalidPageId if the read failed.
64 // Otherwise, future ReadPage() calls with the previous |page_id_| value
65 // would return SQLITE_OK, but the page data buffer might be trashed.
66 page_id_ = (sqlite_status == SQLITE_OK) ? page_id : kInvalidPageId;
71 int DatabasePageReader::RawRead(sqlite3_file* sqlite_file,
74 uint8_t* result_buffer) {
75 DCHECK(sqlite_file != nullptr);
76 DCHECK_GE(read_size, 0);
77 DCHECK_GE(read_offset, 0);
78 DCHECK(result_buffer != nullptr);
80 // Retry the I/O operations a few times if they fail. This is especially
81 // useful when recovering from database corruption.
82 static constexpr int kRetryCount = 10;
85 bool got_lock = false;
86 for (int i = kRetryCount; i > 0; --i) {
88 sqlite_file->pMethods->xLock(sqlite_file, SQLITE_LOCK_SHARED);
89 if (sqlite_status == SQLITE_OK) {
95 // Try reading even if we don't have a shared lock on the database. If the
96 // read fails, the database page is completely skipped, so any data we might
97 // get from the read is better than nothing.
98 for (int i = kRetryCount; i > 0; --i) {
99 sqlite_status = sqlite_file->pMethods->xRead(sqlite_file, result_buffer,
100 read_size, read_offset);
101 if (sqlite_status == SQLITE_OK)
103 if (sqlite_status == SQLITE_IOERR_SHORT_READ) {
104 // The read succeeded, but hit EOF. The extra bytes in the page buffer
105 // are set to zero. This is acceptable for our purposes.
106 sqlite_status = SQLITE_OK;
112 // TODO(pwnall): This logic was ported from the old C-in-SQLite-style patch.
113 // Dropping the lock here is incorrect, because the file
114 // descriptor is shared with the SQLite pager, which may
115 // expect to be holding a lock.
116 sqlite_file->pMethods->xUnlock(sqlite_file, SQLITE_LOCK_NONE);
118 return sqlite_status;
121 } // namespace recover