Rust · Reads and Writes

Reads and Writes

use ustore::ArrowArray;use ustore::ArrowArrayStream;use ustore::ArrowExport;use ustore::ArrowSchema;use ustore::IngestOrder;use ustore::BlobsScanConfig;use ustore::Entry;use ustore::Values;use ustore::Blobs;use ustore::Durability;use ustore::Tape;use ustore::Listing;use ustore::Mapped;use ustore::StringsSampleConfig;use ustore::StringsScanConfig;use ustore::TextRange;use ustore::arrow::ArrowDeviceArray;use ustore::blobs::Write;use ustore::blobs::Remove;use ustore::blobs::RemoveRange;use ustore::blobs::Read;use ustore::db::Rows;use ustore::strings::StringsWrite;use ustore::strings::StringsRemove;use ustore::strings::StringsRead;use ustore::strings::StringsScan;use ustore::strings::StringsSample;use ustore::strings::StringsMeasure; 1.0.0-rc.1 · d6d40961

§ Binary Values

Beneath the collections, every verb takes a batch, names its collections by identifier, and hands back what its call produced.

use ustore::{Arena, CollectionID, RAMOptions, Database, Durability, Entry, Key, Matching, Modality};

let database = Database::init_ram(&RAMOptions::default())?;
let mut arena = Arena::new()?;

let [ada, grace, kent] = [1u64, 2, 3].map(Key::from);
let people = database.collection_create("people", Modality::Blobs)?;
let entries = [Entry::new(ada, b"Ada"), Entry::new(grace, b"Grace")];
database.head().write(people, &entries, Durability::Buffered)?;

let values = database.head().read(people, &[ada, grace, kent], &mut arena)?;
assert_eq!(values.get(0), Some(b"Ada".as_slice()));
assert_eq!(values.get(2), None);

An Entry pairs a key with borrowed bytes, laid out so a slice of them crosses as it stands: the keys as one strided run, and the values through an accessor over the same entries, so nothing is staged on the way in. A write also takes owned keys beside a stringtape::BytesTape<u32> or BytesTape<u64> of their values, one more offset than keys into one run of bytes, which the library reads as it stands. A read hands back a Values, which addresses the tape in the arena rather than copying out of it, and reports a key the database does not hold as None. Values::tape hands the whole batch out as a Tape over that tape: one more offset than values, and the bytes between them, 32-bit while the bytes fit in 4 GiB, as Arrow lays out binary, and 64-bit beyond, as it lays out large_binary. A missing value is empty there, and Values::presences is the bitmap of the keys found, as Arrow reads a validity buffer.

A write carries values and nothing else, so an empty slice writes an empty value, and On::remove removes keys with their values in a batch of its own. A removal therefore never hides inside a batch of writes.

let entries = [Entry::new(ada, b"rewritten"), Entry::new(kent, b"")];
database.head().write(people, &entries, Durability::Buffered)?;
database.head().remove(people, &[grace], Durability::Buffered)?;

let first = database.head().scan(people, Key::from(0u64), 100, &mut arena)?.keys;
let measured = database.head().measure(people, ada..kent, &mut arena)?;
let largest = KeyRange { direction: ScanDirection::Descending, ..KeyRange::default() };
let last = database.head().scan(people, largest, 10, &mut arena)?.keys;
let headers = database.head().read_slices(people, &[ada, kent], ..64, &mut arena)?;
database.head().remove_range(people, ada..kent, Durability::Buffered)?;

On::remove removes whole documents the same way, while a field goes with a merge that carries a JSON null for it. scan lists up to the count it wants from a start key, sample draws keys at random, and measure bounds the keys and the bytes a range of keys holds. Scans, cursors and measures take a KeyRange: a start key, inclusive, an end key, exclusive, and a ScanDirection, where a key alone walks up from it and start..end walks up between them. read_slices reads one byte range of every value, and remove_range, docs_remove_range and graph_remove_vertices_range remove every key of a range in one atomic call. docs_sample, pages_sample and graph_sample_vertices draw keys from the other modalities, as docs_measure, pages_measure and graph_measure_vertices measure them.

A Cursor walks one collection page by page, each pull landing in the arena it is given, with the values of its keys when it asks for them:

