Skip to main content

StorageBackend

Struct StorageBackend 

Source
pub struct StorageBackend { /* private fields */ }
Expand description

Concrete storage backend providing capability traits.

Capability schemas are initialized once per backend. An index removed by another process is repaired on the narrowly supported forced-index read paths. Other external schema changes may still require reopening the backend.

Implementations§

Source§

impl StorageBackend

Source

pub fn sqlite_code_map( path: impl AsRef<Path>, protected_main: &[PathBuf], protected_events: &[PathBuf], ) -> Result<StorageBackend, SqliteError>

Open the dedicated code-map store through the native handle-proving VFS. protected_main and protected_events are configured production names; the guard also samples each name’s SQLite companions. Disk policy and the volume-lock directory come from the process environment, with the per-user default lock directory of crate::default_volume_lock_dir; callers with captured configuration use Self::sqlite_code_map_with_policies.

Source

pub fn sqlite_code_map_with_policies( path: impl AsRef<Path>, protected_main: &[PathBuf], protected_events: &[PathBuf], disk_guard_config: EffectiveDiskGuardConfig, volume_lock_dir: PathBuf, ) -> Result<StorageBackend, SqliteError>

Open a guarded code-map store using the host’s captured disk policy and absolute shared volume-lock directory. Neither value rereads environment.

Source§

impl StorageBackend

Source

pub fn sql(&self) -> Arc<dyn SqlAccess> ⓘ

Get the SQL access capability.

Returns an Arc<dyn SqlAccess> suitable for passing to services.

Source§

impl StorageBackend

Source

pub fn validate_memory_visibility_cutover(&self) -> Result<(), SqliteError>

Validate the sealed memory-receipt cutover without a writer or migration.

Source

pub fn schema_version(&self) -> Result<u32, SqliteError>

Read the applied schema version through the pool’s ordinary reader or writer, without running migrations. Unlike migrations::inspect_schema_version, this goes through the already-open pool rather than a fresh boot-time snapshot connection, so it tolerates a WAL sidecar left by this same backend’s own recent writes.

Source§

impl StorageBackend

Source

pub fn sqlite_with_volume_lock_dir( path: impl AsRef<Path>, volume_lock_dir: PathBuf, ) -> Result<StorageBackend, SqliteError>

File-backed SQLite database whose volume advisory lock files live in volume_lock_dir. Every cooperating process must choose the same directory.

Source

pub fn sqlite_with_max_readers_and_policies( path: impl AsRef<Path>, max_readers: Option<usize>, wal_ceiling: WalCeilingPolicy, disk_guard_config: EffectiveDiskGuardConfig, volume_lock_dir: PathBuf, ) -> Result<StorageBackend, SqliteError>

The host supplies the same captured policies used for daemon identity.

Source§

impl StorageBackend

Source

pub async fn list_namespaces( &self, kind: &str, liveness: NamespaceLiveness, ) -> Result<Vec<String>, StorageError>

List sorted, distinct namespaces containing an exact stored record kind.

This administrative read is unscoped: it unions existing entities, notes and events tables, comparing kind exactly. It does not initialize absent tables, filter schedules by due time, or interpret kind as a substrate. Unknown kinds return an empty list. Pass NamespaceLiveness::default() for live rows; events have no tombstone field, so both modes include them.

Source§

impl StorageBackend

Source

pub fn sqlite_with_claimed_file_identity( path: impl AsRef<Path>, max_readers: Option<usize>, wal_ceiling: WalCeilingPolicy, read_only: bool, expected: DatabaseFileIdentity, ) -> Result<StorageBackend, SqliteError>

Refuse any database other than expected before initializing identity or WAL. read_only retains the explicit topology mode; a writable request still honors an existing file’s filesystem read-only mode.

On Unix, the host must initialize the claimed-file observer at process startup before any SQLite I/O, under pool::initialize_claimed_file_observer’s unsafe precondition. Unsupported or uninitialized Unix hosts refuse. Native open-time I/O is outside the pre-SQL refusal guarantee.

Source

pub fn sqlite_with_claimed_file_identity_and_policies( path: impl AsRef<Path>, max_readers: Option<usize>, wal_ceiling: WalCeilingPolicy, expected: DatabaseFileIdentity, disk_guard_config: EffectiveDiskGuardConfig, volume_lock_dir: PathBuf, ) -> Result<StorageBackend, SqliteError>

Writable Self::sqlite_with_claimed_file_identity that carries the host’s captured disk policy and shared volume-lock directory.

Source§

impl StorageBackend

Source

pub fn sqlite(path: impl AsRef<Path>) -> Result<StorageBackend, SqliteError>

File-backed SQLite database.

Opens (or creates) the database at path. An existing filesystem path whose mode is read-only is opened with the same locked-down pool configuration as Self::sqlite_read_only. The writable pool provides 1 writer + N readers in WAL mode for concurrent access. No service schema is applied — call apply_schema() for each service. The pool may create its internal _khive_database_identity singleton table on a writable open before service migrations run.

The volume-lock directory is the per-user default of crate::default_volume_lock_dir, shared by every process of the user; Self::sqlite_with_volume_lock_dir names one explicitly.

Source

pub fn sqlite_with_max_readers( path: impl AsRef<Path>, max_readers: Option<usize>, ) -> Result<StorageBackend, SqliteError>

Open SQLite with a reader count selected before any connections are opened. None preserves the default pool size and filesystem read-only detection. The volume-lock directory defaults as in Self::sqlite.

Source

pub fn sqlite_with_max_readers_and_wal_ceiling( path: impl AsRef<Path>, max_readers: Option<usize>, wal_ceiling: WalCeilingPolicy, ) -> Result<StorageBackend, SqliteError>

