parent
a64ebdb4a2
commit
cdac4f84cd
16 changed files with 688 additions and 91 deletions
|
@ -70,7 +70,9 @@ impl<'a> Document<'a> {
|
|||
// Return the table or attempt to define it
|
||||
match tb {
|
||||
// The table doesn't exist
|
||||
Err(Error::TbNotFound) => match opt.auth.check(Level::Db) {
|
||||
Err(Error::TbNotFound {
|
||||
value: _,
|
||||
}) => match opt.auth.check(Level::Db) {
|
||||
// We can create the table automatically
|
||||
true => {
|
||||
run.add_and_cache_ns(opt.ns(), opt.strict).await?;
|
||||
|
|
|
@ -171,44 +171,70 @@ pub enum Error {
|
|||
},
|
||||
|
||||
/// The requested namespace does not exist
|
||||
#[error("The namespace does not exist")]
|
||||
NsNotFound,
|
||||
#[error("The namespace '{value}' does not exist")]
|
||||
NsNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested namespace token does not exist
|
||||
#[error("The namespace token does not exist")]
|
||||
NtNotFound,
|
||||
#[error("The namespace token '{value}' does not exist")]
|
||||
NtNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested namespace login does not exist
|
||||
#[error("The namespace login does not exist")]
|
||||
NlNotFound,
|
||||
#[error("The namespace login '{value}' does not exist")]
|
||||
NlNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested database does not exist
|
||||
#[error("The database does not exist")]
|
||||
DbNotFound,
|
||||
#[error("The database '{value}' does not exist")]
|
||||
DbNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested database token does not exist
|
||||
#[error("The database token does not exist")]
|
||||
DtNotFound,
|
||||
#[error("The database token '{value}' does not exist")]
|
||||
DtNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested database login does not exist
|
||||
#[error("The database login does not exist")]
|
||||
DlNotFound,
|
||||
#[error("The database login '{value}' does not exist")]
|
||||
DlNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested function does not exist
|
||||
#[error("The function 'fn::{value}' does not exist")]
|
||||
FcNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested scope does not exist
|
||||
#[error("The scope does not exist")]
|
||||
ScNotFound,
|
||||
#[error("The scope '{value}' does not exist")]
|
||||
ScNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested scope token does not exist
|
||||
#[error("The scope token does not exist")]
|
||||
StNotFound,
|
||||
#[error("The scope token '{value}' does not exist")]
|
||||
StNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested param does not exist
|
||||
#[error("The param does not exist")]
|
||||
PaNotFound,
|
||||
#[error("The param '${value}' does not exist")]
|
||||
PaNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// The requested table does not exist
|
||||
#[error("The table does not exist")]
|
||||
TbNotFound,
|
||||
#[error("The table '{value}' does not exist")]
|
||||
TbNotFound {
|
||||
value: String,
|
||||
},
|
||||
|
||||
/// Unable to perform the realtime query
|
||||
#[error("Unable to perform the realtime query")]
|
||||
|
|
64
lib/src/key/fc.rs
Normal file
64
lib/src/key/fc.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use derive::Key;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Key)]
|
||||
pub struct Fc {
|
||||
__: u8,
|
||||
_a: u8,
|
||||
pub ns: String,
|
||||
_b: u8,
|
||||
pub db: String,
|
||||
_c: u8,
|
||||
_d: u8,
|
||||
_e: u8,
|
||||
pub fc: String,
|
||||
}
|
||||
|
||||
pub fn new(ns: &str, db: &str, fc: &str) -> Fc {
|
||||
Fc::new(ns.to_string(), db.to_string(), fc.to_string())
|
||||
}
|
||||
|
||||
pub fn prefix(ns: &str, db: &str) -> Vec<u8> {
|
||||
let mut k = super::database::new(ns, db).encode().unwrap();
|
||||
k.extend_from_slice(&[0x21, 0x66, 0x6e, 0x00]);
|
||||
k
|
||||
}
|
||||
|
||||
pub fn suffix(ns: &str, db: &str) -> Vec<u8> {
|
||||
let mut k = super::database::new(ns, db).encode().unwrap();
|
||||
k.extend_from_slice(&[0x21, 0x66, 0x6e, 0xff]);
|
||||
k
|
||||
}
|
||||
|
||||
impl Fc {
|
||||
pub fn new(ns: String, db: String, fc: String) -> Fc {
|
||||
Fc {
|
||||
__: 0x2f, // /
|
||||
_a: 0x2a, // *
|
||||
ns,
|
||||
_b: 0x2a, // *
|
||||
db,
|
||||
_c: 0x21, // !
|
||||
_d: 0x66, // f
|
||||
_e: 0x6e, // n
|
||||
fc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn key() {
|
||||
use super::*;
|
||||
#[rustfmt::skip]
|
||||
let val = Fc::new(
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
);
|
||||
let enc = Fc::encode(&val).unwrap();
|
||||
let dec = Fc::decode(&enc).unwrap();
|
||||
assert_eq!(val, dec);
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ pub mod db; // Stores a DEFINE DATABASE config definition
|
|||
pub mod dl; // Stores a DEFINE LOGIN ON DATABASE config definition
|
||||
pub mod dt; // Stores a DEFINE LOGIN ON DATABASE config definition
|
||||
pub mod ev; // Stores a DEFINE EVENT config definition
|
||||
pub mod fc; // Stores a DEFINE FUNCTION config definition
|
||||
pub mod fd; // Stores a DEFINE FIELD config definition
|
||||
pub mod ft; // Stores a DEFINE TABLE AS config definition
|
||||
pub mod graph; // Stores a graph edge pointer
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::kvs::kv::Key;
|
|||
use crate::sql::statements::DefineDatabaseStatement;
|
||||
use crate::sql::statements::DefineEventStatement;
|
||||
use crate::sql::statements::DefineFieldStatement;
|
||||
use crate::sql::statements::DefineFunctionStatement;
|
||||
use crate::sql::statements::DefineIndexStatement;
|
||||
use crate::sql::statements::DefineLoginStatement;
|
||||
use crate::sql::statements::DefineNamespaceStatement;
|
||||
|
@ -15,24 +16,27 @@ use std::sync::Arc;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum Entry {
|
||||
Ns(Arc<DefineNamespaceStatement>),
|
||||
// Single definitions
|
||||
Db(Arc<DefineDatabaseStatement>),
|
||||
Ns(Arc<DefineNamespaceStatement>),
|
||||
Tb(Arc<DefineTableStatement>),
|
||||
Nss(Arc<[DefineNamespaceStatement]>),
|
||||
Nls(Arc<[DefineLoginStatement]>),
|
||||
Nts(Arc<[DefineTokenStatement]>),
|
||||
// Multi definitions
|
||||
Dbs(Arc<[DefineDatabaseStatement]>),
|
||||
Dls(Arc<[DefineLoginStatement]>),
|
||||
Dts(Arc<[DefineTokenStatement]>),
|
||||
Evs(Arc<[DefineEventStatement]>),
|
||||
Fcs(Arc<[DefineFunctionStatement]>),
|
||||
Fds(Arc<[DefineFieldStatement]>),
|
||||
Fts(Arc<[DefineTableStatement]>),
|
||||
Ixs(Arc<[DefineIndexStatement]>),
|
||||
Lvs(Arc<[LiveStatement]>),
|
||||
Nls(Arc<[DefineLoginStatement]>),
|
||||
Nss(Arc<[DefineNamespaceStatement]>),
|
||||
Nts(Arc<[DefineTokenStatement]>),
|
||||
Pas(Arc<[DefineParamStatement]>),
|
||||
Scs(Arc<[DefineScopeStatement]>),
|
||||
Sts(Arc<[DefineTokenStatement]>),
|
||||
Pas(Arc<[DefineParamStatement]>),
|
||||
Tbs(Arc<[DefineTableStatement]>),
|
||||
Evs(Arc<[DefineEventStatement]>),
|
||||
Fds(Arc<[DefineFieldStatement]>),
|
||||
Ixs(Arc<[DefineIndexStatement]>),
|
||||
Fts(Arc<[DefineTableStatement]>),
|
||||
Lvs(Arc<[LiveStatement]>),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -17,6 +17,7 @@ use sql::permission::Permissions;
|
|||
use sql::statements::DefineDatabaseStatement;
|
||||
use sql::statements::DefineEventStatement;
|
||||
use sql::statements::DefineFieldStatement;
|
||||
use sql::statements::DefineFunctionStatement;
|
||||
use sql::statements::DefineIndexStatement;
|
||||
use sql::statements::DefineLoginStatement;
|
||||
use sql::statements::DefineNamespaceStatement;
|
||||
|
@ -53,6 +54,10 @@ pub(super) enum Inner {
|
|||
}
|
||||
|
||||
impl Transaction {
|
||||
// --------------------------------------------------
|
||||
// Integral methods
|
||||
// --------------------------------------------------
|
||||
|
||||
/// Check if transactions is finished.
|
||||
///
|
||||
/// If the transaction has been cancelled or committed,
|
||||
|
@ -92,6 +97,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel a transaction.
|
||||
///
|
||||
/// This reverses all changes made within the transaction.
|
||||
|
@ -128,6 +134,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit a transaction.
|
||||
///
|
||||
/// This attempts to commit all changes made within the transaction.
|
||||
|
@ -164,6 +171,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a key from the datastore.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn del<K>(&mut self, key: K) -> Result<(), Error>
|
||||
|
@ -202,6 +210,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a key exists in the datastore.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn exi<K>(&mut self, key: K) -> Result<bool, Error>
|
||||
|
@ -240,6 +249,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a key from the datastore.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn get<K>(&mut self, key: K) -> Result<Option<Val>, Error>
|
||||
|
@ -278,6 +288,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert or update a key in the datastore.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn set<K, V>(&mut self, key: K, val: V) -> Result<(), Error>
|
||||
|
@ -317,6 +328,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a key if it doesn't exist in the datastore.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn put<K, V>(&mut self, key: K, val: V) -> Result<(), Error>
|
||||
|
@ -356,6 +368,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a specific range of keys from the datastore.
|
||||
///
|
||||
/// This function fetches the full range of key-value pairs, in a single request to the underlying datastore.
|
||||
|
@ -396,6 +409,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a key in the datastore if the current value matches a condition.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn putc<K, V>(&mut self, key: K, val: V, chk: Option<V>) -> Result<(), Error>
|
||||
|
@ -435,6 +449,7 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a key from the datastore if the current value matches a condition.
|
||||
#[allow(unused_variables)]
|
||||
pub async fn delc<K, V>(&mut self, key: K, chk: Option<V>) -> Result<(), Error>
|
||||
|
@ -474,6 +489,11 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Superjacent methods
|
||||
// --------------------------------------------------
|
||||
|
||||
/// Retrieve a specific range of keys from the datastore.
|
||||
///
|
||||
/// This function fetches key-value pairs from the underlying datastore in batches of 1000.
|
||||
|
@ -672,6 +692,11 @@ impl Transaction {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Superimposed methods
|
||||
// --------------------------------------------------
|
||||
|
||||
/// Clear any cache entry for the specified key.
|
||||
pub async fn clr<K>(&mut self, key: K) -> Result<(), Error>
|
||||
where
|
||||
|
@ -681,6 +706,7 @@ impl Transaction {
|
|||
self.cache.del(&key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve all namespace definitions in a datastore.
|
||||
pub async fn all_ns(&mut self) -> Result<Arc<[DefineNamespaceStatement]>, Error> {
|
||||
let key = crate::key::ns::prefix();
|
||||
|
@ -699,6 +725,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all namespace login definitions for a specific namespace.
|
||||
pub async fn all_nl(&mut self, ns: &str) -> Result<Arc<[DefineLoginStatement]>, Error> {
|
||||
let key = crate::key::nl::prefix(ns);
|
||||
|
@ -717,6 +744,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all namespace token definitions for a specific namespace.
|
||||
pub async fn all_nt(&mut self, ns: &str) -> Result<Arc<[DefineTokenStatement]>, Error> {
|
||||
let key = crate::key::nt::prefix(ns);
|
||||
|
@ -735,6 +763,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all database definitions for a specific namespace.
|
||||
pub async fn all_db(&mut self, ns: &str) -> Result<Arc<[DefineDatabaseStatement]>, Error> {
|
||||
let key = crate::key::db::prefix(ns);
|
||||
|
@ -753,6 +782,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all database login definitions for a specific database.
|
||||
pub async fn all_dl(
|
||||
&mut self,
|
||||
|
@ -775,6 +805,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all database token definitions for a specific database.
|
||||
pub async fn all_dt(
|
||||
&mut self,
|
||||
|
@ -797,6 +828,30 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all function definitions for a specific database.
|
||||
pub async fn all_fc(
|
||||
&mut self,
|
||||
ns: &str,
|
||||
db: &str,
|
||||
) -> Result<Arc<[DefineFunctionStatement]>, Error> {
|
||||
let key = crate::key::fc::prefix(ns, db);
|
||||
match self.cache.exi(&key) {
|
||||
true => match self.cache.get(&key) {
|
||||
Some(Entry::Fcs(v)) => Ok(v),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let beg = crate::key::fc::prefix(ns, db);
|
||||
let end = crate::key::fc::suffix(ns, db);
|
||||
let val = self.getr(beg..end, u32::MAX).await?;
|
||||
let val = val.convert().into();
|
||||
self.cache.set(key, Entry::Fcs(Arc::clone(&val)));
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all scope definitions for a specific database.
|
||||
pub async fn all_sc(
|
||||
&mut self,
|
||||
|
@ -819,6 +874,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all scope token definitions for a scope.
|
||||
pub async fn all_st(
|
||||
&mut self,
|
||||
|
@ -842,6 +898,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all scope definitions for a specific database.
|
||||
pub async fn all_pa(
|
||||
&mut self,
|
||||
|
@ -864,6 +921,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all table definitions for a specific database.
|
||||
pub async fn all_tb(
|
||||
&mut self,
|
||||
|
@ -886,6 +944,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all event definitions for a specific table.
|
||||
pub async fn all_ev(
|
||||
&mut self,
|
||||
|
@ -909,6 +968,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all field definitions for a specific table.
|
||||
pub async fn all_fd(
|
||||
&mut self,
|
||||
|
@ -932,6 +992,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all index definitions for a specific table.
|
||||
pub async fn all_ix(
|
||||
&mut self,
|
||||
|
@ -955,6 +1016,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all view definitions for a specific table.
|
||||
pub async fn all_ft(
|
||||
&mut self,
|
||||
|
@ -978,6 +1040,7 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve all live definitions for a specific table.
|
||||
pub async fn all_lv(
|
||||
&mut self,
|
||||
|
@ -1001,30 +1064,43 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a specific namespace definition.
|
||||
pub async fn get_ns(&mut self, ns: &str) -> Result<DefineNamespaceStatement, Error> {
|
||||
let key = crate::key::ns::new(ns);
|
||||
let val = self.get(key).await?.ok_or(Error::NsNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::NsNotFound {
|
||||
value: ns.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific namespace login definition.
|
||||
pub async fn get_nl(&mut self, ns: &str, nl: &str) -> Result<DefineLoginStatement, Error> {
|
||||
let key = crate::key::nl::new(ns, nl);
|
||||
let val = self.get(key).await?.ok_or(Error::NlNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::NlNotFound {
|
||||
value: nl.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific namespace token definition.
|
||||
pub async fn get_nt(&mut self, ns: &str, nt: &str) -> Result<DefineTokenStatement, Error> {
|
||||
let key = crate::key::nt::new(ns, nt);
|
||||
let val = self.get(key).await?.ok_or(Error::NtNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::NtNotFound {
|
||||
value: nt.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific database definition.
|
||||
pub async fn get_db(&mut self, ns: &str, db: &str) -> Result<DefineDatabaseStatement, Error> {
|
||||
let key = crate::key::db::new(ns, db);
|
||||
let val = self.get(key).await?.ok_or(Error::DbNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::DbNotFound {
|
||||
value: db.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific database login definition.
|
||||
pub async fn get_dl(
|
||||
&mut self,
|
||||
|
@ -1033,9 +1109,12 @@ impl Transaction {
|
|||
dl: &str,
|
||||
) -> Result<DefineLoginStatement, Error> {
|
||||
let key = crate::key::dl::new(ns, db, dl);
|
||||
let val = self.get(key).await?.ok_or(Error::DlNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::DlNotFound {
|
||||
value: dl.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific database token definition.
|
||||
pub async fn get_dt(
|
||||
&mut self,
|
||||
|
@ -1044,9 +1123,12 @@ impl Transaction {
|
|||
dt: &str,
|
||||
) -> Result<DefineTokenStatement, Error> {
|
||||
let key = crate::key::dt::new(ns, db, dt);
|
||||
let val = self.get(key).await?.ok_or(Error::DtNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::DtNotFound {
|
||||
value: dt.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific scope definition.
|
||||
pub async fn get_sc(
|
||||
&mut self,
|
||||
|
@ -1055,9 +1137,12 @@ impl Transaction {
|
|||
sc: &str,
|
||||
) -> Result<DefineScopeStatement, Error> {
|
||||
let key = crate::key::sc::new(ns, db, sc);
|
||||
let val = self.get(key).await?.ok_or(Error::ScNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::ScNotFound {
|
||||
value: sc.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific scope token definition.
|
||||
pub async fn get_st(
|
||||
&mut self,
|
||||
|
@ -1067,9 +1152,26 @@ impl Transaction {
|
|||
st: &str,
|
||||
) -> Result<DefineTokenStatement, Error> {
|
||||
let key = crate::key::st::new(ns, db, sc, st);
|
||||
let val = self.get(key).await?.ok_or(Error::StNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::StNotFound {
|
||||
value: st.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific function definition.
|
||||
pub async fn get_fc(
|
||||
&mut self,
|
||||
ns: &str,
|
||||
db: &str,
|
||||
fc: &str,
|
||||
) -> Result<DefineFunctionStatement, Error> {
|
||||
let key = crate::key::fc::new(ns, db, fc);
|
||||
let val = self.get(key).await?.ok_or(Error::FcNotFound {
|
||||
value: fc.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific param definition.
|
||||
pub async fn get_pa(
|
||||
&mut self,
|
||||
|
@ -1078,9 +1180,12 @@ impl Transaction {
|
|||
pa: &str,
|
||||
) -> Result<DefineParamStatement, Error> {
|
||||
let key = crate::key::pa::new(ns, db, pa);
|
||||
let val = self.get(key).await?.ok_or(Error::PaNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::PaNotFound {
|
||||
value: pa.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Retrieve a specific table definition.
|
||||
pub async fn get_tb(
|
||||
&mut self,
|
||||
|
@ -1089,9 +1194,12 @@ impl Transaction {
|
|||
tb: &str,
|
||||
) -> Result<DefineTableStatement, Error> {
|
||||
let key = crate::key::tb::new(ns, db, tb);
|
||||
let val = self.get(key).await?.ok_or(Error::TbNotFound)?;
|
||||
let val = self.get(key).await?.ok_or(Error::TbNotFound {
|
||||
value: tb.to_owned(),
|
||||
})?;
|
||||
Ok(val.into())
|
||||
}
|
||||
|
||||
/// Add a namespace with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_ns(
|
||||
&mut self,
|
||||
|
@ -1099,7 +1207,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<DefineNamespaceStatement, Error> {
|
||||
match self.get_ns(ns).await {
|
||||
Err(Error::NsNotFound) => match strict {
|
||||
Err(Error::NsNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::ns::new(ns);
|
||||
let val = DefineNamespaceStatement {
|
||||
|
@ -1108,12 +1218,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(val)
|
||||
}
|
||||
true => Err(Error::NsNotFound),
|
||||
true => Err(Error::NsNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a database with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_db(
|
||||
&mut self,
|
||||
|
@ -1122,7 +1235,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<DefineDatabaseStatement, Error> {
|
||||
match self.get_db(ns, db).await {
|
||||
Err(Error::DbNotFound) => match strict {
|
||||
Err(Error::DbNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::db::new(ns, db);
|
||||
let val = DefineDatabaseStatement {
|
||||
|
@ -1131,12 +1246,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(val)
|
||||
}
|
||||
true => Err(Error::DbNotFound),
|
||||
true => Err(Error::DbNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a scope with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_sc(
|
||||
&mut self,
|
||||
|
@ -1146,7 +1264,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<DefineScopeStatement, Error> {
|
||||
match self.get_sc(ns, db, sc).await {
|
||||
Err(Error::ScNotFound) => match strict {
|
||||
Err(Error::ScNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::sc::new(ns, db, sc);
|
||||
let val = DefineScopeStatement {
|
||||
|
@ -1156,12 +1276,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(val)
|
||||
}
|
||||
true => Err(Error::ScNotFound),
|
||||
true => Err(Error::ScNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a table with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_tb(
|
||||
&mut self,
|
||||
|
@ -1171,7 +1294,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<DefineTableStatement, Error> {
|
||||
match self.get_tb(ns, db, tb).await {
|
||||
Err(Error::TbNotFound) => match strict {
|
||||
Err(Error::TbNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::tb::new(ns, db, tb);
|
||||
let val = DefineTableStatement {
|
||||
|
@ -1182,12 +1307,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(val)
|
||||
}
|
||||
true => Err(Error::TbNotFound),
|
||||
true => Err(Error::TbNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve and cache a specific namespace definition.
|
||||
pub async fn get_and_cache_ns(
|
||||
&mut self,
|
||||
|
@ -1200,13 +1328,16 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::NsNotFound)?;
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::NsNotFound {
|
||||
value: ns.to_owned(),
|
||||
})?;
|
||||
let val: Arc<DefineNamespaceStatement> = Arc::new(val.into());
|
||||
self.cache.set(key, Entry::Ns(Arc::clone(&val)));
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve and cache a specific database definition.
|
||||
pub async fn get_and_cache_db(
|
||||
&mut self,
|
||||
|
@ -1220,13 +1351,16 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::DbNotFound)?;
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::DbNotFound {
|
||||
value: db.to_owned(),
|
||||
})?;
|
||||
let val: Arc<DefineDatabaseStatement> = Arc::new(val.into());
|
||||
self.cache.set(key, Entry::Db(Arc::clone(&val)));
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve and cache a specific table definition.
|
||||
pub async fn get_and_cache_tb(
|
||||
&mut self,
|
||||
|
@ -1241,13 +1375,16 @@ impl Transaction {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::TbNotFound)?;
|
||||
let val = self.get(key.clone()).await?.ok_or(Error::TbNotFound {
|
||||
value: tb.to_owned(),
|
||||
})?;
|
||||
let val: Arc<DefineTableStatement> = Arc::new(val.into());
|
||||
self.cache.set(key, Entry::Tb(Arc::clone(&val)));
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a namespace with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_and_cache_ns(
|
||||
&mut self,
|
||||
|
@ -1255,7 +1392,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<Arc<DefineNamespaceStatement>, Error> {
|
||||
match self.get_and_cache_ns(ns).await {
|
||||
Err(Error::NsNotFound) => match strict {
|
||||
Err(Error::NsNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::ns::new(ns);
|
||||
let val = DefineNamespaceStatement {
|
||||
|
@ -1264,12 +1403,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(Arc::new(val))
|
||||
}
|
||||
true => Err(Error::NsNotFound),
|
||||
true => Err(Error::NsNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a database with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_and_cache_db(
|
||||
&mut self,
|
||||
|
@ -1278,7 +1420,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<Arc<DefineDatabaseStatement>, Error> {
|
||||
match self.get_and_cache_db(ns, db).await {
|
||||
Err(Error::DbNotFound) => match strict {
|
||||
Err(Error::DbNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::db::new(ns, db);
|
||||
let val = DefineDatabaseStatement {
|
||||
|
@ -1287,12 +1431,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(Arc::new(val))
|
||||
}
|
||||
true => Err(Error::DbNotFound),
|
||||
true => Err(Error::DbNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a table with a default configuration, only if we are in dynamic mode.
|
||||
pub async fn add_and_cache_tb(
|
||||
&mut self,
|
||||
|
@ -1302,7 +1449,9 @@ impl Transaction {
|
|||
strict: bool,
|
||||
) -> Result<Arc<DefineTableStatement>, Error> {
|
||||
match self.get_and_cache_tb(ns, db, tb).await {
|
||||
Err(Error::TbNotFound) => match strict {
|
||||
Err(Error::TbNotFound {
|
||||
value,
|
||||
}) => match strict {
|
||||
false => {
|
||||
let key = crate::key::tb::new(ns, db, tb);
|
||||
let val = DefineTableStatement {
|
||||
|
@ -1313,12 +1462,15 @@ impl Transaction {
|
|||
self.put(key, &val).await?;
|
||||
Ok(Arc::new(val))
|
||||
}
|
||||
true => Err(Error::TbNotFound),
|
||||
true => Err(Error::TbNotFound {
|
||||
value,
|
||||
}),
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve and cache a specific table definition.
|
||||
pub async fn check_ns_db_tb(
|
||||
&mut self,
|
||||
|
@ -1339,6 +1491,11 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Additional methods
|
||||
// --------------------------------------------------
|
||||
|
||||
/// Writes the full database contents as binary SQL.
|
||||
pub async fn export(&mut self, ns: &str, db: &str, chn: Sender<Vec<u8>>) -> Result<(), Error> {
|
||||
// Output OPTIONS
|
||||
|
@ -1350,6 +1507,20 @@ impl Transaction {
|
|||
chn.send(bytes!("OPTION IMPORT;")).await?;
|
||||
chn.send(bytes!("")).await?;
|
||||
}
|
||||
// Output FUNCTIONS
|
||||
{
|
||||
let fcs = self.all_fc(ns, db).await?;
|
||||
if !fcs.is_empty() {
|
||||
chn.send(bytes!("-- ------------------------------")).await?;
|
||||
chn.send(bytes!("-- FUNCTIONS")).await?;
|
||||
chn.send(bytes!("-- ------------------------------")).await?;
|
||||
chn.send(bytes!("")).await?;
|
||||
for fc in fcs.iter() {
|
||||
chn.send(bytes!(format!("{fc};"))).await?;
|
||||
}
|
||||
chn.send(bytes!("")).await?;
|
||||
}
|
||||
}
|
||||
// Output LOGINS
|
||||
{
|
||||
let dls = self.all_dl(ns, db).await?;
|
||||
|
|
|
@ -5,13 +5,17 @@ use crate::err::Error;
|
|||
use crate::fnc;
|
||||
use crate::sql::comment::mightbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::common::val_char;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::script::{script as func, Script};
|
||||
use crate::sql::value::{single, value, Value};
|
||||
use async_recursion::async_recursion;
|
||||
use futures::future::try_join_all;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_while1;
|
||||
use nom::character::complete::char;
|
||||
use nom::multi::separated_list0;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -22,6 +26,7 @@ use std::fmt;
|
|||
pub enum Function {
|
||||
Cast(String, Value),
|
||||
Normal(String, Vec<Value>),
|
||||
Custom(String, Vec<Value>),
|
||||
Script(Script, Vec<Value>),
|
||||
}
|
||||
|
||||
|
@ -37,6 +42,7 @@ impl Function {
|
|||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Normal(n, _) => n.as_str(),
|
||||
Self::Custom(n, _) => n.as_str(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +50,19 @@ impl Function {
|
|||
pub fn args(&self) -> &[Value] {
|
||||
match self {
|
||||
Self::Normal(_, a) => a,
|
||||
Self::Custom(_, a) => a,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
/// Convert function call to a field name
|
||||
pub fn to_idiom(&self) -> Idiom {
|
||||
match self {
|
||||
Self::Script(_, _) => "function".to_string().into(),
|
||||
Self::Normal(f, _) => f.to_owned().into(),
|
||||
Self::Custom(f, _) => format!("fn::{f}").into(),
|
||||
Self::Cast(_, v) => v.to_idiom(),
|
||||
}
|
||||
}
|
||||
/// Convert this function to an aggregate
|
||||
pub fn aggregate(&self, val: Value) -> Self {
|
||||
match self {
|
||||
|
@ -104,12 +120,14 @@ impl Function {
|
|||
}
|
||||
|
||||
impl Function {
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
|
||||
pub(crate) async fn compute(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
doc: Option<&Value>,
|
||||
doc: Option<&'async_recursion Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Prevent long function chains
|
||||
let opt = &opt.dive(1)?;
|
||||
|
@ -129,6 +147,44 @@ impl Function {
|
|||
// Run the normal function
|
||||
fnc::run(ctx, s, a).await
|
||||
}
|
||||
Self::Custom(s, x) => {
|
||||
// Get the function definition
|
||||
let val = {
|
||||
// Clone transaction
|
||||
let run = txn.clone();
|
||||
// Claim transaction
|
||||
let mut run = run.lock().await;
|
||||
// Get the function definition
|
||||
run.get_fc(opt.ns(), opt.db(), s).await?
|
||||
};
|
||||
// Check the function arguments
|
||||
if x.len() != val.args.len() {
|
||||
return Err(Error::InvalidArguments {
|
||||
name: format!("fn::{}", val.name),
|
||||
message: match val.args.len() {
|
||||
1 => String::from("The function expects 1 argument."),
|
||||
l => format!("The function expects {l} arguments."),
|
||||
},
|
||||
});
|
||||
}
|
||||
// Compute the function arguments
|
||||
let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
|
||||
// Duplicate context
|
||||
let mut ctx = Context::new(ctx);
|
||||
// Process the function arguments
|
||||
for (val, (name, kind)) in a.into_iter().zip(val.args) {
|
||||
ctx.add_value(
|
||||
name.to_raw(),
|
||||
match val {
|
||||
Value::None => val,
|
||||
Value::Null => val,
|
||||
_ => val.convert_to(&kind),
|
||||
},
|
||||
);
|
||||
}
|
||||
// Run the custom function
|
||||
val.block.compute(&ctx, opt, txn, doc).await
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
Self::Script(s, x) => {
|
||||
#[cfg(feature = "scripting")]
|
||||
|
@ -152,17 +208,16 @@ impl Function {
|
|||
impl fmt::Display for Function {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Cast(ref s, ref e) => write!(f, "<{s}> {e}"),
|
||||
Self::Script(ref s, ref e) => {
|
||||
write!(f, "function({}) {{{s}}}", Fmt::comma_separated(e))
|
||||
}
|
||||
Self::Normal(ref s, ref e) => write!(f, "{s}({})", Fmt::comma_separated(e)),
|
||||
Self::Cast(s, e) => write!(f, "<{s}> {e}"),
|
||||
Self::Normal(s, e) => write!(f, "{s}({})", Fmt::comma_separated(e)),
|
||||
Self::Custom(s, e) => write!(f, "fn::{s}({})", Fmt::comma_separated(e)),
|
||||
Self::Script(s, e) => write!(f, "function({}) {{{s}}}", Fmt::comma_separated(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function(i: &str) -> IResult<&str, Function> {
|
||||
alt((normal, script, cast))(i)
|
||||
alt((normal, custom, script, cast))(i)
|
||||
}
|
||||
|
||||
fn normal(i: &str) -> IResult<&str, Function> {
|
||||
|
@ -175,6 +230,17 @@ fn normal(i: &str) -> IResult<&str, Function> {
|
|||
Ok((i, Function::Normal(s.to_string(), a)))
|
||||
}
|
||||
|
||||
fn custom(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = tag("fn::")(i)?;
|
||||
let (i, s) = take_while1(val_char)(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, a) = separated_list0(commas, value)(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, Function::Custom(s.to_string(), a)))
|
||||
}
|
||||
|
||||
fn script(i: &str) -> IResult<&str, Function> {
|
||||
let (i, _) = alt((tag("fn::script"), tag("fn"), tag("function")))(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
|
|
|
@ -64,6 +64,11 @@ pub fn ident(i: &str) -> IResult<&str, Ident> {
|
|||
Ok((i, Ident::from(v)))
|
||||
}
|
||||
|
||||
pub fn plain(i: &str) -> IResult<&str, Ident> {
|
||||
let (i, v) = ident_default(i)?;
|
||||
Ok((i, Ident::from(v)))
|
||||
}
|
||||
|
||||
pub fn ident_raw(i: &str) -> IResult<&str, String> {
|
||||
let (i, v) = alt((ident_default, ident_backtick, ident_brackets))(i)?;
|
||||
Ok((i, v))
|
||||
|
|
|
@ -5,12 +5,15 @@ use crate::dbs::Transaction;
|
|||
use crate::err::Error;
|
||||
use crate::sql::algorithm::{algorithm, Algorithm};
|
||||
use crate::sql::base::{base, base_or_scope, Base};
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::block::{block, Block};
|
||||
use crate::sql::comment::{mightbespace, shouldbespace};
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::duration::{duration, Duration};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::escape::escape_str;
|
||||
use crate::sql::fmt::is_pretty;
|
||||
use crate::sql::fmt::pretty_indent;
|
||||
use crate::sql::ident;
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::idiom;
|
||||
use crate::sql::idiom::{Idiom, Idioms};
|
||||
|
@ -24,10 +27,12 @@ use argon2::password_hash::{PasswordHasher, SaltString};
|
|||
use argon2::Argon2;
|
||||
use derive::Store;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::char;
|
||||
use nom::combinator::{map, opt};
|
||||
use nom::multi::many0;
|
||||
use nom::multi::separated_list0;
|
||||
use nom::sequence::tuple;
|
||||
use rand::distributions::Alphanumeric;
|
||||
use rand::rngs::OsRng;
|
||||
|
@ -39,6 +44,7 @@ use std::fmt::{self, Display, Write};
|
|||
pub enum DefineStatement {
|
||||
Namespace(DefineNamespaceStatement),
|
||||
Database(DefineDatabaseStatement),
|
||||
Function(DefineFunctionStatement),
|
||||
Login(DefineLoginStatement),
|
||||
Token(DefineTokenStatement),
|
||||
Scope(DefineScopeStatement),
|
||||
|
@ -60,6 +66,7 @@ impl DefineStatement {
|
|||
match self {
|
||||
Self::Namespace(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Database(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Function(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Login(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Token(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Scope(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
|
@ -77,6 +84,7 @@ impl fmt::Display for DefineStatement {
|
|||
match self {
|
||||
Self::Namespace(v) => Display::fmt(v, f),
|
||||
Self::Database(v) => Display::fmt(v, f),
|
||||
Self::Function(v) => Display::fmt(v, f),
|
||||
Self::Login(v) => Display::fmt(v, f),
|
||||
Self::Token(v) => Display::fmt(v, f),
|
||||
Self::Scope(v) => Display::fmt(v, f),
|
||||
|
@ -93,6 +101,7 @@ pub fn define(i: &str) -> IResult<&str, DefineStatement> {
|
|||
alt((
|
||||
map(namespace, DefineStatement::Namespace),
|
||||
map(database, DefineStatement::Database),
|
||||
map(function, DefineStatement::Function),
|
||||
map(login, DefineStatement::Login),
|
||||
map(token, DefineStatement::Token),
|
||||
map(scope, DefineStatement::Scope),
|
||||
|
@ -211,6 +220,90 @@ fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
|
|||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||
pub struct DefineFunctionStatement {
|
||||
pub name: Ident,
|
||||
pub args: Vec<(Ident, Kind)>,
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
impl DefineFunctionStatement {
|
||||
pub(crate) async fn compute(
|
||||
&self,
|
||||
_ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Selected DB?
|
||||
opt.needs(Level::Db)?;
|
||||
// Allowed to run?
|
||||
opt.check(Level::Db)?;
|
||||
// Clone transaction
|
||||
let run = txn.clone();
|
||||
// Claim transaction
|
||||
let mut run = run.lock().await;
|
||||
// Process the statement
|
||||
let key = crate::key::fc::new(opt.ns(), opt.db(), &self.name);
|
||||
run.add_ns(opt.ns(), opt.strict).await?;
|
||||
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
|
||||
run.set(key, self).await?;
|
||||
// Ok all good
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DefineFunctionStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "DEFINE FUNCTION fn::{}(", self.name)?;
|
||||
for (i, (name, kind)) in self.args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
f.write_str(", ")?;
|
||||
}
|
||||
write!(f, "${name}: {kind}")?;
|
||||
}
|
||||
f.write_str(") ")?;
|
||||
Display::fmt(&self.block, f)
|
||||
}
|
||||
}
|
||||
|
||||
fn function(i: &str) -> IResult<&str, DefineFunctionStatement> {
|
||||
let (i, _) = tag_no_case("DEFINE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag("fn::")(i)?;
|
||||
let (i, name) = ident::plain(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, args) = separated_list0(commas, |i| {
|
||||
let (i, _) = char('$')(i)?;
|
||||
let (i, name) = ident(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char(':')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, kind) = kind(i)?;
|
||||
Ok((i, (name, kind)))
|
||||
})(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, block) = block(i)?;
|
||||
Ok((
|
||||
i,
|
||||
DefineFunctionStatement {
|
||||
name,
|
||||
args,
|
||||
block,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||
pub struct DefineLoginStatement {
|
||||
pub name: Ident,
|
||||
|
|
|
@ -108,6 +108,12 @@ impl InfoStatement {
|
|||
tmp.insert(v.name.to_string(), v.to_string().into());
|
||||
}
|
||||
res.insert("dt".to_owned(), tmp.into());
|
||||
// Process the functions
|
||||
let mut tmp = Object::default();
|
||||
for v in run.all_fc(opt.ns(), opt.db()).await?.iter() {
|
||||
tmp.insert(v.name.to_string(), v.to_string().into());
|
||||
}
|
||||
res.insert("fc".to_owned(), tmp.into());
|
||||
// Process the params
|
||||
let mut tmp = Object::default();
|
||||
for v in run.all_pa(opt.ns(), opt.db()).await?.iter() {
|
||||
|
|
|
@ -39,23 +39,21 @@ pub use self::yuse::UseStatement;
|
|||
|
||||
pub use self::define::DefineDatabaseStatement;
|
||||
pub use self::define::DefineEventStatement;
|
||||
pub use self::define::DefineFieldOption;
|
||||
pub use self::define::DefineFieldStatement;
|
||||
pub use self::define::DefineFunctionStatement;
|
||||
pub use self::define::DefineIndexStatement;
|
||||
pub use self::define::DefineLoginOption;
|
||||
pub use self::define::DefineLoginStatement;
|
||||
pub use self::define::DefineNamespaceStatement;
|
||||
pub use self::define::DefineParamStatement;
|
||||
pub use self::define::DefineScopeOption;
|
||||
pub use self::define::DefineScopeStatement;
|
||||
pub use self::define::DefineStatement;
|
||||
pub use self::define::DefineTableOption;
|
||||
pub use self::define::DefineTableStatement;
|
||||
pub use self::define::DefineTokenStatement;
|
||||
|
||||
pub use self::remove::RemoveDatabaseStatement;
|
||||
pub use self::remove::RemoveEventStatement;
|
||||
pub use self::remove::RemoveFieldStatement;
|
||||
pub use self::remove::RemoveFunctionStatement;
|
||||
pub use self::remove::RemoveIndexStatement;
|
||||
pub use self::remove::RemoveLoginStatement;
|
||||
pub use self::remove::RemoveNamespaceStatement;
|
||||
|
|
|
@ -4,14 +4,16 @@ use crate::dbs::Options;
|
|||
use crate::dbs::Transaction;
|
||||
use crate::err::Error;
|
||||
use crate::sql::base::{base, base_or_scope, Base};
|
||||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::comment::{mightbespace, shouldbespace};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::ident;
|
||||
use crate::sql::ident::{ident, Ident};
|
||||
use crate::sql::idiom;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::value::Value;
|
||||
use derive::Store;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::char;
|
||||
use nom::combinator::{map, opt};
|
||||
|
@ -23,6 +25,7 @@ use std::fmt::{self, Display, Formatter};
|
|||
pub enum RemoveStatement {
|
||||
Namespace(RemoveNamespaceStatement),
|
||||
Database(RemoveDatabaseStatement),
|
||||
Function(RemoveFunctionStatement),
|
||||
Login(RemoveLoginStatement),
|
||||
Token(RemoveTokenStatement),
|
||||
Scope(RemoveScopeStatement),
|
||||
|
@ -44,6 +47,7 @@ impl RemoveStatement {
|
|||
match self {
|
||||
Self::Namespace(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Database(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Function(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Login(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Token(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
Self::Scope(ref v) => v.compute(ctx, opt, txn, doc).await,
|
||||
|
@ -61,6 +65,7 @@ impl Display for RemoveStatement {
|
|||
match self {
|
||||
Self::Namespace(v) => Display::fmt(v, f),
|
||||
Self::Database(v) => Display::fmt(v, f),
|
||||
Self::Function(v) => Display::fmt(v, f),
|
||||
Self::Login(v) => Display::fmt(v, f),
|
||||
Self::Token(v) => Display::fmt(v, f),
|
||||
Self::Scope(v) => Display::fmt(v, f),
|
||||
|
@ -77,6 +82,7 @@ pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
|
|||
alt((
|
||||
map(namespace, RemoveStatement::Namespace),
|
||||
map(database, RemoveStatement::Database),
|
||||
map(function, RemoveStatement::Function),
|
||||
map(login, RemoveStatement::Login),
|
||||
map(token, RemoveStatement::Token),
|
||||
map(scope, RemoveStatement::Scope),
|
||||
|
@ -204,6 +210,67 @@ fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
|
|||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||
pub struct RemoveFunctionStatement {
|
||||
pub name: Ident,
|
||||
}
|
||||
|
||||
impl RemoveFunctionStatement {
|
||||
pub(crate) async fn compute(
|
||||
&self,
|
||||
_ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
txn: &Transaction,
|
||||
_doc: Option<&Value>,
|
||||
) -> Result<Value, Error> {
|
||||
// Selected DB?
|
||||
opt.needs(Level::Db)?;
|
||||
// Allowed to run?
|
||||
opt.check(Level::Db)?;
|
||||
// Clone transaction
|
||||
let run = txn.clone();
|
||||
// Claim transaction
|
||||
let mut run = run.lock().await;
|
||||
// Delete the definition
|
||||
let key = crate::key::fc::new(opt.ns(), opt.db(), &self.name);
|
||||
run.del(key).await?;
|
||||
// Ok all good
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RemoveFunctionStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "REMOVE FUNCTION fn::{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
fn function(i: &str) -> IResult<&str, RemoveFunctionStatement> {
|
||||
let (i, _) = tag_no_case("REMOVE")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag_no_case("FUNCTION")(i)?;
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = tag("fn::")(i)?;
|
||||
let (i, name) = ident::plain(i)?;
|
||||
let (i, _) = opt(|i| {
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char('(')(i)?;
|
||||
let (i, _) = mightbespace(i)?;
|
||||
let (i, _) = char(')')(i)?;
|
||||
Ok((i, ()))
|
||||
})(i)?;
|
||||
Ok((
|
||||
i,
|
||||
RemoveFunctionStatement {
|
||||
name,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
// --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||
pub struct RemoveLoginStatement {
|
||||
pub name: Ident,
|
||||
|
|
|
@ -964,12 +964,8 @@ impl Value {
|
|||
Value::Idiom(v) => v.simplify(),
|
||||
Value::Strand(v) => v.0.to_string().into(),
|
||||
Value::Datetime(v) => v.0.to_string().into(),
|
||||
Value::Future(_) => "fn::future".to_string().into(),
|
||||
Value::Function(v) => match v.as_ref() {
|
||||
Function::Script(_, _) => "fn::script".to_string().into(),
|
||||
Function::Normal(f, _) => f.to_string().into(),
|
||||
Function::Cast(_, v) => v.to_idiom(),
|
||||
},
|
||||
Value::Future(_) => "future".to_string().into(),
|
||||
Value::Function(v) => v.to_idiom(),
|
||||
_ => self.to_string().into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,40 @@ async fn define_statement_database() -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_function() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE FUNCTION fn::test($first: string, $last: string) {
|
||||
RETURN $first + $last;
|
||||
};
|
||||
INFO FOR DB;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 2);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: { test: 'DEFINE FUNCTION fn::test($first: string, $last: string) { RETURN $first + $last; }' },
|
||||
pa: {},
|
||||
sc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: {},
|
||||
}",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn define_statement_table_drop() -> Result<(), Error> {
|
||||
let sql = "
|
||||
|
@ -76,8 +110,7 @@ async fn define_statement_table_drop() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
fc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: { test: 'DEFINE TABLE test DROP SCHEMALESS' },
|
||||
|
@ -107,6 +140,7 @@ async fn define_statement_table_schemaless() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: { test: 'DEFINE TABLE test SCHEMALESS' },
|
||||
|
@ -140,6 +174,7 @@ async fn define_statement_table_schemafull() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: { test: 'DEFINE TABLE test SCHEMAFULL' },
|
||||
|
@ -169,6 +204,7 @@ async fn define_statement_table_schemaful() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: { test: 'DEFINE TABLE test SCHEMAFULL' },
|
||||
|
|
|
@ -27,6 +27,7 @@ async fn define_global_param() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: {},
|
||||
pa: { test: 'DEFINE PARAM $test VALUE 12345' },
|
||||
sc: {},
|
||||
tb: {},
|
||||
|
|
|
@ -21,19 +21,44 @@ async fn strict_mode_no_namespace() -> Result<(), Error> {
|
|||
assert_eq!(res.len(), 5);
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::NsNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::NsNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::NsNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::NsNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::NsNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
@ -57,16 +82,36 @@ async fn strict_mode_no_database() -> Result<(), Error> {
|
|||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::DbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::DbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::DbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::DbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
@ -93,13 +138,28 @@ async fn strict_mode_no_table() -> Result<(), Error> {
|
|||
assert!(tmp.is_ok());
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::TbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::TbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::TbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::TbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
let tmp = res.remove(0).result;
|
||||
assert!(matches!(tmp.err(), Some(Error::TbNotFound)));
|
||||
assert!(matches!(
|
||||
tmp.err(),
|
||||
Some(Error::TbNotFound {
|
||||
value: _
|
||||
})
|
||||
));
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
@ -192,6 +252,7 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
|
|||
"{
|
||||
dl: {},
|
||||
dt: {},
|
||||
fc: {},
|
||||
pa: {},
|
||||
sc: {},
|
||||
tb: { test: 'DEFINE TABLE test SCHEMALESS PERMISSIONS NONE' },
|
||||
|
|
Loading…
Reference in a new issue