main
Ziyang Hu 2 years ago
parent 38a35b2a3c
commit 1c0c480b5e

42
Cargo.lock generated

@ -230,6 +230,25 @@ dependencies = [
"syn",
]
[[package]]
name = "cbindgen"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.0.74"
@ -367,7 +386,7 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cozo"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"approx",
"base64",
@ -412,23 +431,20 @@ dependencies = [
[[package]]
name = "cozo_c"
version = "0.1.1"
version = "0.1.3"
dependencies = [
"cbindgen",
"cozo",
"lazy_static",
"miette",
"serde_json",
]
[[package]]
name = "cozo_java"
version = "0.1.1"
version = "0.1.3"
dependencies = [
"cozo",
"lazy_static",
"miette",
"robusta_jni",
"serde_json",
]
[[package]]
@ -1684,6 +1700,9 @@ name = "serde"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_bytes"
@ -2002,6 +2021,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "twoway"
version = "0.1.8"

@ -1,6 +1,6 @@
[package]
name = "cozo"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
description = "A general-purpose, transactional, relational database that uses Datalog and focuses on graph data and algorithms"
authors = ["Ziyang Hu"]

@ -1,6 +1,6 @@
[package]
name = "cozo_c"
version = "0.1.1"
version = "0.1.3"
edition = "2021"
license = "AGPL-3.0-or-later"
homepage = "https://github.com/cozodb/cozo"
@ -19,6 +19,7 @@ io-uring = ["cozo/io-uring"]
[dependencies]
cozo = { version = "0.1.2", path = ".." }
miette = { version = "=5.3.0", features = ["fancy"] }
serde_json = "1.0.81"
lazy_static = "1.4.0"
[build-dependencies]
cbindgen = "0.24.3"

@ -0,0 +1,27 @@
/*
* Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause.
*/
use std::env;
use cbindgen::{Config, Language};
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let mut config = Config::default();
config.cpp_compat = true;
cbindgen::Builder::new()
.with_config(config)
.with_crate(crate_dir)
.with_language(Language::C)
.with_include_guard("COZO_C_H")
.with_autogen_warning(
"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */",
)
.with_header("/* Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause. */")
.with_documentation(true)
.generate()
.expect("Unable to generate bindings")
.write_to_file("cozo_c.h");
}

@ -1,6 +0,0 @@
language = "C"
include_guard = "cozo_c_h"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
cpp_compat = true
documentation = true
header = "/* Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause. */"

@ -1,7 +1,7 @@
/* Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause. */
#ifndef cozo_c_h
#define cozo_c_h
#ifndef COZO_C_H
#define COZO_C_H
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
@ -49,10 +49,9 @@ bool cozo_close_db(int32_t id);
* `true` if an error occurred.
*
* Returns a UTF-8-encoded C-string that **must** be freed with `cozo_free_str`.
* If `*errored` is false, then the string contains the JSON return value of the query.
* If `*errored` is true, then the string contains the error message.
* The string contains the JSON return value of the query.
*/
char *cozo_run_query(int32_t db_id, const char *script_raw, const char *params_raw, bool *errored);
char *cozo_run_query(int32_t db_id, const char *script_raw, const char *params_raw);
/**
* Free any C-string returned from the Cozo C API.
@ -66,4 +65,4 @@ void cozo_free_str(char *s);
} // extern "C"
#endif // __cplusplus
#endif /* cozo_c_h */
#endif /* COZO_C_H */

