Experiments in applying Entity-Component-System patterns to durable data storage APIs.
1use rusqlite::functions::FunctionFlags;
2use rusqlite::types::{Value, ValueRef};
3use rusqlite::{Connection, Error, Result};
4
5pub(crate) fn add_regexp_function(db: &Connection) -> Result<()> {
6 db.create_scalar_function(
7 "velodb_extract_data",
8 1,
9 FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
10 move |ctx| {
11 assert_eq!(ctx.len(), 1, "called with unexpected number of arguments");
12
13 match ctx.get_raw(0) {
14 ValueRef::Null => Ok(Value::Null),
15 ValueRef::Integer(i) => Ok(Value::Integer(i)),
16 ValueRef::Real(r) => Ok(Value::Real(r)),
17 // Return NULL for BLOB - no JSON extraction possible
18 ValueRef::Blob(_blob) => Ok(Value::Null),
19 // JSON
20 ValueRef::Text(text) => {
21 let value: serde_json::Value = serde_json::from_slice(text)
22 .map_err(|e| Error::UserFunctionError(Box::new(e)))?;
23
24 let sqlite_value = match value {
25 serde_json::Value::Null => Value::Null,
26 serde_json::Value::Bool(true) => Value::Integer(1),
27 serde_json::Value::Bool(false) => Value::Integer(0),
28 serde_json::Value::Number(n) => n
29 .as_i64()
30 .map(Value::Integer)
31 .or(n.as_f64().map(Value::Real))
32 .unwrap(),
33 serde_json::Value::String(s) => Value::Text(s),
34 array @ serde_json::Value::Array(_) => Value::Text(array.to_string()),
35 obj @ serde_json::Value::Object(_) => Value::Text(obj.to_string()),
36 };
37
38 Ok(sqlite_value)
39 }
40 }
41 },
42 )
43}
44
45#[cfg(test)]
46mod tests {
47 #[test]
48 fn custom_fn_test() -> Result<(), anyhow::Error> {
49 let db = crate::Ecs::open_in_memory()?;
50 let result: bool = db.raw_sql().query_row(
51 "select velodb_extract_data(json_quote(10)) > velodb_extract_data(json_quote(2))",
52 [],
53 |row| row.get(0),
54 )?;
55
56 assert!(result);
57
58 Ok(())
59 }
60}