forked from LadybugDB/ladybug
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase_header.cpp
More file actions
131 lines (119 loc) · 5.48 KB
/
Copy pathdatabase_header.cpp
File metadata and controls
131 lines (119 loc) · 5.48 KB
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
#include "storage/database_header.h"
#include <cstring>
#include "common/exception/runtime.h"
#include "common/file_system/file_info.h"
#include "common/serializer/buffered_file.h"
#include "common/serializer/deserializer.h"
#include "common/serializer/serializer.h"
#include "common/system_config.h"
#include "main/client_context.h"
#include "storage/page_manager.h"
#include "storage/storage_version_info.h"
#include <format>
namespace lbug::storage {
static void validateStorageVersion(common::Deserializer& deSer) {
std::string key;
deSer.validateDebuggingInfo(key, "storage_version");
storage_version_t savedStorageVersion = 0;
deSer.deserializeValue(savedStorageVersion);
const auto storageVersion = StorageVersionInfo::getStorageVersion();
if (savedStorageVersion != storageVersion) {
// TODO(Guodong): Add a test case for this.
throw common::RuntimeException(
std::format("Trying to read a database file with a different version. "
"Database file version: {}, Current build storage version: {}",
savedStorageVersion, storageVersion));
}
}
static void validateMagicBytes(common::Deserializer& deSer) {
std::string key;
deSer.validateDebuggingInfo(key, "magic");
const auto numMagicBytes = strlen(StorageVersionInfo::MAGIC_BYTES);
uint8_t magicBytes[4];
for (auto i = 0u; i < numMagicBytes; i++) {
deSer.deserializeValue<uint8_t>(magicBytes[i]);
}
if (memcmp(magicBytes, StorageVersionInfo::MAGIC_BYTES, numMagicBytes) != 0) {
throw common::RuntimeException(
"Unable to open database. The file is not a valid Lbug database file!");
}
}
void DatabaseHeader::updateCatalogPageRange(PageManager& pageManager, PageRange newPageRange) {
if (catalogPageRange.startPageIdx != common::INVALID_PAGE_IDX) {
pageManager.freePageRange(catalogPageRange);
}
catalogPageRange = newPageRange;
}
void DatabaseHeader::freeMetadataPageRange(PageManager& pageManager) const {
if (metadataPageRange.startPageIdx != common::INVALID_PAGE_IDX) {
pageManager.freePageRange(metadataPageRange);
}
}
static constexpr uint8_t HEADER_FORMAT_VERSION_WITH_DATAFILE_NUM_PAGES = 2;
static void writeMagicBytes(common::Serializer& serializer) {
serializer.writeDebuggingInfo("magic");
const auto numMagicBytes = strlen(StorageVersionInfo::MAGIC_BYTES);
for (auto i = 0u; i < numMagicBytes; i++) {
serializer.serializeValue<uint8_t>(StorageVersionInfo::MAGIC_BYTES[i]);
}
}
void DatabaseHeader::serialize(common::Serializer& ser) const {
writeMagicBytes(ser);
ser.writeDebuggingInfo("storage_version");
ser.serializeValue(StorageVersionInfo::getStorageVersion());
ser.writeDebuggingInfo("catalog");
ser.serializeValue(catalogPageRange.startPageIdx);
ser.serializeValue(catalogPageRange.numPages);
ser.writeDebuggingInfo("metadata");
ser.serializeValue(metadataPageRange.startPageIdx);
ser.serializeValue(metadataPageRange.numPages);
ser.writeDebuggingInfo("databaseID");
ser.serializeValue(databaseID.value);
// No debugging info so old readers can stop here; new readers read 9 bytes (version + size).
ser.serializeValue(static_cast<uint8_t>(HEADER_FORMAT_VERSION_WITH_DATAFILE_NUM_PAGES));
ser.serializeValue(dataFileNumPages);
}
DatabaseHeader DatabaseHeader::deserialize(common::Deserializer& deSer) {
validateMagicBytes(deSer);
validateStorageVersion(deSer);
PageRange catalogPageRange{}, metaPageRange{};
common::uuid databaseID{};
std::string key;
deSer.validateDebuggingInfo(key, "catalog");
deSer.deserializeValue(catalogPageRange.startPageIdx);
deSer.deserializeValue(catalogPageRange.numPages);
deSer.validateDebuggingInfo(key, "metadata");
deSer.deserializeValue(metaPageRange.startPageIdx);
deSer.deserializeValue(metaPageRange.numPages);
deSer.validateDebuggingInfo(key, "databaseID");
deSer.deserializeValue(databaseID.value);
common::page_idx_t dataFileNumPages = 0;
uint8_t headerFormatVersion = 0;
// Old format (e.g. lbug-0.14.1) ends here; reading 9 bytes would read beyond logical header.
// Read one byte: only if it matches our format version do we read the following 8 bytes.
deSer.deserializeValue(headerFormatVersion);
if (headerFormatVersion == HEADER_FORMAT_VERSION_WITH_DATAFILE_NUM_PAGES) {
deSer.deserializeValue(dataFileNumPages);
}
return {catalogPageRange, metaPageRange, dataFileNumPages, databaseID};
}
DatabaseHeader DatabaseHeader::createInitialHeader(common::RandomEngine* randomEngine) {
// We generate a random UUID to act as the database ID
return DatabaseHeader{{}, {}, 0, common::uuid::generateRandomUUID(randomEngine)};
}
std::optional<DatabaseHeader> DatabaseHeader::readDatabaseHeader(common::FileInfo& dataFileInfo) {
if (dataFileInfo.getFileSize() < common::LBUG_PAGE_SIZE) {
// If the data file hasn't been written to there is no existing database header
return std::nullopt;
}
auto reader = std::make_unique<common::BufferedFileReader>(dataFileInfo);
common::Deserializer deSer(std::move(reader));
try {
return DatabaseHeader::deserialize(deSer);
} catch (const common::RuntimeException&) {
// It is possible we optimistically write to the database file before the first checkpoint
// In this case the magic bytes check will fail and we assume there is no existing header
return std::nullopt;
}
}
} // namespace lbug::storage