@ -3,23 +3,17 @@
#include <stdbool.h>
#include "cozo_c.h"
void run_query(int32_t db_id, const char* query) {
void run_query(int32_t db_id, const char *query) {
const char *empty_params = "{}";
bool errored;
char *res;
res = cozo_run_query(db_id, query, empty_params, &errored);
if (errored) {
printf("encountered an error:\n%s\n\n", res);
} else {
printf("query is successful with result:\n%s\n\n", res);
}
res = cozo_run_query(db_id, query, empty_params);
printf("%s\n", res);
cozo_free_str(res);
}
int main() {
int32_t db_id;
char* err = cozo_open_db("_test_db", &db_id);
char *err = cozo_open_db("_test_db", &db_id);
if (err) {
printf("%s", err);
@ -27,8 +21,7 @@ int main() {
return -1;
}
run_query(db_id, "?[a, b, c] <- [[1, 2, 3]]");
run_query(db_id, "?[a] <- [[1, 2, 3]]");
run_query(db_id, "?[] <- [[1, 2, 3]]");
cozo_close_db(db_id);

@ -77,20 +77,19 @@ pub unsafe extern "C" fn cozo_close_db(id: i32) -> bool {
/// `true` if an error occurred.
///
/// Returns a UTF-8-encoded C-string that **must** be freed with `cozo_free_str`.
/// If `*errored` is false, then the string contains the JSON return value of the query.
/// If `*errored` is true, then the string contains the error message.
/// The string contains the JSON return value of the query.
#[no_mangle]
pub unsafe extern "C" fn cozo_run_query(
db_id: i32,
script_raw: *const c_char,
params_raw: *const c_char,
errored: &mut bool,
) -> *mut c_char {
let script = match CStr::from_ptr(script_raw).to_str() {
Ok(p) => p,
Err(err) => {
*errored = true;
return CString::new(format!("{}", err)).unwrap().into_raw();
Err(_) => {
return CString::new(r##"{"ok":false,"message":"script is not UTF-8 encoded"}"##)
.unwrap()
.into_raw();
}
};
let db = {
@ -100,52 +99,26 @@ pub unsafe extern "C" fn cozo_run_query(
};
match db_ref {
None => {
*errored = true;
return CString::new("database already closed").unwrap().into_raw();
return CString::new(r##"{"ok":false,"message":"database closed"}"##)
.unwrap()
.into_raw();
}
Some(db) => db,
}
};
let params_str = match CStr::from_ptr(params_raw).to_str() {
Ok(p) => p,
Err(err) => {
*errored = true;
return CString::new(format!("{}", err)).unwrap().into_raw();
}
};
let params_map: serde_json::Value = match serde_json::from_str(&params_str) {
Ok(m) => m,
Err(_) => {
*errored = true;
return CString::new("the given params argument is not valid JSON")
.unwrap()
.into_raw();
return CString::new(
r##"{"ok":false,"message":"params argument is not UTF-8 encoded"}"##,
)
.unwrap()
.into_raw();
}
};
let params_arg: BTreeMap<_, _> = match params_map {
serde_json::Value::Object(m) => m.into_iter().collect(),
_ => {
*errored = true;
return CString::new("the given params argument is not a JSON map")
.unwrap()
.into_raw();
}
};
let result = db.run_script(script, &params_arg);
match result {
Ok(json) => {
*errored = false;
let json_str = json.to_string();
CString::new(json_str).unwrap().into_raw()
}
Err(err) => {
let err_str = format!("{:?}", err);
*errored = true;
CString::new(err_str).unwrap().into_raw()
}
}
let result = db.run_script_str(script, &params_str);
CString::new(result).unwrap().into_raw()
}
/// Free any C-string returned from the Cozo C API.

@ -1,6 +1,6 @@
[package]
name = "cozo_java"
version = "0.1.1"
version = "0.1.3"
edition = "2021"
license = "AGPL-3.0-or-later"
homepage = "https://github.com/cozodb/cozo"
@ -19,7 +19,5 @@ io-uring = ["cozo/io-uring"]
[dependencies]
robusta_jni = "0.2.0"
cozo = { version = "0.1.2", path = ".." }
miette = { version = "=5.3.0", features = ["fancy"] }
serde_json = "1.0.81"
cozo = { version = "0.1.3", path = ".." }
lazy_static = "1.4.0"

@ -22,7 +22,6 @@ lazy_static! {
#[bridge]
mod jni {
use std::collections::BTreeMap;
use std::sync::atomic::Ordering;
use robusta_jni::convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue};
@ -73,22 +72,7 @@ mod jni {
let db = db_ref.ok_or_else(|| JniError::from("database already closed"))?;
db
};
let params_map: serde_json::Value = serde_json::from_str(&params_str)
.map_err(|_| JniError::from("the given params argument is not valid JSON"))?;
let params_arg: BTreeMap<_, _> = match params_map {
serde_json::Value::Object(m) => m.into_iter().collect(),
_ => {
return Err(JniError::from(
"the given params argument is not a JSON map",
))
}
};
let result = db.run_script(&script, &params_arg);
match result {
Ok(json) => Ok(json.to_string()),
Err(err) => Err(JniError::from(format!("{:?}", err))),
}
Ok(db.run_script_str(&script, &params_str))
}
}
}

@ -2,20 +2,17 @@
* Copyright 2022, The Cozo Project Authors. Licensed under AGPL-3 or later.
*/
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::fs;
use std::net::Ipv6Addr;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Instant;
use clap::Parser;
use env_logger::Env;
use log::{error, info};
use rand::Rng;
use rouille::{router, try_or_400, Request, Response};
use serde_json::json;
use cozo::Db;
@ -96,24 +93,30 @@ fn main() {
#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
struct QueryPayload {
script: String,
params: BTreeMap<String, serde_json::Value>,
params: serde_json::Map<String, serde_json::Value>,
}
let payload: QueryPayload = try_or_400!(rouille::input::json_input(request));
let start = Instant::now();
match db.run_script(&payload.script, &payload.params) {
Ok(mut result) => {
if let Some(obj) = result.as_object_mut() {
obj.insert(
"time_taken".to_string(),
json!(start.elapsed().as_millis() as u64),
);
}
Response::json(&result)
}
Err(e) => Response::text(format!("{:?}", e)).with_status_code(400),
let result = db.run_script_fold_err(&payload.script, &payload.params);
let response = Response::json(&result);
if let Some(serde_json::Value::Bool(true)) = result.get("ok") {
response
} else {
response.with_status_code(400)
}
// {
//
// Ok(mut result) => {
// if let Some(obj) = result.as_object_mut() {
// obj.insert(
// "time_taken".to_string(),
// json!(start.elapsed().as_millis() as u64),
// );
// }
// Response::json(&result)
// }
// _ => Response::json(&result).with_status_code(400)
// }
},
(GET) (/) => {
Response::html(HTML_CONTENT)
@ -163,7 +166,7 @@ const HTML_CONTENT: &str = r##"
}))
}
} else {
console.error(await resp.text())
console.error((await resp.json()).display)
}
}
console.log(

@ -7,13 +7,17 @@ use std::fmt::{Debug, Formatter};
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use std::{fs, thread};
use either::{Left, Right};
use itertools::Itertools;
use miette::{bail, ensure, miette, Diagnostic, IntoDiagnostic, Result, WrapErr};
use serde_json::json;
use lazy_static::lazy_static;
use miette::{
bail, ensure, miette, Diagnostic, GraphicalReportHandler, IntoDiagnostic, JSONReportHandler,
Result, WrapErr,
};
use serde_json::{json, Map};
use smartstring::SmartString;
use thiserror::Error;
@ -79,6 +83,11 @@ impl Debug for Db {
#[diagnostic(code(db::init))]
struct BadDbInit(#[help] String);
lazy_static! {
static ref TEXT_ERR_HANDLER: GraphicalReportHandler = miette::GraphicalReportHandler::new();
static ref JSON_ERR_HANDLER: JSONReportHandler = miette::JSONReportHandler::new();
}
impl Db {
/// Creates a database object.
pub fn new(path: impl AsRef<str>) -> Result<Self> {
@ -175,24 +184,65 @@ impl Db {
Ok(ret)
}
/// Run the CozoScript passed in. The `params` argument is a map of parameters.
pub fn run_script(
&self,
payload: &str,
params: &BTreeMap<String, JsonValue>,
) -> Result<JsonValue> {
self.do_run_script(payload, params).map_err(|err| {
if err.source_code().is_some() {
err
} else {
err.with_source_code(payload.to_string())
pub fn run_script(&self, payload: &str, params: &Map<String, JsonValue>) -> Result<JsonValue> {
let start = Instant::now();
match self.do_run_script(payload, params) {
Ok(mut json) => {
let took = start.elapsed().as_secs_f64();
let map = json.as_object_mut().unwrap();
map.insert("ok".to_string(), json!(true));
map.insert("took".to_string(), json!(took));
Ok(json)
}
})
err => err,
}
}
fn do_run_script(
&self,
payload: &str,
params: &BTreeMap<String, JsonValue>,
) -> Result<JsonValue> {
/// Run the CozoScript passed in. The `params` argument is a map of parameters.
/// Fold any error into the return JSON itself.
pub fn run_script_fold_err(&self, payload: &str, params: &Map<String, JsonValue>) -> JsonValue {
match self.run_script(payload, params) {
Ok(json) => json,
Err(mut err) => {
if err.source_code().is_none() {
err = err.with_source_code(payload.to_string());
}
let mut text_err = String::new();
let mut json_err = String::new();
TEXT_ERR_HANDLER
.render_report(&mut text_err, err.as_ref())
.expect("render text error failed");
JSON_ERR_HANDLER
.render_report(&mut json_err, err.as_ref())
.expect("render json error failed");
let mut json: serde_json::Value =
serde_json::from_str(&json_err).expect("parse rendered json error failed");
let map = json.as_object_mut().unwrap();
map.insert("ok".to_string(), json!(false));
map.insert("display".to_string(), json!(text_err));
json
}
}
}
/// Run the CozoScript passed in. The `params` argument is a map of parameters formatted as JSON.
pub fn run_script_str(&self, payload: &str, params: &str) -> String {
let params_json = if params.is_empty() {
Map::default()
} else {
match serde_json::from_str::<serde_json::Value>(params) {
Ok(serde_json::Value::Object(map)) => map,
Ok(_) => {
return json!({"ok": false, "message": "params argument is not valid JSON"})
.to_string()
}
Err(_) => {
return json!({"ok": false, "message": "params argument is not a JSON map"})
.to_string()
}
}
};
self.run_script_fold_err(payload, &params_json).to_string()
}
fn do_run_script(&self, payload: &str, params: &Map<String, JsonValue>) -> Result<JsonValue> {
let param_pool = params
.iter()
.map(|(k, v)| (k.clone(), DataValue::from(v)))

Loading…
Cancel
Save