kglite 0.17.8

Pure-Rust embedded Cypher knowledge graph engine with in-memory, mmap, and disk storage, and agent-facing schema introspection
Documentation
//! `kglite::api::session` — canonical query + transaction surface.
//!
//! Pure-Rust, no PyO3, no async, no transport. Bindings (pyapi,
//! bolt-server, mcp-server, future Go/TS) wrap this module's types
//! and free functions. The Cypher pipeline orchestration + the
//! snapshot/working CoW transaction mechanics live here exactly
//! once.
//!
//! **Why this module exists.** The same pipeline
//! (parse → validate → rewrite_text_score → optimize → mark_lazy →
//! mutation gate → execute) was duplicated three times — once in
//! `src/graph/pyapi/kg_core.rs::cypher`, once in
//! `crates/kglite-mcp-server/src/tools.rs::cypher_query`, and once
//! in `crates/kglite-bolt-server/src/backend.rs`. The CoW
//! transaction state was duplicated twice (pyapi/transaction.rs +
//! bolt-server backend). That drift cost the team twice in real
//! bugs: `validate_schema` was missing from two consumers; the
//! bolt-server's incorrect `mark_lazy_eligibility` call returned
//! 0 rows for any non-ORDER-BY RETURN until the robustness pass
//! surfaced it.
//!
//! See [`docs/history/bolt-implementation.md`](../../../../../docs/history/bolt-implementation.md)
//! for the full rationale.
//!
//! ## Surface
//!
//! - [`Session`] — shared graph state with commit-swap semantics
//!   (`Arc<DirGraph>` behind a `Mutex` for atomic swap). Bindings
//!   wrap a Session inside their own concurrency model.
//! - [`Transaction`] — snapshot/working CoW state, built via
//!   [`Session::begin`] and finalized via [`Session::commit`] or
//!   [`Session::rollback`].
//! - [`execute_read`] / [`execute_mut`] — pure-Rust pipeline
//!   orchestration. Bindings call these for every Cypher query
//!   (auto-commit reads use `execute_read` against a snapshot;
//!   in-transaction queries use the helpers on `Session` that
//!   route reads vs writes against `Transaction::current()` vs
//!   `Transaction::working_mut()`).
//! - [`ExecuteOptions`] — single struct for all per-query knobs
//!   (params, deadline, max_work_units, lazy_eligible flag, disabled
//!   planner passes, optional embedder reference).
//! - [`ExecuteOutcome`] — wraps `CypherResult` with `is_mutation`,
//!   `output_format`, `explain` flags that callers need for
//!   serialization decisions.
//! - [`CommitOutcome`] — `NoWritesNoOp` / `Committed` /
//!   `ConflictDetected` / `DurabilityFailed` so the binding maps to
//!   its own error type (PyErr / BoltError / etc.).
//! - [`Session::open_durable`] + [`Session::sync`] — the write-ahead
//!   log wired through the session: commits append a frame before
//!   they publish, and [`Session::save`] is the four-step checkpoint.
//!   See [`durable`] for the orderings that are correctness.

pub use self::execute::{execute_mut, execute_read, ExecuteOptions, ExecuteOutcome};
pub(crate) use self::noderefs::{
    property_value_needs_snapshot, snapshot_dataframe_properties, snapshot_property_values,
};
pub use self::noderefs::{resolve_noderef_value, resolve_noderefs};
pub use self::query_defaults::{
    deadline_from, QueryDefaults, ResolvedQueryOptions, DEFAULT_TIMEOUT_MS,
};
pub use self::transaction::{CommitOutcome, Session, Transaction};

#[cfg(test)]
mod append_capacity_tests;
#[cfg(test)]
mod compaction_tests;
pub(crate) mod durable;
#[cfg(test)]
mod endpoint_contract_tests;
pub(crate) mod execute;
pub(crate) mod noderefs;
#[cfg(test)]
mod param_presence_tests;
#[cfg(test)]
mod plan_cache_cost_tests;
pub(crate) mod query_defaults;
#[cfg(test)]
mod query_warnings_tests;
#[cfg(test)]
mod row_limit_tests;
#[cfg(test)]
mod stored_property_admission_tests;
#[cfg(test)]
mod strict_reads_tests;
pub(crate) mod transaction;

/// Stack size a thread must have to run [`execute_read`] / [`execute_mut`]
/// safely — servers that dispatch queries onto their own threads should
/// configure their pool with this value.
///
/// The pipeline recurses once per level of expression/predicate nesting, in
/// the parser, in ~25 planner walkers, in the executor's predicate evaluator,
/// and again in the drop glue of the boxed AST. The parser caps nesting so
/// that recursion is bounded (a "simplify the query" parse error past the
/// budget), but the *bound* still has to fit in the thread's stack, and a
/// runtime default worker stack (tokio: 2 MiB) is not comfortably larger than
/// the deepest permitted tree in a debug build. A thread with less than this
/// cannot merely fail a query — a Rust stack overflow aborts the **process**,
/// so one client's deep query would take down every other session sharing it.
///
/// 8 MiB matches the main-thread default that the CLI and the Python wheel
/// already get for free, so every frontend has the same headroom.
pub const QUERY_THREAD_STACK_SIZE: usize = 8 * 1024 * 1024;