| 91 | } |
| 92 | |
| 93 | void ShadowFile::replayShadowPageRecords(ClientContext& context) { |
| 94 | if (context.getDBConfig()->readOnly) { |
| 95 | throw RuntimeException("Couldn't replay shadow pages under read-only mode. Please re-open " |
| 96 | "the database with read-write mode to replay shadow pages."); |
| 97 | } |
| 98 | auto vfs = VirtualFileSystem::GetUnsafe(context); |
| 99 | auto shadowFilePath = StorageUtils::getShadowFilePath(context.getDatabasePath()); |
| 100 | auto shadowFileInfo = vfs->openFile(shadowFilePath, FileOpenFlags(FileFlags::READ_ONLY)); |
| 101 | |
| 102 | std::unique_ptr<FileInfo> dataFileInfo; |
| 103 | try { |
| 104 | dataFileInfo = vfs->openFile(context.getDatabasePath(), |
| 105 | FileOpenFlags{FileFlags::WRITE | FileFlags::READ_ONLY, FileLockType::WRITE_LOCK}); |
| 106 | } catch (IOException& e) { |
| 107 | throw RuntimeException(std::format( |
| 108 | "Found shadow file {} but no corresponding database file. This file " |
| 109 | "may have been left behind from a previous database with the same name. If it is safe " |
| 110 | "to do so, please delete this file and restart the database.", |
| 111 | shadowFilePath)); |
| 112 | } |
| 113 | |
| 114 | ShadowFileHeader header; |
| 115 | const auto headerBuffer = std::make_unique<uint8_t[]>(LBUG_PAGE_SIZE); |
| 116 | shadowFileInfo->readFromFile(headerBuffer.get(), LBUG_PAGE_SIZE, 0); |
| 117 | memcpy(&header, headerBuffer.get(), sizeof(ShadowFileHeader)); |
| 118 | |
| 119 | // When replaying the shadow file we haven't read the database ID from the database |
| 120 | // header yet |
| 121 | // So we need to do it separately here to verify the shadow file matches the database |
| 122 | auto oldDatabaseID = getOldDatabaseID(*dataFileInfo); |
| 123 | FileDBIDUtils::verifyDatabaseID(*shadowFileInfo, oldDatabaseID, header.databaseID); |
| 124 | |
| 125 | std::vector<ShadowPageRecord> shadowPageRecords; |
| 126 | shadowPageRecords.reserve(header.numShadowPages); |
| 127 | auto reader = std::make_unique<BufferedFileReader>(*shadowFileInfo); |
| 128 | reader->resetReadOffset((header.numShadowPages + 1) * LBUG_PAGE_SIZE); |
| 129 | Deserializer deSer(std::move(reader)); |
| 130 | deSer.deserializeVector(shadowPageRecords); |
| 131 | |
| 132 | const auto pageBuffer = std::make_unique<uint8_t[]>(LBUG_PAGE_SIZE); |
| 133 | page_idx_t shadowPageIdx = 1; |
| 134 | for (const auto& record : shadowPageRecords) { |
| 135 | shadowFileInfo->readFromFile(pageBuffer.get(), LBUG_PAGE_SIZE, |
| 136 | shadowPageIdx * LBUG_PAGE_SIZE); |
| 137 | dataFileInfo->writeFile(pageBuffer.get(), LBUG_PAGE_SIZE, |
| 138 | record.originalPageIdx * LBUG_PAGE_SIZE); |
| 139 | shadowPageIdx++; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | void ShadowFile::flushAll(main::ClientContext& context) const { |
| 144 | // Write header page to file. |
nothing calls this directly
no test coverage detected