Use upstream clang 14 for desktop
[platform/framework/web/chromium-efl.git] / sql / recover_module / pager.cc
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.
4
5 #include "sql/recover_module/pager.h"
6
7 #include <limits>
8
9 #include "sql/recover_module/table.h"
10 #include "third_party/sqlite/sqlite3.h"
11
12 namespace sql {
13 namespace recover {
14
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;
21
22 static_assert(DatabasePageReader::kMaxPageId <= std::numeric_limits<int>::max(),
23               "ints are not appropriate for representing page IDs");
24
25 DatabasePageReader::DatabasePageReader(VirtualTable* table)
26     : page_data_(std::make_unique<uint8_t[]>(table->page_size())),
27       table_(table) {
28   DCHECK(table != nullptr);
29   DCHECK(IsValidPageSize(table->page_size()));
30 }
31
32 DatabasePageReader::~DatabasePageReader() = default;
33
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);
38
39   if (page_id_ == page_id)
40     return SQLITE_OK;
41
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");
48
49   page_size_ = read_size;
50   DCHECK_GE(page_size_, kMinUsablePageSize);
51   DCHECK_LE(page_size_, kMaxPageSize);
52
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");
59
60   int sqlite_status =
61       RawRead(sqlite_file, read_size, read_offset, page_data_.get());
62
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;
67   return sqlite_status;
68 }
69
70 // static
71 int DatabasePageReader::RawRead(sqlite3_file* sqlite_file,
72                                 int read_size,
73                                 int64_t read_offset,
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);
79
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;
83
84   int sqlite_status;
85   bool got_lock = false;
86   for (int i = kRetryCount; i > 0; --i) {
87     sqlite_status =
88         sqlite_file->pMethods->xLock(sqlite_file, SQLITE_LOCK_SHARED);
89     if (sqlite_status == SQLITE_OK) {
90       got_lock = true;
91       break;
92     }
93   }
94
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)
102       break;
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;
107       break;
108     }
109   }
110
111   if (got_lock) {
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);
117   }
118   return sqlite_status;
119 }
120
121 }  // namespace recover
122 }  // namespace sql