Open a file-backed SQLite backend with its already-resolved WAL ceiling. A nonzero policy fails closed until the WAL I/O limiter is installed. The volume-lock directory defaults as in Self::sqlite.

Source

pub fn sqlite_read_only( path: impl AsRef<Path>, ) -> Result<StorageBackend, SqliteError>

File-backed SQLite database opened read-only.

Opens the database at path and sets PRAGMA query_only = ON on the writer connection so that any write attempt (INSERT/UPDATE/DELETE) returns an error. Reader connections are opened with SQLITE_OPEN_READ_ONLY by the pool; at least one remains dedicated even for a rollback-journal snapshot, while this PRAGMA extends the protection to the otherwise-unused writer slot.

The database file must already exist — unlike sqlite() this constructor does not create a new file.

Source

pub fn sqlite_read_only_with_max_readers( path: impl AsRef<Path>, max_readers: Option<usize>, ) -> Result<StorageBackend, SqliteError>

Open a read-only SQLite store with a construction-time reader count.

Source

pub fn sqlite_read_only_with_max_readers_and_wal_ceiling( path: impl AsRef<Path>, max_readers: Option<usize>, wal_ceiling: WalCeilingPolicy, ) -> Result<StorageBackend, SqliteError>

Open a read-only SQLite backend while retaining WAL ceiling configuration for diagnostics. No writer policy is enforced.

Source

pub fn memory() -> Result<StorageBackend, SqliteError>

In-memory SQLite database (for tests).

All data is lost when the backend is dropped. The pool degrades to single-connection mode since in-memory databases cannot be shared across multiple connections.

Source

pub fn apply_schema(&self, plan: &ServiceSchemaPlan) -> Result<(), SqliteError>

Apply a service’s schema plan (run migrations).

Each migration in the plan’s sqlite list is applied idempotently, including when another opener commits it first. Already-applied migrations are skipped after taking the SQLite write lock. The _schema_versions table tracks which migrations have been run.

Source

