Persistent storage

Lets start with designing the disk storage format. The non-goal Writing low level code stears off designing a file format and using direct file access. A good alternative is SQLite. Lets set it up first.

Bootstrapping the project

I'll call the project sakkosekk, which is Norwegian for bean bag chair.

Bootstrapping with Cargo:

~/g/build-your-own-couchdb $ cargo init sakkosekk Created binary (application) package ~/g/build-your-own-couchdb $ cd sakkosekk/ ~/g/b/sakkosekk $ cargo run Compiling sakkosekk v0.1.0 (/Users/arve/git/build-your-own-couchdb/sakkosekk) Finished dev [unoptimized + debuginfo] target(s) in 10.24s Running `target/debug/sakkosekk` Hello, world!

Adding SQLite

Bindings for SQLite is available through the rusqlite crate:

~/g/b/sakkosekk $ echo '[dependencies]' >> Cargo.toml ~/g/b/sakkosekk $ echo 'rusqlite = { version = "0.20", features = ["bundled"] }' >> Cargo.toml

The bundled feature is enabled for hassle free sqlite3 linking.

Database schema

Documents in the database will have the columns:

  • indentifier,
  • revision,
  • hash and
  • document data.

Open database:

use rusqlite::{named_params, Connection}; fn main() { let db = Connection::open("database.sqlite").expect("Unable to open 'database.sqlite'.");

Creating table:

db.execute_batch( "create table documents ( id text primary key not null, revision integer not null, hash blob not null, data text not null )", ) .expect("Unable to create documents table.");

Inserting document:

db.execute_named( "insert into documents (id, revision, hash, data) values (:id, :revision, :hash, :data)", named_params!( ":id": "asdf", ":revision": 0, ":hash": vec![0u8], ":data": r#"{ "a": 1, "b": 123 }"# ), ) .expect("Unable to insert document.");

Reading document by the identifier:

let data: String = db .query_row_named( "select data from documents where id=:id", named_params!(":id": "asdf"), |row| row.get(0), ) .expect("Unable to get document with id 'asdf'"); println!("data: {}", data); }

Result:

~/g/b/sakkosekk $ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.31s Running `target/debug/sakkosekk` data: { "a": 1, "b": 123 }

Next up: Abstractions around creating the database schema, inserting documents and reading documents.