2021-07-26 21:00:05 +02:00
|
|
|
#![macro_use]
|
|
|
|
|
2021-07-26 23:10:12 +02:00
|
|
|
#[macro_use]
|
|
|
|
mod macros;
|
|
|
|
|
2022-08-19 14:04:18 +02:00
|
|
|
pub mod stores;
|
|
|
|
|
2021-07-26 21:00:05 +02:00
|
|
|
pub mod constants;
|
2022-04-15 20:35:26 +02:00
|
|
|
pub mod engine;
|
2021-07-26 21:00:05 +02:00
|
|
|
pub mod entry;
|
2021-08-18 11:06:36 +02:00
|
|
|
pub mod hierarchies;
|
2021-07-26 21:14:12 +02:00
|
|
|
pub mod inner;
|
2021-07-26 21:00:05 +02:00
|
|
|
pub mod lang;
|
|
|
|
|
2021-12-17 23:04:35 +01:00
|
|
|
use crate::addressing::{Address, Addressable};
|
2022-01-31 15:57:35 +01:00
|
|
|
use crate::database::constants::{
|
|
|
|
IS_OF_TYPE_ATTR, LABEL_ATTR, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_INVARIANT,
|
|
|
|
};
|
2022-04-15 20:35:26 +02:00
|
|
|
use crate::database::engine::execute;
|
2022-01-21 17:59:53 +01:00
|
|
|
use crate::database::entry::{Entry, EntryValue, ImmutableEntry};
|
2021-07-26 21:14:12 +02:00
|
|
|
use crate::database::inner::models;
|
|
|
|
use crate::database::inner::schema::data;
|
2021-07-26 21:00:05 +02:00
|
|
|
use crate::database::lang::Query;
|
2022-01-21 17:59:53 +01:00
|
|
|
use crate::util::hash::Hash;
|
2021-07-26 21:00:05 +02:00
|
|
|
use crate::util::LoggerSink;
|
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
use diesel::prelude::*;
|
2022-09-05 21:51:44 +02:00
|
|
|
use diesel::r2d2::{self, ConnectionManager};
|
2021-07-26 21:00:05 +02:00
|
|
|
use diesel::result::{DatabaseErrorKind, Error};
|
2021-12-23 11:10:16 +01:00
|
|
|
use diesel::sqlite::SqliteConnection;
|
2021-08-18 11:06:36 +02:00
|
|
|
use hierarchies::initialize_hier;
|
2022-08-19 14:04:18 +02:00
|
|
|
use std::convert::TryFrom;
|
2021-07-26 21:00:05 +02:00
|
|
|
use std::fs;
|
|
|
|
use std::path::{Path, PathBuf};
|
2022-08-07 14:09:57 +02:00
|
|
|
use std::sync::{Arc, RwLock};
|
2021-07-26 21:00:05 +02:00
|
|
|
use std::time::Duration;
|
2022-08-07 14:09:57 +02:00
|
|
|
use tracing::{debug, trace};
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ConnectionOptions {
|
|
|
|
pub enable_foreign_keys: bool,
|
|
|
|
pub busy_timeout: Option<Duration>,
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
impl ConnectionOptions {
|
|
|
|
pub fn apply(&self, conn: &SqliteConnection) -> QueryResult<()> {
|
|
|
|
if self.enable_foreign_keys {
|
2022-03-14 14:02:03 +01:00
|
|
|
debug!("Enabling foreign keys");
|
2021-12-23 11:10:16 +01:00
|
|
|
conn.execute("PRAGMA foreign_keys = ON;")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(duration) = self.busy_timeout {
|
2022-03-14 14:02:03 +01:00
|
|
|
debug!("Setting busy_timeout to {:?}", duration);
|
2021-12-23 11:10:16 +01:00
|
|
|
conn.execute(&format!("PRAGMA busy_timeout = {};", duration.as_millis()))?;
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2022-03-28 20:06:00 +02:00
|
|
|
debug!(r#"Setting "synchronous" to NORMAL"#);
|
2021-12-23 11:10:16 +01:00
|
|
|
conn.execute("PRAGMA synchronous = NORMAL;")?;
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
impl diesel::r2d2::CustomizeConnection<SqliteConnection, diesel::r2d2::Error>
|
|
|
|
for ConnectionOptions
|
|
|
|
{
|
|
|
|
fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<(), diesel::r2d2::Error> {
|
|
|
|
self.apply(conn).map_err(diesel::r2d2::Error::QueryError)
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
type DbPool = r2d2::Pool<ConnectionManager<SqliteConnection>>;
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
pub struct OpenResult {
|
|
|
|
pub db: UpEndDatabase,
|
|
|
|
pub new: bool,
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
pub struct UpEndDatabase {
|
2022-09-05 21:51:44 +02:00
|
|
|
pool: Arc<DbPool>,
|
2022-07-31 22:49:31 +02:00
|
|
|
lock: Arc<RwLock<()>>,
|
2022-09-13 19:16:22 +02:00
|
|
|
vault_path: Arc<PathBuf>
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
pub const UPEND_SUBDIR: &str = ".upend";
|
|
|
|
pub const DATABASE_FILENAME: &str = "upend.sqlite3";
|
2021-12-05 22:57:47 +01:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
impl UpEndDatabase {
|
|
|
|
pub fn open<P: AsRef<Path>>(
|
|
|
|
dirpath: P,
|
|
|
|
db_path: Option<PathBuf>,
|
|
|
|
reinitialize: bool,
|
|
|
|
) -> Result<OpenResult> {
|
|
|
|
embed_migrations!("./migrations/upend/");
|
2021-12-05 22:57:47 +01:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
let upend_path = db_path.unwrap_or_else(|| dirpath.as_ref().join(UPEND_SUBDIR));
|
2021-12-05 22:57:47 +01:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
if reinitialize {
|
2022-03-14 14:02:03 +01:00
|
|
|
debug!("Reinitializing - removing previous database...");
|
2021-12-23 11:10:16 +01:00
|
|
|
let _ = fs::remove_dir_all(&upend_path);
|
|
|
|
}
|
|
|
|
let new = !upend_path.exists();
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
if new {
|
|
|
|
trace!("Creating UpEnd subdirectory...");
|
|
|
|
fs::create_dir(&upend_path)?;
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
trace!("Creating pool.");
|
|
|
|
let manager = ConnectionManager::<SqliteConnection>::new(
|
|
|
|
upend_path.join(DATABASE_FILENAME).to_str().unwrap(),
|
|
|
|
);
|
|
|
|
let pool = r2d2::Pool::builder()
|
|
|
|
.connection_customizer(Box::new(ConnectionOptions {
|
|
|
|
enable_foreign_keys: true,
|
|
|
|
busy_timeout: Some(Duration::from_secs(30)),
|
|
|
|
}))
|
|
|
|
.build(manager)?;
|
2022-01-31 15:57:35 +01:00
|
|
|
trace!("Pool created.");
|
2021-12-23 11:10:16 +01:00
|
|
|
|
|
|
|
let db = UpEndDatabase {
|
2022-09-05 21:51:44 +02:00
|
|
|
pool: Arc::new(pool),
|
2022-07-31 22:49:31 +02:00
|
|
|
lock: Arc::new(RwLock::new(())),
|
2022-01-23 14:51:54 +01:00
|
|
|
vault_path: Arc::new(dirpath.as_ref().canonicalize()?),
|
2021-12-23 11:10:16 +01:00
|
|
|
};
|
|
|
|
let connection = db.connection().unwrap();
|
|
|
|
|
2022-01-31 15:57:35 +01:00
|
|
|
if !new {
|
|
|
|
let db_major: u64 = connection.get_meta("VERSION")?.parse()?;
|
|
|
|
if db_major > crate::common::PKG_VERSION_MAJOR.parse().unwrap() {
|
|
|
|
return Err(anyhow!("Incompatible database! Found version "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trace!("Running initial config.");
|
2021-12-23 11:10:16 +01:00
|
|
|
let enable_wal_mode = true;
|
|
|
|
connection.execute(if enable_wal_mode {
|
2022-03-14 14:02:03 +01:00
|
|
|
debug!("Enabling WAL journal mode & truncating WAL log...");
|
|
|
|
"PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 1000; PRAGMA wal_checkpoint(TRUNCATE);"
|
2021-12-23 11:10:16 +01:00
|
|
|
} else {
|
2022-03-14 14:02:03 +01:00
|
|
|
debug!("Enabling TRUNCATE journal mode");
|
2021-12-23 11:10:16 +01:00
|
|
|
"PRAGMA journal_mode = TRUNCATE;"
|
|
|
|
})?;
|
|
|
|
|
2022-01-31 15:57:35 +01:00
|
|
|
trace!("Running migrations...");
|
2021-12-23 11:10:16 +01:00
|
|
|
|
|
|
|
embedded_migrations::run_with_output(
|
|
|
|
&db.pool.get()?,
|
|
|
|
&mut LoggerSink {
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
|
|
|
|
trace!("Initializing types...");
|
|
|
|
connection.insert_entry(Entry::try_from(&*TYPE_INVARIANT)?)?;
|
2022-01-18 16:59:32 +01:00
|
|
|
upend_insert_addr!(connection, TYPE_ADDR, IS_OF_TYPE_ATTR, TYPE_ADDR)?;
|
|
|
|
upend_insert_val!(connection, TYPE_ADDR, TYPE_HAS_ATTR, TYPE_HAS_ATTR)?;
|
2022-01-27 17:42:59 +01:00
|
|
|
upend_insert_val!(connection, TYPE_ADDR, LABEL_ATTR, "UpEnd Type")?;
|
2021-12-23 11:10:16 +01:00
|
|
|
|
|
|
|
initialize_hier(&connection)?;
|
|
|
|
|
|
|
|
Ok(OpenResult { db, new })
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:18:04 +01:00
|
|
|
pub fn connection(&self) -> Result<UpEndConnection> {
|
2021-12-23 23:45:46 +01:00
|
|
|
Ok(UpEndConnection {
|
2022-09-05 21:51:44 +02:00
|
|
|
pool: self.pool.clone(),
|
2022-07-31 22:49:31 +02:00
|
|
|
lock: self.lock.clone(),
|
2021-12-23 23:45:46 +01:00
|
|
|
})
|
2021-12-23 11:10:16 +01:00
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 23:45:46 +01:00
|
|
|
pub struct UpEndConnection {
|
2022-09-05 21:51:44 +02:00
|
|
|
pool: Arc<DbPool>,
|
2022-07-31 22:49:31 +02:00
|
|
|
lock: Arc<RwLock<()>>,
|
2021-12-23 23:45:46 +01:00
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
impl UpEndConnection {
|
2022-09-05 21:51:44 +02:00
|
|
|
pub fn execute<S: AsRef<str>>(&self, query: S) -> Result<usize> {
|
|
|
|
let _lock = self.lock.write().unwrap();
|
|
|
|
let conn = self.pool.get()?;
|
|
|
|
Ok(conn.execute(query.as_ref())?)
|
2021-12-23 11:10:16 +01:00
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
pub fn transaction<T, E, F>(&self, f: F) -> Result<T, E>
|
|
|
|
where
|
|
|
|
F: FnOnce() -> Result<T, E>,
|
|
|
|
E: From<Error>,
|
|
|
|
{
|
2022-08-07 14:09:57 +02:00
|
|
|
/*
|
|
|
|
let span = span!(tracing::Level::TRACE, "transaction");
|
|
|
|
let _span = span.enter();
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.transaction_lock.lock().unwrap();
|
|
|
|
self.conn.exclusive_transaction(f)
|
2022-08-07 14:09:57 +02:00
|
|
|
*/
|
|
|
|
// Disable transactions for now.
|
|
|
|
f()
|
2021-12-23 11:10:16 +01:00
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2022-01-31 15:57:35 +01:00
|
|
|
pub fn get_meta<S: AsRef<str>>(&self, key: S) -> Result<String> {
|
|
|
|
use crate::database::inner::schema::meta::dsl;
|
|
|
|
let key = key.as_ref();
|
|
|
|
|
|
|
|
debug!("Querying META:{key}");
|
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2022-01-31 15:57:35 +01:00
|
|
|
dsl::meta
|
|
|
|
.filter(dsl::key.eq(key))
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<models::MetaValue>(&conn)?
|
2022-01-31 15:57:35 +01:00
|
|
|
.first()
|
2022-03-28 20:06:00 +02:00
|
|
|
.ok_or(anyhow!(r#"No META "{key}" value found."#))
|
2022-01-31 15:57:35 +01:00
|
|
|
.map(|mv| mv.value.clone())
|
|
|
|
}
|
|
|
|
|
2022-02-27 15:03:15 +01:00
|
|
|
pub fn retrieve_entry(&self, hash: &Hash) -> Result<Option<Entry>> {
|
2022-01-14 22:04:53 +01:00
|
|
|
use crate::database::inner::schema::data::dsl::*;
|
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2022-01-14 22:04:53 +01:00
|
|
|
let entry = data
|
2022-02-27 15:03:15 +01:00
|
|
|
.filter(identity.eq(Address::Hash(hash.clone()).encode()?))
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<models::Entry>(&conn)?;
|
2022-01-14 22:04:53 +01:00
|
|
|
|
|
|
|
match entry.len() {
|
|
|
|
0 => Ok(None),
|
|
|
|
1 => Ok(Some(Entry::try_from(entry.get(0).unwrap())?)),
|
|
|
|
_ => {
|
2022-01-21 17:03:50 +01:00
|
|
|
unreachable!(
|
|
|
|
"Multiple entries returned with the same hash - this should be impossible!"
|
|
|
|
)
|
2022-01-14 22:04:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 20:46:17 +01:00
|
|
|
pub fn retrieve_object(&self, object_address: &Address) -> Result<Vec<Entry>> {
|
2021-12-23 11:10:16 +01:00
|
|
|
use crate::database::inner::schema::data::dsl::*;
|
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
let primary = data
|
|
|
|
.filter(entity.eq(object_address.encode()?))
|
2022-02-07 20:46:17 +01:00
|
|
|
.or_filter(value_str.eq(EntryValue::Address(object_address.clone()).to_string()?))
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<models::Entry>(&conn)?;
|
2021-12-23 11:10:16 +01:00
|
|
|
|
|
|
|
let entries = primary
|
|
|
|
.iter()
|
|
|
|
.map(Entry::try_from)
|
|
|
|
.collect::<Result<Vec<Entry>>>()?;
|
|
|
|
|
|
|
|
let secondary = data
|
|
|
|
.filter(
|
|
|
|
entity.eq_any(
|
|
|
|
entries
|
|
|
|
.iter()
|
|
|
|
.map(|e| e.address())
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
.map(|addr| addr.encode())
|
|
|
|
.collect::<Result<Vec<Vec<u8>>>>()?,
|
|
|
|
),
|
|
|
|
)
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<models::Entry>(&conn)?;
|
2021-12-23 11:10:16 +01:00
|
|
|
|
|
|
|
let secondary_entries = secondary
|
|
|
|
.iter()
|
|
|
|
.map(Entry::try_from)
|
|
|
|
.collect::<Result<Vec<Entry>>>()?;
|
|
|
|
|
|
|
|
Ok([entries, secondary_entries].concat())
|
|
|
|
}
|
2021-12-05 19:06:04 +01:00
|
|
|
|
2021-12-23 11:18:04 +01:00
|
|
|
pub fn remove_object(&self, object_address: Address) -> Result<usize> {
|
2021-12-23 11:10:16 +01:00
|
|
|
use crate::database::inner::schema::data::dsl::*;
|
2021-12-05 19:06:04 +01:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
debug!("Deleting {}!", object_address);
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.write().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
let matches = data
|
|
|
|
.filter(identity.eq(object_address.encode()?))
|
|
|
|
.or_filter(entity.eq(object_address.encode()?))
|
2022-01-28 18:17:14 +01:00
|
|
|
.or_filter(value_str.eq(EntryValue::Address(object_address).to_string()?));
|
2021-12-23 11:10:16 +01:00
|
|
|
|
2022-09-05 21:51:44 +02:00
|
|
|
Ok(diesel::delete(matches).execute(&conn)?)
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:18:04 +01:00
|
|
|
pub fn query(&self, query: Query) -> Result<Vec<Entry>> {
|
2021-12-23 11:10:16 +01:00
|
|
|
trace!("Querying: {:?}", query);
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2022-09-05 21:51:44 +02:00
|
|
|
let entries = execute(&conn, query)?;
|
2022-04-15 20:35:26 +02:00
|
|
|
let entries = entries
|
2021-12-23 11:10:16 +01:00
|
|
|
.iter()
|
|
|
|
.map(Entry::try_from)
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
.collect();
|
2021-12-05 12:43:43 +01:00
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
Ok(entries)
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2021-12-23 11:18:04 +01:00
|
|
|
pub fn insert_entry(&self, entry: Entry) -> Result<Address> {
|
2021-12-23 11:10:16 +01:00
|
|
|
debug!("Inserting: {}", entry);
|
2022-01-22 17:45:46 +01:00
|
|
|
let db_entry = models::Entry::try_from(&entry)?;
|
2022-01-23 13:00:59 +01:00
|
|
|
self.insert_model_entry(db_entry)?;
|
2022-01-22 17:45:46 +01:00
|
|
|
entry.address()
|
2022-01-21 17:59:53 +01:00
|
|
|
}
|
2021-12-23 11:10:16 +01:00
|
|
|
|
2022-01-21 17:59:53 +01:00
|
|
|
pub fn insert_entry_immutable(&self, entry: Entry) -> Result<Address> {
|
|
|
|
debug!("Inserting immutably: {}", entry);
|
2022-01-22 17:45:46 +01:00
|
|
|
let address = entry.address()?;
|
|
|
|
let db_entry = models::Entry::try_from(&ImmutableEntry(entry))?;
|
2022-01-23 13:00:59 +01:00
|
|
|
self.insert_model_entry(db_entry)?;
|
2022-01-22 17:45:46 +01:00
|
|
|
Ok(address)
|
2022-01-21 17:59:53 +01:00
|
|
|
}
|
2021-12-23 11:10:16 +01:00
|
|
|
|
2022-01-23 13:00:59 +01:00
|
|
|
fn insert_model_entry(&self, entry: models::Entry) -> Result<usize> {
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.write().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
|
|
|
|
2021-12-23 11:10:16 +01:00
|
|
|
let result = diesel::insert_into(data::table)
|
2022-01-21 17:59:53 +01:00
|
|
|
.values(&entry)
|
2022-09-05 21:51:44 +02:00
|
|
|
.execute(&conn);
|
2021-12-23 11:10:16 +01:00
|
|
|
|
2022-01-22 17:45:46 +01:00
|
|
|
match result {
|
|
|
|
Ok(num) => Ok(num),
|
|
|
|
Err(error) => match error {
|
|
|
|
Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _) => Ok(0),
|
|
|
|
_ => Err(anyhow!(error)),
|
|
|
|
},
|
2021-12-23 11:10:16 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-04 21:58:23 +01:00
|
|
|
|
2022-03-02 01:14:46 +01:00
|
|
|
// #[deprecated]
|
|
|
|
pub fn get_all_addresses(&self) -> Result<Vec<Address>> {
|
|
|
|
use crate::database::inner::schema::data::dsl::*;
|
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2022-03-02 01:14:46 +01:00
|
|
|
let result = data
|
|
|
|
.select(entity)
|
|
|
|
.distinct()
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<Vec<u8>>(&conn)?
|
2022-03-02 01:14:46 +01:00
|
|
|
.into_iter()
|
|
|
|
.filter_map(|buf| Address::decode(&buf).ok())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
2022-01-04 21:58:23 +01:00
|
|
|
// #[deprecated]
|
|
|
|
pub fn get_all_attributes(&self) -> Result<Vec<String>> {
|
|
|
|
use crate::database::inner::schema::data::dsl::*;
|
|
|
|
|
2022-07-31 22:49:31 +02:00
|
|
|
let _lock = self.lock.read().unwrap();
|
2022-09-05 21:51:44 +02:00
|
|
|
let conn = self.pool.get()?;
|
2022-07-31 22:49:31 +02:00
|
|
|
|
2022-01-04 21:58:23 +01:00
|
|
|
let result = data
|
|
|
|
.select(attribute)
|
|
|
|
.distinct()
|
|
|
|
.order_by(attribute)
|
2022-09-05 21:51:44 +02:00
|
|
|
.load::<String>(&conn)?;
|
2022-01-04 21:58:23 +01:00
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
2021-10-24 13:37:36 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2021-12-27 12:36:38 +01:00
|
|
|
use tempfile::TempDir;
|
2021-10-24 13:37:36 +02:00
|
|
|
|
|
|
|
#[test]
|
2021-12-23 11:10:16 +01:00
|
|
|
fn test_open() {
|
2021-12-27 12:36:38 +01:00
|
|
|
let tempdir = TempDir::new().unwrap();
|
2021-12-23 11:11:36 +01:00
|
|
|
|
|
|
|
let result = UpEndDatabase::open(&tempdir, None, false);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
assert!(result.unwrap().new);
|
|
|
|
|
|
|
|
// Not new
|
|
|
|
let result = UpEndDatabase::open(&tempdir, None, false);
|
|
|
|
assert!(result.is_ok());
|
|
|
|
assert!(!result.unwrap().new);
|
|
|
|
|
|
|
|
// reinitialize true, new again
|
|
|
|
let result = UpEndDatabase::open(&tempdir, None, true);
|
2021-12-23 11:10:16 +01:00
|
|
|
assert!(result.is_ok());
|
2021-12-23 11:11:36 +01:00
|
|
|
assert!(result.unwrap().new);
|
2021-10-24 13:37:36 +02:00
|
|
|
}
|
2022-03-28 17:34:11 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_query() {
|
|
|
|
let tempdir = TempDir::new().unwrap();
|
|
|
|
let result = UpEndDatabase::open(&tempdir, None, false).unwrap();
|
|
|
|
let db = result.db;
|
|
|
|
|
|
|
|
let connection = db.connection().unwrap();
|
|
|
|
|
|
|
|
let random_entity = Address::Uuid(uuid::Uuid::new_v4());
|
|
|
|
upend_insert_val!(connection, random_entity, LABEL_ATTR, "FOOBAR").unwrap();
|
|
|
|
upend_insert_val!(connection, random_entity, "FLAVOUR", "STRANGE").unwrap();
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = format!(r#"(matches @{random_entity} ? ?)"#)
|
2022-03-28 17:34:11 +02:00
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 2);
|
|
|
|
|
|
|
|
let other_entity = Address::Uuid(uuid::Uuid::new_v4());
|
|
|
|
upend_insert_val!(connection, random_entity, LABEL_ATTR, "BAZQUX").unwrap();
|
|
|
|
upend_insert_val!(connection, random_entity, "CHARGE", "POSITIVE").unwrap();
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = format!(r#"(matches (in @{random_entity} @{other_entity}) ? ?)"#)
|
2022-03-28 17:34:11 +02:00
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 4);
|
|
|
|
|
2022-03-28 20:06:00 +02:00
|
|
|
let query = r#"(matches ? (in "FLAVOUR" "CHARGE") ?)"#.parse().unwrap();
|
2022-03-28 17:34:11 +02:00
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 2);
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = format!(r#"(matches ? "{LABEL_ATTR}" (in "FOOBAR" "BAZQUX"))"#)
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 2);
|
2022-03-28 17:34:11 +02:00
|
|
|
|
|
|
|
let query = format!(r#"(matches ? "{LABEL_ATTR}" (contains "OOBA"))"#)
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 1);
|
2022-03-28 19:15:16 +02:00
|
|
|
|
2022-03-28 20:06:00 +02:00
|
|
|
let query = r#"(or (matches ? ? (contains "OOBA")) (matches ? (contains "HARGE") ?) )"#
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
2022-03-28 19:15:16 +02:00
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 2);
|
|
|
|
|
|
|
|
let query =
|
|
|
|
format!(r#"(and (matches ? ? (contains "OOBA")) (matches ? "{LABEL_ATTR}" ?) )"#)
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
|
2022-03-30 12:20:19 +02:00
|
|
|
let query = format!(
|
|
|
|
r#"(and
|
|
|
|
(or
|
|
|
|
(matches ? ? (contains "OOBA"))
|
|
|
|
(matches ? (contains "HARGE") ?)
|
|
|
|
)
|
|
|
|
(not (matches ? "{LABEL_ATTR}" ?))
|
|
|
|
)"#
|
|
|
|
)
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 1);
|
2022-04-16 00:55:09 +02:00
|
|
|
|
|
|
|
let query = format!(
|
|
|
|
r#"(join
|
|
|
|
(matches ?a "FLAVOUR" ?)
|
|
|
|
(matches ?a "{LABEL_ATTR}" "FOOBAR")
|
|
|
|
)"#
|
|
|
|
)
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
|
|
|
let result = connection.query(query).unwrap();
|
|
|
|
assert_eq!(result.len(), 1);
|
|
|
|
assert_eq!(result[0].value, "STRANGE".into());
|
2022-03-28 17:34:11 +02:00
|
|
|
}
|
2021-10-24 13:37:36 +02:00
|
|
|
}
|