summaryrefslogtreecommitdiff
path: root/chromium/sql/recover_module/pager.h
blob: 5976d21679e660cfb1bf022298f2729e68ab175f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SQL_RECOVER_MODULE_PAGER_H_
#define SQL_RECOVER_MODULE_PAGER_H_

#include <cstdint>
#include <memory>
#include <ostream>

#include "base/check_op.h"
#include "base/sequence_checker.h"

struct sqlite3_file;

namespace sql {
namespace recover {

class VirtualTable;

// Page reader for SQLite database files.
//
// Contains logic for retrying reads on I/O errors. Caches the last read page,
// to facilitate layering in higher-level code.
//
// Instances should be members of high-level constructs such as tables or
// cursors. Instances are not thread-safe.
class DatabasePageReader {
 public:
  // Guaranteed to be an invalid page number.
  static constexpr int kInvalidPageId = 0;

  // Minimum database page size supported by SQLite.
  static constexpr int kMinPageSize = 512;
  // Maximum database page size supported by SQLite.
  static constexpr int kMaxPageSize = 65536;

  // The size of the header at the beginning of a SQLite database file.
  static constexpr int kDatabaseHeaderSize = 100;

  // Minimum usable size of a SQLite database page.
  //
  // This differs from |kMinPageSize| because the first page in a SQLite
  // database starts with the database header. That page's header starts right
  // after the database header.
  static constexpr int kMinUsablePageSize = kMinPageSize - kDatabaseHeaderSize;

  // Largest valid page ID in a SQLite database.
  //
  // This is the maximum value of SQLITE_MAX_PAGE_COUNT plus 1, because page IDs
  // start at 1. The numerical value, which is the same as
  // std::numeric_limits<int32_t>::max() - 1, is quoted from
  // https://www.sqlite.org/limits.html.
  static constexpr int kMaxPageId = 2147483646 + 1;

  // Creates a reader that uses the SQLite VFS backing |table|.
  //
  // |table| must outlive this instance.
  explicit DatabasePageReader(VirtualTable* table);
  ~DatabasePageReader();

  DatabasePageReader(const DatabasePageReader&) = delete;
  DatabasePageReader& operator=(const DatabasePageReader&) = delete;

  // The page data read by the last ReadPage() call.
  //
  // The page data is undefined if the last ReadPage() call failed, or if
  // ReadPage() was never called.
  const uint8_t* page_data() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    DCHECK_NE(page_id_, kInvalidPageId)
        << "Successful ReadPage() required before accessing pager state";
    return page_data_.get();
  }

  // The number of bytes in the page read by the last ReadPage() call.
  //
  // The result is guaranteed to be in [kMinUsablePageSize, kMaxPageSize].
  //
  // In general, pages have the same size. However, the first page in each
  // database is smaller, because it starts after the database header.
  //
  // The result is undefined if the last ReadPage() call failed, or if
  // ReadPage() was never called.
  int page_size() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    DCHECK_NE(page_id_, kInvalidPageId)
        << "Successful ReadPage() required before accessing pager state";
    DCHECK_GE(page_size_, kMinUsablePageSize);
    DCHECK_LE(page_size_, kMaxPageSize);
    return page_size_;
  }

  // Returns the |page_id| argument for the last successful ReadPage() call.
  //
  // The result is undefined if the last ReadPage() call failed, or if
  // ReadPage() was never called.
  int page_id() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    DCHECK_NE(page_id_, kInvalidPageId)
        << "Successful ReadPage() required before accessing pager state";
    return page_id_;
  }

  // Reads a database page. Returns a SQLite status code.
  //
  // SQLite uses 1-based indexing for its page numbers.
  //
  // This method is idempotent, because it caches its result.
  int ReadPage(int page_id);

  // True if the given database page size is supported by SQLite.
  static constexpr bool IsValidPageSize(int page_size) noexcept {
    // SQLite page sizes must be powers of two.
    return page_size >= kMinPageSize && page_size <= kMaxPageSize &&
           (page_size & (page_size - 1)) == 0;
  }

  // True if the given number is a valid SQLite database page ID.
  //
  // Valid page IDs are positive 32-bit integers.
  static constexpr bool IsValidPageId(int64_t page_id) noexcept {
    return page_id > kInvalidPageId && page_id <= kMaxPageId;
  }

  // Low-level read wrapper. Returns a SQLite error code.
  //
  // |read_size| and |read_offset| are expressed in bytes.
  static int RawRead(sqlite3_file* sqlite_file,
                     int read_size,
                     int64_t read_offset,
                     uint8_t* buffer);

 private:
  // Points to the last page successfully read by ReadPage().
  // Set to kInvalidPageId if the last read was unsuccessful.
  int page_id_ = kInvalidPageId;
  // Stores the bytes of the last page successfully read by ReadPage().
  // The content is undefined if the last call to ReadPage() did not succeed.
  const std::unique_ptr<uint8_t[]> page_data_;
  // Raw pointer usage is acceptable because this instance's owner is expected
  // to ensure that the VirtualTable outlives this.
  VirtualTable* const table_;
  int page_size_ = 0;

  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace recover
}  // namespace sql

#endif  // SQL_RECOVER_MODULE_PAGER_H_