diff options
| author | Marcos Magueta <maguetamarcos@gmail.com> | 2026-05-09 23:03:00 -0300 |
|---|---|---|
| committer | Marcos Magueta <maguetamarcos@gmail.com> | 2026-05-09 23:03:00 -0300 |
| commit | c375b1c69665ae7c16ffa3f17264386dfc257d8e (patch) | |
| tree | c0a45b370512ae6d4b4a9500c244419567a357ef | |
| parent | 921de259fa441fb562aec701f0c8c14310f126e4 (diff) | |
| download | RNT-c375b1c69665ae7c16ffa3f17264386dfc257d8e.tar.gz RNT-c375b1c69665ae7c16ffa3f17264386dfc257d8e.zip | |
FOL VM with cursor mocked storage
| -rw-r--r-- | CLAUDE.md | 79 | ||||
| -rw-r--r-- | CMakeLists.txt | 38 | ||||
| -rw-r--r-- | CMakePresets.json | 63 | ||||
| -rw-r--r-- | RelationalNT.sln | 27 | ||||
| -rw-r--r-- | RelationalNT.vcxproj | 113 | ||||
| -rw-r--r-- | RelationalNT.vcxproj.filters | 94 | ||||
| -rw-r--r-- | include/CursorManager.h | 54 | ||||
| -rw-r--r-- | include/HandlerManager.h | 22 | ||||
| -rw-r--r-- | include/ObjectManager.h | 55 | ||||
| -rw-r--r-- | include/Types.h | 43 | ||||
| -rw-r--r-- | include/VM.h | 45 | ||||
| -rw-r--r-- | launch.vs.json | 12 | ||||
| -rw-r--r-- | src/CursorManager.cpp | 45 | ||||
| -rw-r--r-- | src/HandlerManager.cpp | 35 | ||||
| -rw-r--r-- | src/LifecycleManager.cpp | 4 | ||||
| -rw-r--r-- | src/NT.cpp | 75 | ||||
| -rw-r--r-- | src/ObjectManager.cpp | 26 | ||||
| -rw-r--r-- | src/VM.cpp | 15 |
18 files changed, 566 insertions, 279 deletions
diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..077be25 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,79 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Relational NT is an experimental database kernel inspired by the Windows NT kernel's object-management model. It applies explicit object management principles to database internals — relations, tuples, transactions, namespaces, permissions, cursors, and handles are first-class runtime-managed objects rather than isolated subsystems. + +The project is early-stage and intentionally skeletal: headers define the architecture and document design constraints; implementations are stubs filling out as the runtime shape solidifies. + +## Build Commands + +**Windows (NMake):** +``` +nmake /f Makefile windows +nmake /f Makefile clean +``` + +**Unix/Linux/macOS:** +``` +make unix +make macos +make clean +``` + +Requires C++17. Output binary: `RNT.exe` (Windows) or `RNT` (Unix). No external dependencies beyond the standard library. + +**Generate documentation:** +``` +doxygen Doxyfile +``` +Output lands in `docs/generated/html`. Docs are auto-published to GitHub Pages on pushes to `master`. + +## Architecture + +### Manager Pipeline + +The system is organized as a set of single-responsibility managers. The canonical **open-handle workflow** chains them in this order: + +``` +HandlerManager::Open() + → ObjectManager::Find() — locate object by logical path + → PermissionsManager::Access() — capability check (READ/WRITE vs security descriptor) + → IdentityManager::CanOpen() — verify object supports OPEN method + → LifecycleManager::Contention() — detect mutability conflicts + → LifecycleManager::Monitor() — pin the object for the handle's lifetime +``` + +`HandlerManager::Close()` reverses monitoring and deallocates the handle. This explicit separation means permissions policy can evolve without touching object lookup or lifecycle code. + +### Manager Responsibilities + +| Manager | Role | +|---|---| +| `ObjectManager` | Central registry — stores and finds all objects (Multigroup, Relation, Tuple, Attribute, Transaction) by logical path; tracks ref counts, handle counts, security descriptors | +| `PermissionsManager` | Capability-based auth — `Firewall()` validates connection auth method; `Access()` checks object-level claims (READ/WRITE) | +| `IdentityManager` | Lifecycle eligibility — `CanOpen()` / `CanClose()` confirm an object's method set supports the requested operation | +| `LifecycleManager` | Monitoring, pinning, and contention — `Monitor()` / `Unmonitor()` track open handles; `Contention()` prevents conflicting concurrent writes; blocks history pruning when cursors hold snapshots | +| `HandlerManager` | Orchestrates the full open/close workflow above; owns handle allocation | +| `CursorManager` | Volcano-style pull iterator over storage — abstracts pagination, snapshot pinning, prefetch, buffering, and scan boundary enforcement (commit boundaries, logical branches) | +| `NamespaceReferenceManager` | Logical path mapping and namespace isolation — supports private branch shadowing (`/user_a/refs/branch/abc`); enforces all-or-nothing batching for multi-reference atomic updates | +| `VM` | Logic execution boundary — hosts two cores: **Tarski** (FOL relational calculus via Volcano iterators, guaranteed termination) and **Karuta** (SOL WAM-based higher-order logic, may not terminate); pull-based and lazy | + +### Shared Types (`include/Types.h`) + +- `OBJECT_TYPE`: `MULTIGROUP`, `RELATION`, `TUPLE`, `ATTRIBUTE`, `TRANSACTION` +- `METHOD`: `OPEN`, `CLOSE`, `PARSE`, `SECURITY` +- `AUTH_CLAIM`: `READ`, `WRITE` +- `AUTH_METHOD`: `CERTIFICATE`, `PLAIN_TEXT` + +### Entry Points + +- `include/NT.h` — main public API include +- `include/Runtime.h` — top-level `NT` facade (`IsRunning()`, `SimulateEntryCall()`) +- `src/NT.cpp` — console harness used for manual testing + +### Headers as Design Record + +Because implementations are intentionally minimal, the **header files are the authoritative design record**. Doxygen comments in headers explain invariants, constraints, and design rationale. Read the header before touching any `.cpp`. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..77d2321 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.20) +project(RelationalNT LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Toggle the "Press Enter to exit" prompt. ON by default for interactive runs; +# set -DNT_WAIT_ON_EXIT=OFF on the cmake command line to disable (e.g. in CI). +option(NT_WAIT_ON_EXIT "Wait for Enter key before exiting the console app" ON) + +set(SOURCES + src/CursorManager.cpp + src/HandlerManager.cpp + src/IdentityManager.cpp + src/LifecycleManager.cpp + src/NamespaceReferenceManager.cpp + src/NT.cpp + src/ObjectManager.cpp + src/PermissionsManager.cpp + src/VM.cpp +) + +add_executable(RNT ${SOURCES}) + +target_include_directories(RNT PRIVATE include) + +target_compile_definitions(RNT PRIVATE NT_CONSOLE_APP) + +if(NT_WAIT_ON_EXIT) + target_compile_definitions(RNT PRIVATE NT_WAIT_ON_EXIT) +endif() + +if(MSVC) + target_compile_options(RNT PRIVATE /W4) +else() + target_compile_options(RNT PRIVATE -Wall -Wextra) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..7b03857 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,63 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "windows-debug", + "displayName": "Windows Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "NT_WAIT_ON_EXIT": "ON" + } + }, + { + "name": "windows-release", + "displayName": "Windows Release", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "NT_WAIT_ON_EXIT": "OFF" + } + }, + { + "name": "unix-debug", + "displayName": "Unix Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "NT_WAIT_ON_EXIT": "ON" + } + }, + { + "name": "unix-release", + "displayName": "Unix Release", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/release", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "NT_WAIT_ON_EXIT": "OFF" + } + } + ], + "buildPresets": [ + { + "name": "windows-debug", + "configurePreset": "windows-debug" + }, + { + "name": "windows-release", + "configurePreset": "windows-release" + }, + { + "name": "unix-debug", + "configurePreset": "unix-debug" + }, + { + "name": "unix-release", + "configurePreset": "unix-release" + } + ] +} diff --git a/RelationalNT.sln b/RelationalNT.sln deleted file mode 100644 index aa88815..0000000 --- a/RelationalNT.sln +++ /dev/null @@ -1,27 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.0.35730.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{BC8A1FFA-BEE3-4634-8014-F334798102B3}") = "RelationalNT", "RelationalNT.vcxproj", "{FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Debug|x64.ActiveCfg = Debug|x64 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Debug|x64.Build.0 = Debug|x64 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Debug|x86.ActiveCfg = Debug|Win32 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Debug|x86.Build.0 = Debug|Win32 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Release|x64.ActiveCfg = Release|x64 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Release|x64.Build.0 = Release|x64 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Release|x86.ActiveCfg = Release|Win32 - {FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/RelationalNT.vcxproj b/RelationalNT.vcxproj deleted file mode 100644 index b2d68f1..0000000 --- a/RelationalNT.vcxproj +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Globals"> - <VCProjectVersion>18.0</VCProjectVersion> - <ProjectGuid>{FBC1C0B4-8B54-4A18-A91E-8A72767C5AE7}</ProjectGuid> - <RootNamespace>RelationalNT</RootNamespace> - <Keyword>MakeFileProj</Keyword> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>Makefile</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v145</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>Makefile</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v145</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> - <ConfigurationType>Makefile</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v145</PlatformToolset> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> - <ConfigurationType>Makefile</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v145</PlatformToolset> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <PropertyGroup> - <NMakeBuildCommandLine>nmake /f "$(ProjectDir)Makefile" windows</NMakeBuildCommandLine> - <NMakeReBuildCommandLine>nmake /f "$(ProjectDir)Makefile" clean && nmake /f "$(ProjectDir)Makefile" windows</NMakeReBuildCommandLine> - <NMakeCleanCommandLine>nmake /f "$(ProjectDir)Makefile" clean</NMakeCleanCommandLine> - <NMakeOutput>$(ProjectDir)RNT.exe</NMakeOutput> - <NMakePreprocessorDefinitions>NT_CONSOLE_APP;NT_WAIT_ON_EXIT;_CONSOLE</NMakePreprocessorDefinitions> - <NMakeIncludeSearchPath>$(ProjectDir)include</NMakeIncludeSearchPath> - <LocalDebuggerCommand>$(ProjectDir)RNT.exe</LocalDebuggerCommand> - <LocalDebuggerCommandArguments> - </LocalDebuggerCommandArguments> - <LocalDebuggerWorkingDirectory>$(ProjectDir)</LocalDebuggerWorkingDirectory> - </PropertyGroup> - <ItemDefinitionGroup> - </ItemDefinitionGroup> - <ItemGroup> - <ClCompile Include="src\CursorManager.cpp" /> - <ClCompile Include="src\HandlerManager.cpp" /> - <ClCompile Include="src\IdentityManager.cpp" /> - <ClCompile Include="src\LifecycleManager.cpp" /> - <ClCompile Include="src\NamespaceReferenceManager.cpp" /> - <ClCompile Include="src\NT.cpp" /> - <ClCompile Include="src\ObjectManager.cpp" /> - <ClCompile Include="src\PermissionsManager.cpp" /> - <ClCompile Include="src\VM.cpp" /> - </ItemGroup> - <ItemGroup> - <ClInclude Include="include\Api.h" /> - <ClInclude Include="include\CursorManager.h" /> - <ClInclude Include="include\HandlerManager.h" /> - <ClInclude Include="include\IdentityManager.h" /> - <ClInclude Include="include\LifecycleManager.h" /> - <ClInclude Include="include\NamespaceReferenceManager.h" /> - <ClInclude Include="include\NT.h" /> - <ClInclude Include="include\ObjectManager.h" /> - <ClInclude Include="include\PermissionsManager.h" /> - <ClInclude Include="include\Runtime.h" /> - <ClInclude Include="include\Types.h" /> - <ClInclude Include="include\VM.h" /> - </ItemGroup> - <ItemGroup> - <None Include="Doxyfile" /> - <None Include="docs\mainpage.dox" /> - <None Include="Makefile" /> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - </ImportGroup> -</Project> diff --git a/RelationalNT.vcxproj.filters b/RelationalNT.vcxproj.filters deleted file mode 100644 index ec1b7ae..0000000 --- a/RelationalNT.vcxproj.filters +++ /dev/null @@ -1,94 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{76F32F28-90C1-4399-9DA9-BAA6B807C2C8}</UniqueIdentifier> - <Extensions>cpp;c;cxx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{0F8561B5-9FD4-4E00-AEDB-9630D26B0C4E}</UniqueIdentifier> - <Extensions>h;hpp;hxx</Extensions> - </Filter> - <Filter Include="Build Files"> - <UniqueIdentifier>{21EA6EE4-A66E-43F8-843B-F8E25DFF8E5F}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClCompile Include="src\CursorManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\HandlerManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\IdentityManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\LifecycleManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\NamespaceReferenceManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\NT.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\ObjectManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\PermissionsManager.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="src\WAM.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <ClInclude Include="include\Api.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\CursorManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\HandlerManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\IdentityManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\LifecycleManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\NamespaceReferenceManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\NT.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\ObjectManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\PermissionsManager.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\Runtime.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\Types.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="include\WAM.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <None Include="Doxyfile"> - <Filter>Build Files</Filter> - </None> - <None Include="docs\mainpage.dox"> - <Filter>Build Files</Filter> - </None> - <None Include="Makefile"> - <Filter>Build Files</Filter> - </None> - </ItemGroup> -</Project> diff --git a/include/CursorManager.h b/include/CursorManager.h index 7e1ebe0..78ea24c 100644 --- a/include/CursorManager.h +++ b/include/CursorManager.h @@ -1,6 +1,12 @@ #pragma once #include "Api.h" +#include "HandlerManager.h" +#include "Types.h" + +#include <string> +#include <unordered_map> +#include <vector> /** * @file CursorManager.h @@ -35,5 +41,53 @@ namespace nt */ class NT_API CursorManager { + public: + /** @brief Active scan state for a single relation. */ + struct cursor { + /** Handle that authorized this cursor. */ + HandlerManager::handle* handle; + // TODO: Replace with a lazy iterator into the physical storage backend. + // Pre-loading all tuples on Open() is a mock simplification. + std::vector<Tuple> tuples; + size_t position = 0; + }; + + /** + * @brief Opens a cursor on the relation referenced by the handle. + * @param handle Authorized handle to a RELATION object. + * @return A new cursor, or nullptr if the object is not a RELATION. + */ + cursor* Open(HandlerManager::handle* handle); + + /** + * @brief Pulls the next tuple from the cursor. + * + * The returned pointer is valid until the next Next() or Close() call. + * + * @param cursor Active cursor. + * @return Pointer to the next tuple, or nullptr when exhausted. + */ + Tuple* Next(cursor* cursor); + + /** + * @brief Closes the cursor and releases its resources. + * @param cursor Cursor to close. + */ + void Close(cursor* cursor); + + /** + * @brief Inserts one tuple into the mock store for a given relation path. + * + * TODO: Replace with physical storage writes when the backend is wired up. + * + * @param path Logical path of the relation. + * @param tuple Attribute values for the tuple to insert. + */ + void MockInsert(std::vector<std::string> path, std::vector<Attribute> tuple); + + private: + // TODO: Replace with physical storage backend access. + // Keyed by the relation path joined as "seg/seg/...". + std::unordered_map<std::string, std::vector<std::vector<Attribute>>> mock_store_; }; } diff --git a/include/HandlerManager.h b/include/HandlerManager.h index 185cc55..4f63f17 100644 --- a/include/HandlerManager.h +++ b/include/HandlerManager.h @@ -13,11 +13,27 @@ namespace nt { + class IdentityManager; + class LifecycleManager; + class PermissionsManager; + /** @brief Opens and closes authorized handles to registry objects. */ class NT_API HandlerManager { public: /** + * @brief Constructs a HandlerManager with shared runtime dependencies. + * + * All managers are injected so that state (registry entries, lifecycle + * counters, permission policy) persists across calls rather than being + * rebuilt per open/close. + */ + HandlerManager(ObjectManager& objects, + PermissionsManager& permissions, + IdentityManager& identities, + LifecycleManager& lifecycles); + + /** * @brief Represents authorized access to an object in the registry. * * This increases the handle count in the object manager. We should @@ -51,5 +67,11 @@ namespace nt * @return True when the handle was closed successfully. */ bool Close(struct handle* handle); + + private: + ObjectManager& objects_; + PermissionsManager& permissions_; + IdentityManager& identities_; + LifecycleManager& lifecycles_; }; } diff --git a/include/ObjectManager.h b/include/ObjectManager.h index 52ba602..aab3a6f 100644 --- a/include/ObjectManager.h +++ b/include/ObjectManager.h @@ -4,6 +4,7 @@ #include "Types.h" #include <cstdint> +#include <memory> #include <set> #include <string> #include <vector> @@ -31,14 +32,12 @@ namespace nt virtual ~IObject() = default; }; - /** @brief Object representing a multigroup. */ - class Multigroup : IObject {}; - /** @brief Object representing a relation. */ - class Relation : IObject {}; - /** @brief Object representing a tuple. */ - class Tuple : IObject {}; - /** @brief Object representing a transaction. */ - class Transaction : IObject {}; + /** @brief Abstract registry object representing a multigroup. */ + class Multigroup : public IObject {}; + /** @brief Abstract registry object representing a relation. */ + class Relation : public IObject {}; + /** @brief Abstract registry object representing a transaction. */ + class Transaction : public IObject {}; /** @brief Describes the behavior and capabilities of an object category. */ struct object_type { @@ -52,12 +51,14 @@ namespace nt /** @brief Shared metadata stored at the head of a registry entry. */ struct registry_head { - /** Number of object-to-object references. */ - uint32_t reference_count; - /** Number of open handles. */ - uint32_t handle_count; - /** Object type metadata. */ - struct object_type* type; + // TODO: Increment reference_count when one registry object depends on another + // (e.g. an ephemeral view that references base relations). Decrement when the + // dependency is removed. Used to block cleanup of objects still referenced by others. + uint32_t reference_count = 0; + /** Number of open handles. Maintained by LifecycleManager::Monitor/Unmonitor. */ + uint32_t handle_count = 0; + /** Object type metadata. Owned by this head. */ + std::unique_ptr<object_type> type; /** Logical object path inside the namespace. */ std::vector<std::string> path; /** security_descriptor to be added later, maybe also a set of labels */ @@ -66,21 +67,31 @@ namespace nt /** @brief Node in the object registry. */ struct registry { /** Shared metadata for this object. */ - struct registry_head* head; - /** Stored object payload. */ - IObject object; + std::unique_ptr<registry_head> head; + /** Abstract object payload. Owned by this entry. */ + std::unique_ptr<IObject> object; /** TODO: Treat as a list for now, but ideally it is a tree. */ - struct registry* next; + std::unique_ptr<registry> next; }; - /** Root registry entries. */ - struct registry** entries; + /** @brief Head of the registry linked list. Owned by this manager. */ + std::unique_ptr<registry> entries; + + /** + * @brief Registers an object under the given path, taking ownership of it. + * @param path Logical object path. + * @param object Object payload. Ownership is transferred to the registry. + * @param type Object type descriptor. Ownership is transferred to the registry. + */ + void Register(std::vector<std::string> path, + std::unique_ptr<IObject> object, + std::unique_ptr<object_type> type); /** * @brief Looks into this registry and attempts to retrieve an entry. * @param object_path Logical path to search for. - * @return Matching registry entry, or nullptr when no object is found. + * @return Borrowed pointer to the matching entry, or nullptr when not found. */ - struct registry* Find(const std::vector<std::string> object_path); + registry* Find(const std::vector<std::string> object_path); }; } diff --git a/include/Types.h b/include/Types.h index 9d55765..c6f4b9f 100644 --- a/include/Types.h +++ b/include/Types.h @@ -1,8 +1,11 @@ #pragma once +#include <string> +#include <vector> + /** * @file Types.h - * @brief Shared enum types used by the runtime managers. + * @brief Shared enum types and core data types used by the runtime. */ /** @brief Runtime object categories known by the object manager. */ @@ -11,9 +14,7 @@ enum OBJECT_TYPE { MULTIGROUP, /** A relation object. */ RELATION, - /** A tuple object. */ - TUPLE, - /** An attribute object. */ + /** An attribute object (schema metadata). */ ATTRIBUTE, /** A transaction object. */ TRANSACTION @@ -46,3 +47,37 @@ enum AUTH_METHOD { /** Plain-text authentication. */ PLAIN_TEXT }; + +namespace nt +{ + /** @brief A single named value in a tuple, produced by the cursor layer. */ + struct Attribute { + std::string name; + std::string value; + }; + + /** + * @brief A single row of data streamed lazily from the cursor layer. + * + * Follows the Volcano pull model: callers repeatedly call Next() to consume + * one attribute at a time. Returns nullptr when all attributes are exhausted. + */ + class Tuple { + public: + explicit Tuple(std::vector<Attribute> attributes) + : attributes_(std::move(attributes)) {} + + /** @brief Returns the next attribute, or nullptr when exhausted. */ + const Attribute* Next() { + if (position_ < attributes_.size()) + return &attributes_[position_++]; + return nullptr; + } + + void Reset() { position_ = 0; } + + private: + std::vector<Attribute> attributes_; + size_t position_ = 0; + }; +} diff --git a/include/VM.h b/include/VM.h index 2defe79..d6402b3 100644 --- a/include/VM.h +++ b/include/VM.h @@ -1,6 +1,8 @@ #pragma once #include "Api.h" +#include "CursorManager.h" +#include "Types.h" /** * @file VM.h @@ -45,7 +47,50 @@ namespace nt * snapshots pinned through LifecycleManager::Monitor only for the minimum * time required. */ + /** + * @brief A node in the Volcano operator tree executed by the Tarski (FOL) runtime. + * + * Each node represents one relational algebra operator. The tree is demand-driven: + * VM::Next() propagates pull requests from root to leaves. + * + * TODO: Add FILTER, PROJECT, and JOIN operators as the query layer grows. + */ + struct NT_API PlanNode + { + enum class Op { SCAN }; + + Op op; + PlanNode* left = nullptr; /**< Left child (binary operators). */ + PlanNode* right = nullptr; /**< Right child (binary operators). */ + + /** Cursor to scan. Set when op == SCAN. */ + CursorManager::cursor* scan_cursor = nullptr; + }; + class NT_API VM { + public: + // TODO: CursorManager should be injected as a shared runtime instance, + // matching the pattern flagged for HandlerManager and LifecycleManager. + // + // A single CursorManager instance supports multiple simultaneous cursors — + // all iteration state lives in each cursor struct, not in the manager. + // A JOIN plan, for example, holds two independent cursors (one per SCAN leaf) + // and both are driven through the same CursorManager reference. + explicit VM(CursorManager& cursors); + + /** + * @brief Pulls the next tuple from the plan tree (Tarski/FOL core). + * + * Propagates a Next() call down the operator tree. The call chain + * terminates at a SCAN node, which delegates to CursorManager. + * + * @param node Root of the plan subtree to evaluate. + * @return Next matching tuple, or nullptr when the plan is exhausted. + */ + Tuple* Next(PlanNode* node); + + private: + CursorManager& cursors_; }; } diff --git a/launch.vs.json b/launch.vs.json new file mode 100644 index 0000000..4533583 --- /dev/null +++ b/launch.vs.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.1", + "configurations": [ + { + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "RNT.exe", + "name": "RNT.exe (Debug)", + "currentDir": "${workspaceRoot}" + } + ] +} diff --git a/src/CursorManager.cpp b/src/CursorManager.cpp index 8ae4496..f80ac54 100644 --- a/src/CursorManager.cpp +++ b/src/CursorManager.cpp @@ -1,5 +1,50 @@ #include "CursorManager.h" +#include <numeric> + namespace nt { + static std::string JoinPath(const std::vector<std::string>& path) + { + return std::accumulate( + std::next(path.begin()), path.end(), path.front(), + [](std::string a, const std::string& b) { return std::move(a) + "/" + b; }); + } + + CursorManager::cursor* CursorManager::Open(HandlerManager::handle* handle) + { + if (handle == nullptr || handle->object == nullptr) + return nullptr; + + if (handle->object->head->type->label != RELATION) + return nullptr; + + const std::string key = JoinPath(handle->object->head->path); + auto it = mock_store_.find(key); + if (it == mock_store_.end()) + return nullptr; + + auto* c = new cursor(); + c->handle = handle; + for (const auto& row : it->second) + c->tuples.emplace_back(row); + return c; + } + + Tuple* CursorManager::Next(cursor* cursor) + { + if (cursor == nullptr || cursor->position >= cursor->tuples.size()) + return nullptr; + return &cursor->tuples[cursor->position++]; + } + + void CursorManager::Close(cursor* cursor) + { + delete cursor; + } + + void CursorManager::MockInsert(std::vector<std::string> path, std::vector<Attribute> tuple) + { + mock_store_[JoinPath(path)].push_back(std::move(tuple)); + } } diff --git a/src/HandlerManager.cpp b/src/HandlerManager.cpp index 7d7015d..8149ff2 100644 --- a/src/HandlerManager.cpp +++ b/src/HandlerManager.cpp @@ -11,18 +11,24 @@ namespace nt delete handle; } + HandlerManager::HandlerManager(ObjectManager& objects, + PermissionsManager& permissions, + IdentityManager& identities, + LifecycleManager& lifecycles) + : objects_(objects) + , permissions_(permissions) + , identities_(identities) + , lifecycles_(lifecycles) + {} + struct HandlerManager::handle* HandlerManager::Open(std::vector<std::string> object_path, void* connection_context) { - ObjectManager objects; - PermissionsManager permissions; - IdentityManager identities; - LifecycleManager lifecycles; - ObjectManager::registry* retrieved_object = objects.Find((const std::vector<std::string>)object_path); - if (permissions.Access(retrieved_object, (const void*)connection_context) - && identities.CanOpen(retrieved_object) - && lifecycles.Contention(retrieved_object)) { + ObjectManager::registry* retrieved_object = objects_.Find((const std::vector<std::string>)object_path); + if (permissions_.Access(retrieved_object, (const void*)connection_context) + && identities_.CanOpen(retrieved_object) + && lifecycles_.Contention(retrieved_object)) { // Starts monitoring this guy - lifecycles.Monitor(retrieved_object); + lifecycles_.Monitor(retrieved_object); return new handle{ retrieved_object, connection_context }; // Return a successful handle } else { @@ -36,22 +42,19 @@ namespace nt if (handle == nullptr) { return false; } - PermissionsManager permissions; - IdentityManager identities; - LifecycleManager lifecycles; // Somehow grab the object pointer and connection (maybe drop it...) from the handle ObjectManager::registry* object = handle->object; void* connection_context = handle->connection_context; // Checking for permissions here may be relevant. // What if we have a handle but no permission? Spooky - if (permissions.Access(object, connection_context) - && identities.CanClose(object) + if (permissions_.Access(object, connection_context) + && identities_.CanClose(object) // TODO: I think that in this case we do not need to worry about contention. // But be careful to evaluate this scenario later. - //&& LifecycleManager::Contention(retrieved_object) + //&& lifecycles_.Contention(object) ) { // Stops monitoring this guy - lifecycles.Unmonitor(object); + lifecycles_.Unmonitor(object); DeallocateHandle(handle); return true; // Return a successful handle } diff --git a/src/LifecycleManager.cpp b/src/LifecycleManager.cpp index bcab4b5..a83c9e1 100644 --- a/src/LifecycleManager.cpp +++ b/src/LifecycleManager.cpp @@ -4,6 +4,8 @@ namespace nt { void LifecycleManager::Monitor(ObjectManager::registry* object) { + if (object != nullptr) + ++object->head->handle_count; } const bool LifecycleManager::Contention(ObjectManager::registry* object) @@ -13,5 +15,7 @@ namespace nt void LifecycleManager::Unmonitor(ObjectManager::registry* object) { + if (object != nullptr && object->head->handle_count > 0) + --object->head->handle_count; } } @@ -1,6 +1,14 @@ #include "Runtime.h" #ifdef NT_CONSOLE_APP +#include "CursorManager.h" +#include "HandlerManager.h" +#include "IdentityManager.h" +#include "LifecycleManager.h" +#include "ObjectManager.h" +#include "PermissionsManager.h" +#include "VM.h" + #include <iostream> #endif @@ -21,13 +29,76 @@ int main() { const nt::NT runtime; std::cout << "RNT running: " << (runtime.IsRunning() ? "yes" : "no") << '\n'; - const int exit_code = runtime.IsRunning() ? 0 : 1; + + // --- Mock: register RelationX in the object registry --- + nt::ObjectManager objects; + + auto type = std::make_unique<nt::ObjectManager::object_type>(); + type->label = RELATION; + type->disposable = false; + type->methods = { OPEN, CLOSE }; + + objects.Register({ "RelationX" }, std::make_unique<nt::ObjectManager::Relation>(), std::move(type)); + + // --- Mock: populate the cursor store with three tuples --- + nt::CursorManager cursors; + cursors.MockInsert({ "RelationX" }, { { "name", "Alice" }, { "age", "30" } }); + cursors.MockInsert({ "RelationX" }, { { "name", "Bob" }, { "age", "25" } }); + cursors.MockInsert({ "RelationX" }, { { "name", "Carol" }, { "age", "35" } }); + + // --- Open a handle on RelationX through the full manager pipeline --- + nt::PermissionsManager permissions; + nt::IdentityManager identities; + nt::LifecycleManager lifecycles; + nt::HandlerManager handler(objects, permissions, identities, lifecycles); + + int connection = 1; // dummy connection context + nt::HandlerManager::handle* handle = handler.Open({ "RelationX" }, &connection); + if (handle == nullptr) + { + std::cout << "Failed to open handle for RelationX\n"; + return 1; + } + + // --- Open a cursor on the relation --- + nt::CursorManager::cursor* cursor = cursors.Open(handle); + if (cursor == nullptr) + { + std::cout << "Failed to open cursor for RelationX\n"; + handler.Close(handle); + return 1; + } + + // --- Build a SCAN plan and pull all tuples lazily through the FOL VM --- + nt::PlanNode plan; + plan.op = nt::PlanNode::Op::SCAN; + plan.scan_cursor = cursor; + + nt::VM vm(cursors); + std::cout << "Tuples in RelationX:\n"; + while (nt::Tuple* t = vm.Next(&plan)) + { + std::cout << " "; + const nt::Attribute* attr = t->Next(); + while (attr != nullptr) + { + std::cout << attr->name << "=" << attr->value; + attr = t->Next(); + if (attr != nullptr) + std::cout << ", "; + } + std::cout << '\n'; + } + + // --- Cleanup --- + cursors.Close(cursor); + handler.Close(handle); #ifdef NT_WAIT_ON_EXIT std::cout << "Press Enter to exit..."; std::cin.get(); #endif - return exit_code; + return runtime.IsRunning() ? 0 : 1; } #endif diff --git a/src/ObjectManager.cpp b/src/ObjectManager.cpp index ff85af2..19f862d 100644 --- a/src/ObjectManager.cpp +++ b/src/ObjectManager.cpp @@ -2,8 +2,32 @@ namespace nt { - struct ObjectManager::registry* ObjectManager::Find(const std::vector<std::string> object_path) + void ObjectManager::Register(std::vector<std::string> path, + std::unique_ptr<IObject> object, + std::unique_ptr<object_type> type) { + auto head = std::make_unique<registry_head>(); + head->reference_count = 0; + head->handle_count = 0; + head->path = std::move(path); + head->type = std::move(type); + + auto entry = std::make_unique<registry>(); + entry->head = std::move(head); + entry->object = std::move(object); + entry->next = std::move(entries); + entries = std::move(entry); + } + + ObjectManager::registry* ObjectManager::Find(const std::vector<std::string> object_path) + { + registry* current = entries.get(); + while (current != nullptr) + { + if (current->head->path == object_path) + return current; + current = current->next.get(); + } return nullptr; } } @@ -2,4 +2,19 @@ namespace nt { + VM::VM(CursorManager& cursors) : cursors_(cursors) {} + + Tuple* VM::Next(PlanNode* node) + { + if (node == nullptr) + return nullptr; + + switch (node->op) + { + case PlanNode::Op::SCAN: + return cursors_.Next(node->scan_cursor); + } + + return nullptr; + } } |
