diff --git a/CMakeLists.txt b/CMakeLists.txt index b7a875df96..92f0a46726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.11) -project(Kuzu VERSION 0.0.3.1 LANGUAGES CXX) +project(Kuzu VERSION 0.0.3 LANGUAGES CXX) find_package(Threads REQUIRED) @@ -78,6 +78,7 @@ function(add_kuzu_test TEST_NAME) endfunction() add_definitions(-DKUZU_ROOT_DIRECTORY="${PROJECT_SOURCE_DIR}") +add_definitions(-DKUZU_STORAGE_VERSION="${CMAKE_PROJECT_VERSION}") set(ARROW_INSTALL ${CMAKE_CURRENT_SOURCE_DIR}/external/build/arrow/install) find_library(ARROW_DEPS_PATH arrow_bundled_dependencies HINTS ${ARROW_INSTALL}/lib ${ARROW_INSTALL}/lib64) diff --git a/src/catalog/catalog.cpp b/src/catalog/catalog.cpp index 9ec75ac4a9..dd33f9d6eb 100644 --- a/src/catalog/catalog.cpp +++ b/src/catalog/catalog.cpp @@ -296,6 +296,9 @@ void CatalogContent::saveToFile(const std::string& directory, DBFileType dbFileT auto catalogPath = StorageUtils::getCatalogFilePath(directory, dbFileType); auto fileInfo = FileUtils::openFile(catalogPath, O_WRONLY | O_CREAT); uint64_t offset = 0; + writeMagicBytes(fileInfo.get(), offset); + offset = SerDeser::serializeValue( + StorageVersionInfo::getStorageVersion(), fileInfo.get(), offset); offset = SerDeser::serializeValue(nodeTableSchemas.size(), fileInfo.get(), offset); offset = SerDeser::serializeValue(relTableSchemas.size(), fileInfo.get(), offset); for (auto& nodeTableSchema : nodeTableSchemas) { @@ -316,6 +319,10 @@ void CatalogContent::readFromFile(const std::string& directory, DBFileType dbFil logger->debug("Reading from {}.", catalogPath); auto fileInfo = FileUtils::openFile(catalogPath, O_RDONLY); uint64_t offset = 0; + validateMagicBytes(fileInfo.get(), offset); + storage::storage_version_t savedStorageVersion; + offset = SerDeser::deserializeValue(savedStorageVersion, fileInfo.get(), offset); + validateStorageVersion(savedStorageVersion); uint64_t numNodeTables, numRelTables; offset = SerDeser::deserializeValue(numNodeTables, fileInfo.get(), offset); offset = SerDeser::deserializeValue(numRelTables, fileInfo.get(), offset); @@ -342,6 +349,36 @@ void CatalogContent::readFromFile(const std::string& directory, DBFileType dbFil SerDeser::deserializeValue(nextTableID, fileInfo.get(), offset); } +void CatalogContent::validateStorageVersion(storage_version_t savedStorageVersion) const { + auto storageVersion = StorageVersionInfo::getStorageVersion(); + if (savedStorageVersion != storageVersion) { + throw common::RuntimeException(StringUtils::string_format( + "Trying to read a database file with a different version. " + "Database file version: {}, Current build storage version: {}", + savedStorageVersion, storageVersion)); + } +} + +void CatalogContent::validateMagicBytes(FileInfo* fileInfo, offset_t& offset) const { + auto numMagicBytes = strlen(StorageVersionInfo::MAGIC_BYTES); + uint8_t magicBytes[4]; + for (auto i = 0u; i < numMagicBytes; i++) { + offset = SerDeser::deserializeValue(magicBytes[i], fileInfo, offset); + } + if (memcmp(magicBytes, StorageVersionInfo::MAGIC_BYTES, numMagicBytes) != 0) { + throw common::RuntimeException( + "This is not a valid Kuzu database directory for the current version of Kuzu."); + } +} + +void CatalogContent::writeMagicBytes(FileInfo* fileInfo, offset_t& offset) const { + auto numMagicBytes = strlen(StorageVersionInfo::MAGIC_BYTES); + for (auto i = 0u; i < numMagicBytes; i++) { + offset = + SerDeser::serializeValue(StorageVersionInfo::MAGIC_BYTES[i], fileInfo, offset); + } +} + Catalog::Catalog() : wal{nullptr} { catalogContentForReadOnlyTrx = std::make_unique(); builtInVectorOperations = std::make_unique(); diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h index 3a142f60cb..fa8fd2812a 100644 --- a/src/include/catalog/catalog.h +++ b/src/include/catalog/catalog.h @@ -11,6 +11,7 @@ #include "common/utils.h" #include "function/aggregate/built_in_aggregate_functions.h" #include "function/built_in_vector_operations.h" +#include "storage/storage_info.h" #include "storage/wal/wal.h" namespace spdlog { @@ -133,6 +134,12 @@ class CatalogContent { private: inline common::table_id_t assignNextTableID() { return nextTableID++; } + void validateStorageVersion(storage::storage_version_t savedStorageVersion) const; + + void validateMagicBytes(common::FileInfo* fileInfo, common::offset_t& offset) const; + + void writeMagicBytes(common::FileInfo* fileInfo, common::offset_t& offset) const; + private: std::shared_ptr logger; std::unordered_map> nodeTableSchemas; diff --git a/src/include/storage/storage_info.h b/src/include/storage/storage_info.h new file mode 100644 index 0000000000..08e8905395 --- /dev/null +++ b/src/include/storage/storage_info.h @@ -0,0 +1,31 @@ +#pragma once + +#include "exception" +#include + +#include "common/utils.h" + +namespace kuzu { +namespace storage { + +using storage_version_t = uint64_t; + +struct StorageVersionInfo { + static std::unordered_map getStorageVersionInfo() { + return {{"0.0.3", 1}}; + } + + static storage_version_t getStorageVersion() { + auto storageVersionInfo = getStorageVersionInfo(); + if (!storageVersionInfo.contains(KUZU_STORAGE_VERSION)) { + throw common::RuntimeException(common::StringUtils::string_format( + "Invalid storage version name: {}", KUZU_STORAGE_VERSION)); + } + return storageVersionInfo.at(KUZU_STORAGE_VERSION); + } + + static constexpr const char* MAGIC_BYTES = "KUZU"; +}; + +} // namespace storage +} // namespace kuzu