From 3151815ee82a7e5d967677cd95d2c194fef6810f Mon Sep 17 00:00:00 2001 From: Adrian Zawadzki Date: Fri, 5 Dec 2025 22:05:07 +0100 Subject: [PATCH] fix(workspace): prevent invalid exception when opening prefix in RW mode fails Fixed issue #334 where opening a locked prefix in read-write mode would throw exception and Workspace open silences exception and returns null. Right now null is returned only when PrefixNotFoundException is thrown. Changes: - Added new exception type PrefixNotFoundException for clearer error handling - Modified PrefixCatalog, Workspace, and PyEnumType to throw PrefixNotFoundException instead of InputException when prefix does not exist - Improved error handling in Workspace::open() to properly clean up incomplete files for all exception types while preserving the original exception for non-prefix-not-found cases Fixes #334 --- src/dbzero/bindings/python/types/PyEnumType.cpp | 2 +- src/dbzero/core/exception/Exceptions.cpp | 5 +++++ src/dbzero/core/exception/Exceptions.hpp | 7 +++++++ src/dbzero/workspace/PrefixCatalog.cpp | 4 ++-- src/dbzero/workspace/Workspace.cpp | 14 ++++++++++---- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/dbzero/bindings/python/types/PyEnumType.cpp b/src/dbzero/bindings/python/types/PyEnumType.cpp index 7d4f8971..b2f9e0fc 100644 --- a/src/dbzero/bindings/python/types/PyEnumType.cpp +++ b/src/dbzero/bindings/python/types/PyEnumType.cpp @@ -124,7 +124,7 @@ namespace db0::python auto prefix_name_ptr = m_enum_type_def->getPrefixNamePtr(); if (prefix_name_ptr) { // make the error message more informative - THROWF(db0::InputException) << "Prefix does not exist or unable to open: " << prefix_name_ptr; + THROWF(db0::PrefixNotFoundException) << "Prefix does not exist or unable to open: " << prefix_name_ptr; } THROWF(db0::InputException) << "Unable to resolve the scope of: " << *m_enum_type_def; } diff --git a/src/dbzero/core/exception/Exceptions.cpp b/src/dbzero/core/exception/Exceptions.cpp index b3269032..446eece9 100644 --- a/src/dbzero/core/exception/Exceptions.cpp +++ b/src/dbzero/core/exception/Exceptions.cpp @@ -72,4 +72,9 @@ namespace db0 { } + PrefixNotFoundException::PrefixNotFoundException() + : RecoverableException(exception_id) + { + } + } \ No newline at end of file diff --git a/src/dbzero/core/exception/Exceptions.hpp b/src/dbzero/core/exception/Exceptions.hpp index 7dac00f3..cb67f839 100644 --- a/src/dbzero/core/exception/Exceptions.hpp +++ b/src/dbzero/core/exception/Exceptions.hpp @@ -128,4 +128,11 @@ namespace db0 CacheException(); }; + class PrefixNotFoundException: public RecoverableException + { + public: + static constexpr int exception_id = EXCEPTION_ID_PREFIX::BASIC | 0x10; + PrefixNotFoundException(); + }; + } \ No newline at end of file diff --git a/src/dbzero/workspace/PrefixCatalog.cpp b/src/dbzero/workspace/PrefixCatalog.cpp index b578e207..a19a25de 100644 --- a/src/dbzero/workspace/PrefixCatalog.cpp +++ b/src/dbzero/workspace/PrefixCatalog.cpp @@ -53,7 +53,7 @@ namespace db0 auto file_name = getFileName(prefix_name); bool file_exists = CFile::exists(file_name.string()); if (!if_exists && !file_exists) { - THROWF(db0::InputException) << "Prefix does not exist: " << prefix_name; + THROWF(db0::PrefixNotFoundException) << "Prefix does not exist: " << prefix_name; } if (file_exists) { std::remove(file_name.string().c_str()); @@ -203,7 +203,7 @@ namespace db0 fs::path FixtureCatalog::getPrefixFileName(const PrefixName &prefix_name) const { if (!m_prefix_catalog.exists(prefix_name)) { - THROWF(db0::InputException) << "Prefix does not exist: " << prefix_name; + THROWF(db0::PrefixNotFoundException) << "Prefix does not exist: " << prefix_name; } return m_prefix_catalog.getFileName(prefix_name); } diff --git a/src/dbzero/workspace/Workspace.cpp b/src/dbzero/workspace/Workspace.cpp index d33c9e39..9d7537bb 100644 --- a/src/dbzero/workspace/Workspace.cpp +++ b/src/dbzero/workspace/Workspace.cpp @@ -51,7 +51,7 @@ namespace db0 if (!m_prefix_catalog.exists(prefix_name)) { // create new file if READ-WRITE access permitted if (access_type == AccessType::READ_ONLY) { - THROWF(db0::InputException) << "Prefix does not exist: " << prefix_name; + THROWF(db0::PrefixNotFoundException) << "Prefix does not exist: " << prefix_name; } BDevStorage::create(file_name, *page_size, *sparse_index_node_size, page_io_step_size); @@ -390,13 +390,19 @@ namespace db0 m_on_open_callback(it->second, file_created); } } - } catch (std::exception &ex) { + } catch (db0::PrefixNotFoundException &ex) { if (file_created) { // remove incomplete file m_fixture_catalog.drop(prefix_name); } - std::cerr << "Error opening prefix '" << prefix_name << "': " << ex.what() << std::endl; - return nullptr; + return nullptr; + } + catch (std::exception &ex) { + if (file_created) { + // remove incomplete file + m_fixture_catalog.drop(prefix_name); + } + throw; } // Validate access type