update the C library

main
Ziyang Hu 2 years ago
parent f15be33d58
commit 5fd85b9d98

@ -330,6 +330,13 @@ impl<'s, S: Storage<'s>> Db<S> {
#[cfg(not(feature = "storage-sqlite"))]
bail!("backup requires the 'storage-sqlite' feature to be enabled")
}
/// Backup the running database into an Sqlite file, with JSON string return value
pub fn backup_db_str(&'s self, out_file: &str) -> String {
match self.backup_db(out_file.to_string()) {
Ok(_) => json!({"ok": true}).to_string(),
Err(err) => json!({"ok": false, "message": err.to_string()}).to_string(),
}
}
/// Restore from an Sqlite backup
pub fn restore_backup(&'s self, in_file: String) -> Result<()> {
#[cfg(feature = "storage-sqlite")]
@ -344,6 +351,13 @@ impl<'s, S: Storage<'s>> Db<S> {
#[cfg(not(feature = "storage-sqlite"))]
bail!("backup requires the 'storage-sqlite' feature to be enabled")
}
/// Restore from an Sqlite backup, with JSON string return value
pub fn restore_backup_str(&'s self, in_file: &str) -> String {
match self.restore_backup(in_file.to_string()) {
Ok(_) => json!({"ok": true}).to_string(),
Err(err) => json!({"ok": false, "message": err.to_string()}).to_string(),
}
}
fn compact_relation(&'s self) -> Result<()> {
let l = Tuple::default().encode_as_key(RelationId(0));

@ -18,6 +18,7 @@ use crate::runtime::relation::decode_tuple_from_kv;
use crate::storage::{Storage, StoreTx};
/// The Sqlite storage engine
#[derive(Clone)]
pub struct SqliteStorage {
lock: Arc<RwLock<()>>,
name: String,

@ -12,13 +12,35 @@ description = "C bindings for CozoDB"
crate-type = ["cdylib", "staticlib"]
[features]
#! # Features
## Enables the `minimal`, `requests` and `graph-algo` features
compact = ["minimal", "requests", "graph-algo", "rayon"]
## Enables the `minimal`, `requests` and `graph-algo` features in single threaded mode
compact-single-threaded = ["minimal", "requests", "graph-algo"]
## Enables the `storage-sqlite` feature
minimal = ["storage-sqlite"]
## Enables the [Sqlite](https://www.sqlite.org/index.html) backend, also allows backup and restore with Sqlite data files.
storage-sqlite = ["cozo/storage-sqlite"]
## Enables the [RocksDB](http://rocksdb.org/) backend
storage-rocksdb = ["cozo/storage-rocksdb"]
## Enables the graph algorithms
graph-algo = ["cozo/graph-algo"]
## Allows the utilities to make web requests to fetch data
requests = ["cozo/requests"]
## Uses jemalloc as the global allocator, can make a difference in performance
jemalloc = ["cozo/jemalloc"]
## Enables io-uring option for the RocksDB storage
io-uring = ["cozo/io-uring"]
## Allows threading and enables the use of the `rayon` library for parallelizing algorithms
rayon = ["cozo/rayon"]
## Disallows the use of threads
nothread = ["cozo/nothread"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cozo = { version = "0.1.2", path = "../cozo-core" }
cozo = { version = "0.1.2", path = "../cozo-core", default_features = false }
lazy_static = "1.4.0"
[build-dependencies]

@ -17,6 +17,7 @@ extern "C" {
/**
* Open a database.
*
* `engine`: Which storage engine to use, can be "mem", "sqlite" or "rocksdb".
* `path`: should contain the UTF-8 encoded path name as a null-terminated C-string.
* `db_id`: will contain the id of the database opened.
*
@ -24,7 +25,7 @@ extern "C" {
* otherwise a pointer to a C-string containing the error message will be returned.
* The returned C-string must be freed with `cozo_free_str`.
*/
char *cozo_open_db(const char *path, int32_t *db_id);
char *cozo_open_db(const char *engine, const char *path, int32_t *db_id);
/**
* Close a database.
@ -53,6 +54,49 @@ bool cozo_close_db(int32_t id);
*/
char *cozo_run_query(int32_t db_id, const char *script_raw, const char *params_raw);
/**
* Import data into a relation
* `db_id`: the ID representing the database.
* `json_payload`: a UTF-8 encoded JSON payload, see the manual for the expected fields.
*
* Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
*/
char *cozo_import_relation(int32_t db_id,
const char *json_payload);
/**
* Export relations into JSON
*
* `db_id`: the ID representing the database.
* `json_payload`: a UTF-8 encoded JSON payload, see the manual for the expected fields.
*
* Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
*/
char *cozo_export_relations(int32_t db_id,
const char *json_payload);
/**
* Backup the database.
*
* `db_id`: the ID representing the database.
* `out_path`: path of the output file.
*
* Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
*/
char *cozo_backup(int32_t db_id,
const char *out_path);
/**
* Restore the database from a backup.
*
* `db_id`: the ID representing the database.
* `in_path`: path of the input file.
*
* Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
*/
char *cozo_restore(int32_t db_id,
const char *in_path);
/**
* Free any C-string returned from the Cozo C API.
* Must be called exactly once for each returned C-string.

@ -15,16 +15,104 @@ use std::sync::Mutex;
use lazy_static::lazy_static;
use cozo::RocksDbStorage;
use cozo::{new_cozo_rocksdb, Db};
use cozo::*;
struct Handles<S> {
#[derive(Clone)]
enum DbInstance {
Mem(Db<MemStorage>),
#[cfg(feature = "storage-sqlite")]
Sqlite(Db<SqliteStorage>),
#[cfg(feature = "storage-rocksdb")]
RocksDb(Db<RocksDbStorage>),
}
impl DbInstance {
fn new(engine: &str, path: &str) -> Result<Self, String> {
match engine {
"mem" => Ok(Self::Mem(new_cozo_mem().map_err(|err| err.to_string())?)),
"sqlite" => {
#[cfg(feature = "storage-sqlite")]
{
return Ok(Self::Sqlite(
new_cozo_sqlite(path.to_string()).map_err(|err| err.to_string())?,
));
}
#[cfg(not(feature = "storage-sqlite"))]
{
return Err("support for sqlite not compiled".to_string());
}
}
"rocksdb" => {
#[cfg(feature = "storage-rocksdb")]
{
return Ok(Self::RocksDb(
new_cozo_rocksdb(path.to_string()).map_err(|err| err.to_string())?,
));
}
#[cfg(not(feature = "storage-rocksdb"))]
{
return Err("support for rocksdb not compiled".to_string());
}
}
_ => Err(format!("unsupported engine: {}", engine)),
}
}
fn run_script_str(&self, payload: &str, params: &str) -> String {
match self {
DbInstance::Mem(db) => db.run_script_str(payload, params),
#[cfg(feature = "storage-sqlite")]
DbInstance::Sqlite(db) => db.run_script_str(payload, params),
#[cfg(feature = "storage-rocksdb")]
DbInstance::RocksDb(db) => db.run_script_str(payload, params),
}
}
fn import_relations(&self, data: &str) -> String {
match self {
DbInstance::Mem(db) => db.import_relation_str(data),
#[cfg(feature = "storage-sqlite")]
DbInstance::Sqlite(db) => db.import_relation_str(data),
#[cfg(feature = "storage-rocksdb")]
DbInstance::RocksDb(db) => db.import_relation_str(data),
}
}
fn export_relations(&self, data: &str) -> String {
match self {
DbInstance::Mem(db) => db.export_relations_str(data),
#[cfg(feature = "storage-sqlite")]
DbInstance::Sqlite(db) => db.export_relations_str(data),
#[cfg(feature = "storage-rocksdb")]
DbInstance::RocksDb(db) => db.export_relations_str(data),
}
}
fn backup(&self, path: &str) -> String {
match self {
DbInstance::Mem(db) => db.backup_db_str(path),
#[cfg(feature = "storage-sqlite")]
DbInstance::Sqlite(db) => db.backup_db_str(path),
#[cfg(feature = "storage-rocksdb")]
DbInstance::RocksDb(db) => db.backup_db_str(path),
}
}
fn restore(&self, path: &str) -> String {
match self {
DbInstance::Mem(db) => db.restore_backup_str(path),
#[cfg(feature = "storage-sqlite")]
DbInstance::Sqlite(db) => db.restore_backup_str(path),
#[cfg(feature = "storage-rocksdb")]
DbInstance::RocksDb(db) => db.restore_backup_str(path),
}
}
}
struct Handles {
current: AtomicI32,
dbs: Mutex<BTreeMap<i32, Db<S>>>,
dbs: Mutex<BTreeMap<i32, DbInstance>>,
}
lazy_static! {
static ref HANDLES: Handles<RocksDbStorage> = Handles {
static ref HANDLES: Handles = Handles {
current: Default::default(),
dbs: Mutex::new(Default::default())
};
@ -32,6 +120,7 @@ lazy_static! {
/// Open a database.
///
/// `engine`: Which storage engine to use, can be "mem", "sqlite" or "rocksdb".
/// `path`: should contain the UTF-8 encoded path name as a null-terminated C-string.
/// `db_id`: will contain the id of the database opened.
///
@ -39,23 +128,32 @@ lazy_static! {
/// otherwise a pointer to a C-string containing the error message will be returned.
/// The returned C-string must be freed with `cozo_free_str`.
#[no_mangle]
pub unsafe extern "C" fn cozo_open_db(path: *const c_char, db_id: &mut i32) -> *mut c_char {
pub unsafe extern "C" fn cozo_open_db(
engine: *const c_char,
path: *const c_char,
db_id: &mut i32,
) -> *mut c_char {
let path = match CStr::from_ptr(path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
match new_cozo_rocksdb(path) {
Ok(db) => {
let engine = match CStr::from_ptr(engine).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
let db = match DbInstance::new(engine, path) {
Ok(db) => db,
Err(err) => return CString::new(err).unwrap().into_raw(),
};
let id = HANDLES.current.fetch_add(1, Ordering::AcqRel);
let mut dbs = HANDLES.dbs.lock().unwrap();
dbs.insert(id, db);
*db_id = id;
null_mut()
}
Err(err) => CString::new(format!("{}", err)).unwrap().into_raw(),
}
}
/// Close a database.
///
@ -128,6 +226,127 @@ pub unsafe extern "C" fn cozo_run_query(
CString::new(result).unwrap().into_raw()
}
#[no_mangle]
/// Import data into a relation
/// `db_id`: the ID representing the database.
/// `json_payload`: a UTF-8 encoded JSON payload, see the manual for the expected fields.
///
/// Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
pub unsafe extern "C" fn cozo_import_relation(
db_id: i32,
json_payload: *const c_char,
) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(json_payload).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
CString::new(db.import_relations(data)).unwrap().into_raw()
}
#[no_mangle]
/// Export relations into JSON
///
/// `db_id`: the ID representing the database.
/// `json_payload`: a UTF-8 encoded JSON payload, see the manual for the expected fields.
///
/// Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
pub unsafe extern "C" fn cozo_export_relations(
db_id: i32,
json_payload: *const c_char,
) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(json_payload).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
CString::new(db.export_relations(data)).unwrap().into_raw()
}
#[no_mangle]
/// Backup the database.
///
/// `db_id`: the ID representing the database.
/// `out_path`: path of the output file.
///
/// Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
pub unsafe extern "C" fn cozo_backup(db_id: i32, out_path: *const c_char) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(out_path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
CString::new(db.backup(data)).unwrap().into_raw()
}
#[no_mangle]
/// Restore the database from a backup.
///
/// `db_id`: the ID representing the database.
/// `in_path`: path of the input file.
///
/// Returns a UTF-8-encoded C-string indicating the result that **must** be freed with `cozo_free_str`.
pub unsafe extern "C" fn cozo_restore(db_id: i32, in_path: *const c_char) -> *mut c_char {
let db = {
let db_ref = {
let dbs = HANDLES.dbs.lock().unwrap();
dbs.get(&db_id).cloned()
};
match db_ref {
None => {
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let data = match CStr::from_ptr(in_path).to_str() {
Ok(p) => p,
Err(err) => return CString::new(format!("{}", err)).unwrap().into_raw(),
};
CString::new(db.restore(data)).unwrap().into_raw()
}
/// Free any C-string returned from the Cozo C API.
/// Must be called exactly once for each returned C-string.
///

Loading…
Cancel
Save