package granary

  1. Overview
  2. Docs
Pure-OCaml SQL engine

Install

dune-project
 Dependency

Authors

Maintainers

Sources

0.0.3.tar.gz
sha256=8b18780ea373be48301d9f333925860a2f9110fc0ac28684295118d72b65a67e
sha512=25ca3c9c5e2b528704a542502e0f37dc33ba003f65622d969b8c2b800778585f8ef0cf89b36e6679832e3993e8303aecddfc662742baf7044d6afe4a796b8f11

doc/granary.storage/Granary_storage/Btree/index.html

Module Granary_storage.BtreeSource

Copy-on-write B+-tree over the Pager layer.

Every modification (put/del) allocates fresh pages, writes the new state into them via Pager.write, and frees the old pages via Pager.free. Returns a new t whose root_page reflects the latest root.

Keys are sorted by Bytes.compare. Max key size 512 bytes, max value size 1024 bytes.

Sourcetype t
Sourcetype error =
  1. | Pager_error of Pager.error
  2. | Key_too_large of int
  3. | Value_too_large of int
  4. | Tree_corrupt of string
Sourceval pp : Format.formatter -> t -> unit

Pretty-print the tree's root page and snapshot frame bound.

Sourceval pp_error : Format.formatter -> error -> unit

Pretty-print an error.

Sourceval create : ?snapshot_frames:int -> ?pin_set:(int64, unit) Hashtbl.t -> Pager.t -> root_page:int64 -> t

Create a tree view. root_page=0L means an empty tree (no root yet). snapshot_frames: when Some n, reads resolve against WAL frames < n (RO-snapshot semantics); when None (default), reads consult the writer's dirty set and latest WAL. pin_set: when provided (RO snapshots only), reads through this view pin the pages they materialise into the given set (#159) so concurrent writer CoW churn can't evict the reader's working set; released via Pager.unpin_all.

Sourceval root_page : t -> int64

The current root page id. Changes after each mutation. 0L means an empty tree.

Sourceval get : t -> bytes -> (bytes option, error) result Lwt.t

Point lookup.

Sourceval put : t -> bytes -> bytes -> (t, error) result Lwt.t

Insert or replace. Returns an updated t whose root_page reflects the new root. Mutations stamp freed pages via Pager.get_txn_id; the pager manages the transaction ID internally — the caller is responsible only for calling Store.rw_begin which sets the correct txn_id.

  • returns

    Error (Key_too_large n) if the key is more than 512 bytes.

  • returns

    Error (Value_too_large n) if the value is more than 1024 bytes.

Sourceval put_x : t -> bytes -> bytes -> (t * bytes option, error) result Lwt.t

put_x t key value inserts value at key in a single tree descent. Returns (t', None) when key was absent (value written). Returns (t, Some Bytes.empty) when key already existed (NOT overwritten). The returned bytes on conflict are a sentinel — callers that need the old value must fetch it separately via get.

Sourcetype append_cursor

Opaque cached position at a tree's rightmost leaf, enabling O(1) appends for a run of strictly-increasing inserts (#356). Held by the Store per tree-id; re-validated against the live page on every use.

Sourceval append_cursor_max_key : append_cursor -> bytes

The cursor's current maximum key (the tree's rightmost key).

Sourcetype append_outcome =
  1. | Appended of append_cursor
    (*

    inserted in place; carries the updated cursor

    *)
  2. | Not_applicable
    (*

    cursor stale or leaf full — caller must use put_x

    *)
  3. | Append_failed of error
Sourceval try_inplace_append : t -> append_cursor -> key:bytes -> value:bytes -> append_outcome Lwt.t

try_inplace_append t ac ~key ~value appends (key, value) to the rightmost leaf cached in ac in O(1), if ac still matches the live page and the entry fits. key must exceed every key in the tree (the caller guarantees this; it is re-validated). Returns Not_applicable — never an error — when the cursor is stale or the leaf is full, so the caller falls back to put_x.

Sourceval rightmost_append_cursor : t -> (append_cursor option, error) result Lwt.t

Descend the rightmost spine and build a fresh append_cursor for the tree's current rightmost leaf, or None for an empty tree. Used to (re)prime the cursor after a general-path append or a split.

Sourceval del : t -> bytes -> (t, error) result Lwt.t

Delete a key. No-op if absent. Returns an updated t.

Phase 1 deletion is lazy: an emptied leaf is left in the tree (callers won't see it as get returns None and the cursor skips it). The only structural change is that if the root itself becomes empty, root_page is reset to 0L.

Sourcetype cursor
Sourceval cursor_open : t -> (cursor, error) result Lwt.t

Open a forward cursor over the tree. The cursor is positioned at the first entry, so the very first cursor_next returns the first entry.

Sourceval cursor_seek : cursor -> bytes -> ([ `Found | `Not_found_after of bytes ], error) result Lwt.t

Advance the cursor to the first entry >= the given key.

  • returns

    `Found if an exact match exists.

  • returns

    `Not_found_after k if the cursor stopped at a key greater than k (or past the end of the tree).

Sourceval cursor_next : cursor -> ((bytes * bytes) option, error) result Lwt.t

Return the entry at the current cursor position and advance. Returns None when the cursor has passed the last entry.

Sourceval cursor_close : cursor -> unit

Cursor is purely in-memory state — close is a no-op for now but kept for API symmetry / future resource management.