pub struct DbImpl<Store: StorageData> { /* private fields */ }Expand description
An instance of the agdb database. To create a database:
use agdb::Db;
let mut db = Db::new("db1.agdb").unwrap();This will try to create or load the database file path db.agdb.
If the file does not exist a new database will be initialized creating
the given file. If the file does exist the database will try to load
it and memory map the data.
These are the available variants of the database to choose from:
Db: [default] File based and memory mapped database.DbFile: File based only (no memory mapping).DbMemory: In-memory database only.DbAny: Database variant that can be any of the other variants.
For each of these there are convenient using declarations, e.g. DbTransaction,
DbFileTransaction, DbMemoryTransactionMut etc. in case you need to name
the related types of the main database type.
You can execute queries or transactions on the database object with
- exec() //immutable queries
- exec_mut() //mutable queries
- transaction() //immutable transactions
- transaction_mut() // mutable transaction
§Examples
use agdb::{Db, QueryBuilder, DbError};
let mut db = Db::new("db2.agdb").unwrap();
// Insert single node
db.exec_mut(QueryBuilder::insert().nodes().count(1).query()).unwrap();
// Insert single node as a transaction
db.transaction_mut(|t| -> Result<(), DbError> { t.exec_mut(QueryBuilder::insert().nodes().count(1).query())?; Ok(()) }).unwrap();
// Select single database element with id 1
db.exec(QueryBuilder::select().ids(1).query()).unwrap();
// Select single database element with id 1 as a transaction
db.transaction(|t| -> Result<(), DbError> { t.exec(QueryBuilder::select().ids(1).query())?; Ok(()) }).unwrap();
// Search the database starting at element 1
db.exec(QueryBuilder::search().from(1).query()).unwrap();§Transactions
All queries are transactions. Explicit transactions take closures that are passed
the transaction object to record & execute queries. You cannot explicitly commit
nor rollback transactions. To commit a transaction simply return Ok from the
transaction closure. Conversely to rollback a transaction return Err. Nested
transactions are not allowed.
§Multithreading
The agdb is multithreading enabled. It is recommended to use Arc<RwLock>:
use std::sync::{Arc, RwLock};
use agdb::Db;
let db = Arc::new(RwLock::new(Db::new("db3.agdb").unwrap()));
db.read().unwrap(); //for a read lock allowing Db::exec() and Db::transaction()
db.write().unwrap(); //for a write lock allowing additionally Db::exec_mut() and Db::transaction_mut()Using the database in the multi-threaded environment is then the same as in a single
threaded application (minus the locking). Nevertheless while Rust does prevent
race conditions you still need to be on a lookout for potential deadlocks. This is
one of the reasons why nested transactions are not supported by the agdb.
Akin to the Rust borrow checker rules the agdb can handle unlimited number
of concurrent reads (transactional or regular) but only single write operation
at any one time. For that reason the transactions are not database states or objects
but rather a function taking a closure executing the queries in an attempt to limit
their scope as much as possible (and therefore the duration of the [exclusive] lock).
§Storage
The agdb is using a single database file to store all of its data. Additionally
a single shadow file with a . prefix of the main database file name is used as
a write ahead log (WAL). On drop of the Db object the WAL is processed and removed
aborting any unfinished transactions. Furthermore the database data is defragmented.
On load, if the WAL file is present (e.g. due to a crash), it will be processed restoring any consistent state that existed before the crash. Data is only written to the main file if the reverse operation has been committed to the WAL file. The WAL is then purged on commit of a transaction (all queries are transactional even if the transaction is not explicitly used).
Implementations§
Source§impl<Store: StorageData> DbImpl<Store>
impl<Store: StorageData> DbImpl<Store>
Sourcepub fn new(filename: &str) -> Result<Self, DbError>
pub fn new(filename: &str) -> Result<Self, DbError>
Tries to create or load filename file as Db object. For in-memory storage
this will either load the data from file once (if present) or create an empty database.
If used with the DbAny variant the database will be of variant Db (memory mapped). Use
DbAny::new_*() to construct the other variants.
Sourcepub fn backup(&self, filename: &str) -> Result<(), DbError>
pub fn backup(&self, filename: &str) -> Result<(), DbError>
Flushes the underlying file and copies it
to filename path. Consider calling optimize_storage()
prior to this function to reduce the size of the storage
file. If speed is of the essence you may omit that operation
at expense of the file size. For memory based storage this will
dump the internal buffer to the filename which can be used to
restore back the database by DbMemory::new().
Sourcepub fn copy(&self, filename: &str) -> Result<Self, DbError>
pub fn copy(&self, filename: &str) -> Result<Self, DbError>
Copies the database to filename path. Consider calling
optimize_storage() prior to this function to reduce the
size of the storage file to be copied.
Sourcepub fn exec<T: Query>(&self, query: T) -> Result<QueryResult, DbError>
pub fn exec<T: Query>(&self, query: T) -> Result<QueryResult, DbError>
Executes immutable query:
- Select elements
- Select values
- Select keys
- Select key count
- Select aliases
- Select all aliases
- Search
It runs the query as a transaction and returns either the result or error describing what went wrong (e.g. query error, logic error, data error etc.).
Sourcepub fn exec_mut<T: QueryMut>(
&mut self,
query: T,
) -> Result<QueryResult, DbError>
pub fn exec_mut<T: QueryMut>( &mut self, query: T, ) -> Result<QueryResult, DbError>
Executes mutable query:
- Insert nodes
- Insert edges
- Insert aliases
- Insert values
- Remove elements
- Remove aliases
- Remove values
It runs the query as a transaction and returns either the result or error describing what went wrong (e.g. query error, logic error, data error etc.).
Sourcepub fn optimize_storage(&mut self) -> Result<(), DbError>
pub fn optimize_storage(&mut self) -> Result<(), DbError>
Reclaims no longer used segments of the database file by packing all used storage segments together. This operation is done automatically when the database goes out of scope. In long running programs it might be desired to perform the storage file optimization without fully shutting down.
Sourcepub fn rename(&mut self, filename: &str) -> Result<(), DbError>
pub fn rename(&mut self, filename: &str) -> Result<(), DbError>
Changes the name of the database changing also the names of the files (if the storage is file based).
Sourcepub fn transaction<T, E>(
&self,
f: impl FnOnce(&Transaction<'_, Store>) -> Result<T, E>,
) -> Result<T, E>
pub fn transaction<T, E>( &self, f: impl FnOnce(&Transaction<'_, Store>) -> Result<T, E>, ) -> Result<T, E>
Executes immutable transaction. The transaction is running a closure f
that will receive &Transaction object to run exec queries as if run
on the main database object. You shall specify the return type T
(can be ()) and the error type E that must be constructible from the DbError
(E can be DbError).
Read transactions cannot be committed or rolled back but their main function is to ensure
that the database data does not change during their duration. Through its generic
parameters it also allows transforming the query results into a type T.
Sourcepub fn transaction_mut<T, E: From<DbError>>(
&mut self,
f: impl FnOnce(&mut TransactionMut<'_, Store>) -> Result<T, E>,
) -> Result<T, E>
pub fn transaction_mut<T, E: From<DbError>>( &mut self, f: impl FnOnce(&mut TransactionMut<'_, Store>) -> Result<T, E>, ) -> Result<T, E>
Executes mutable transaction. The transaction is running a closure f
that will receive &mut Transaction to execute exec and exec_mut queries
as if run on the main database object. You shall specify the return type T
(can be ()) and the error type E that must be constructible from the DbError
(E can be DbError).
Write transactions are committed if the closure returns Ok and rolled back if
the closure returns Err. If the code panics and the program exits the write
ahead log (WAL) makes sure the data in the main database file is restored to a
consistent state prior to the transaction.
Typical use case for a write transaction is to insert nodes and edges together. When not using a transaction you could end up only with nodes being inserted.
Through its generic parameters the transaction also allows transforming the query
results into a type T.
Sourcepub fn size(&self) -> u64
pub fn size(&self) -> u64
Returns the database size in bytes. Depending on the underlying storage the physical size in hardware might be higher. For example for in-memory storage this function reports used storage but actually allocated memory will likely be higher (expecting database growth in order to prevent too frequent allocations). For file based storages this number will be accurate but the actually used space on disk will be higher up to the next file system block size.
Source§impl DbImpl<AnyStorage>
impl DbImpl<AnyStorage>
Sourcepub fn new_file(filename: &str) -> Result<Self, DbError>
pub fn new_file(filename: &str) -> Result<Self, DbError>
Creates a new DbAny instance using DbFile.
Sourcepub fn new_mapped(filename: &str) -> Result<Self, DbError>
pub fn new_mapped(filename: &str) -> Result<Self, DbError>
Creates a new DbAny instance using Db.
Sourcepub fn new_memory(filename: &str) -> Result<Self, DbError>
pub fn new_memory(filename: &str) -> Result<Self, DbError>
Creates a new DbAny instance using DbMemory.