let mut cursor = database.head().on(&executor).cursor(people, Key::from(0u64), Matching::default())?;
loop {
    let page = cursor.next_pairs(1024, &mut arena)?;
    for (index, key) in page.keys().iter().enumerate() {
        println!("{key}: {:?}", page.get(index));
    }
    if page.keys().len() < 1024 { break; }
}

Every pull resumes past the last key the pull before it handed out, so a key written behind the walk is never handed out and one written ahead of it is. next pulls the keys alone, and next_pairs reads their values in the same call, where a key removed at the head between the listing and the read of one pull reads as an empty value. A page shorter than the count it wanted exhausts the walk for good, so every pull after it hands out nothing, even once more keys are written, and a pull that fails leaves the cursor where it was. A cursor opens from a blocking view, sees what that view sees pull by pull, and pulls on its executor; it holds the database open, and the transaction or snapshot it reads through, until it is dropped.

Database::changes_cursor opens a feed of the changes committed to one collection after a CommitStamp, oldest first, pinning that stamp as a snapshot does:

let mut feed = database.changes_cursor(people, stamp)?;
let changes = feed.next_pairs(1024, &mut arena)?;
for (index, (key, kind)) in changes.keys().iter().zip(changes.kinds()).enumerate() {
    println!("{key} {kind} at {:?}: {:?}", changes.stamps()[index], changes.value(index));
}

Every pull resumes past the last change the one before it handed out, and an empty one reached the newest commit. A ChangeKind is a write, a removal or a partial change, and a range removal comes as the removal of every key it removed. A feed whose history is already gone is refused with Status::WriteConflict, and LevelDB, which keeps no stamps, refuses it with Status::Unsupported.

API

StructArrowArrayThe buffers of an Arrow array, as struct ArrowArray lays them out.1
arrow.rs:79 ↗
pub struct ArrowArray

Fields

lengthi64The rows.
null_counti64The nulls, or -1 when not yet counted.
offseti64The first row, into every buffer.
n_buffersi64The number of buffers.
n_childreni64The number of children.
buffers*mut *const c_voidThe buffers, validity first.
children*mut *mut ArrowArrayThe children.
dictionary*mut ArrowArrayThe dictionary, or null.
releaseOption<unsafe extern "C" fn(*mut ArrowArray)>Frees the array, null once released or moved out.
private_data*mut c_voidThe producer's own state.

Implements

Debug, Drop
emptyA released array, for a producer to write into.
arrow.rs:157 ↗
pub fn empty() -> Self
StructArrowArrayStreamA stream of Arrow record batches of one schema, as struct ArrowArrayStream lays it out.4
arrow.rs:125 ↗
pub struct ArrowArrayStream

Fields

get_schemaOption<unsafe extern "C" fn(*mut ArrowArrayStream, *mut ArrowSchema) -> c_int>Writes the schema every batch shares, answering 0 or an errno code.
get_nextOption<unsafe extern "C" fn(*mut ArrowArrayStream, *mut ArrowArray) -> c_int>Writes the next batch, released once the stream ends, answering 0 or an errno code.
get_last_errorOption<unsafe extern "C" fn(*mut ArrowArrayStream) -> *const c_char>The message of the last failed callback, or null.
releaseOption<unsafe extern "C" fn(*mut ArrowArrayStream)>Frees the stream, which every batch it answered outlives; null once released or moved out.
private_data*mut c_voidThe producer's own state.

Implements

Debug, Drop
emptyA released stream, for a producer to write into.
arrow.rs:221 ↗
pub fn empty() -> Self
schemaThe schema every batch shares.
arrow.rs:232 ↗
pub fn schema(&mut self) -> Result<ArrowSchema>
next_batchThe next batch, or None once the stream ends.
arrow.rs:246 ↗
pub fn next_batch(&mut self) -> Result<Option<ArrowArray>>
last_errorThe message of the last failed callback, which lives until the next call on the stream.
arrow.rs:260 ↗
pub fn last_error(&mut self) -> Option<&CStr>
StructArrowExportOne column exported as Arrow in place, borrowing the arena it sits in until dropped.3
arrow.rs:191 ↗
pub struct ArrowExport<'arena>

