Skip to content
Current documentation — tracks the engine's development branch (v0.4.0 at the last sync).v0.2.1v0.3.0v0.3.1v0.3.2v0.4.0v0.4.1About these docs

Handles

Every non-trivial object crosses the ABI as an opaque handle. One table rules them all:

HandleBacked by (Rust)Thread contractCreated byFreed by
corvid_db*Arc<corvid::Db>thread-safe: concurrent reads from many threads; writes serialized by the enginecorvid_open, corvid_open_memorycorvid_close
corvid_coll*Arc<Db> + collection namethread-safe (shares the Arc<Db>)corvid_collectioncorvid_collection_free
corvid_value*corvid::Valuebuilder handles single-threaded; borrowed children ride the parent’s lifetimeany corvid_value_* constructor, corvid_get, corvid_query_min/max, corvid_value_clonecorvid_value_free (owned values only)
corvid_pred*Predicate treesingle-threaded constructionthe 10 corvid_pred_* constructorscorvid_pred_free (never-consumed roots only); consumed by and/or/not/filter/delete_where
corvid_query*owned QueryBuilder statesingle-threaded buildcorvid_query_newcorvid_query_run and every aggregate (CONSUME); corvid_query_free for abandoned builders
corvid_rows*materialized Vec<ResultRow> + cursorread-only cursor; single-threaded usecorvid_query_run, corvid_page, corvid_phrase_searchcorvid_rows_free
corvid_strs*owned byte-string vector + cursorread-only cursor; single-threadedcorvid_collections, corvid_neighbors, corvid_in_neighbors, corvid_traverse, corvid_value_map_keyscorvid_strs_free
corvid_geohits*owned hit vector + cursorread-only cursor; single-threadedthe 3 corvid_geo_* fns, corvid_neighbors_weightedcorvid_geohits_free
corvid_groupiter*owned group list (sorted by group key) + cursorread-only cursor; single-threadedcorvid_query_group_count/sum/avg (consume the query)corvid_groupiter_free
corvid_schemaiter*owned field list + cursorread-only cursor; single-threadedcorvid_schemacorvid_schemaiter_free
  • corvid_db holds the only strong reference after open; every corvid_coll clones the Arc. corvid_close drops the handle’s reference — the Db (and its file locks) release when the last derived handle is gone. Freeing the db while collection handles live is fine (the collection keeps the engine open).
  • Collections are created lazily on first write (engine Db::collection is infallible) — corvid_collection never fails for name reasons; reserved/invalid names surface at write time, exactly as in Rust.
  • Cross-family frees are forbidden. Each handle has exactly one destructor. Passing a handle to any function of another family is undefined behavior (C’s type system cannot stop it). _free(NULL) is a no-op for every family.
  • corvid_compact requires exclusivity (see admin) — the counter-plus-Arc::get_mut gate answers CORVID_E_BUSY.

Implementation errata (recorded, contract-unchanged)

Section titled “Implementation errata (recorded, contract-unchanged)”
  • The derived-handle counter for corvid_compact is necessary but not sufficient alone: a query’s execute releases its count at entry while its engine Arc clone lives through the engine call — the gate is the counter at exactly 1 and sole Arc ownership.
  • corvid_strs*’s backing is Vec<Vec<u8>>, not Vec<String> — graph endpoints are document keys (arbitrary bytes), so the cursor preserves bytes and hands out the same binary-safe (pointer, length) pairs either way.

Next: errors and NULL discipline.