aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-05-09 23:03:00 -0300
committerMarcos Magueta <maguetamarcos@gmail.com>2026-05-09 23:03:00 -0300
commitc375b1c69665ae7c16ffa3f17264386dfc257d8e (patch)
treec0a45b370512ae6d4b4a9500c244419567a357ef
parent921de259fa441fb562aec701f0c8c14310f126e4 (diff)
downloadRNT-c375b1c69665ae7c16ffa3f17264386dfc257d8e.tar.gz
RNT-c375b1c69665ae7c16ffa3f17264386dfc257d8e.zip
FOL VM with cursor mocked storage
-rw-r--r--CLAUDE.md79
-rw-r--r--CMakeLists.txt38
-rw-r--r--CMakePresets.json63
-rw-r--r--RelationalNT.sln27
-rw-r--r--RelationalNT.vcxproj113
-rw-r--r--RelationalNT.vcxproj.filters94
-rw-r--r--include/CursorManager.h54
-rw-r--r--include/HandlerManager.h22
-rw-r--r--include/ObjectManager.h55
-rw-r--r--include/Types.h43
-rw-r--r--include/VM.h45
-rw-r--r--launch.vs.json12
-rw-r--r--src/CursorManager.cpp45
-rw-r--r--src/HandlerManager.cpp35
-rw-r--r--src/LifecycleManager.cpp4
-rw-r--r--src/NT.cpp75
-rw-r--r--src/ObjectManager.cpp26
-rw-r--r--src/VM.cpp15
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 &amp;&amp; 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;
}
}
diff --git a/src/NT.cpp b/src/NT.cpp
index 4d391a8..66752ef 100644
--- a/src/NT.cpp
+++ b/src/NT.cpp
@@ -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;
}
}
diff --git a/src/VM.cpp b/src/VM.cpp
index 4e69b8e..b5824d0 100644
--- a/src/VM.cpp
+++ b/src/VM.cpp
@@ -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;
+ }
}