Implements

Debug
schemaThe column's type.
arrow.rs:199 ↗
pub fn schema(&self) -> &ArrowSchema
arrayThe column's buffers and the device holding them.
arrow.rs:204 ↗
pub fn array(&self) -> &ArrowDeviceArray
into_partsHands the column over past the arena's borrow, for a consumer that takes the structures by value.
arrow.rs:214 ↗
pub unsafe fn into_parts(self) -> (ArrowSchema, ArrowDeviceArray)

Safety

The arena must outlive the release of both, which no borrow checks any more.

StructArrowSchemaThe type of an Arrow array: its format, name, metadata and children, as struct ArrowSchema lays them out.1
arrow.rs:55 ↗
pub struct ArrowSchema

Fields

format*const c_charThe format string, such as +s for a struct or w:16 for 16-byte keys.
name*const c_charThe field name, or null.
metadata*const c_charThe binary-encoded key-value metadata, or null.
flagsi64The ARROW_FLAG_* bits.
n_childreni64The number of children.
children*mut *mut ArrowSchemaThe children.
dictionary*mut ArrowSchemaThe dictionary type, or null.
releaseOption<unsafe extern "C" fn(*mut ArrowSchema)>Frees the schema, null once released or moved out.
private_data*mut c_voidThe producer's own state.

Implements

Debug, Drop
emptyA released schema, for a producer to write into.
arrow.rs:140 ↗
pub fn empty() -> Self
EnumIngestOrderIn what order an ingest's rows arrive, as ustore_ingest_order_t spells it.
arrow.rs:298 ↗
pub enum IngestOrder

Values

Sorted0Keys strictly ascend across every batch, checked as each one is written into runs of its own.
Unsorted1Keys arrive in any order and may repeat, the last row of a key winning; every batch is held until the end.

Implements

Clone, Copy, StructuralPartialEq, PartialEq, Eq, Debug, Default, Hash, Display, FromStr
StructBlobsScanConfigThe keys a scan keeps, what admits them and how it reports; see On::scan.
blobs.rs:296 ↗
pub struct BlobsScanConfig<'config>

Fields

wantedusizeKeys kept, at most.
matchingMatching<'config>Admits only the keys it approves; every key by default.
modeScanModeIn what order the keys come.
progressOption<&'config mut ProgressCallback<'config>>Reports the scans finished and stops wherever it says to.

Implements

From<usize>
StructEntryOne key and the bytes stored under it, laid out so a slice of them crosses as it stands: the keys as one strided run, and the values through an accessor over the same entries.3
blobs.rs:208 ↗
pub struct Entry<'value>

Implements

Clone, Copy, Debug
newPairs a key with the bytes to store under it.
blobs.rs:216 ↗
pub fn new(key: Key, value: &'value [u8]) -> Self
keyThe key this entry stores under.
blobs.rs:225 ↗
pub fn key(&self) -> Key
valueThe bytes this entry stores.
blobs.rs:230 ↗
pub fn value(&self) -> &'value [u8]
StructValuesThe values one read exported, addressed by the position of their key in the batch.6
blobs.rs:355 ↗
pub struct Values<'arena>

The presences, the offsets and the bytes all live in the arena, so the run borrows it, and the next call through it cannot overwrite them while the run lives.

Implements

Clone, Copy, Debug, IntoIterator
lenHow many keys the read covered.
blobs.rs:384 ↗
pub fn len(&self) -> usize
is_emptyWhether the read covered no keys at all.
blobs.rs:389 ↗
pub fn is_empty(&self) -> bool
getThe value of the key at index, or None for a key the database does not hold.
blobs.rs:394 ↗
pub fn get(&self, index: usize) -> Option<&'arena [u8]>
iterEvery value in the order its key was asked for.
blobs.rs:402 ↗
pub fn iter(&self) -> Rows<Values<'arena>, Option<&'arena [u8]>>
presencesWhich keys the read found, one bit per key as Arrow reads a validity buffer.
blobs.rs:407 ↗
pub fn presences(&self) -> Bitmap<'arena>
tapeEvery value on the one tape the read exported, without a copy.
blobs.rs:414 ↗
pub fn tape(&self) -> Tape<'arena>

A missing value is empty on the tape, and Values::presences tells it apart from an empty one.

StructBlobsThe binary values of one collection.5
collection.rs:252 ↗
pub struct Blobs<'collection, 'view, Parent = Database>

Every verb consumes the view; Collection::blobs hands out the next one.

Implements

Debug
getThe value under key, copied out of a one-off arena, or None for a key the collection does not hold.
collection.rs:258 ↗
pub fn get(self, key: Key) -> Result<Option<Vec<u8>>>
readThe values under keys in order, read into arena, which Values::tape hands out as one tape.
collection.rs:263 ↗
pub fn read<'arena>(self, keys: &[Key], arena: &'arena mut Arena) -> Result<Values<'arena>>
containsWhether the collection holds key, exporting no value into the one-off arena it reads through.
collection.rs:269 ↗
pub fn contains(self, key: Key) -> Result<bool>
setStores value under key, creating the collection to hold blobs when it is missing.
collection.rs:279 ↗
pub fn set(self, key: Key, value: &[u8]) -> Result<()>
removeRemoves key, whether or not the collection holds it.
collection.rs:287 ↗
pub fn remove(self, key: Key) -> Result<()>
EnumDurabilityWhether a write reaches the drive before the call returns, as ustore_durability_t spells it.
db.rs:1217 ↗
pub enum Durability

