diff --git a/include/arrow/table_info.hpp b/include/arrow/table_info.hpp index 8401068..c319fdc 100644 --- a/include/arrow/table_info.hpp +++ b/include/arrow/table_info.hpp @@ -8,19 +8,29 @@ namespace tundradb { +/// Precomputed per-column chunk boundaries for mapping logical row indices to +/// Arrow chunks. class TableInfo { public: + /// Builds cumulative row boundaries for each column from the table’s chunked + /// arrays. explicit TableInfo(const std::shared_ptr& table); + /// Identifies which Arrow chunk holds a logical row and the offset inside + /// that chunk. struct ChunkInfo { int chunk_index; int64_t offset_in_chunk; }; + /// Maps logical row_index in column column_index to a chunk and in-chunk + /// offset; throws if out of range. [[nodiscard]] ChunkInfo get_chunk_info(int column_index, int64_t row_index) const; + /// Number of columns (same as the source table). int num_columns() const { return chunk_boundaries_.size(); } + /// Total logical row count (same as table->num_rows() at construction). [[nodiscard]] int64_t num_rows() const { return num_rows_; } private: diff --git a/include/common/config.hpp b/include/common/config.hpp index 4a37bb0..bac8770 100644 --- a/include/common/config.hpp +++ b/include/common/config.hpp @@ -13,6 +13,8 @@ constexpr size_t MANAGER_MEMORY_POOL_SIZE = 100 * 1024 * 1024; // 100 MB constexpr size_t DATABASE_MEMORY_POOL_SIZE = 1024 * 1024 * 1024; // 1 GB } // namespace defaults +/// Immutable database tuning: shard sizing, memory pools, storage path, and +/// feature toggles. class DatabaseConfig { private: // Maximum number of nodes per shard @@ -42,73 +44,96 @@ class DatabaseConfig { friend class DatabaseConfigBuilder; public: + /// Max nodes per shard before layout or split policy applies. size_t get_shard_capacity() const { return shard_capacity; } + /// Row chunk size used when creating tables. size_t get_chunk_size() const { return chunk_size; } + /// Per-shard memory pool budget in bytes. size_t get_shard_memory_pool_size() const { return shard_memory_pool_size; } + /// Shard-manager memory pool budget in bytes. size_t get_manager_memory_pool_size() const { return manager_memory_pool_size; } + /// Top-level database memory pool budget in bytes. size_t get_database_memory_pool_size() const { return database_memory_pool_size; } + /// Filesystem directory for database files (empty if unset). std::string get_db_path() const { return db_path; } + /// Whether data is written through to durable storage. bool is_persistence_enabled() const { return persistence_enabled; } + /// Whether structural or data validation checks run on operations. bool is_validation_enabled() const { return validation_enabled; } + /// Whether temporal versioning (copy-on-write for time travel) is active. bool is_versioning_enabled() const { return versioning_enabled_; } }; +/// Fluent builder for \ref DatabaseConfig; initial field values match the +/// constants in namespace defaults. class DatabaseConfigBuilder { private: DatabaseConfig config; public: + /// Starts from default capacities, pools, path, and flags. DatabaseConfigBuilder() = default; + /// Sets \ref DatabaseConfig::get_shard_capacity. DatabaseConfigBuilder &with_shard_capacity(const size_t capacity) { config.shard_capacity = capacity; return *this; } + /// Sets \ref DatabaseConfig::get_chunk_size. DatabaseConfigBuilder &with_chunk_size(const size_t size) { config.chunk_size = size; return *this; } + /// Sets \ref DatabaseConfig::get_shard_memory_pool_size (bytes). DatabaseConfigBuilder &with_shard_memory_pool_size(const size_t size) { config.shard_memory_pool_size = size; return *this; } + /// Sets \ref DatabaseConfig::get_manager_memory_pool_size (bytes). DatabaseConfigBuilder &with_manager_memory_pool_size(const size_t size) { config.manager_memory_pool_size = size; return *this; } + /// Sets \ref DatabaseConfig::get_database_memory_pool_size (bytes). DatabaseConfigBuilder &with_database_memory_pool_size(const size_t size) { config.database_memory_pool_size = size; return *this; } + /// Sets \ref DatabaseConfig::get_db_path. DatabaseConfigBuilder &with_db_path(const std::string &directory) { config.db_path = directory; return *this; } + /// Sets \ref DatabaseConfig::is_persistence_enabled. DatabaseConfigBuilder &with_persistence_enabled(const bool enabled) { config.persistence_enabled = enabled; return *this; } + /// Sets \ref DatabaseConfig::is_validation_enabled. DatabaseConfigBuilder &with_validation_enabled(const bool enabled) { config.validation_enabled = enabled; return *this; } + /// Sets \ref DatabaseConfig::is_versioning_enabled. DatabaseConfigBuilder &with_versioning_enabled(const bool enabled) { config.versioning_enabled_ = enabled; return *this; } + /// Scales shard, manager, and database memory pools by \p factor from each + /// per-layer default byte size. DatabaseConfigBuilder &with_memory_scale_factor(const double factor) { config.shard_memory_pool_size = static_cast(defaults::SHARD_MEMORY_POOL_SIZE * factor); @@ -119,9 +144,12 @@ class DatabaseConfigBuilder { return *this; } + /// Returns the configured snapshot; safe to call multiple times (copies out). [[nodiscard]] DatabaseConfig build() const { return config; } }; +/// Convenience entry point equivalent to a default-constructed \ref +/// DatabaseConfigBuilder. inline DatabaseConfigBuilder make_config() { return {}; } } // namespace tundradb diff --git a/include/common/logger.hpp b/include/common/logger.hpp index 81a0c6a..04db9fa 100644 --- a/include/common/logger.hpp +++ b/include/common/logger.hpp @@ -12,13 +12,19 @@ namespace tundradb { enum class LogLevel { DEBUG, INFO, WARN, ERROR }; +/// Process-wide logging facade over spdlog: level, optional file sink, and +/// source-location-aware messages. class Logger { public: + /// Returns the singleton used by the `log_*` helpers declared later in this + /// header. static Logger& get_instance() { static Logger instance; return instance; } + /// Maps \ref LogLevel to the spdlog global cutoff (messages below it are + /// dropped). void set_level(LogLevel level) { switch (level) { case LogLevel::DEBUG: @@ -36,6 +42,8 @@ class Logger { } } + /// Current spdlog level coerced to \ref LogLevel (trace/off and similar map + /// to INFO). LogLevel get_level() { switch (spdlog::get_level()) { case spdlog::level::trace: @@ -57,6 +65,8 @@ class Logger { } } + /// Replaces the default logger with an appending file sink at \p filename; + /// failures are reported with spdlog::error. void set_log_to_file(const std::string& filename) { try { auto file_sink = @@ -69,65 +79,79 @@ class Logger { } } + /// DEBUG with explicit \p location (file basename and line appear in the log + /// line). template void debug(const std::source_location& location, spdlog::format_string_t fmt, Args&&... args) { log(spdlog::level::debug, location, fmt, std::forward(args)...); } + /// INFO with explicit \p location in the output pattern. template void info(const std::source_location& location, spdlog::format_string_t fmt, Args&&... args) { log(spdlog::level::info, location, fmt, std::forward(args)...); } + /// WARN with explicit \p location in the output pattern. template void warn(const std::source_location& location, spdlog::format_string_t fmt, Args&&... args) { log(spdlog::level::warn, location, fmt, std::forward(args)...); } + /// ERROR with explicit \p location in the output pattern. template void error(const std::source_location& location, spdlog::format_string_t fmt, Args&&... args) { log(spdlog::level::err, location, fmt, std::forward(args)...); } + /// DEBUG using \ref std::source_location::current for the callsite. template void debug(spdlog::format_string_t fmt, Args&&... args) { debug(std::source_location::current(), fmt, std::forward(args)...); } + /// INFO using \ref std::source_location::current for the callsite. template void info(spdlog::format_string_t fmt, Args&&... args) { info(std::source_location::current(), fmt, std::forward(args)...); } + /// WARN using \ref std::source_location::current for the callsite. template void warn(spdlog::format_string_t fmt, Args&&... args) { warn(std::source_location::current(), fmt, std::forward(args)...); } + /// ERROR using \ref std::source_location::current for the callsite. template void error(spdlog::format_string_t fmt, Args&&... args) { error(std::source_location::current(), fmt, std::forward(args)...); } + /// DEBUG for a plain string (no format placeholders); \p location defaults to + /// the callsite. void debug(const std::string& message, const std::source_location& location = std::source_location::current()) { debug(location, "{}", message); } + /// INFO for a plain string; \p location defaults to the callsite. void info(const std::string& message, const std::source_location& location = std::source_location::current()) { info(location, "{}", message); } + /// WARN for a plain string; \p location defaults to the callsite. void warn(const std::string& message, const std::source_location& location = std::source_location::current()) { warn(location, "{}", message); } + /// ERROR for a plain string; \p location defaults to the callsite. void error(const std::string& message, const std::source_location& location = std::source_location::current()) { error(location, "{}", message); @@ -222,10 +246,15 @@ inline void log_error( Logger::get_instance().error(message, location); } +/// Prefixes every emitted line (e.g. subsystem name) before forwarding to the +/// global \ref Logger. class ContextLogger { public: + /// \p prefix is copied and prepended as `prefix + ": " + message` for each + /// log call. explicit ContextLogger(std::string prefix) : prefix_(std::move(prefix)) {} + /// DEBUG with format string; output includes the stored prefix. template void debug(spdlog::format_string_t fmt, Args&&... args) { std::string message = @@ -233,6 +262,7 @@ class ContextLogger { log_debug(prefix_ + ": " + message); } + /// INFO with format string; output includes the stored prefix. template void info(spdlog::format_string_t fmt, Args&&... args) { std::string message = @@ -240,6 +270,7 @@ class ContextLogger { log_info(prefix_ + ": " + message); } + /// WARN with format string; output includes the stored prefix. template void warn(spdlog::format_string_t fmt, Args&&... args) { std::string message = @@ -247,6 +278,7 @@ class ContextLogger { log_warn(prefix_ + ": " + message); } + /// ERROR with format string; output includes the stored prefix. template void error(spdlog::format_string_t fmt, Args&&... args) { std::string message = @@ -254,21 +286,29 @@ class ContextLogger { log_error(prefix_ + ": " + message); } + /// DEBUG for a plain string; \p location is forwarded to the underlying + /// global log call. void debug(const std::string& message, const std::source_location& location = std::source_location::current()) { log_debug(prefix_ + ": " + message, location); } + /// INFO for a plain string; \p location is forwarded to the underlying global + /// log call. void info(const std::string& message, const std::source_location& location = std::source_location::current()) { log_info(prefix_ + ": " + message, location); } + /// WARN for a plain string; \p location is forwarded to the underlying global + /// log call. void warn(const std::string& message, const std::source_location& location = std::source_location::current()) { log_warn(prefix_ + ": " + message, location); } + /// ERROR for a plain string; \p location is forwarded to the underlying + /// global log call. void error(const std::string& message, const std::source_location& location = std::source_location::current()) { log_error(prefix_ + ": " + message, location); diff --git a/include/common/types.hpp b/include/common/types.hpp index 1fc31bb..3925fb7 100644 --- a/include/common/types.hpp +++ b/include/common/types.hpp @@ -18,34 +18,37 @@ namespace tundradb { +/// A type-erased, variant-backed value that can hold any field type. +/// +/// Supports scalar types (int32, int64, float, double, bool, string), +/// arena-backed containers (StringRef, ArrayRef, MapRef), and raw +/// containers (std::vector, std::map) that +/// are converted to arena refs on insertion into the arena. class Value { public: + /// Construct a NULL value (ValueType::NA). Value() : type_(ValueType::NA), data_(std::monostate{}) {} explicit Value(double v) : type_(ValueType::DOUBLE), data_(v) {} explicit Value(std::string v) : type_(ValueType::STRING), data_(std::move(v)) {} - explicit Value(StringRef v) - : type_(ValueType::STRING), - data_(v) {} // Store as StringRef for all string types + /// Construct from an arena-backed string reference. + explicit Value(StringRef v) : type_(ValueType::STRING), data_(v) {} - // Constructor for creating StringRef value with specific string type + /// Construct from a StringRef with an explicit string subtype + /// (e.g. FIXED_STRING16). Value(StringRef v, const ValueType string_type) : type_(string_type), data_(v) { assert(is_string_type(string_type)); } - // Arena-backed array (already allocated in ArrayArena) + /// Construct from an arena-backed array reference. explicit Value(ArrayRef v) : type_(ValueType::ARRAY), data_(std::move(v)) {} - - // Arena-backed map (already allocated in MapArena) + /// Construct from an arena-backed map reference. explicit Value(MapRef v) : type_(ValueType::MAP), data_(std::move(v)) {} - - // Raw map data - will be converted to MapRef by NodeArena + /// Construct from raw map data (converted to MapRef on arena insertion). explicit Value(std::map v) : type_(ValueType::MAP), data_(std::move(v)) {} - - // Raw array data - will be converted to ArrayRef by NodeArena - // (same pattern as std::string -> StringRef for strings) + /// Construct from raw array data (converted to ArrayRef on arena insertion). explicit Value(std::vector v) : type_(ValueType::ARRAY), data_(std::move(v)) {} @@ -75,33 +78,33 @@ class Value { [[nodiscard]] bool as_bool() const { return get(); } [[nodiscard]] bool is_null() const { return type_ == ValueType::NA; } - // Check if the Value contains a StringRef (vs std::string) + /// True when the string variant holds a StringRef (arena-backed). [[nodiscard]] bool holds_string_ref() const { return is_string_type(type_) && std::holds_alternative(data_); } - // Check if the Value contains a std::string + /// True when the string variant holds a std::string (heap-allocated). [[nodiscard]] bool holds_std_string() const { return is_string_type(type_) && std::holds_alternative(data_); } - // Check if the Value contains an ArrayRef (arena-backed) + /// True when the array variant holds an ArrayRef (arena-backed). [[nodiscard]] bool holds_array_ref() const { return type_ == ValueType::ARRAY && std::holds_alternative(data_); } - // Check if the Value contains a MapRef (arena-backed) + /// True when the map variant holds a MapRef (arena-backed). [[nodiscard]] bool holds_map_ref() const { return type_ == ValueType::MAP && std::holds_alternative(data_); } - // Check if the Value contains a raw map (std::map) + /// True when the map variant holds a raw std::map (not yet in arena). [[nodiscard]] bool holds_raw_map() const { return type_ == ValueType::MAP && std::holds_alternative>(data_); } - // Check if the Value contains a raw array (std::vector) + /// True when the array variant holds a raw std::vector (not yet in arena). [[nodiscard]] bool holds_raw_array() const { return type_ == ValueType::ARRAY && std::holds_alternative>(data_); @@ -123,6 +126,7 @@ class Value { return std::get>(data_); } + /// Append a single element (or splice another raw array) to this array. arrow::Status append_element(Value element) { if (element.holds_raw_array()) { return append_all(std::move(element)); @@ -164,8 +168,7 @@ class Value { return arrow::Status::OK(); } - // Convert the Value to its raw string representation (without quotes for - // strings) + /// Convert the value to a human-readable string (no quotes around strings). [[nodiscard]] std::string to_string() const { switch (type_) { case ValueType::NA: @@ -234,6 +237,7 @@ class Value { } } + /// Reinterpret a raw memory pointer as a Value of the given type. static Value read_value_from_memory(const char* ptr, const ValueType type) { if (ptr == nullptr) { return Value{}; @@ -284,6 +288,10 @@ class Value { data_; }; +/// A lightweight, non-owning reference to a typed value in arena memory. +/// +/// ValueRef avoids copying by pointing directly into the arena. The +/// caller must ensure the referenced memory outlives the ValueRef. struct ValueRef { const char* data; ValueType type; @@ -328,6 +336,7 @@ struct ValueRef { return *reinterpret_cast(data); } + /// Convert the referenced value to an Arrow Scalar for compute kernels. arrow::Result> as_scalar() const { switch (type) { case ValueType::INT32: @@ -426,7 +435,7 @@ struct ValueRef { return *this == other; } - // todo rename + /// Human-readable string representation (strings are quoted). std::string ToString() const { if (data == nullptr) { return "NULL"; diff --git a/include/common/utils.hpp b/include/common/utils.hpp index adc43d3..46ec2b4 100644 --- a/include/common/utils.hpp +++ b/include/common/utils.hpp @@ -18,9 +18,9 @@ #include "arrow/utils.hpp" #include "common/constants.hpp" #include "common/logger.hpp" +#include "common/types.hpp" #include "core/node.hpp" #include "query/query.hpp" -#include "common/types.hpp" namespace tundradb { diff --git a/include/core/edge.hpp b/include/core/edge.hpp index 92447ac..bc81b91 100644 --- a/include/core/edge.hpp +++ b/include/core/edge.hpp @@ -8,15 +8,21 @@ #include #include "common/constants.hpp" +#include "common/types.hpp" #include "core/edge_view.hpp" +#include "core/update_type.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "memory/schema_layout.hpp" #include "query/temporal_context.hpp" -#include "common/types.hpp" -#include "core/update_type.hpp" +#include "schema/schema.hpp" namespace tundradb { + +/// A directed, typed relationship between two nodes. +/// +/// Edges always carry structural fields (id, source_id, target_id, type, +/// created_ts). When an edge schema has been registered the edge also +/// owns an arena-backed property store accessed through get_value / update. class Edge { private: const int64_t id_; @@ -31,6 +37,7 @@ class Edge { std::shared_ptr layout_; public: + /// Construct a lightweight edge without property storage. Edge(int64_t id, int64_t source_id, int64_t target_id, std::string type, int64_t created_ts) : id_(id), @@ -39,6 +46,7 @@ class Edge { type_(std::move(type)), created_ts_(created_ts) {} + /// Construct an arena-backed edge with property fields. Edge(int64_t id, int64_t source_id, int64_t target_id, std::string type, int64_t created_ts, std::unique_ptr handle, std::shared_ptr arena, std::shared_ptr schema, @@ -64,6 +72,7 @@ class Edge { [[nodiscard]] const std::string& get_type() const { return type_; } [[nodiscard]] int64_t get_created_ts() const { return created_ts_; } + /// True when the edge has a registered schema and arena-backed properties. [[nodiscard]] bool has_schema() const { return schema_ != nullptr; } [[nodiscard]] std::shared_ptr get_schema() const { return schema_; } [[nodiscard]] std::shared_ptr get_layout() const { @@ -72,8 +81,9 @@ class Edge { [[nodiscard]] NodeHandle* get_handle() const { return handle_.get(); } [[nodiscard]] NodeArena* get_arena() const { return arena_.get(); } - // --- Unified field access --- - + /// Read a field value by Field descriptor. + /// Structural fields (id, source_id, target_id, created_ts) are returned + /// directly; user-defined properties are read from the arena. [[nodiscard]] arrow::Result get_value( const std::shared_ptr& field) const { if (field && (field->name() == field_names::kId || @@ -93,6 +103,7 @@ class Edge { return NodeArena::get_value(*handle_, layout_, field); } + /// Return a raw pointer to the field's in-memory representation. [[nodiscard]] arrow::Result get_value_ptr( const std::shared_ptr& field) const { if (!field) { @@ -114,6 +125,7 @@ class Edge { return arrow::Status::KeyError("Field not found: ", field->name()); } + /// Apply a batch of field updates atomically (one new version). arrow::Result update_fields(const std::vector& updates) { if (!arena_ || !handle_) { return arrow::Status::Invalid( @@ -122,11 +134,14 @@ class Edge { return arena_->apply_updates(*handle_, layout_, updates); } + /// Update a single field (convenience wrapper around update_fields). arrow::Result update(const std::shared_ptr& field, Value value, UpdateType update_type = UpdateType::SET) { return update_fields({{field, std::move(value), update_type}}); } + /// Create a point-in-time view of this edge. + /// When @p ctx is nullptr the current (latest) version is used. EdgeView view(TemporalContext* ctx = nullptr) { if (!ctx) { VersionInfo* vi = handle_ ? handle_->version_info_ : nullptr; diff --git a/include/core/edge_store.hpp b/include/core/edge_store.hpp index 8626fdf..0c49253 100644 --- a/include/core/edge_store.hpp +++ b/include/core/edge_store.hpp @@ -12,14 +12,20 @@ #include #include "common/concurrency.hpp" +#include "common/utils.hpp" #include "core/edge.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "memory/schema_layout.hpp" -#include "common/utils.hpp" +#include "schema/schema.hpp" namespace tundradb { +/// Central registry and index for all edges in the database. +/// +/// Maintains directional adjacency indexes (outgoing/incoming), a +/// type-based grouping, and an optional per-type Arrow Table cache for +/// query materialisation. Edge schemas may be registered to add +/// arena-backed property fields to edges of a given type. class EdgeStore { struct TableCache; @@ -81,24 +87,25 @@ class EdgeStore { tables_.clear(); } + /// Current value of the edge ID sequence. int64_t get_edge_id_counter() const { return edge_id_counter_; } - // --- Edge schema management --- - + /// Register a typed property schema for edges of @p edge_type. arrow::Result register_edge_schema( const std::string &edge_type, const std::vector> &fields); + /// True when a property schema has been registered for this edge type. [[nodiscard]] bool has_edge_schema(const std::string &edge_type) const; + /// Return the Schema for a registered edge type (nullptr if none). [[nodiscard]] std::shared_ptr get_edge_schema( const std::string &edge_type) const; + /// Return the memory layout for a registered edge type. [[nodiscard]] std::shared_ptr get_edge_layout( const std::string &edge_type) const; - // --- Edge CRUD --- - /** * Creates a new edge at runtime (user API / `connect`). * @@ -138,17 +145,20 @@ class EdgeStore { */ arrow::Result add(const std::shared_ptr &edge); + /// Remove an edge from all indexes. arrow::Result remove(int64_t edge_id); + /// Look up a single edge by ID. arrow::Result> get(int64_t edge_id) const; + /// Bulk-fetch edges by a set of IDs. std::vector> get(const std::set &ids) const; - // Template overload for any iterable container (including - // ConcurrentSet::LockedView) + /// Bulk-fetch edges from any iterable container of IDs. template std::vector> get(const Container &ids) const; + /// Return the number of edges for a given type (0 if type does not exist). int64_t get_count_by_type(const std::string &type) const { if (auto res = get_by_type(type); res.ok()) { return res.ValueOrDie().size(); @@ -156,29 +166,33 @@ class EdgeStore { return 0; } + /// Return all outgoing edges from a node, optionally filtered by type. arrow::Result>> get_outgoing_edges( int64_t id, const std::string &type = "") const; + /// Return all incoming edges to a node, optionally filtered by type. arrow::Result>> get_incoming_edges( int64_t id, const std::string &type = "") const; + /// Return all edges of a given type. arrow::Result>> get_by_type( const std::string &type) const; + /// Materialise edges of a type into a cached Arrow Table. arrow::Result> get_table( const std::string &edge_type); + /// Return the current mutation version counter for an edge type. arrow::Result get_version(const std::string &edge_type) const; + /// Return the set of all registered edge type names. std::set get_edge_types() const; int64_t get_chunk_size() const { return chunk_size_; } - size_t size() const { return edges.size(); } - bool empty() const { return edges.empty(); } - // todo temporary solution, add initialize instead + /// Override the edge ID sequence (used during snapshot restore). void set_id_seq(const int64_t v) { edge_id_counter_.store(v, std::memory_order_relaxed); } diff --git a/include/core/edge_view.hpp b/include/core/edge_view.hpp index 79b8f53..3d6cdd5 100644 --- a/include/core/edge_view.hpp +++ b/include/core/edge_view.hpp @@ -5,14 +5,20 @@ #include +#include "common/types.hpp" #include "memory/node_arena.hpp" #include "schema/schema.hpp" -#include "common/types.hpp" namespace tundradb { class Edge; +/// A read-only, point-in-time projection of an Edge. +/// +/// When temporal versioning is active, the view resolves field values to +/// the version that was current at the requested snapshot. Structural +/// fields (id, source_id, target_id, created_ts) are always delegated to +/// the owning Edge. class EdgeView { private: Edge* edge_; @@ -26,18 +32,26 @@ class EdgeView { resolved_version_(resolved_version), layout_(std::move(layout)) {} + /// Read a field value, resolving through the version chain if needed. arrow::Result get_value(const std::shared_ptr& field) const; + + /// Return a raw pointer to the field's in-memory representation. arrow::Result get_value_ptr( const std::shared_ptr& field) const; + + /// Return a lightweight non-owning reference to the field value. arrow::Result get_value_ref( const std::shared_ptr& field) const; + /// True when the edge has a visible version at the requested snapshot. [[nodiscard]] bool is_visible() const; + /// The resolved version (nullptr for non-versioned or current edges). [[nodiscard]] const VersionInfo* get_resolved_version() const { return resolved_version_; } + /// The underlying Edge pointer. [[nodiscard]] Edge* get_edge() const { return edge_; } }; diff --git a/include/core/field_update.hpp b/include/core/field_update.hpp index 36c6714..3af40ca 100644 --- a/include/core/field_update.hpp +++ b/include/core/field_update.hpp @@ -6,9 +6,9 @@ #include #include -#include "schema/schema.hpp" #include "common/types.hpp" #include "core/update_type.hpp" +#include "schema/schema.hpp" namespace tundradb { diff --git a/include/core/node.hpp b/include/core/node.hpp index 33cfbf4..acd5fe1 100644 --- a/include/core/node.hpp +++ b/include/core/node.hpp @@ -8,15 +8,21 @@ #include "common/constants.hpp" #include "common/logger.hpp" -#include "memory/node_arena.hpp" -#include "core/node_view.hpp" -#include "schema/schema.hpp" -#include "query/temporal_context.hpp" #include "common/types.hpp" +#include "core/node_view.hpp" #include "core/update_type.hpp" +#include "memory/node_arena.hpp" +#include "query/temporal_context.hpp" +#include "schema/schema.hpp" namespace tundradb { +/// A graph node backed by an arena-allocated memory block. +/// +/// Every node belongs to a schema and stores its fields in a NodeArena +/// via a NodeHandle. Fields are read through get_value / get_value_ptr +/// and written through update / update_fields. Temporal views are +/// created with view(). class Node { private: std::unique_ptr handle_; @@ -40,6 +46,7 @@ class Node { id(id), schema_name(std::move(schema_name)) {} + /// Return a raw pointer to the field's in-memory representation. arrow::Result get_value_ptr( const std::shared_ptr &field) const { if (arena_ != nullptr) { @@ -48,14 +55,14 @@ class Node { return arrow::Status::NotImplemented(""); } + /// Return a lightweight non-owning reference to the field value. [[nodiscard]] ValueRef get_value_ref( const std::shared_ptr &field) const { const char *ptr = arena_->get_value_ptr(*handle_, layout_, field); return {ptr, field->type()}; } - // --- Unified field access --- - + /// Read a field value by Field descriptor (returns a copy). arrow::Result get_value(const std::shared_ptr &field) const { if (!arena_ || !handle_) { return arrow::Status::Invalid( @@ -68,8 +75,7 @@ class Node { [[nodiscard]] NodeHandle *get_handle() const { return handle_.get(); } [[nodiscard]] NodeArena *get_arena() const { return arena_.get(); } - // --- Unified update --- - + /// Apply a batch of field updates atomically (one new version). arrow::Result update_fields(const std::vector &updates) { if (!arena_ || !handle_) { return arrow::Status::Invalid( @@ -78,11 +84,13 @@ class Node { return arena_->apply_updates(*handle_, layout_, updates); } + /// Update a single field (convenience wrapper around update_fields). arrow::Result update(const std::shared_ptr &field, Value value, UpdateType update_type = UpdateType::SET) { return update_fields({{field, std::move(value), update_type}}); } + /// Shorthand for update(field, value, UpdateType::SET). arrow::Result set_value(const std::shared_ptr &field, const Value &value) { return update(field, value, UpdateType::SET); @@ -112,8 +120,16 @@ class Node { } }; +/// Owns the shared NodeArena and manages per-schema node collections. +/// +/// Responsible for creating, retrieving, and removing nodes, assigning +/// auto-incremented per-schema IDs, and validating field types and +/// required constraints when validation is enabled. class NodeManager { public: + /// @param validation_enabled Check field types and required constraints. + /// @param use_node_arena Must be true (non-arena path is removed). + /// @param enable_versioning Enable temporal version chains in the arena. explicit NodeManager(std::shared_ptr schema_registry, const bool validation_enabled = true, const bool use_node_arena = true, @@ -130,6 +146,7 @@ class NodeManager { ~NodeManager() = default; + /// Look up a node by schema name and ID. arrow::Result> get_node(const std::string &schema_name, const int64_t id) { auto schema_it = nodes_.find(schema_name); @@ -145,6 +162,7 @@ class NodeManager { return node_it->second; } + /// Remove a node from the in-memory index. Returns false if not found. bool remove_node(const std::string &schema_name, const int64_t id) { auto schema_it = nodes_.find(schema_name); if (schema_it == nodes_.end()) { @@ -153,6 +171,9 @@ class NodeManager { return schema_it->second.erase(id) > 0; } + /// Create a new node, allocate arena storage, and populate initial fields. + /// @param add When true, the caller supplies the "id" value (used during + /// snapshot restore); otherwise an auto-incremented ID is used. arrow::Result> create_node( const std::string &schema_name, const std::unordered_map &data, @@ -237,10 +258,12 @@ class NodeManager { return node; } + /// Override the next-ID counter for a schema (used during restore). void set_id_counter(const std::string &schema_name, const int64_t value) { id_counters_[schema_name].store(value); } + /// Return the current value of the per-schema ID counter. int64_t get_id_counter(const std::string &schema_name) const { auto it = id_counters_.find(schema_name); if (it == id_counters_.end()) { @@ -249,7 +272,7 @@ class NodeManager { return it->second.load(); } - // Get all schema ID counters (for snapshot/manifest) + /// Return all per-schema ID counters (for snapshot/manifest persistence). std::unordered_map get_all_id_counters() const { std::unordered_map result; for (const auto &[schema_name, counter] : id_counters_) { @@ -258,7 +281,7 @@ class NodeManager { return result; } - // Set all schema ID counters (for snapshot/manifest restore) + /// Restore all per-schema ID counters from a snapshot/manifest. void set_all_id_counters( const std::unordered_map &counters) { for (const auto &[schema_name, value] : counters) { diff --git a/include/core/node_view.hpp b/include/core/node_view.hpp index ee9c903..dccb21a 100644 --- a/include/core/node_view.hpp +++ b/include/core/node_view.hpp @@ -5,10 +5,10 @@ #include +#include "common/types.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "query/temporal_context.hpp" -#include "common/types.hpp" +#include "schema/schema.hpp" namespace tundradb { diff --git a/include/main/database.hpp b/include/main/database.hpp index 9ec7bb7..0bd24a5 100644 --- a/include/main/database.hpp +++ b/include/main/database.hpp @@ -11,21 +11,27 @@ #include "arrow/utils.hpp" #include "common/config.hpp" +#include "common/logger.hpp" +#include "common/utils.hpp" #include "core/edge_store.hpp" #include "core/field_update.hpp" -#include "common/logger.hpp" -#include "storage/metadata.hpp" #include "core/node.hpp" -#include "query/query.hpp" #include "query/execution.hpp" +#include "query/query.hpp" #include "schema/schema.hpp" +#include "storage/metadata.hpp" #include "storage/shard.hpp" #include "storage/snapshot_manager.hpp" #include "storage/storage.hpp" -#include "common/utils.hpp" namespace tundradb { +/// The main entry point for interacting with TundraDB. +/// +/// Owns the schema registry, shard manager, node manager, edge store, +/// and (optionally) persistence subsystems. All public methods are +/// thread-compatible; external synchronisation is required for concurrent +/// access. class Database { private: std::shared_ptr schema_registry_; @@ -39,6 +45,9 @@ class Database { std::shared_ptr edge_store_; public: + /// Construct a database from the given configuration. + /// When persistence is enabled, the storage, metadata, and snapshot + /// subsystems are initialised from `config.get_db_path()`. explicit Database(const DatabaseConfig &config = DatabaseConfig()) : schema_registry_(std::make_shared()), shard_manager_( @@ -73,18 +82,25 @@ class Database { } } + /// Return a copy of the configuration used to create this database. DatabaseConfig get_config() const { return config_; } + /// Return the schema registry (shared ownership). std::shared_ptr get_schema_registry() { return schema_registry_; } + /// Return the metadata manager (nullptr when persistence is disabled). std::shared_ptr get_metadata_manager() { return metadata_manager_; } + /// Return the node manager (shared ownership). std::shared_ptr get_node_manager() { return node_manager_; } + /// Initialise persistence subsystems (storage, metadata, snapshots). + /// Must be called once after construction when persistence is enabled. + /// Returns `true` on success or skips silently when persistence is off. arrow::Result initialize() { if (persistence_enabled_) { auto storage_init = this->storage_->initialize(); @@ -105,6 +121,9 @@ class Database { return true; } + /// Create a new node in the given schema with the supplied field values. + /// The node is assigned a unique auto-incremented ID and inserted into + /// the appropriate shard. arrow::Result> create_node( const std::string &schema_name, const std::unordered_map &data) { @@ -117,6 +136,8 @@ class Database { return node; } + /// Update a single field on a node identified by schema name and ID. + /// @param field Resolved field descriptor. arrow::Result update_node(const std::string &schema_name, const int64_t id, const std::shared_ptr &field, @@ -126,6 +147,7 @@ class Database { update_type); } + /// Update a single field on a node, looked up by field name. arrow::Result update_node(const std::string &schema_name, const int64_t id, const std::string &field_name, @@ -146,6 +168,7 @@ class Database { update_type); } + /// Remove a node from both the node manager and its shard. arrow::Result remove_node(const std::string &schema_name, int64_t node_id) { if (auto res = node_manager_->remove_node(schema_name, node_id); !res) { @@ -155,12 +178,15 @@ class Database { return shard_manager_->remove_node(schema_name, node_id); } + /// Register a typed edge schema so that edges of @p edge_type can carry + /// the given property fields. arrow::Result register_edge_schema( const std::string &edge_type, const std::vector> &fields) { return edge_store_->register_edge_schema(edge_type, fields); } + /// Create an edge from @p source_id to @p target_id with the given type. arrow::Result connect(const int64_t source_id, const std::string &type, const int64_t target_id) { const auto edge = @@ -169,6 +195,7 @@ class Database { return true; } + /// Create an edge with property values attached. arrow::Result connect( const int64_t source_id, const std::string &type, const int64_t target_id, std::unordered_map properties) { @@ -179,24 +206,33 @@ class Database { return true; } + /// Remove an edge by its unique ID. arrow::Result remove_edge(const int64_t edge_id) { return edge_store_->remove(edge_id); } + /// Compact shards for a single schema, merging small shards together. arrow::Result compact(const std::string &schema_name) { return shard_manager_->compact(schema_name); } + /// Return the edge store (shared ownership). [[nodiscard]] std::shared_ptr get_edge_store() const { return edge_store_; } + /// Return the shard manager (shared ownership). [[nodiscard]] std::shared_ptr get_shard_manager() const { return shard_manager_; } + /// Compact shards for every registered schema. arrow::Result compact_all() { return shard_manager_->compact_all(); } + /// Materialise all nodes of a schema into an Arrow Table. + /// @param temporal_context Optional temporal filter; when non-null, only + /// field versions visible at the requested point-in-time are included. + /// @param chunk_size Maximum number of rows per Arrow record batch. arrow::Result> get_table( const std::string &schema_name, TemporalContext *temporal_context = nullptr, @@ -225,6 +261,7 @@ class Database { return create_table(schema, all_nodes, chunk_size, temporal_context); } + /// Return the number of shards backing the given schema. arrow::Result get_shard_count(const std::string &schema_name) const { if (!schema_registry_->exists(schema_name)) { return arrow::Status::Invalid("Schema '", schema_name, "' not found"); @@ -232,6 +269,7 @@ class Database { return shard_manager_->get_shard_count(schema_name); } + /// Return the node count in each shard for the given schema. arrow::Result> get_shard_sizes( const std::string &schema_name) const { if (!schema_registry_->exists(schema_name)) { @@ -240,6 +278,7 @@ class Database { return shard_manager_->get_shard_sizes(schema_name); } + /// Return the [min_id, max_id] range for each shard of the given schema. arrow::Result>> get_shard_ranges( const std::string &schema_name) const { if (!schema_registry_->exists(schema_name)) { @@ -248,10 +287,13 @@ class Database { return shard_manager_->get_shard_ranges(schema_name); } + /// Persist the current database state to disk as a new snapshot. + /// Requires persistence to be enabled. arrow::Result create_snapshot() { return snapshot_manager_->commit(); } + /// Execute a read-only query and return the result set as an Arrow Table. [[nodiscard]] arrow::Result> query( const Query &query) const; diff --git a/include/memory/array_arena.hpp b/include/memory/array_arena.hpp index dde43f9..97338bc 100644 --- a/include/memory/array_arena.hpp +++ b/include/memory/array_arena.hpp @@ -9,10 +9,10 @@ #include #include +#include "common/value_type.hpp" #include "memory/array_ref.hpp" #include "memory/free_list_arena.hpp" #include "memory/string_ref.hpp" -#include "common/value_type.hpp" namespace tundradb { diff --git a/include/memory/free_list_arena.hpp b/include/memory/free_list_arena.hpp index 548f5d7..876ba7d 100644 --- a/include/memory/free_list_arena.hpp +++ b/include/memory/free_list_arena.hpp @@ -162,16 +162,23 @@ class FreeListArena : public MemArena { */ // Statistics + /// Total bytes reserved in all chunks (unchanged by reset; cleared by + /// clear()). size_t get_total_allocated() const override { return total_allocated_; } // Total chunk memory + /// Cumulative bytes returned to the free list (used for fragmentation + /// metrics). size_t get_freed_bytes() const { return freed_bytes_; } // Cumulative deallocations (for fragmentation) + /// Sum of live allocated block sizes (decreases on deallocate). size_t get_used_bytes() const { return total_used_; } // Currently used memory + /// Number of allocated chunks. size_t get_chunk_count() const override { return chunks_.size(); } + /// Number of distinct free blocks indexed in the size map. size_t get_free_block_count() const { size_t count = 0; for (const auto& blocks : free_blocks_by_size_ | std::views::values) { @@ -180,6 +187,7 @@ class FreeListArena : public MemArena { return count; } + /// Ratio of cumulative freed bytes to total chunk memory (0 if no chunks). double get_fragmentation_ratio() const { if (total_allocated_ == 0) return 0.0; return static_cast(freed_bytes_) / total_allocated_; diff --git a/include/memory/map_arena.hpp b/include/memory/map_arena.hpp index de75c95..94fe6aa 100644 --- a/include/memory/map_arena.hpp +++ b/include/memory/map_arena.hpp @@ -9,12 +9,12 @@ #include #include +#include "common/types.hpp" +#include "common/value_type.hpp" #include "memory/array_ref.hpp" #include "memory/free_list_arena.hpp" #include "memory/map_ref.hpp" #include "memory/string_ref.hpp" -#include "common/types.hpp" -#include "common/value_type.hpp" namespace tundradb { diff --git a/include/memory/map_ref.hpp b/include/memory/map_ref.hpp index 6354acd..b5073f4 100644 --- a/include/memory/map_ref.hpp +++ b/include/memory/map_ref.hpp @@ -8,8 +8,8 @@ #include #include "common/constants.hpp" -#include "memory/string_ref.hpp" #include "common/value_type.hpp" +#include "memory/string_ref.hpp" namespace tundradb { @@ -63,6 +63,8 @@ static_assert(sizeof(MapEntry) == 40, "MapEntry must be 40 bytes"); */ class MapRef { public: + /// In-memory header preceding map entry storage (refcount, flags, size, + /// owning arena). struct MapHeader { std::atomic ref_count; // 4 bytes uint32_t flags; // 4 bytes - bit 0: marked_for_deletion @@ -70,9 +72,12 @@ class MapRef { uint32_t capacity; // 4 bytes - allocated entry slots MapArena* arena; // 8 bytes - owning arena (for release) + /// True if this map block has been marked for deferred reclamation. [[nodiscard]] bool is_marked_for_deletion() const { return (flags & arena_flags::kMarkedForDeletion) != 0; } + /// Sets the marked-for-deletion flag (arena may collect when refcount + /// allows). void mark_for_deletion() { flags |= arena_flags::kMarkedForDeletion; } }; @@ -132,40 +137,50 @@ class MapRef { // PUBLIC INTERFACE // ======================================================================== + /// Raw pointer to the first MapEntry (nullptr when null ref). [[nodiscard]] char* data() const { return data_; } + /// Current number of key-value entries. [[nodiscard]] uint32_t count() const { const auto* h = get_header(); return h ? h->count : 0; } + /// Allocated entry slot count (may be greater than count()). [[nodiscard]] uint32_t capacity() const { const auto* h = get_header(); return h ? h->capacity : 0; } + /// True if this handle does not reference map storage. [[nodiscard]] bool is_null() const { return data_ == nullptr; } + /// True if there are no entries (null refs are considered empty). [[nodiscard]] bool empty() const { return count() == 0; } + /// Const pointer to the i-th entry; \p i must be less than count(). [[nodiscard]] const MapEntry* entry_ptr(uint32_t i) const { assert(i < count()); return reinterpret_cast(data_ + static_cast(i) * sizeof(MapEntry)); } + /// Mutable pointer to slot \p i; \p i must be less than capacity(). [[nodiscard]] MapEntry* mutable_entry_ptr(uint32_t i) const { assert(i < capacity()); return reinterpret_cast(data_ + static_cast(i) * sizeof(MapEntry)); } + /// True if another entry can be added without growing the backing block. [[nodiscard]] bool has_capacity() const { return count() < capacity(); } + /// Header refcount, or 0 when null. [[nodiscard]] int32_t get_ref_count() const { const auto* h = get_header(); return h ? h->ref_count.load(std::memory_order_relaxed) : 0; } + /// True if the header exists and is marked for deletion. [[nodiscard]] bool is_marked_for_deletion() const { const auto* h = get_header(); return h && h->is_marked_for_deletion(); @@ -190,6 +205,8 @@ class MapRef { // OPERATORS // ======================================================================== + /// Equality: same backing pointer, or both null, or same entry bytes when + /// counts match. bool operator==(const MapRef& other) const { if (data_ == other.data_) return true; if (is_null() && other.is_null()) return true; @@ -198,6 +215,7 @@ class MapRef { return std::memcmp(data_, other.data_, count() * sizeof(MapEntry)) == 0; } + /// Negation of operator==. bool operator!=(const MapRef& other) const { return !(*this == other); } private: diff --git a/include/memory/memory_arena.hpp b/include/memory/memory_arena.hpp index 56a12df..864f10e 100644 --- a/include/memory/memory_arena.hpp +++ b/include/memory/memory_arena.hpp @@ -112,9 +112,13 @@ class MemoryArena : public MemArena { } // Statistics + /// Sum of sizes passed to successful allocate() calls since the last reset. size_t get_total_allocated() const override { return total_allocated_; } + /// Number of underlying chunk buffers. size_t get_chunk_count() const override { return chunks_.size(); } + /// Bytes used in the active chunk from its start (next allocation offset). size_t get_current_chunk_usage() const { return current_offset_; } + /// Size in bytes of the current (active) chunk. size_t get_current_chunk_size() const { return chunk_size_; } protected: @@ -151,19 +155,23 @@ class ArenaAllocator { template ArenaAllocator(const ArenaAllocator& other) : arena_(other.arena_) {} + /// Allocates \p n elements from the backing MemoryArena (no individual free). T* allocate(size_t n) { return static_cast(arena_->allocate(n * sizeof(T), alignof(T))); } + /// No-op; bulk release uses MemoryArena::reset/clear. void deallocate(T*, size_t) { // Arena handles deallocation in bulk } + /// True if both allocators use the same MemoryArena instance. template bool operator==(const ArenaAllocator& other) const { return arena_ == other.arena_; } + /// True if the backing arenas differ. template bool operator!=(const ArenaAllocator& other) const { return !(*this == other); diff --git a/include/memory/node_arena.hpp b/include/memory/node_arena.hpp index 48f8d78..ce2f17d 100644 --- a/include/memory/node_arena.hpp +++ b/include/memory/node_arena.hpp @@ -11,17 +11,17 @@ #include #include -#include "memory/array_arena.hpp" #include "common/clock.hpp" +#include "common/types.hpp" #include "core/field_update.hpp" +#include "core/update_type.hpp" +#include "memory/array_arena.hpp" #include "memory/free_list_arena.hpp" #include "memory/map_arena.hpp" #include "memory/mem_arena.hpp" #include "memory/memory_arena.hpp" #include "memory/schema_layout.hpp" #include "memory/string_arena.hpp" -#include "common/types.hpp" -#include "core/update_type.hpp" namespace tundradb { @@ -62,25 +62,26 @@ struct VersionInfo { VersionInfo() = default; - // Constructor: initializes both valid and tx times to the same value VersionInfo(uint64_t vid, uint64_t ts_from, VersionInfo* prev_ver = nullptr) : version_id(vid), valid_from(ts_from), tx_from(ts_from), // Initially tx_from = valid_from prev(prev_ver) {} - // Check if valid at a specific VALIDTIME + /// Returns true if VALIDTIME \p ts lies in [valid_from, valid_to). bool is_valid_at(uint64_t ts) const { return valid_from <= ts && ts < valid_to; } - // Check if visible at a bitemporal snapshot (valid_time, tx_time) + /// Returns true if this version is visible at the given VALIDTIME and + /// TXNTIME. bool is_visible_at(uint64_t valid_time, uint64_t tx_time) const { return (valid_from <= valid_time && valid_time < valid_to) && (tx_from <= tx_time && tx_time < tx_to); } - // Find version visible at bitemporal snapshot + /// Walks the version chain and returns the version visible at the snapshot, + /// or nullptr. const VersionInfo* find_version_at_snapshot(uint64_t valid_time, uint64_t tx_time) const { const VersionInfo* current = this; @@ -93,7 +94,8 @@ struct VersionInfo { return nullptr; } - // Legacy: find version at VALIDTIME only (ignores TXNTIME) + /// Walks the chain using VALIDTIME only (ignores TXNTIME); nullptr if none + /// matches. const VersionInfo* find_version_at_time(uint64_t ts) const { const VersionInfo* current = this; while (current != nullptr) { @@ -103,6 +105,7 @@ struct VersionInfo { return nullptr; } + /// Number of versions in this chain (including this node). size_t count_versions() const { size_t count = 1; const VersionInfo* current = prev; @@ -113,15 +116,20 @@ struct VersionInfo { return count; } + /// True if effective value for \p field_idx is present in the lazy field + /// cache. bool is_field_cached(uint16_t field_idx) const { if (field_idx >= 64) return field_cache_.count(field_idx) > 0; return (cache_bitset_ & (1ULL << field_idx)) != 0; } + /// Records that the effective value for \p field_idx is cached (fast path + /// uses a bitset). void mark_field_cached(uint16_t field_idx) const { if (field_idx < 64) cache_bitset_ |= (1ULL << field_idx); } + /// Clears the lazy field cache (bitset and map). void clear_cache() const { field_cache_.clear(); cache_bitset_ = 0; @@ -195,42 +203,57 @@ struct NodeHandle { version_info_ = nullptr; } + /// True if this handle does not reference node storage. bool is_null() const { return ptr == nullptr; } + /// True if temporal versioning metadata is attached. bool is_versioned() const { return version_info_ != nullptr; } + /// Attaches or replaces the version chain head (owned by the arena, not freed + /// here). void set_version_info(VersionInfo* version_info) { version_info_ = version_info; } + /// For versioned nodes, checks VALIDTIME on the current head; always true if + /// not versioned. bool is_valid_at(uint64_t ts) const { if (!is_versioned()) return true; return version_info_->is_valid_at(ts); } + /// Current head's version id, or 0 when not versioned. uint64_t get_version_id() const { return is_versioned() ? version_info_->version_id : 0; } + /// VALIDTIME start of the current head, or 0 when not versioned. uint64_t get_valid_from() const { return is_versioned() ? version_info_->valid_from : 0; } + /// VALIDTIME end of the current head, or max when not versioned. uint64_t get_valid_to() const { return is_versioned() ? version_info_->valid_to : std::numeric_limits::max(); } + /// Pointer to the version chain head, or nullptr when not versioned. VersionInfo* get_version_info() const { return version_info_; } + /// Counts versions along the chain from the current head (1 if not + /// versioned). size_t count_versions() const { if (!is_versioned()) return 1; return version_info_->count_versions(); } + /// Starting from the current head, finds the version valid at VALIDTIME \p + /// ts. const VersionInfo* find_version_at_time(uint64_t ts) const { if (!is_versioned()) return nullptr; return version_info_->find_version_at_time(ts); } + /// Previous version link from the current head, or nullptr. const VersionInfo* get_prev_version() const { if (!is_versioned()) return nullptr; return version_info_->prev; @@ -265,12 +288,15 @@ struct NodeHandle { NodeHandle(const NodeHandle& other) = default; NodeHandle& operator=(const NodeHandle& other) = default; + /// Compares pointer, size, schema name, and schema version (not version chain + /// identity). bool operator==(const NodeHandle& other) const { return ptr == other.ptr && size == other.size && schema_name == other.schema_name && schema_version == other.schema_version; } + /// Negation of operator==. bool operator!=(const NodeHandle& other) const { return !(*this == other); } }; @@ -420,6 +446,7 @@ class NodeArena { return layout->get_value_ptr(static_cast(handle.ptr), field); } + /// Reads a field as Value, resolving version-chain overrides when versioned. static Value get_value(const NodeHandle& handle, const std::shared_ptr& layout, const std::shared_ptr& field) { @@ -552,6 +579,7 @@ class NodeArena { // apply_updates — single public write entry point // ========================================================================= + /// Applies field updates; creates a new version when versioning is enabled. arrow::Result apply_updates(NodeHandle& handle, const std::shared_ptr& layout, const std::vector& updates) { @@ -614,12 +642,18 @@ class NodeArena { MapArena* get_map_arena() const { return map_arena_.get(); } // Statistics and getters + /// Sum of bytes returned from the underlying node MemArena (see MemArena + /// semantics). size_t get_total_allocated() const { return mem_arena_->get_total_allocated(); } + /// Number of chunks backing the node MemArena. size_t get_chunk_count() const { return mem_arena_->get_chunk_count(); } + /// Underlying arena used for fixed-size node payloads. MemArena* get_mem_arena() const { return mem_arena_.get(); } + /// Whether temporal versioning (version arena) is active for this NodeArena. bool is_versioning_enabled() const { return versioning_enabled_; } + /// Monotonic counter used to assign unique VersionInfo::version_id values. uint64_t get_version_counter() const { return version_counter_.load(std::memory_order_relaxed); } diff --git a/include/memory/schema_layout.hpp b/include/memory/schema_layout.hpp index 556df29..bddbfdc 100644 --- a/include/memory/schema_layout.hpp +++ b/include/memory/schema_layout.hpp @@ -9,13 +9,13 @@ #include #include -#include "memory/array_ref.hpp" +#include "common/types.hpp" #include "llvm/ADT/StringMap.h" +#include "memory/array_ref.hpp" #include "memory/map_ref.hpp" #include "memory/mem_utils.hpp" #include "schema/schema.hpp" #include "schema/type_descriptor.hpp" -#include "common/types.hpp" namespace tundradb { @@ -116,6 +116,7 @@ class SchemaLayout { return data_offset_ + total_size_; } + /// Pointer to field storage, or nullptr if the field bit is unset. const char* get_value_ptr(const char* node_data, const size_t field_index) const { const FieldLayout& field_layout = fields_[field_index]; @@ -130,24 +131,28 @@ class SchemaLayout { return field_ptr; } - // gets a pointer to field value + /// Pointer to field storage for \p field, or nullptr if unset. const char* get_value_ptr(const char* node_data, const std::shared_ptr& field) const { return get_value_ptr(node_data, field->index_); } + /// Reads the field at \p field_index from node memory (unset reads as + /// default/null). Value get_value(const char* node_data, const size_t field_index) const { const FieldLayout& field_layout = fields_[field_index]; return Value::read_value_from_memory(get_value_ptr(node_data, field_index), field_layout.type); } + /// Reads \p field_layout from node memory using its index and type. Value get_value(const char* node_data, const FieldLayout& field_layout) const { return Value::read_value_from_memory( get_value_ptr(node_data, field_layout.index), field_layout.type); } + /// Reads the field identified by \p field from node memory. Value get_value(const char* node_data, const std::shared_ptr& field) const { return get_value(node_data, field->index_); @@ -213,12 +218,20 @@ class SchemaLayout { } // Getters + /// Schema name this layout was built for. const std::string& get_schema_name() const { return schema_name_; } + /// Size of the data region after the bitset (excludes bitset and padding to + /// alignment). size_t get_total_size() const { return total_size_; } + /// Maximum alignment required by any field in this layout. size_t get_alignment() const { return alignment_; } + /// True after finalize() (or implicit finalize from construction). bool is_finalized() const { return finalized_; } + /// Ordered field layouts (indices match schema field order). const std::vector& get_fields() const { return fields_; } + /// Layout entry for \p field, or nullptr if index is out of range or field is + /// null. const FieldLayout* get_field_layout( const std::shared_ptr& field) const { if (!field) { @@ -353,10 +366,12 @@ class LayoutRegistry { return it != layouts_.end() ? it->second : nullptr; } + /// True if a layout is registered for \p schema_name. bool exists(const std::string& schema_name) const { return layouts_.contains(schema_name); } + /// Builds a SchemaLayout from \p schema and stores it under the schema name. std::shared_ptr create_layout( const std::shared_ptr& schema) { auto layout = std::make_shared(schema); @@ -365,10 +380,12 @@ class LayoutRegistry { return layout; } + /// Erases the layout for \p schema_name; returns whether an entry existed. bool remove_layout(const std::string& schema_name) { return layouts_.erase(schema_name) > 0; } + /// All registered schema names (order unspecified). [[nodiscard]] std::vector get_schema_names() const { std::vector names; names.reserve(layouts_.size()); @@ -378,8 +395,11 @@ class LayoutRegistry { return names; } + /// Number of registered layouts. size_t size() const { return layouts_.size(); } + /// True if no layouts are registered. bool empty() const { return layouts_.empty(); } + /// Removes all layouts from the registry. void clear() { layouts_.clear(); } private: diff --git a/include/memory/string_arena.hpp b/include/memory/string_arena.hpp index 27653d3..7b81f26 100644 --- a/include/memory/string_arena.hpp +++ b/include/memory/string_arena.hpp @@ -13,10 +13,10 @@ #include #include +#include "common/value_type.hpp" // ValueType enum and helpers #include "memory/free_list_arena.hpp" #include "memory/memory_arena.hpp" #include "memory/string_ref.hpp" // StringRef class -#include "common/value_type.hpp" // ValueType enum and helpers namespace tundradb { diff --git a/include/query/query.hpp b/include/query/query.hpp index 7fbb08d..f93b7fa 100644 --- a/include/query/query.hpp +++ b/include/query/query.hpp @@ -18,10 +18,10 @@ #include #include +#include "common/types.hpp" #include "core/node.hpp" -#include "schema/schema.hpp" #include "query/temporal_context.hpp" -#include "common/types.hpp" +#include "schema/schema.hpp" namespace tundradb { diff --git a/include/query/row.hpp b/include/query/row.hpp index 9bb6100..d475804 100644 --- a/include/query/row.hpp +++ b/include/query/row.hpp @@ -13,11 +13,11 @@ #include #include "common/constants.hpp" -#include "core/edge.hpp" #include "common/logger.hpp" +#include "common/types.hpp" +#include "core/edge.hpp" #include "core/node.hpp" #include "query/query.hpp" -#include "common/types.hpp" namespace tundradb { diff --git a/include/schema/schema.hpp b/include/schema/schema.hpp index ce01edd..2d33f1b 100644 --- a/include/schema/schema.hpp +++ b/include/schema/schema.hpp @@ -10,14 +10,16 @@ #include #include +#include "common/logger.hpp" +#include "common/types.hpp" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" -#include "common/logger.hpp" #include "schema/type_descriptor.hpp" -#include "common/types.hpp" namespace tundradb { +/// Named column in a schema: identifier, full type (including arrays/maps), and +/// nullability. struct Field { private: uint32_t index_; @@ -43,6 +45,7 @@ struct Field { type_desc_(TypeDescriptor::from_value_type(type)), nullable_(nullable) {} + /// Column name as stored in the schema and field map. [[nodiscard]] const std::string &name() const { return name_; } /// Returns the base ValueType for switch-based dispatch. @@ -53,6 +56,7 @@ struct Field { return type_desc_; } + /// Whether values in this column may be absent (SQL NULL / Arrow null). [[nodiscard]] bool nullable() const { return nullable_; } /** @@ -64,9 +68,13 @@ struct Field { static arrow::Result from_arrow( const std::shared_ptr &field); + /// Builds the equivalent Arrow field (name, type, nullability); fails if the + /// type cannot be mapped. [[nodiscard]] arrow::Result> to_arrow() const; }; +/// Versioned collection of columns with a matching Arrow schema and fast lookup +/// by name. struct Schema { private: std::string name_; @@ -77,6 +85,8 @@ struct Schema { std::shared_ptr arrow_schema_; public: + /// Builds a schema with the given fields and a caller-supplied matching Arrow + /// schema. Schema(std::string name, uint32_t version, llvm::SmallVector, 4> fields, std::shared_ptr arrow_schema) @@ -90,17 +100,25 @@ struct Schema { } } + /// Same as the four-argument constructor, but derives the Arrow schema from + /// the fields. Schema(std::string name, uint32_t version, llvm::SmallVector, 4> fields) : Schema(name, version, fields, crete_arrow_schema(fields)) {} + /// Lookup table from field name to shared Field (same entries as fields(), + /// different index). const llvm::StringMap> &fields_map() { return field_map_; } + /// Creates an Arrow schema by converting each Field to an Arrow field (see + /// Field::to_arrow). static std::shared_ptr crete_arrow_schema( const llvm::SmallVector, 4> &fields); + /// Converts a Tundra schema to its Arrow representation (one shared Arrow + /// schema object). static std::shared_ptr to_arrow( const std::shared_ptr &schema); @@ -115,17 +133,27 @@ struct Schema { const std::string &schema_name, const std::shared_ptr &schema); + /// Fields in column order (indices match field(int)). [[nodiscard]] const llvm::SmallVector, 4> &fields() const; + /// Logical schema name (e.g. table or graph label set identifier). [[nodiscard]] const std::string &name() const; + /// Monotonic schema revision for evolution / compatibility checks. [[nodiscard]] uint32_t version() const; + /// Number of columns; same as fields().size(). [[nodiscard]] size_t num_fields() const; + /// Field at column index i; index must be valid (same range as num_fields()). [[nodiscard]] std::shared_ptr field(int i) const; + /// Field by name, or nullptr if no column with that name exists. [[nodiscard]] std::shared_ptr get_field(const std::string &name) const; + /// True when there are no columns. [[nodiscard]] bool empty() const; + /// Cached Arrow schema kept in sync with this schema’s fields. [[nodiscard]] std::shared_ptr arrow() const; }; +/// Named catalog of schemas: each entry keeps both the Arrow schema and the +/// parsed Tundra Schema. class SchemaRegistry { private: std::unordered_map> schemas_; @@ -135,20 +163,29 @@ class SchemaRegistry { public: SchemaRegistry() = default; + /// Registers under name after prepending a synthetic id column to the Arrow + /// schema, then add_arrow. arrow::Result create(const std::string &name, const std::shared_ptr &schema); + /// Stores the Arrow schema and builds the Tundra Schema from it; fails if + /// conversion fails. arrow::Result add_arrow(const std::string &name, std::shared_ptr schema); + /// Arrow schema for name, or not-found if never registered. arrow::Result> get_arrow( const std::string &name) const; + /// Tundra Schema for name, or not-found if absent. [[nodiscard]] arrow::Result> get( const std::string &name) const; + /// True if a schema was successfully registered under name (native Schema + /// map). [[nodiscard]] bool exists(const std::string &name) const; + /// Names of all registered schemas (keys of the native Schema map). [[nodiscard]] std::vector get_schema_names() const; }; } // namespace tundradb diff --git a/include/schema/type_descriptor.hpp b/include/schema/type_descriptor.hpp index 2ef050a..a955794 100644 --- a/include/schema/type_descriptor.hpp +++ b/include/schema/type_descriptor.hpp @@ -33,21 +33,30 @@ struct TypeDescriptor { // Factory methods // ======================================================================== + /// Missing / placeholder scalar (NA). static TypeDescriptor na() { return {ValueType::NA}; } + /// 32-bit signed integer. static TypeDescriptor int32() { return {ValueType::INT32}; } + /// 64-bit signed integer. static TypeDescriptor int64() { return {ValueType::INT64}; } + /// IEEE single-precision float. static TypeDescriptor float32() { return {ValueType::FLOAT}; } + /// IEEE double-precision float. static TypeDescriptor float64() { return {ValueType::DOUBLE}; } + /// Boolean. static TypeDescriptor boolean() { return {ValueType::BOOL}; } + /// UTF-8 string; max_size caps byte length, 0 means unbounded. static TypeDescriptor string(uint32_t max_size = 0) { return {ValueType::STRING, ValueType::NA, 0, max_size}; } + /// Array of elem; fixed > 0 fixes the length, 0 means variable-length. static TypeDescriptor array(ValueType elem, uint32_t fixed = 0) { return {ValueType::ARRAY, elem, fixed, 0}; } + /// Key-value property map (MAP). static TypeDescriptor properties() { return {ValueType::MAP}; } /** @@ -71,11 +80,14 @@ struct TypeDescriptor { // Quick checks (for hot paths - no virtual dispatch) // ======================================================================== + /// True for non-NA scalars other than STRING, ARRAY, and MAP (see is_string / + /// is_array / is_map). [[nodiscard]] bool is_primitive() const { return base_type != ValueType::NA && base_type != ValueType::ARRAY && base_type != ValueType::MAP && !is_string(); } + /// True if STRING or a legacy fixed-string ValueType encoding. [[nodiscard]] bool is_string() const { return base_type == ValueType::STRING || base_type == ValueType::FIXED_STRING16 || @@ -83,16 +95,21 @@ struct TypeDescriptor { base_type == ValueType::FIXED_STRING64; } + /// True if this is an ARRAY type (see element_type and fixed_size). [[nodiscard]] bool is_array() const { return base_type == ValueType::ARRAY; } + /// True if this is a MAP / properties type. [[nodiscard]] bool is_map() const { return base_type == ValueType::MAP; } + /// True if base_type is NA (unset type). [[nodiscard]] bool is_null() const { return base_type == ValueType::NA; } + /// True for ARRAY with a non-zero fixed_size (fixed-length array). [[nodiscard]] bool is_fixed_size_array() const { return is_array() && fixed_size > 0; } + /// True for ARRAY with fixed_size == 0 (no fixed length cap in the type). [[nodiscard]] bool is_dynamic_array() const { return is_array() && fixed_size == 0; } @@ -113,6 +130,8 @@ struct TypeDescriptor { // String representation // ======================================================================== + /// Pretty type syntax, e.g. ARRAY, STRING(n), or the base ValueType + /// name. [[nodiscard]] std::string to_string() const { if (is_array()) { std::string result = "ARRAY<" + tundradb::to_string(element_type); @@ -132,12 +151,15 @@ struct TypeDescriptor { // Comparison // ======================================================================== + /// True if base_type, element_type, fixed_size, and max_string_size all + /// match. bool operator==(const TypeDescriptor& other) const { return base_type == other.base_type && element_type == other.element_type && fixed_size == other.fixed_size && max_string_size == other.max_string_size; } + /// True if any descriptor component differs from other. bool operator!=(const TypeDescriptor& other) const { return !(*this == other); } diff --git a/include/storage/metadata.hpp b/include/storage/metadata.hpp index d3d994d..1edcbb7 100644 --- a/include/storage/metadata.hpp +++ b/include/storage/metadata.hpp @@ -10,13 +10,13 @@ #include #include "arrow/map_union_types.hpp" -#include "storage/file_utils.hpp" +#include "common/logger.hpp" +#include "common/types.hpp" #include "json.hpp" #include "llvm/ADT/SmallVector.h" -#include "common/logger.hpp" #include "schema/schema.hpp" #include "schema/type_descriptor.hpp" -#include "common/types.hpp" +#include "storage/file_utils.hpp" using namespace std::string_literals; diff --git a/include/storage/shard.hpp b/include/storage/shard.hpp index 83ccb4b..f43b708 100644 --- a/include/storage/shard.hpp +++ b/include/storage/shard.hpp @@ -22,10 +22,12 @@ namespace tundradb { class TemporalContext; -// ========================================================================= -// Shard - a contiguous range of nodes for one schema -// ========================================================================= - +/// A fixed-capacity bucket of nodes for a single schema, keyed by node ID +/// range [min_id, max_id]. +/// +/// Nodes are stored in a PMR monotonic-buffer-backed map for cache-friendly +/// allocation. A shard tracks its dirty flag for incremental persistence +/// and can materialise its contents into an Arrow Table. class Shard { private: std::pmr::monotonic_buffer_resource memory_pool_; @@ -57,35 +59,49 @@ class Shard { ~Shard(); + /// True when the shard has been modified since the last snapshot. [[nodiscard]] bool is_updated() const; + /// Set or clear the dirty flag; returns the previous value. bool set_updated(bool v); + /// Timestamp of the last modification. [[nodiscard]] int64_t get_updated_ts() const; + /// Return "schema_name:shard_id" (used in logging and metadata keys). [[nodiscard]] std::string compound_id() const; + /// Insert a node whose ID falls within [min_id, max_id]. arrow::Result add(const std::shared_ptr &node); + /// Insert a node and widen max_id if necessary. arrow::Result extend(const std::shared_ptr &node); + /// Remove and return a node by ID. arrow::Result> remove(int64_t id); + /// Remove and return the node with the smallest ID. arrow::Result> poll_first(); + /// Update a single field on a node inside this shard. arrow::Result update(int64_t node_id, std::shared_ptr field, const Value &value, UpdateType update_type); + /// Batch-update multiple fields on a node inside this shard. arrow::Result update_fields( int64_t node_id, const std::vector &field_updates, UpdateType update_type); + /// Materialise shard contents into an Arrow Table. arrow::Result> get_table(TemporalContext *ctx); [[nodiscard]] size_t size() const; + /// True when the shard has room for more nodes (size < capacity). [[nodiscard]] bool has_space() const; [[nodiscard]] bool empty() const; + /// Return a snapshot of all nodes in insertion order. [[nodiscard]] std::vector> get_nodes() const; }; -// ========================================================================= -// ShardManager - manages per-schema shard collections -// ========================================================================= - +/// Manages per-schema collections of Shards. +/// +/// When a node is inserted, the manager finds an existing shard with +/// capacity or creates a new one. Compaction merges under-filled shards +/// to reduce fragmentation. class ShardManager { private: std::pmr::monotonic_buffer_resource memory_pool_; @@ -105,50 +121,67 @@ class ShardManager { explicit ShardManager(std::shared_ptr schema_registry, const DatabaseConfig &config); + /// Override the global shard ID counter (used during restore). void set_id_counter(int64_t value); [[nodiscard]] int64_t get_id_counter() const; + /// Override the per-schema shard index counter (used during restore). void set_index_counter(const std::string &schema_name, int64_t value); [[nodiscard]] int64_t get_index_counter(const std::string &schema_name) const; + /// Look up a shard by schema name and shard ID. arrow::Result> get_shard( const std::string &schema_name, int64_t id); + /// Return the list of schema names that have at least one shard. [[nodiscard]] std::vector get_schema_names() const; + /// Return all shards for a schema (in index order). [[nodiscard]] arrow::Result>> get_shards( const std::string &schema_name) const; + /// True when the shard has not been modified since the last snapshot. arrow::Result is_shard_clean(std::string s, int64_t id); + /// Merge under-filled shards for a schema. arrow::Result compact(const std::string &schema_name); + /// Compact all schemas. arrow::Result compact_all(); + /// Insert a node into the appropriate shard (creating one if needed). arrow::Result insert_node(const std::shared_ptr &node); + /// Look up a node across all shards for a schema. arrow::Result> get_node(const std::string &schema_name, int64_t node_id); + /// Remove a node from its shard. arrow::Result remove_node(const std::string &schema_name, int64_t node_id); + /// Update a single field on a node (by Field descriptor). arrow::Result update_node(const std::string &schema_name, int64_t id, const std::shared_ptr &field, const Value &value, UpdateType update_type); + /// Update a single field on a node (by field name). arrow::Result update_node(const std::string &schema_name, int64_t id, const std::string &field_name, const Value &value, UpdateType update_type); + /// Batch-update multiple fields on a node. arrow::Result update_node_fields( const std::string &schema_name, int64_t id, const std::vector &field_updates, UpdateType update_type); + /// Collect all nodes across shards for a schema. arrow::Result>> get_nodes( const std::string &schema_name); + /// Materialise all shards of a schema into Arrow Tables. arrow::Result>> get_tables( const std::string &schema_name, TemporalContext *temporal_context); + /// True when at least one shard exists for the schema. [[nodiscard]] bool has_shards(const std::string &schema_name) const; [[nodiscard]] arrow::Result get_shard_count( @@ -160,7 +193,9 @@ class ShardManager { [[nodiscard]] arrow::Result>> get_shard_ranges(const std::string &schema_name) const; + /// Add a pre-built shard (used during snapshot restore). arrow::Result add_shard(const std::shared_ptr &shard); + /// Clear the dirty flag on all shards across all schemas. arrow::Result reset_all_updated(); }; diff --git a/include/storage/snapshot_manager.hpp b/include/storage/snapshot_manager.hpp index d52c874..feb5276 100644 --- a/include/storage/snapshot_manager.hpp +++ b/include/storage/snapshot_manager.hpp @@ -5,8 +5,8 @@ #include #include "core/edge_store.hpp" -#include "storage/metadata.hpp" #include "schema/schema.hpp" +#include "storage/metadata.hpp" namespace tundradb { @@ -15,6 +15,11 @@ class ShardManager; class Storage; class NodeManager; +/// Orchestrates persistence: saving and restoring database state to disk. +/// +/// On `initialize()`, the manager restores metadata, schemas, shards, and +/// edges from the most recent snapshot. On `commit()`, dirty shards and +/// edges are written to Parquet and a new metadata+manifest pair is saved. class SnapshotManager { public: explicit SnapshotManager(std::shared_ptr metadata_manager, @@ -24,9 +29,16 @@ class SnapshotManager { std::shared_ptr node_manager, std::shared_ptr schema_registry); + /// Restore the database from the latest snapshot on disk. arrow::Result initialize(); + + /// Write all dirty shards and edges to disk and create a new snapshot. arrow::Result commit(); + + /// Return a pointer to the most recent snapshot (nullptr if none). Snapshot *current_snapshot(); + + /// Return the current manifest (nullptr if no snapshot loaded). std::shared_ptr get_manifest(); private: diff --git a/include/storage/storage.hpp b/include/storage/storage.hpp index d4fdb31..ac019bc 100644 --- a/include/storage/storage.hpp +++ b/include/storage/storage.hpp @@ -17,6 +17,8 @@ class NodeManager; class Shard; class EdgeStore; +/// Low-level persistence layer: reads and writes Parquet files for +/// shards and edge tables under the configured data directory. class Storage { private: std::string data_directory_; @@ -32,18 +34,23 @@ class Storage { std::shared_ptr node_manager_, DatabaseConfig config = DatabaseConfig()); + /// Create the data directory structure on disk. arrow::Result initialize(); + /// Write an Arrow Table to a new Parquet file and return the file path. [[nodiscard]] arrow::Result write_table( const std::shared_ptr& table, int64_t chunk_size, const std::string& prefix_path = "") const; + /// Materialise a Shard into an Arrow Table and write it to Parquet. [[nodiscard]] arrow::Result write_shard( const std::shared_ptr& shard) const; + /// Read a Shard back from Parquet using metadata for path resolution. arrow::Result> read_shard( const ShardMetadata& shard_metadata); + /// Read edge rows from Parquet and restore them into the EdgeStore. [[nodiscard]] arrow::Result read_edges( const EdgeMetadata& edge_metadata, const std::shared_ptr& edge_store) const; diff --git a/src/core/edge_store.cpp b/src/core/edge_store.cpp index e70e6be..d9b135f 100644 --- a/src/core/edge_store.cpp +++ b/src/core/edge_store.cpp @@ -4,8 +4,8 @@ #include #include "common/constants.hpp" -#include "json.hpp" #include "common/logger.hpp" +#include "json.hpp" #include "storage/metadata.hpp" namespace tundradb { diff --git a/src/main/database.cpp b/src/main/database.cpp index 730dc53..6b90614 100644 --- a/src/main/database.cpp +++ b/src/main/database.cpp @@ -21,11 +21,11 @@ #include #include "arrow/utils.hpp" -#include "query/join.hpp" #include "common/logger.hpp" +#include "common/utils.hpp" +#include "query/join.hpp" #include "query/row.hpp" #include "query/temporal_context.hpp" -#include "common/utils.hpp" namespace fs = std::filesystem; diff --git a/src/query/execution.cpp b/src/query/execution.cpp index 0360b0a..f1829d1 100644 --- a/src/query/execution.cpp +++ b/src/query/execution.cpp @@ -6,9 +6,9 @@ #include "arrow/map_union_types.hpp" #include "arrow/utils.hpp" #include "common/constants.hpp" -#include "core/edge_store.hpp" #include "common/logger.hpp" #include "common/utils.hpp" +#include "core/edge_store.hpp" namespace tundradb { diff --git a/src/query/query.cpp b/src/query/query.cpp index d67537b..8a52eaf 100644 --- a/src/query/query.cpp +++ b/src/query/query.cpp @@ -5,8 +5,8 @@ #include #include -#include "core/edge.hpp" #include "common/logger.hpp" +#include "core/edge.hpp" namespace tundradb { diff --git a/src/storage/metadata.cpp b/src/storage/metadata.cpp index b972788..dd7f6de 100644 --- a/src/storage/metadata.cpp +++ b/src/storage/metadata.cpp @@ -6,9 +6,9 @@ #include #include -#include "json.hpp" #include "common/logger.hpp" #include "common/utils.hpp" +#include "json.hpp" namespace tundradb { diff --git a/src/storage/snapshot_manager.cpp b/src/storage/snapshot_manager.cpp index f302de7..b4f2eb2 100644 --- a/src/storage/snapshot_manager.cpp +++ b/src/storage/snapshot_manager.cpp @@ -1,11 +1,11 @@ #include "storage/snapshot_manager.hpp" -#include "core/edge.hpp" #include "common/logger.hpp" +#include "common/utils.hpp" +#include "core/edge.hpp" #include "core/node.hpp" #include "storage/shard.hpp" #include "storage/storage.hpp" -#include "common/utils.hpp" namespace tundradb { diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 1ac6781..143b31b 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -19,15 +19,15 @@ #include #include +#include "arrow/table_info.hpp" #include "arrow/utils.hpp" #include "common/constants.hpp" +#include "common/logger.hpp" #include "core/edge_store.hpp" +#include "core/node.hpp" #include "json.hpp" -#include "common/logger.hpp" #include "storage/metadata.hpp" -#include "core/node.hpp" #include "storage/shard.hpp" -#include "arrow/table_info.hpp" namespace tundradb { diff --git a/tests/array_query_test.cpp b/tests/array_query_test.cpp index 95ddf23..3369536 100644 --- a/tests/array_query_test.cpp +++ b/tests/array_query_test.cpp @@ -5,9 +5,9 @@ #include #include "common/clock.hpp" +#include "common/utils.hpp" #include "main/database.hpp" #include "query/query.hpp" -#include "common/utils.hpp" #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/benchmark_test.cpp b/tests/benchmark_test.cpp index ddc1560..af04f74 100644 --- a/tests/benchmark_test.cpp +++ b/tests/benchmark_test.cpp @@ -9,11 +9,11 @@ #include #include -#include "main/database.hpp" #include "common/logger.hpp" -#include "storage/metadata.hpp" -#include "query/query.hpp" #include "common/types.hpp" +#include "main/database.hpp" +#include "query/query.hpp" +#include "storage/metadata.hpp" // Helper macro for Arrow operations #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/database_test.cpp b/tests/database_test.cpp index ea6f285..f7ba788 100644 --- a/tests/database_test.cpp +++ b/tests/database_test.cpp @@ -1,3 +1,5 @@ +#include "main/database.hpp" + #include #include #include @@ -6,7 +8,6 @@ #include -#include "main/database.hpp" #include "common/logger.hpp" using namespace tundradb; diff --git a/tests/join_test.cpp b/tests/join_test.cpp index 1f6de37..5df8a24 100644 --- a/tests/join_test.cpp +++ b/tests/join_test.cpp @@ -4,11 +4,11 @@ #include #include -#include "main/database.hpp" #include "common/debug_utils.hpp" #include "common/logger.hpp" -#include "storage/metadata.hpp" +#include "main/database.hpp" #include "query/query.hpp" +#include "storage/metadata.hpp" // Helper macro for Arrow operations #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/map_arena_test.cpp b/tests/map_arena_test.cpp index 1ed9716..94c0f3a 100644 --- a/tests/map_arena_test.cpp +++ b/tests/map_arena_test.cpp @@ -5,15 +5,15 @@ #include #include -#include "memory/array_arena.hpp" #include "common/clock.hpp" +#include "common/types.hpp" #include "core/edge.hpp" +#include "core/edge_store.hpp" #include "core/node.hpp" +#include "memory/array_arena.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "memory/schema_layout.hpp" -#include "common/types.hpp" -#include "core/edge_store.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/node_arena_test.cpp b/tests/node_arena_test.cpp index e44fd96..7160a46 100644 --- a/tests/node_arena_test.cpp +++ b/tests/node_arena_test.cpp @@ -7,12 +7,12 @@ #include #include +#include "common/types.hpp" #include "core/field_update.hpp" -#include "memory/memory_arena.hpp" #include "core/node.hpp" -#include "schema/schema.hpp" +#include "memory/memory_arena.hpp" #include "memory/schema_layout.hpp" -#include "common/types.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/node_test.cpp b/tests/node_test.cpp index ba02739..d915de0 100644 --- a/tests/node_test.cpp +++ b/tests/node_test.cpp @@ -7,10 +7,10 @@ #include #include -#include "core/field_update.hpp" #include "common/logger.hpp" -#include "schema/schema.hpp" #include "common/types.hpp" +#include "core/field_update.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/node_version_test.cpp b/tests/node_version_test.cpp index 9732455..2482d8a 100644 --- a/tests/node_version_test.cpp +++ b/tests/node_version_test.cpp @@ -5,8 +5,8 @@ #include "memory/map_arena.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "memory/schema_layout.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/node_view_test.cpp b/tests/node_view_test.cpp index 9384e60..2ad6664 100644 --- a/tests/node_view_test.cpp +++ b/tests/node_view_test.cpp @@ -7,9 +7,9 @@ #include "core/node.hpp" #include "memory/node_arena.hpp" -#include "schema/schema.hpp" #include "memory/schema_layout.hpp" #include "query/temporal_context.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/schema_utils_test.cpp b/tests/schema_utils_test.cpp index 66723dd..7f3cb2b 100644 --- a/tests/schema_utils_test.cpp +++ b/tests/schema_utils_test.cpp @@ -1,9 +1,8 @@ -#include "schema/utils.hpp" - #include #include #include "arrow/utils.hpp" +#include "schema/utils.hpp" namespace tundradb { diff --git a/tests/sharding_test.cpp b/tests/sharding_test.cpp index 417733d..b5da228 100644 --- a/tests/sharding_test.cpp +++ b/tests/sharding_test.cpp @@ -5,8 +5,8 @@ #include #include "arrow/utils.hpp" -#include "main/database.hpp" #include "common/debug_utils.hpp" +#include "main/database.hpp" using namespace tundradb; class ShardingTest : public ::testing::Test { diff --git a/tests/snapshot_test.cpp b/tests/snapshot_test.cpp index ec26e96..1fd7cd7 100644 --- a/tests/snapshot_test.cpp +++ b/tests/snapshot_test.cpp @@ -7,9 +7,9 @@ #include #include "arrow/map_union_types.hpp" -#include "main/database.hpp" -#include "core/field_update.hpp" #include "common/logger.hpp" +#include "core/field_update.hpp" +#include "main/database.hpp" using namespace std::string_literals; using namespace tundradb; diff --git a/tests/string_ref_concurrent_test.cpp b/tests/string_ref_concurrent_test.cpp index 57af26e..281bf72 100644 --- a/tests/string_ref_concurrent_test.cpp +++ b/tests/string_ref_concurrent_test.cpp @@ -14,9 +14,9 @@ #include #include +#include "common/types.hpp" #include "memory/string_arena.hpp" #include "memory/string_ref.hpp" -#include "common/types.hpp" using namespace tundradb; diff --git a/tests/temporal_query_test.cpp b/tests/temporal_query_test.cpp index 44ecc11..07c8d7a 100644 --- a/tests/temporal_query_test.cpp +++ b/tests/temporal_query_test.cpp @@ -3,8 +3,8 @@ #include #include "common/clock.hpp" -#include "main/database.hpp" #include "common/logger.hpp" +#include "main/database.hpp" #include "memory/node_arena.hpp" #include "query/query.hpp" #include "query/temporal_context.hpp" diff --git a/tests/type_conversion_test.cpp b/tests/type_conversion_test.cpp index 6e0a439..0ebda33 100644 --- a/tests/type_conversion_test.cpp +++ b/tests/type_conversion_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "schema/schema.hpp" #include "common/types.hpp" +#include "schema/schema.hpp" using namespace tundradb; diff --git a/tests/type_system_test.cpp b/tests/type_system_test.cpp index 10cac29..c04869b 100644 --- a/tests/type_system_test.cpp +++ b/tests/type_system_test.cpp @@ -2,8 +2,8 @@ #include -#include "schema/type_descriptor.hpp" #include "common/value_type.hpp" +#include "schema/type_descriptor.hpp" using namespace tundradb; diff --git a/tests/update_query_join_test.cpp b/tests/update_query_join_test.cpp index e075126..a86291e 100644 --- a/tests/update_query_join_test.cpp +++ b/tests/update_query_join_test.cpp @@ -6,9 +6,9 @@ #include #include +#include "common/utils.hpp" #include "main/database.hpp" #include "query/query.hpp" -#include "common/utils.hpp" #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/update_query_test.cpp b/tests/update_query_test.cpp index 5bbc907..9c63525 100644 --- a/tests/update_query_test.cpp +++ b/tests/update_query_test.cpp @@ -5,9 +5,9 @@ #include #include "arrow/map_union_types.hpp" +#include "common/utils.hpp" #include "main/database.hpp" #include "query/query.hpp" -#include "common/utils.hpp" #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/where_expression_test.cpp b/tests/where_expression_test.cpp index 7b0b1ae..3b8c57d 100644 --- a/tests/where_expression_test.cpp +++ b/tests/where_expression_test.cpp @@ -6,12 +6,12 @@ #include #include "arrow/map_union_types.hpp" -#include "main/database.hpp" -#include "core/field_update.hpp" #include "common/logger.hpp" -#include "storage/metadata.hpp" -#include "query/query.hpp" #include "common/utils.hpp" +#include "core/field_update.hpp" +#include "main/database.hpp" +#include "query/query.hpp" +#include "storage/metadata.hpp" // Helper macro for Arrow operations #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok()) diff --git a/tests/where_pushdown_join_test.cpp b/tests/where_pushdown_join_test.cpp index a3f876a..9fba8ce 100644 --- a/tests/where_pushdown_join_test.cpp +++ b/tests/where_pushdown_join_test.cpp @@ -9,11 +9,11 @@ #include #include -#include "main/database.hpp" #include "common/logger.hpp" -#include "storage/metadata.hpp" -#include "query/query.hpp" #include "common/utils.hpp" +#include "main/database.hpp" +#include "query/query.hpp" +#include "storage/metadata.hpp" // Helper macro for Arrow operations #define ASSERT_OK(expr) ASSERT_TRUE((expr).ok())