pub fn apply_pack_ddl_statements( &self, statements: &[&'static str], ) -> Result<(), SqliteError>

Apply pack-auxiliary DDL statements.

Executes the full plan in one transaction, applying each DDL statement idempotently via execute_batch. Each statement MUST be self-contained and use CREATE TABLE IF NOT EXISTS (or equivalent idempotent DDL) so that calling this method more than once does not fail.

Pack auxiliary tables are NOT tracked in _schema_versions — they are non-versioned. Use apply_schema with a ServiceSchemaPlan when version tracking is needed.

Plans declaring nullable-column upgrades must use Self::apply_pack_ddl_statements_with_columns. The runtime supplies the SQL slice and column metadata separately because its SchemaPlan type lives above this crate in the dependency chain.

Source

pub fn apply_pack_ddl_statements_with_columns( &self, statements: &[&'static str], additions: &[PackColumnAddition], ) -> Result<(), SqliteError>

Apply a pack’s nullable-column upgrades and idempotent SQL atomically.

Missing columns are added only to existing tables; the full SQL plan creates fresh tables. Existing columns and the final schema must match the declarations. Schema inspection, additions, and SQL all run under one writer transaction, including rollback if any later step fails.

Source

pub fn validate_pack_schema_columns( &self, additions: &[PackColumnAddition], ) -> Result<(), SqliteError>

Validate a pack’s declared columns without applying SQL or acquiring a writer. Read-only hosts use this before exposing verbs that require these columns.

Source

pub fn prepare_core_schema(&self) -> Result<u32, SqliteError>

Prepare the core schema for runtime boot.

Writable backends acquire the canonical database-GC owner before the writer, apply the ordinary versioned prefix, and may finish V21 only through its zero-legacy-reference fast path. A legacy V20 database remains at V20 for the async host’s application-assisted attachment cutover; this method alone is not a serving boot gate. Read-only backends perform a query-only compatibility check and require the snapshot to be at this build’s exact latest schema version.

Source

pub fn attachment_cutover_status( &self, ) -> Result<AttachmentCutoverStatus, SqliteError>

Inspect the coordinated V21 attachment cutover state.

Source

pub fn stage_attachment_cutover( &self, owner: &DatabaseGcOwnerGuard, ) -> Result<(), SqliteError>

Commit resumable V21 stage 1 while the caller owns this database’s GC protocol. The owner must remain live through verified application backfill and finalization.

Source

pub fn apply_verified_attachments( &self, owner: &DatabaseGcOwnerGuard, attachments: &[Attachment], ) -> Result<(), SqliteError>

Atomically publish a verified batch of pack-owned attachment roles.

Source

pub fn finalize_attachment_cutover( &self, owner: &DatabaseGcOwnerGuard, ) -> Result<(), SqliteError>

Atomically swap GC liveness/fences to attachments, remove the legacy entity column, and record V21 while the canonical owner is held.

Source

pub fn entities(&self) -> Result<Arc<dyn EntityStore>, SqliteError>

Get an EntityStore. Applies the entities DDL if not already present.

Idempotent — safe to call multiple times.

Source

pub fn entities_for_namespace( &self, namespace: &str, ) -> Result<Arc<dyn EntityStore>, SqliteError>

Get an EntityStore. The namespace parameter is validated (non-empty) and the entities schema is applied, but the store itself is unscoped — namespace is the caller’s responsibility on each query/delete call.

Source

pub fn attachments(&self) -> Result<Arc<dyn AttachmentStore>, SqliteError>

Get the role-keyed attachment store.

Unlike the legacy capability accessors, this does not install DDL on demand. The coordinated V21 core cutover owns creation of the table, reference fences, GC liveness swap, and removal of the legacy entity column as one boot-gated operation.

Source

pub fn graph(&self) -> Result<Arc<dyn GraphStore>, SqliteError>

Get a GraphStore for the default namespace.

Creates the graph_edges table (with indexes) if it does not already exist. Idempotent — safe to call multiple times.

Source

pub fn graph_for_namespace( &self, namespace: &str, ) -> Result<Arc<dyn GraphStore>, SqliteError>

Get a GraphStore scoped to a namespace.

Source

pub fn notes(&self) -> Result<Arc<dyn NoteStore>, SqliteError>

Get a NoteStore. Applies the notes DDL if not already present.

Idempotent — safe to call multiple times.

Source

pub fn notes_for_namespace( &self, namespace: &str, ) -> Result<Arc<dyn NoteStore>, SqliteError>

Get a NoteStore. The namespace parameter is validated (non-empty) and the notes schema is applied, but the store itself is unscoped — namespace is the caller’s responsibility on each query/delete call.

Source

pub fn notes_seq_repair_run_count(&self) -> usize

How many times the lazy notes_seq anti-join repair has actually executed against this backend’s pool. Exposed for regression tests asserting the repair runs at most once per backend for the process’s lifetime, not once per notes_for_namespace call (khive #827).

Source

pub fn events(&self) -> Result<Arc<dyn EventStore>, SqliteError>

Get an EventStore for the default namespace.

Creates the events table (with indexes) if it does not already exist. Idempotent — safe to call multiple times.

Source

pub fn events_for_namespace( &self, namespace: &str, ) -> Result<Arc<dyn EventStore>, SqliteError>

Get an EventStore scoped to a namespace.

Source

pub fn agents(&self) -> Result<Arc<dyn AgentStore>, SqliteError>

Get the agent-process store (ADR-142 §1). Applies the agents DDL if not already present. Idempotent — safe to call multiple times. Unlike the other stores here, agent-process records are not namespace-scoped, so there is no _for_namespace variant.

Source

pub fn vectors( &self, model_key: &str, embedding_model: &str, dimensions: usize, ) -> Result<Arc<dyn VectorStore>, SqliteError>

Get a VectorStore for a specific embedding model, scoped to the default namespace.

Creates the vec0 virtual table if it does not already exist. The model_key must contain only ASCII alphanumeric/underscore characters. The embedding_model is the canonical display name stored in each vector row.

Source

pub fn vectors_for_namespace( &self, model_key: &str, embedding_model: &str, dimensions: usize, namespace: &str, ) -> Result<Arc<dyn VectorStore>, SqliteError>

Get a VectorStore for a specific embedding model with a default namespace.

Creates the vec0 virtual table if it does not already exist. The namespace is a default for trait methods that lack a per-call namespace parameter (count, delete, info). Access control is enforced at the runtime layer.

The model_key must contain only ASCII alphanumeric/underscore characters. The embedding_model is the canonical display name stored in the embedding_model column of each vector row (e.g. "all-minilm-l6-v2").

Source

pub fn ensure_vector_tables( &self, models: &[(&str, usize)], ) -> Result<(), SqliteError>

Ensure all requested vector tables with one schema-writer acquisition. Read-only backends inspect the same tables using one reader instead.

A writable backend prepares each model’s table once: the first call that names it validates or creates the table under the writer and records the model only after every step succeeded. Later calls for a recorded model return without taking the writer, and a failed call records nothing, so the next call repeats the check.

Source

pub fn register_embedding_model( &self, engine_name: &str, model_id: &str, key_version: &str, dimensions: u32, ) -> Result<(), SqliteError>

Register an embedding model in the _embedding_models registry table.

Idempotent: if a row with the same canonical_key already exists, updates its status back to 'active' without changing other fields.

Source

pub fn sparse( &self, model_key: &str, ) -> Result<Arc<dyn SparseStore>, SqliteError>

Get a SparseStore for a specific model key, scoped to the default namespace.

Creates the sparse table if it does not already exist.

Source

pub fn sparse_for_namespace( &self, model_key: &str, namespace: &str, ) -> Result<Arc<dyn SparseStore>, SqliteError>

Get a SparseStore for a specific model key with an explicit default namespace.

The model_key must contain only ASCII alphanumeric/underscore characters.

Source

pub fn text(&self, table_key: &str) -> Result<Arc<dyn TextSearch>, SqliteError>

Get a TextSearch for a specific table key.

Creates the FTS5 virtual table if it does not already exist. Uses the trigram tokenizer by default (CJK-safe).

The table_key must contain only ASCII alphanumeric/underscore characters.

Source

pub fn text_with_tokenizer( &self, table_key: &str, tokenizer: &str, ) -> Result<Arc<dyn TextSearch>, SqliteError>

Get a TextSearch with an explicit FTS5 tokenizer.

Use when you need a tokenizer other than the default trigram — for example unicode61 for Latin-only corpora.

Both table_key and tokenizer must contain only ASCII alphanumeric/underscore characters.

Source

pub fn blob_store( &self, config_root: Option<&Path>, floor_bytes: Option<u64>, ) -> Result<Arc<dyn BlobStore>, SqliteError>

Get a BlobStore rooted per khive#292’s precedence chain: KHIVE_BLOB_ROOT env var > config_root (a caller-resolved khive.toml override — khive-db has no TOML parser of its own) > beside this backend’s database directory. floor_bytes overrides the default 100 GB fail-closed free-space floor (None keeps the default). Errors if none of the three roots apply — e.g. an in-memory backend with no override and no env var has nowhere to default to.

Source

pub fn blob_store_read_only( &self, config_root: Option<&Path>, floor_bytes: Option<u64>, ) -> Result<Arc<dyn BlobStore>, SqliteError>

Resolve the filesystem blob root exactly like Self::blob_store but require it to exist instead of creating it. Snapshot runtimes wrap the returned capability so its read methods remain available while every mutator is refused.

Source

pub fn is_file_backed(&self) -> bool

Is this a file-backed backend?

Source

pub fn is_read_only(&self) -> bool

Whether this backend was opened with SQLite’s read-only/query-only contract, explicitly or after filesystem-mode detection.

Source

pub fn data_dir(&self) -> Option<PathBuf>

Return the directory containing the backend’s database file, or None for an in-memory backend.

Source

pub fn ann_root(&self) -> Option<PathBuf>

Root directory for this database’s ANN segment tree, or None for an in-memory backend. Derived from the database file name itself (<db-file>.ann/ beside the file), so two databases sharing a parent directory can never adopt each other’s segments or UUID maps. The suffix is appended at the OsString byte level — a lossy UTF-8 conversion would collapse distinct non-UTF-8 filenames into one replacement-character root, breaking exactly that isolation.

Source

pub fn pool(&self) -> &ConnectionPool

Access the underlying pool (escape hatch).

Source

pub fn database_owner_identity( &self, ) -> Result<DatabaseOwnerIdentity, DatabaseOwnerIdentityError>

Durable owner evidence for this backend, captured at pool construction. The caller must choose the effective main backend before comparing it.

Source

pub fn verify_database_owner( &self, expected: &DatabaseOwnerIdentity, ) -> Result<(), DatabaseOwnerIdentityError>

Refuse a secondary or copied database as the supplied database owner.

Source

pub fn pool_arc(&self) -> Arc<ConnectionPool> ⓘ

Clone the underlying pool Arc.

Trait Implementations§

Source§

impl EntityStore for StorageBackend

Source§

fn upsert_entity<'life0, 'async_trait>( &'life0 self, entity: Entity, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert at version one or update the current row and advance its version once. Incoming version values do not override the stored counter. Raw SQL replacement of an existing entity is forbidden: use a conflict UPDATE or the typed store.
Source§

fn insert_entity_if_absent<'life0, 'async_trait>( &'life0 self, _entity: Entity, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert an entity only when no row with its id or another conflicting key exists. Returns true when this call inserted the row and false when an existing row won the race. The existing row is never updated. Read more
Source§

fn upsert_entity_with_attachments<'life0, 'async_trait>( &'life0 self, _entity: Entity, _attachments: Vec<Attachment>, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Atomically insert/update an entity and all supplied attachment roles.
Source§

fn upsert_entities<'life0, 'async_trait>( &'life0 self, entities: Vec<Entity>, ) -> Pin<Box<dyn Future<Output = Result<BatchWriteSummary, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a batch of entities.
Source§

fn replace_entity_if_unchanged<'life0, 'async_trait>( &'life0 self, _entity: Entity, _expected_updated_at: i64, _expected_deleted_at: Option<i64>, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Replace an entity only when the persisted row still matches the caller’s read snapshot. Read more
Source§

fn get_entity<'life0, 'async_trait>( &'life0 self, id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<Entity>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch an entity by UUID, returning None if absent.
Source§

fn delete_entity<'life0, 'async_trait>( &'life0 self, id: Uuid, mode: DeleteMode, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Delete an entity by UUID using the specified delete mode.
Source§

fn query_entities<'life0, 'life1, 'async_trait>( &'life0 self, namespace: &'life1 str, filter: EntityFilter, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Entity>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Query entities by namespace with filter and pagination.
Source§

fn query_entities_count_free<'life0, 'life1, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filter: EntityFilter, _page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Entity>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Query an offset page without computing an exact total. Read more
Source§

fn entity_sequence<'life0, 'async_trait>( &'life0 self, _id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<i64>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Resolve an entity id to its immutable insertion sequence.
Source§

fn query_entities_after<'life0, 'life1, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filter: EntityFilter, _after: Option<SeekCursor>, _limit: u32, ) -> Pin<Box<dyn Future<Output = Result<SeekPage<Entity>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Query an immutable insertion-sequence keyset page. Read more
Source§

fn count_entities<'life0, 'life1, 'async_trait>( &'life0 self, namespace: &'life1 str, filter: EntityFilter, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Count entities in a namespace matching the given filter.
Source§

fn count_entities_by_type<'life0, 'life1, 'async_trait>( &'life0 self, _namespaces: &'life1 [String], ) -> Pin<Box<dyn Future<Output = Result<Option<Vec<(Option<String>, u64)>>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Report complete, disjoint live entity counts by exact stored type across the supplied namespace set, from one backend read snapshot. Duplicate namespaces count once; an empty set produces no groups. SQL NULL is None, distinct from every string label. Ordering is unspecified. Read more
Source§

fn get_entity_including_deleted<'life0, 'async_trait>( &'life0 self, id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<Entity>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch an entity by UUID regardless of soft-deletion state. Read more
Source§

impl EventStore for StorageBackend

Source§

fn append_event<'life0, 'async_trait>( &'life0 self, event: Event, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Append a single event to the log.
Source§

fn append_events<'life0, 'async_trait>( &'life0 self, events: Vec<Event>, ) -> Pin<Box<dyn Future<Output = Result<BatchWriteSummary, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Append a batch of events to the log.
Source§

fn get_event<'life0, 'async_trait>( &'life0 self, id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<Event>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch an event by UUID, returning None if absent.
Source§

fn query_events<'life0, 'async_trait>( &'life0 self, filter: EventFilter, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Event>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Query events matching a filter with pagination.
Source§

fn query_event_page<'life0, 'async_trait>( &'life0 self, query: EventPageQuery, ) -> Pin<Box<dyn Future<Output = Result<EventPageWindow, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Read at most 4096 rows in ascending physical time/ID order, without counting. Filters and exclusions apply before the row bound. A backend must refuse unsupported paging rather than substitute an offset query.
Source§

fn count_events<'life0, 'async_trait>( &'life0 self, filter: EventFilter, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Count events matching a filter.
Source§

fn count_events_grouped<'life0, 'async_trait>( &'life0 self, filter: EventFilter, group_by: EventGroupBy, ) -> Pin<Box<dyn Future<Output = Result<BTreeMap<String, u64>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Count matching rows per stored verb, kind, or actor in this store’s namespace. Empty groups are omitted. Backends must aggregate in storage rather than substitute a bounded row query that could silently undercount.
Source§

fn preflight_event(&self, event: &Event) -> Result<(), StorageError>

Validate event against the exact insert/observation shape the backend would build at append time, performing no I/O. Rejects a malformed row before it is ever enqueued for a write, so one bad producer input cannot poison a batch shared with other callers. Read more
Source§

fn append_events_idempotent<'life0, 'async_trait>( &'life0 self, events: Vec<Event>, ) -> Pin<Box<dyn Future<Output = Result<IdempotentEventBatchResult, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Append a batch of events with idempotent retry semantics: a row carrying an id that already exists is compared against every persisted column and its ordered observation projection rather than treated as a write conflict. Exact equality reports EventAppendDisposition::AlreadyPresentIdentical instead of re-inserting; any mismatch reports EventAppendDisposition::IdentityConflict for that row alone, while unrelated rows in the same batch still commit. Read more
Source§

fn supports_idempotent_audit_batch(&self) -> bool

Whether this backend implements preflight_event and append_events_idempotent for real, rather than inheriting their Unsupported-returning defaults above. Read more
Source§

impl GraphStore for StorageBackend

Source§

fn latest_annotating_note<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _node_id: Uuid, _kind: &'life1 str, _tag: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Option<(Uuid, i64)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Return the newest live note of kind carrying the exact string tag in its properties.tags array and connected to node_id by a live incoming annotates edge in this store’s namespace. Returns its UUID and creation timestamp; equal timestamps choose the smallest UUID. Read more
Source§

fn latest_annotating_note_with_property<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, _node_id: Uuid, _kind: &'life1 str, _tag: &'life2 str, _property_key: &'life3 str, _property_value: &'life4 str, ) -> Pin<Box<dyn Future<Output = Result<Option<(Uuid, i64)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, StorageBackend: 'async_trait,

Like latest_annotating_note, but also require one exact top-level string property before selecting the newest candidate.
Source§

fn upsert_edge<'life0, 'async_trait>( &'life0 self, edge: Edge, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a single edge.
Source§

fn insert_edge_if_absent<'life0, 'async_trait>( &'life0 self, _edge: Edge, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert an edge only when neither its id nor natural key already exists. Returns true when this call inserted the row and false when an existing row won the race. The existing row is never updated. Read more
Source§

fn insert_commit_annotation_if_absent<'life0, 'async_trait>( &'life0 self, _edge: Edge, _guard: CommitAnnotationGuard, ) -> Pin<Box<dyn Future<Output = Result<CommitAnnotationInsertOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Create an annotates edge only while its source is a live commit note in edge.namespace with the exact SHA, its target is the live project for the selected source, the paired commit cursor rows still match the preview, and no row of the edge’s natural key exists, including a tombstone. All checks and the insert occur in one writer transaction. Existing rows are immutable.
Source§

fn upsert_edges<'life0, 'async_trait>( &'life0 self, edges: Vec<Edge>, ) -> Pin<Box<dyn Future<Output = Result<BatchWriteSummary, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a batch of edges.
Source§

fn upsert_edge_observed<'life0, 'async_trait>( &'life0 self, _request: EdgeUpsertRequest, ) -> Pin<Box<dyn Future<Output = Result<EdgeUpsertResult, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or replace one edge and return the transaction-observed disposition plus preimage. Tombstone restoration is controlled by the request rather than being an implicit side effect of every upsert.
Source§

fn replace_edge_if_unchanged<'life0, 'async_trait>( &'life0 self, _edge: Edge, _expected_updated_at: DateTime<Utc>, _expected_deleted_at: Option<DateTime<Utc>>, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Replace an edge only when the persisted row still matches the caller’s read snapshot. Read more
Source§

fn update_symmetric_edge_if_unchanged<'life0, 'async_trait>( &'life0 self, _request: SymmetricEdgeUpdateRequest, ) -> Pin<Box<dyn Future<Output = Result<SymmetricEdgeUpdateOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Apply a validated symmetric-edge patch in one writer transaction. Read more
Source§

fn upsert_edge_guarded<'life0, 'async_trait>( &'life0 self, _edge: Edge, ) -> Pin<Box<dyn Future<Output = Result<GuardedWriteOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a single edge, re-checking that both endpoints still exist (and are not soft-deleted) as part of the same write, not a separate prior read. Closes the TOCTOU window between an async prepare-time existence check and a later, unconditional write: a concurrent hard-delete of an endpoint that lands between the two can otherwise leave a durably dangling edge (#769). Read more
Source§

fn upsert_edge_guarded_observed<'life0, 'async_trait>( &'life0 self, _request: EdgeUpsertRequest, ) -> Pin<Box<dyn Future<Output = Result<GuardedEdgeUpsertOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Observed form of GraphStore::upsert_edge_guarded. In addition to the endpoint guard, it distinguishes create, live replacement, and explicit resurrection without a caller-side read/write race.
Source§

fn upsert_edges_guarded<'life0, 'async_trait>( &'life0 self, _edges: Vec<Edge>, ) -> Pin<Box<dyn Future<Output = Result<GuardedBatchOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Batch form of GraphStore::upsert_edge_guarded. All-or-nothing: if any edge’s endpoints are missing at write time, no edge from the batch is persisted, BatchWriteSummary::affected is 0, and GuardedBatchOutcome::refused names the first failing batch entry and its missing endpoint(s) — determined by the same in-transaction pre-check that aborted the batch, not a post-hoc re-read. Retain the original ordered input to enumerate every aborted write via GuardedBatchOutcome::refusal_page beyond the default bounded sample. Read more
Source§

fn upsert_edges_guarded_observed<'life0, 'async_trait>( &'life0 self, _requests: Vec<EdgeUpsertRequest>, ) -> Pin<Box<dyn Future<Output = Result<GuardedEdgeBatchUpsertOutcome, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

All-or-nothing observed batch form. Implementations must perform endpoint and tombstone-policy preflight in the same write transaction before applying any row.
Source§

fn get_edge<'life0, 'async_trait>( &'life0 self, id: LinkId, ) -> Pin<Box<dyn Future<Output = Result<Option<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch an edge by link ID, returning None if absent. Filters soft-deleted rows.
Source§

fn get_edge_including_deleted<'life0, 'async_trait>( &'life0 self, id: LinkId, ) -> Pin<Box<dyn Future<Output = Result<Option<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch an edge by link ID including soft-deleted rows. Used by the runtime hard-delete path to locate and namespace-check an already-soft-deleted edge before purging it.
Source§

fn get_edge_by_natural_key_including_deleted<'life0, 'life1, 'async_trait>( &'life0 self, namespace: &'life1 str, source_id: Uuid, target_id: Uuid, relation: EdgeRelation, ) -> Pin<Box<dyn Future<Output = Result<Option<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Fetch an edge by natural key (namespace, source, target, relation) including soft-deleted rows. Used by the atomic-apply result renderer for a symmetric-relation update whose surviving canonical row may be tombstoned (ADR-039 DO NOTHING) — the normal query_edges/list_edges path filters deleted_at IS NULL and would report “not found” for exactly that row. Read more
Source§

fn delete_edge<'life0, 'async_trait>( &'life0 self, id: LinkId, mode: DeleteMode, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Delete an edge by link ID using the specified delete mode.
Source§

fn query_edges<'life0, 'async_trait>( &'life0 self, filter: EdgeFilter, sort: Vec<SortOrder<EdgeSortField>>, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Query edges with filter, sort, and pagination without an implicit exact count. Implementations should return total: None; callers that need a count use Self::count_edges explicitly.
Source§

fn query_edges_in_namespaces<'life0, 'life1, 'async_trait>( &'life0 self, namespaces: &'life1 [String], filter: EdgeFilter, sort: Vec<SortOrder<EdgeSortField>>, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Query edges across the given namespaces in one deterministic query with real SQL paging. The multi-namespace analogue of Self::query_edges: a single statement with namespace IN (...) keeps offset continuation coherent, where fetching per-namespace prefixes and slicing a client-side merge floats the window between calls (silent duplicate/skip enumeration). Backends without batched namespace support retain the single-namespace path and reject multi-namespace requests explicitly. Implementations should return total: None; callers that need a count use Self::count_edges_in_namespaces explicitly.
Source§

fn count_edges<'life0, 'async_trait>( &'life0 self, filter: EdgeFilter, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Count edges matching the given filter.
Source§

fn count_edges_in_namespaces<'life0, 'life1, 'async_trait>( &'life0 self, namespaces: &'life1 [String], filter: EdgeFilter, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Count edges across the given namespaces in one aggregate query. Backends without batched namespace support retain the single-namespace path and reject multi-namespace requests explicitly.
Source§

fn count_edges_by_relation<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeRelation, u64)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Count edges grouped by relation, ignoring soft-deleted rows. Cheap aggregate (GROUP BY relation) used to report the true per-relation population for full-graph audits (#702.3).
Source§

fn count_edges_by_relation_in_namespaces<'life0, 'life1, 'async_trait>( &'life0 self, namespaces: &'life1 [String], ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeRelation, u64)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Count edges grouped by relation across the given namespaces in one aggregate query.
Source§

fn count_edges_by_endpoint_base<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<EdgeEndpointBaseCounts, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Count live edges grouped by the base each endpoint resolves against. Read more
Source§

fn count_edges_by_endpoint_base_in_namespaces<'life0, 'life1, 'async_trait>( &'life0 self, namespaces: &'life1 [String], ) -> Pin<Box<dyn Future<Output = Result<EdgeEndpointBaseCounts, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Count live edges by endpoint base across the given namespaces in one aggregate query.
Source§

fn count_edges_by_relation_and_endpoint_base_in_namespaces<'life0, 'life1, 'async_trait>( &'life0 self, namespaces: &'life1 [String], ) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeRelation, EdgeEndpointBaseCounts)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Count live edges across the given namespaces grouped by relation and, within each relation, by the base each endpoint resolves against. Read more
Source§

fn query_edges_after<'life0, 'async_trait>( &'life0 self, filter: EdgeFilter, after: Option<Uuid>, limit: u32, ) -> Pin<Box<dyn Future<Output = Result<EdgeSeekPage, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Seek-pagination page of edges ordered by id ascending, using an indexed range scan (id > after) against the (namespace, id) primary key instead of OFFSET. after is exclusive; None starts from the beginning of the set. This remains an efficient compatibility path for a fixed edge set, but random UUIDs inserted concurrently may sort behind an issued boundary. Public concurrent walks use Self::query_edges_sequence_after instead (#1424).
Source§

fn edge_sequence<'life0, 'async_trait>( &'life0 self, _id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<i64>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Resolve an edge id to its immutable insertion sequence.
Source§

fn edge_sequences<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [Uuid], ) -> Pin<Box<dyn Future<Output = Result<Vec<(Uuid, i64)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Resolve edge ids to immutable insertion sequences. Implementations may override this to batch the lookup; the default preserves correctness.
Source§

fn query_edges_sequence_after<'life0, 'async_trait>( &'life0 self, _filter: EdgeFilter, _after: Option<SeekCursor>, _limit: u32, ) -> Pin<Box<dyn Future<Output = Result<SeekPage<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Seek-pagination page ordered by immutable insertion sequence. This is the stable public-list contract for walks overlapping inserts (#1424).
Source§

fn neighbors<'life0, 'async_trait>( &'life0 self, node_id: Uuid, query: NeighborQuery, ) -> Pin<Box<dyn Future<Output = Result<Vec<NeighborHit>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Return immediate neighbors of a graph node.
Source§

fn neighbors_page<'life0, 'async_trait>( &'life0 self, node_id: Uuid, query: NeighborQuery, after: Option<NeighborCursor>, neighbor_kinds: Option<Vec<String>>, ) -> Pin<Box<dyn Future<Output = Result<Vec<NeighborHit>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Return one deterministic neighbor page. after is exclusive and neighbor_kinds, when present, filters entity and note kinds before the limit is applied. Backends that do not implement kind-aware paging retain an explicit unsupported result rather than silently returning a misleading page.
Source§

fn neighbors_both_directions<'life0, 'async_trait>( &'life0 self, node_id: Uuid, query: NeighborQuery, ) -> Pin<Box<dyn Future<Output = Result<Vec<DirectedNeighborHit>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Return neighbors in BOTH directions in a single call, each tagged with the direction (Out/In) it was found in. query.direction is ignored — this always fetches both directions. Read more
Source§

fn get_edges<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [LinkId], ) -> Pin<Box<dyn Future<Output = Result<Vec<Edge>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Fetch multiple edges by their link IDs in a single round-trip. Read more
Source§

fn get_edge_read_outcomes<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [LinkId], ) -> Pin<Box<dyn Future<Output = Result<Vec<Result<Option<Edge>, StorageError>>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Read live edges in input order, retaining each row’s decode outcome. Read more
Source§

fn batch_neighbors<'life0, 'life1, 'async_trait>( &'life0 self, sources: &'life1 [Uuid], query: NeighborQuery, ) -> Pin<Box<dyn Future<Output = Result<Vec<(Uuid, NeighborHit)>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Return neighbors for multiple source nodes in a single round-trip, yielding (source_id, hit) pairs. Read more
Source§

fn traverse<'life0, 'async_trait>( &'life0 self, request: TraversalRequest, ) -> Pin<Box<dyn Future<Output = Result<Vec<GraphPath>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Bounded multi-hop BFS traversal from the given roots. Read more
Source§

fn purge_incident_edges<'life0, 'async_trait>( &'life0 self, node_id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Hard-delete every incident edge (source or target) for node_id, regardless of soft-delete state. Used during endpoint hard-delete to prevent dangling graph_edges rows (ADR-002 no-dangling-references contract).
Source§

impl NoteStore for StorageBackend

Source§

fn get_live_notes_by_key<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _namespace: &'life1 str, _key: &'life2 str, _kind: Option<&'life3 str>, ) -> Pin<Box<dyn Future<Output = Result<Vec<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, StorageBackend: 'async_trait,

Source§

fn query_keyed_notes<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filter: &'life2 NoteFilter, _prefix: &'life3 str, _after: Option<&'life4 NoteKeyCursor>, _page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<(Vec<Note>, Option<NoteKeyCursor>), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, StorageBackend: 'async_trait,

Source§

fn upsert_note<'life0, 'async_trait>( &'life0 self, note: Note, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a single note. Updates preserve the stored immutable key.
Source§

fn replace_note_if_unchanged<'life0, 'async_trait>( &'life0 self, _note: Note, _expected_updated_at: i64, _expected_deleted_at: Option<i64>, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Replace a note only when the persisted row still matches the caller’s read snapshot. Read more
Source§

fn insert_note_if_absent<'life0, 'async_trait>( &'life0 self, _note: Note, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert a note only if no row already holds its id, reporting whether this call is the one that inserted it. Read more
Source§

fn upsert_notes<'life0, 'async_trait>( &'life0 self, notes: Vec<Note>, ) -> Pin<Box<dyn Future<Output = Result<BatchWriteSummary, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Insert or update a batch of notes. Updates preserve each stored immutable key.
Source§

fn get_note<'life0, 'async_trait>( &'life0 self, id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch a note by UUID, returning None if absent.
Source§

fn get_note_including_deleted<'life0, 'async_trait>( &'life0 self, id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Fetch a note by UUID regardless of soft-deletion state. Read more
Source§

fn delete_note<'life0, 'async_trait>( &'life0 self, id: Uuid, mode: DeleteMode, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Delete a note by UUID using the specified delete mode.
Source§

fn update_note_properties<'life0, 'async_trait>( &'life0 self, id: Uuid, properties: Option<Value>, updated_at: i64, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Patch properties/updated_at on an existing note in place via a real UPDATE, leaving every other column (including the row’s rowid) untouched. Read more
Source§

fn set_note_property<'life0, 'life1, 'async_trait>( &'life0 self, id: Uuid, key: &'life1 str, value: Value, updated_at: i64, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Atomically set one top-level key in a note’s JSON properties object. Read more
Source§

fn try_patch_note_property<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, id: Uuid, namespace: &'life1 str, filter: &'life2 NoteFilter, json_path: &'life3 str, value: Value, updated_at: i64, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, StorageBackend: 'async_trait,

Atomically patch a single properties JSON key on a note, but only when the row’s current state (re-evaluated inside this same statement, not a snapshot the caller fetched earlier) still satisfies filter’s namespace/kind/property_filters. Read more
Source§

fn patch_note_property_atomic<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, _ids: Vec<Uuid>, _namespace: &'life1 str, _filter: &'life2 NoteFilter, _json_path: &'life3 str, _value: Value, _updated_at: i64, ) -> Pin<Box<dyn Future<Output = Result<(), StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, StorageBackend: 'async_trait,

Atomically patch one JSON property on every supplied note. Read more
Source§

fn query_notes<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, namespace: &'life1 str, kind: Option<&'life2 str>, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Query notes by namespace and optional kind with pagination. The returned total and page items must come from one consistent backend snapshot.
Source§

fn query_notes_count_free<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _namespace: &'life1 str, _kind: Option<&'life2 str>, _page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Query notes by namespace and optional kind without computing an exact total. Read more
Source§

fn query_notes_filtered<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, namespace: &'life1 str, filter: &'life2 NoteFilter, page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Query notes with property-based filtering and custom sort. The returned total and page items must come from one consistent backend snapshot.
Source§

fn query_notes_filtered_count_free<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filter: &'life2 NoteFilter, _page: PageRequest, ) -> Pin<Box<dyn Future<Output = Result<Page<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Query a filtered note page without computing an exact total. Read more
Source§

fn count_notes_filtered_in_snapshot<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filters: &'life2 [NoteFilter], ) -> Pin<Box<dyn Future<Output = Result<Vec<u64>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Count several filtered note populations in one consistent backend snapshot. Backends that cannot provide that guarantee must return crate::StorageError::Unsupported rather than composing independent queries or treating an absent page total as zero. SQL backends should override this operation so callers can retain separate index-friendly predicates without racing between their counts.
Source§

fn count_notes_filtered_bounded_in_snapshot<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filters: &'life2 [NoteFilter], _cap: u32, ) -> Pin<Box<dyn Future<Output = Result<Vec<BoundedCount>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Count several filtered note populations in one backend snapshot, with each count bounded by cap. Read more
Source§

fn note_sequence<'life0, 'async_trait>( &'life0 self, _id: Uuid, ) -> Pin<Box<dyn Future<Output = Result<Option<i64>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Resolve a note id to its immutable insertion sequence.
Source§

fn query_notes_filtered_after<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, _namespace: &'life1 str, _filter: &'life2 NoteFilter, _after: Option<SeekCursor>, _limit: u32, ) -> Pin<Box<dyn Future<Output = Result<SeekPage<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Query an immutable insertion-sequence keyset page with the same predicates as Self::query_notes_filtered.
Source§

fn query_notes_filtered_bounded<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, namespace: &'life1 str, filter: &'life2 NoteFilter, max_rows: u32, ) -> Pin<Box<dyn Future<Output = Result<Vec<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Fetch up to max_rows + 1 notes matching filter in a single deterministically-ordered SQL statement, with no separate COUNT(*) and no pagination loop. Read more
Source§

fn count_notes<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, namespace: &'life1 str, kind: Option<&'life2 str>, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Count notes in a namespace, optionally filtered by kind.
Source§

fn count_notes_in_namespaces<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, namespaces: &'life1 [String], kind: Option<&'life2 str>, ) -> Pin<Box<dyn Future<Output = Result<u64, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, StorageBackend: 'async_trait,

Count notes across the given namespaces, optionally filtered by kind. The default preserves compatibility by summing the existing single-namespace operation; SQL backends should override this with one IN aggregate.
Source§

fn try_insert_note<'life0, 'async_trait>( &'life0 self, note: Note, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Attempt to insert a note without overwriting an existing row. Read more
Source§

fn try_insert_note_with_attachments<'life0, 'async_trait>( &'life0 self, _note: Note, _attachments: Vec<Attachment>, ) -> Pin<Box<dyn Future<Output = Result<bool, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Atomically insert an ingest note and the blob attachments owned by it. A deduplicated note writes no attachments; callers must inspect the existing note before repairing a legacy missing role.
Source§

fn get_notes_batch<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [Uuid], ) -> Pin<Box<dyn Future<Output = Result<Vec<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Fetch multiple notes by UUID in a single call.
Source§

fn get_notes_batch_including_deleted<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [Uuid], ) -> Pin<Box<dyn Future<Output = Result<Vec<Note>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Fetch multiple notes, including soft-deleted rows, for mutation policy checks. Missing IDs are omitted; callers correlate rows by ID rather than result order.
Source§

fn get_note_visibility_batch<'life0, 'life1, 'async_trait>( &'life0 self, ids: &'life1 [Uuid], ) -> Pin<Box<dyn Future<Output = Result<Vec<NoteVisibility>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, StorageBackend: 'async_trait,

Fetch only the columns needed for note visibility checks by UUID. Missing IDs are omitted; soft-deleted rows retain their deletion time.
Source§

impl SparseStoreFactory for StorageBackend

Source§

fn open( &self, model_key: &str, namespace: &str, ) -> Result<Arc<dyn SparseStore>, StorageError>

Source§

impl SqlAccess for StorageBackend

Source§

fn database_path(&self) -> Option<PathBuf>

Canonical filesystem identity for this SQL database, when file-backed. Read more
Source§

fn reader<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Box<dyn SqlReader>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Acquire a read-only connection from the pool.
Source§

fn writer<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Box<dyn SqlWriter>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Acquire a read-write connection from the pool.
Source§

fn atomic_unit<'life0, 'async_trait>( &'life0 self, op: Box<dyn for<'w> FnOnce(&'w mut (dyn SqlWriter + 'static)) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, StorageError>> + Send + 'w>> + Send>, ) -> Pin<Box<dyn Future<Output = Result<Box<dyn Any + Send>, StorageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, StorageBackend: 'async_trait,

Run op as ONE atomic unit of work (ADR-067 Component A, Fork C slice 2). Read more
Source§

impl TextSearchFactory for StorageBackend

Source§

fn open( &self, table_key: &str, tokenizer: &str, ) -> Result<Arc<dyn TextSearch>, StorageError>

Source§

impl VectorStoreFactory for StorageBackend

Source§

fn open( &self, model_key: &str, embedding_model: &str, dimensions: usize, namespace: &str, ) -> Result<Arc<dyn VectorStore>, StorageError>

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more