A transaction flushes at its commit, so Flushed on a transaction's write is refused.

Values

Buffered0The engine decides when the bytes reach the drive.
Flushed1The call returns once the bytes are on the drive, at a cost that varies by engine.

Implements

Clone, Copy, StructuralPartialEq, PartialEq, Eq, Debug, Default, Hash, Display, FromStr
EnumTapeThe strings a call wrote as one tape: one more offset than strings, and the bytes between them, as Arrow lays out binary while the bytes fit in 4 GiB and large_binary beyond.3
db.rs:1826 ↗
pub enum Tape<'arena>

Values

U32(BytesTapeViewU32&lt;'arena&gt;)A tape of 32-bit offsets, as Arrow lays out binary.
U64(BytesTapeViewU64&lt;'arena&gt;)A tape of 64-bit offsets, as Arrow lays out large_binary.

Implements

Debug
lenHow many strings the tape holds.
db.rs:1835 ↗
pub fn len(&self) -> usize
is_emptyWhether the tape holds no strings.
db.rs:1843 ↗
pub fn is_empty(&self) -> bool
*× 2The string at index, or None past the end.
db.rs:1848 ↗
pub fn get(&self, index: usize) -> Option<&[u8]>
StructListingThe strings one scan or sample listed, as byte runs inside the arena, and the BM25 scores of a scan whose patterns carried weights.4
strings.rs:407 ↗
pub struct Listing<'arena>

Implements

Clone, Copy, Debug
lenHow many strings the call listed.
strings.rs:433 ↗
pub fn len(&self) -> usize
is_emptyWhether the call listed nothing.
strings.rs:438 ↗
pub fn is_empty(&self) -> bool
iterEvery string in the order the call listed it.
strings.rs:448 ↗
pub fn iter(&self) -> Rows<Listing<'arena>, &'arena [u8]>
scoresOne score per string when the patterns carried weights.
strings.rs:455 ↗
pub fn scores(&self) -> Option<&'arena [f32]>
StructMappedThe keys one read found, addressed by the position of their string in the batch.5
strings.rs:369 ↗
pub struct Mapped<'arena>

Implements

Clone, Copy, Debug
lenHow many strings the read covered.
strings.rs:376 ↗
pub fn len(&self) -> usize
is_emptyWhether the read covered no strings at all.
strings.rs:381 ↗
pub fn is_empty(&self) -> bool
getThe key of the string at index, or None for a string the database does not hold.
strings.rs:386 ↗
pub fn get(&self, index: usize) -> Option<Key>
iterEvery key in the order its string was asked for.
strings.rs:394 ↗
pub fn iter(&self) -> Rows<Mapped<'arena>, Option<Key>>
presencesWhich strings the read found, one bit per string as Arrow reads a validity buffer.
strings.rs:399 ↗
pub fn presences(&self) -> Bitmap<'arena>
StructStringsSampleConfigThe strings a sample draws; see On::strings_sample.
strings.rs:284 ↗
pub struct StringsSampleConfig

Fields

wantedusizeStrings drawn, at most.

Implements

Clone, Copy, Debug, From<usize>
StructStringsScanConfigThe strings a scan keeps, what admits them and how it reports; see On::strings_scan.
strings.rs:260 ↗
pub struct StringsScanConfig<'config>

Fields

wantedusizeStrings kept, at most.
matchingMatching<'config>Admits only the strings it approves; every string by default.
modeScanModeIn what order the strings come.
progressOption<&'config mut ProgressCallback<'config>>Reports the scans finished and stops wherever it says to.

Implements

From<usize>
StructTextRangeWhich strings of one collection a walk covers: those starting with prefix, from start, inclusive, up to end, exclusive, in the order of their bytes or the reverse.
strings.rs:340 ↗
pub struct TextRange<'text>

A string converts into the ascending range of the strings starting with it.

Fields

prefixOption<&'text str>What every string starts with, or None for every string.
startOption<&'text str>The string the walk starts from, or None for the first one in its direction.
endOption<&'text str>The string the walk stops before, or None to walk to the last one in its direction.
directionScanDirectionWhich way the walk goes from start.

Implements

Clone, Copy, StructuralPartialEq, PartialEq, Eq, Debug, Default, Hash, From<&'text str>
StructArrowDeviceArrayAn Arrow array and the device holding its buffers, as struct ArrowDeviceArray lays them out.2
arrow.rs:108 ↗
pub struct ArrowDeviceArray

The buffers of an array on a device are addresses there, which only the raw pointers of ArrowArray carry, so no safe host slice ever reads them.

Fields

arrayArrowArrayThe array.
device_idi64The vendor's ordinal of the device, or -1 on the host.
device_typei32The ARROW_DEVICE_* type of the device.
sync_event*mut c_voidWhat to wait on before reading the buffers, or null, as an exporter here waited for its device.
reserved[i64; 3]Zeroed.

Implements

Debug
CPUARROW_DEVICE_CPU, the type of memory the host reads in place.
arrow.rs:175 ↗
pub const CPU: i32 = 1;
emptyA released array, for an exporter to write into.
arrow.rs:178 ↗
pub fn empty() -> Self
StructWriteAn upsert of a batch of values; see On::write.
blobs.rs:740 ↗
pub struct Write<'collections, Entries>

Implements

Debug
StructRemoveA removal of a batch of keys; see On::remove.
blobs.rs:785 ↗
pub struct Remove<'collections, Keys>

Implements

Debug
StructRemoveRangeA removal of one range of keys; see On::remove_range.
blobs.rs:831 ↗
pub struct RemoveRange

Implements

Debug
StructReadA read of a batch of keys; see On::read.
blobs.rs:873 ↗
pub struct Read<'arena, 'collections, Keys>

Implements

Debug
StructRowsThe rows of a batch result in order, from either end, each read through the result's own get.
db.rs:1670 ↗
pub struct Rows<Results, Row>

Implements

Clone, Debug, Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator
StructStringsWriteAn upsert of a batch of strings; see On::strings_write.
strings.rs:613 ↗
pub struct StringsWrite<'collections, Strs, Keys>

Implements

Debug
StructStringsRemoveA removal of a batch of strings; see On::strings_remove.
strings.rs:663 ↗
pub struct StringsRemove<'collections, Strs>

Implements

Debug
StructStringsReadA read of the keys of a batch of strings; see On::strings_read.
strings.rs:708 ↗
pub struct StringsRead<'arena, 'collections, Strs>

Implements

Debug
StructStringsScanA walk over the strings of one collection; see On::strings_scan.
strings.rs:771 ↗
pub struct StringsScan<'arena, 'text, 'matching>

Implements

Debug
StructStringsSampleA random draw of strings from one collection; see On::strings_sample.
strings.rs:848 ↗
pub struct StringsSample<'arena>

Implements

Debug
StructStringsMeasureAn estimate of what a range of strings holds; see On::strings_measure.
strings.rs:894 ↗
pub struct StringsMeasure<'arena, 'text>

Implements

Debug