Improve DEFINE and REMOVE statements code (#2455)

This commit is contained in:
Tobie Morgan Hitchcock 2023-08-18 14:21:02 +01:00 committed by GitHub
parent c514c39e9d
commit 44dabfa9d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 3373 additions and 2803 deletions

View file

@ -11,6 +11,7 @@ use_small_heuristics = "Off"
# -----------------------------------
#blank_lines_lower_bound = 1
#group_imports = "One"
#indent_style = "Block"
#match_arm_blocks = true
#reorder_impl_items = true

View file

@ -916,8 +916,8 @@ where
/// ```no_run
/// # #[tokio::main]
/// # async fn main() -> surrealdb::Result<()> {
/// # let db = surrealdb::engine::any::connect("mem://").unwrap();
/// db.tick_at(123).await.unwrap();
/// # let db = surrealdb::engine::any::connect("mem://").await?;
/// db.tick(123).await?;
/// # Ok(())
/// # }
/// ```

View file

@ -1,6 +1,5 @@
use crate::dbs::Session;
use crate::err::Error;
use crate::error;
use crate::iam::token::Claims;
use crate::iam::Auth;
use crate::iam::{Actor, Level, Role};
@ -454,21 +453,6 @@ async fn verify_ns_creds(
let user_res = match tx.get_ns_user(ns, user).await {
Ok(u) => Ok(u),
// TODO(sgirones): Remove this fallback once we remove LOGIN from the system completely. We are backwards compatible with LOGIN for now.
// If the USER resource is not found in the namespace, try to find the LOGIN resource
Err(error::Db::UserNsNotFound {
ns: _,
value: _,
}) => match tx.get_nl(ns, user).await {
Ok(u) => Ok(DefineUserStatement {
base: u.base,
name: u.name,
hash: u.hash,
code: u.code,
roles: vec![Role::Editor.into()],
}),
Err(e) => Err(e),
},
Err(e) => Err(e),
}?;
@ -488,22 +472,6 @@ async fn verify_db_creds(
let user_res = match tx.get_db_user(ns, db, user).await {
Ok(u) => Ok(u),
// TODO(sgirones): Remove this fallback once we remove LOGIN from the system completely. We are backwards compatible with LOGIN for now.
// If the USER resource is not found in the database, try to find the LOGIN resource
Err(error::Db::UserDbNotFound {
ns: _,
db: _,
value: _,
}) => match tx.get_dl(ns, db, user).await {
Ok(u) => Ok(DefineUserStatement {
base: u.base,
name: u.name,
hash: u.hash,
code: u.code,
roles: vec![Role::Editor.into()],
}),
Err(e) => Err(e),
},
Err(e) => Err(e),
}?;

View file

@ -6,12 +6,12 @@ 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;
use crate::sql::statements::DefineParamStatement;
use crate::sql::statements::DefineScopeStatement;
use crate::sql::statements::DefineTableStatement;
use crate::sql::statements::DefineTokenStatement;
use crate::sql::statements::DefineUserStatement;
use crate::sql::statements::LiveStatement;
use std::collections::HashMap;
use std::sync::Arc;
@ -25,17 +25,17 @@ pub enum Entry {
// Multi definitions
Azs(Arc<[DefineAnalyzerStatement]>),
Dbs(Arc<[DefineDatabaseStatement]>),
Dls(Arc<[DefineLoginStatement]>),
Dts(Arc<[DefineTokenStatement]>),
Dus(Arc<[DefineUserStatement]>),
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]>),
Nus(Arc<[DefineUserStatement]>),
Pas(Arc<[DefineParamStatement]>),
Scs(Arc<[DefineScopeStatement]>),
Seq(U32),
@ -59,4 +59,8 @@ impl Cache {
pub fn del(&mut self, key: &Key) -> Option<Entry> {
self.0.remove(key)
}
/// Clears a cache completely
pub fn clear(&mut self) {
self.0.clear()
}
}

View file

@ -22,6 +22,7 @@ async fn table_definitions_can_be_scanned() {
view: None,
permissions: Default::default(),
changefeed: None,
comment: None,
};
tx.set(&key, &value).await.unwrap();
@ -57,6 +58,7 @@ async fn table_definitions_can_be_deleted() {
view: None,
permissions: Default::default(),
changefeed: None,
comment: None,
};
tx.set(&key, &value).await.unwrap();

View file

@ -15,7 +15,6 @@ use crate::sql;
use crate::sql::paths::EDGE;
use crate::sql::paths::IN;
use crate::sql::paths::OUT;
use crate::sql::statements::DefineUserStatement;
use crate::sql::thing::Thing;
use crate::sql::Strand;
use crate::sql::Value;
@ -30,12 +29,12 @@ use sql::statements::DefineEventStatement;
use sql::statements::DefineFieldStatement;
use sql::statements::DefineFunctionStatement;
use sql::statements::DefineIndexStatement;
use sql::statements::DefineLoginStatement;
use sql::statements::DefineNamespaceStatement;
use sql::statements::DefineParamStatement;
use sql::statements::DefineScopeStatement;
use sql::statements::DefineTableStatement;
use sql::statements::DefineTokenStatement;
use sql::statements::DefineUserStatement;
use sql::statements::LiveStatement;
use std::borrow::Cow;
use std::collections::HashMap;
@ -1205,34 +1204,25 @@ 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::namespace::lg::prefix(ns);
/// Retrieve all namespace user definitions for a specific namespace.
pub async fn all_ns_users(&mut self, ns: &str) -> Result<Arc<[DefineUserStatement]>, Error> {
let key = crate::key::namespace::us::prefix(ns);
Ok(if let Some(e) = self.cache.get(&key) {
if let Entry::Nls(v) = e {
if let Entry::Nus(v) = e {
v
} else {
unreachable!();
}
} else {
let beg = crate::key::namespace::lg::prefix(ns);
let end = crate::key::namespace::lg::suffix(ns);
let beg = crate::key::namespace::us::prefix(ns);
let end = crate::key::namespace::us::suffix(ns);
let val = self.getr(beg..end, u32::MAX).await?;
let val = val.convert().into();
self.cache.set(key, Entry::Nls(Arc::clone(&val)));
self.cache.set(key, Entry::Nus(Arc::clone(&val)));
val
})
}
/// Retrieve all namespace user definitions for a specific namespace.
pub async fn all_ns_users(&mut self, ns: &str) -> Result<Arc<[DefineUserStatement]>, Error> {
let beg = crate::key::namespace::us::prefix(ns);
let end = crate::key::namespace::us::suffix(ns);
let val = self.getr(beg..end, u32::MAX).await?;
let val = val.convert().into();
Ok(val)
}
/// 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::namespace::tk::prefix(ns);
@ -1271,40 +1261,27 @@ impl Transaction {
})
}
/// Retrieve all database login definitions for a specific database.
pub async fn all_dl(
&mut self,
ns: &str,
db: &str,
) -> Result<Arc<[DefineLoginStatement]>, Error> {
let key = crate::key::database::lg::prefix(ns, db);
Ok(if let Some(e) = self.cache.get(&key) {
if let Entry::Dls(v) = e {
v
} else {
unreachable!();
}
} else {
let beg = crate::key::database::lg::prefix(ns, db);
let end = crate::key::database::lg::suffix(ns, db);
let val = self.getr(beg..end, u32::MAX).await?;
let val = val.convert().into();
self.cache.set(key, Entry::Dls(Arc::clone(&val)));
val
})
}
/// Retrieve all database user definitions for a specific database.
pub async fn all_db_users(
&mut self,
ns: &str,
db: &str,
) -> Result<Arc<[DefineUserStatement]>, Error> {
let beg = crate::key::database::us::prefix(ns, db);
let end = crate::key::database::us::suffix(ns, db);
let val = self.getr(beg..end, u32::MAX).await?;
let val = val.convert().into();
Ok(val)
let key = crate::key::database::us::prefix(ns, db);
Ok(if let Some(e) = self.cache.get(&key) {
if let Entry::Dus(v) = e {
v
} else {
unreachable!();
}
} else {
let beg = crate::key::database::us::prefix(ns, db);
let end = crate::key::database::us::suffix(ns, db);
let val = self.getr(beg..end, u32::MAX).await?;
let val = val.convert().into();
self.cache.set(key, Entry::Dus(Arc::clone(&val)));
val
})
}
/// Retrieve all database token definitions for a specific database.
@ -1631,15 +1608,6 @@ impl Transaction {
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::namespace::lg::new(ns, nl);
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::namespace::tk::new(ns, nt);
@ -1658,20 +1626,6 @@ impl Transaction {
Ok(val.into())
}
/// Retrieve a specific database login definition.
pub async fn get_dl(
&mut self,
ns: &str,
db: &str,
dl: &str,
) -> Result<DefineLoginStatement, Error> {
let key = crate::key::database::lg::new(ns, db, dl);
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,
@ -1868,7 +1822,7 @@ impl Transaction {
let key = crate::key::root::ns::new(ns);
let val = DefineNamespaceStatement {
name: ns.to_owned().into(),
id: None,
..Default::default()
};
self.put(key, &val).await?;
Ok(val)
@ -1897,8 +1851,7 @@ impl Transaction {
let key = crate::key::namespace::db::new(ns, db);
let val = DefineDatabaseStatement {
name: db.to_owned().into(),
changefeed: None,
id: None,
..Default::default()
};
self.put(key, &val).await?;
Ok(val)
@ -1928,7 +1881,7 @@ impl Transaction {
let key = crate::key::database::sc::new(ns, db, sc);
let val = DefineScopeStatement {
name: sc.to_owned().into(),
..DefineScopeStatement::default()
..Default::default()
};
self.put(key, &val).await?;
Ok(val)
@ -1959,8 +1912,7 @@ impl Transaction {
let val = DefineTableStatement {
name: tb.to_owned().into(),
permissions: Permissions::none(),
changefeed: None,
..DefineTableStatement::default()
..Default::default()
};
self.put(key, &val).await?;
Ok(val)
@ -2057,7 +2009,7 @@ impl Transaction {
let key = crate::key::root::ns::new(ns);
let val = DefineNamespaceStatement {
name: ns.to_owned().into(),
id: None,
..Default::default()
};
self.put(key, &val).await?;
Ok(Arc::new(val))
@ -2086,8 +2038,7 @@ impl Transaction {
let key = crate::key::namespace::db::new(ns, db);
let val = DefineDatabaseStatement {
name: db.to_owned().into(),
changefeed: None,
id: None,
..Default::default()
};
self.put(key, &val).await?;
Ok(Arc::new(val))
@ -2118,8 +2069,7 @@ impl Transaction {
let val = DefineTableStatement {
name: tb.to_owned().into(),
permissions: Permissions::none(),
changefeed: None,
..DefineTableStatement::default()
..Default::default()
};
self.put(key, &val).await?;
Ok(Arc::new(val))
@ -2183,16 +2133,16 @@ impl Transaction {
chn.send(bytes!("")).await?;
}
}
// Output LOGINS
// Output USERS
{
let dls = self.all_dl(ns, db).await?;
if !dls.is_empty() {
let dus = self.all_db_users(ns, db).await?;
if !dus.is_empty() {
chn.send(bytes!("-- ------------------------------")).await?;
chn.send(bytes!("-- LOGINS")).await?;
chn.send(bytes!("-- USERS")).await?;
chn.send(bytes!("-- ------------------------------")).await?;
chn.send(bytes!("")).await?;
for dl in dls.iter() {
chn.send(bytes!(format!("{dl};"))).await?;
for us in dus.iter() {
chn.send(bytes!(format!("{us};"))).await?;
}
chn.send(bytes!("")).await?;
}
@ -2385,6 +2335,13 @@ impl Transaction {
Ok(())
}
// change will record the change in the changefeed if enabled.
// To actually persist the record changes into the underlying kvs,
// you must call the `complete_changes` function and then commit the transaction.
pub(crate) fn clear_cache(&mut self) {
self.cache.clear()
}
// change will record the change in the changefeed if enabled.
// To actually persist the record changes into the underlying kvs,
// you must call the `complete_changes` function and then commit the transaction.
@ -2800,6 +2757,9 @@ mod tests {
let key2 = crate::key::namespace::us::new("ns", "user2");
let _ = txn.set(key1, data.to_owned()).await.unwrap();
let _ = txn.set(key2, data.to_owned()).await.unwrap();
txn.cache.clear();
let res = txn.all_ns_users("ns").await.unwrap();
assert_eq!(res.len(), 2);
@ -2808,7 +2768,7 @@ mod tests {
}
#[tokio::test]
async fn test_db_users() {
async fn test_all_db_users() {
let ds = Datastore::new("memory").await.unwrap();
let mut txn = ds.transaction(true, false).await.unwrap();
@ -2827,6 +2787,9 @@ mod tests {
let key2 = crate::key::database::us::new("ns", "db", "user2");
let _ = txn.set(key1, data.to_owned()).await.unwrap();
let _ = txn.set(key2, data.to_owned()).await.unwrap();
txn.cache.clear();
let res = txn.all_db_users("ns", "db").await.unwrap();
assert_eq!(res.len(), 2);

View file

@ -1,4 +1,3 @@
use crate::sql::comment::shouldbespace;
use crate::sql::common::{closeparentheses, commas, openparentheses};
use crate::sql::error::IResult;
use crate::sql::language::{language, Language};
@ -83,7 +82,5 @@ fn filter(i: &str) -> IResult<&str, Filter> {
}
pub(super) fn filters(i: &str) -> IResult<&str, Vec<Filter>> {
let (i, _) = tag_no_case("FILTERS")(i)?;
let (i, _) = shouldbespace(i)?;
separated_list1(commas, filter)(i)
}

View file

@ -11,10 +11,11 @@ use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[revisioned(revision = 1)]
pub enum Index {
/// (Basic) non unique
#[default]
Idx,
/// Unique index
Uniq,
@ -27,12 +28,6 @@ pub enum Index {
},
}
impl Default for Index {
fn default() -> Self {
Self::Idx
}
}
impl fmt::Display for Index {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -55,12 +50,7 @@ impl fmt::Display for Index {
}
pub fn index(i: &str) -> IResult<&str, Index> {
alt((unique, search, non_unique))(i)
}
pub fn non_unique(i: &str) -> IResult<&str, Index> {
let (i, _) = tag("")(i)?;
Ok((i, Index::Idx))
alt((unique, search))(i)
}
pub fn unique(i: &str) -> IResult<&str, Index> {

View file

@ -107,6 +107,7 @@ pub use self::id::Id;
pub use self::ident::Ident;
pub use self::idiom::Idiom;
pub use self::idiom::Idioms;
pub use self::index::Index;
pub use self::kind::Kind;
pub use self::limit::Limit;
pub use self::model::Model;
@ -124,6 +125,7 @@ pub use self::permission::Permissions;
pub use self::query::Query;
pub use self::range::Range;
pub use self::regex::Regex;
pub use self::scoring::Scoring;
pub use self::script::Script;
pub use self::split::Split;
pub use self::split::Splits;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,139 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::filter::{filters, Filter};
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::tokenizer::{tokenizers, Tokenizer};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineAnalyzerStatement {
pub name: Ident,
pub tokenizers: Option<Vec<Tokenizer>>,
pub filters: Option<Vec<Filter>>,
pub comment: Option<Strand>,
}
impl DefineAnalyzerStatement {
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Analyzer, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::az::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?;
// Release the transaction
drop(run); // Do we really need this?
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineAnalyzerStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE ANALYZER {}", self.name)?;
if let Some(v) = &self.tokenizers {
let tokens: Vec<String> = v.iter().map(|f| f.to_string()).collect();
write!(f, " TOKENIZERS {}", tokens.join(","))?;
}
if let Some(v) = &self.filters {
let tokens: Vec<String> = v.iter().map(|f| f.to_string()).collect();
write!(f, " FILTERS {}", tokens.join(","))?;
}
if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")?
}
Ok(())
}
}
pub fn analyzer(i: &str) -> IResult<&str, DefineAnalyzerStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ANALYZER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(analyzer_opts)(i)?;
// Create the base statement
let mut res = DefineAnalyzerStatement {
name,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineAnalyzerOption::Comment(v) => {
res.comment = Some(v);
}
DefineAnalyzerOption::Filters(v) => {
res.filters = Some(v);
}
DefineAnalyzerOption::Tokenizers(v) => {
res.tokenizers = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineAnalyzerOption {
Comment(Strand),
Filters(Vec<Filter>),
Tokenizers(Vec<Tokenizer>),
}
fn analyzer_opts(i: &str) -> IResult<&str, DefineAnalyzerOption> {
alt((analyzer_comment, analyzer_filters, analyzer_tokenizers))(i)
}
fn analyzer_comment(i: &str) -> IResult<&str, DefineAnalyzerOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineAnalyzerOption::Comment(v)))
}
fn analyzer_filters(i: &str) -> IResult<&str, DefineAnalyzerOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("FILTERS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = filters(i)?;
Ok((i, DefineAnalyzerOption::Filters(v)))
}
fn analyzer_tokenizers(i: &str) -> IResult<&str, DefineAnalyzerOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TOKENIZERS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = tokenizers(i)?;
Ok((i, DefineAnalyzerOption::Tokenizers(v)))
}

View file

@ -0,0 +1,145 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::changefeed::{changefeed, ChangeFeed};
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineDatabaseStatement {
pub id: Option<u32>,
pub name: Ident,
pub comment: Option<Strand>,
pub changefeed: Option<ChangeFeed>,
}
impl DefineDatabaseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Database, &Base::Ns)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::namespace::db::new(opt.ns(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?;
// Set the id
if self.id.is_none() && ns.id.is_some() {
let mut db = self.clone();
db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?);
// Store the db
run.set(key, db).await?;
} else {
// Store the db
run.set(key, self).await?;
}
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineDatabaseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE DATABASE {}", self.name)?;
if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")?
}
if let Some(ref v) = self.changefeed {
write!(f, " {v}")?;
}
Ok(())
}
}
pub fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(database_opts)(i)?;
// Create the base statement
let mut res = DefineDatabaseStatement {
name,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineDatabaseOption::Comment(v) => {
res.comment = Some(v);
}
DefineDatabaseOption::ChangeFeed(v) => {
res.changefeed = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineDatabaseOption {
Comment(Strand),
ChangeFeed(ChangeFeed),
}
fn database_opts(i: &str) -> IResult<&str, DefineDatabaseOption> {
alt((database_comment, database_changefeed))(i)
}
fn database_comment(i: &str) -> IResult<&str, DefineDatabaseOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineDatabaseOption::Comment(v)))
}
fn database_changefeed(i: &str) -> IResult<&str, DefineDatabaseOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = changefeed(i)?;
Ok((i, DefineDatabaseOption::ChangeFeed(v)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn define_database_with_changefeed() {
let sql = "DEFINE DATABASE mydatabase CHANGEFEED 1h";
let res = database(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out));
let serialized: Vec<u8> = (&out).try_into().unwrap();
let deserialized = DefineDatabaseStatement::try_from(&serialized).unwrap();
assert_eq!(out, deserialized);
}
}

View file

@ -0,0 +1,145 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{value, values, Value, Values};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::many0;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineEventStatement {
pub name: Ident,
pub what: Ident,
pub when: Value,
pub then: Values,
pub comment: Option<Strand>,
}
impl DefineEventStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Event, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name);
run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?;
run.set(key, self).await?;
// Clear the cache
let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineEventStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DEFINE EVENT {} ON {} WHEN {} THEN {}",
self.name, self.what, self.when, self.then
)
}
}
pub fn event(i: &str) -> IResult<&str, DefineEventStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("EVENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
let (i, opts) = many0(event_opts)(i)?;
// Create the base statement
let mut res = DefineEventStatement {
name,
what,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineEventOption::When(v) => {
res.when = v;
}
DefineEventOption::Then(v) => {
res.then = v;
}
DefineEventOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Check necessary options
if res.then.is_empty() {
// TODO throw error
}
// Return the statement
Ok((i, res))
}
enum DefineEventOption {
When(Value),
Then(Values),
Comment(Strand),
}
fn event_opts(i: &str) -> IResult<&str, DefineEventOption> {
alt((event_when, event_then, event_comment))(i)
}
fn event_when(i: &str) -> IResult<&str, DefineEventOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("WHEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineEventOption::When(v)))
}
fn event_then(i: &str) -> IResult<&str, DefineEventOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("THEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = values(i)?;
Ok((i, DefineEventOption::Then(v)))
}
fn event_comment(i: &str) -> IResult<&str, DefineEventOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineEventOption::Comment(v)))
}

View file

@ -0,0 +1,214 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::idiom;
use crate::sql::idiom::Idiom;
use crate::sql::kind::{kind, Kind};
use crate::sql::permission::{permissions, Permissions};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{value, Value};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::many0;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineFieldStatement {
pub name: Idiom,
pub what: Ident,
pub flex: bool,
pub kind: Option<Kind>,
pub value: Option<Value>,
pub assert: Option<Value>,
pub default: Option<Value>,
pub permissions: Permissions,
pub comment: Option<Strand>,
}
impl DefineFieldStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let fd = self.name.to_string();
let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd);
run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?;
run.set(key, self).await?;
// Clear the cache
let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineFieldStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE FIELD {} ON {}", self.name, self.what)?;
if self.flex {
write!(f, " FLEXIBLE")?
}
if let Some(ref v) = self.kind {
write!(f, " TYPE {v}")?
}
if let Some(ref v) = self.value {
write!(f, " VALUE {v}")?
}
if let Some(ref v) = self.assert {
write!(f, " ASSERT {v}")?
}
if !self.permissions.is_full() {
write!(f, " {}", self.permissions)?;
}
Ok(())
}
}
pub fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("FIELD")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = idiom::local(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
let (i, opts) = many0(field_opts)(i)?;
// Create the base statement
let mut res = DefineFieldStatement {
name,
what,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineFieldOption::Flex => {
res.flex = true;
}
DefineFieldOption::Kind(v) => {
res.kind = Some(v);
}
DefineFieldOption::Value(v) => {
res.value = Some(v);
}
DefineFieldOption::Assert(v) => {
res.assert = Some(v);
}
DefineFieldOption::Default(v) => {
res.default = Some(v);
}
DefineFieldOption::Comment(v) => {
res.comment = Some(v);
}
DefineFieldOption::Permissions(v) => {
res.permissions = v;
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineFieldOption {
Flex,
Kind(Kind),
Value(Value),
Assert(Value),
Default(Value),
Comment(Strand),
Permissions(Permissions),
}
fn field_opts(i: &str) -> IResult<&str, DefineFieldOption> {
alt((
field_flex,
field_kind,
field_value,
field_assert,
field_default,
field_comment,
field_permissions,
))(i)
}
fn field_flex(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("FLEXIBLE"), tag_no_case("FLEXI"), tag_no_case("FLEX")))(i)?;
Ok((i, DefineFieldOption::Flex))
}
fn field_kind(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TYPE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = kind(i)?;
Ok((i, DefineFieldOption::Kind(v)))
}
fn field_value(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("VALUE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineFieldOption::Value(v)))
}
fn field_assert(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ASSERT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineFieldOption::Assert(v)))
}
fn field_default(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("DEFAULT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineFieldOption::Default(v)))
}
fn field_comment(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineFieldOption::Comment(v)))
}
fn field_permissions(i: &str) -> IResult<&str, DefineFieldOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = permissions(i)?;
Ok((i, DefineFieldOption::Permissions(v)))
}

View file

@ -0,0 +1,137 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::block::{block, Block};
use crate::sql::comment::{mightbespace, shouldbespace};
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::ident;
use crate::sql::ident::{ident, Ident};
use crate::sql::kind::{kind, Kind};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use nom::multi::many0;
use nom::multi::separated_list0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineFunctionStatement {
pub name: Ident,
pub args: Vec<(Ident, Kind)>,
pub block: Block,
pub comment: Option<Strand>,
}
impl DefineFunctionStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::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)?;
if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")?
}
Ok(())
}
}
pub 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::multi(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)?;
let (i, opts) = many0(function_opts)(i)?;
// Create the base statement
let mut res = DefineFunctionStatement {
name,
args,
block,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineFunctionOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineFunctionOption {
Comment(Strand),
}
fn function_opts(i: &str) -> IResult<&str, DefineFunctionOption> {
function_comment(i)
}
fn function_comment(i: &str) -> IResult<&str, DefineFunctionOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineFunctionOption::Comment(v)))
}

View file

@ -0,0 +1,264 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::idiom;
use crate::sql::idiom::Idioms;
use crate::sql::index;
use crate::sql::index::Index;
use crate::sql::statements::UpdateStatement;
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{Value, Values};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::multi::many0;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineIndexStatement {
pub name: Ident,
pub what: Ident,
pub cols: Idioms,
pub index: Index,
pub comment: Option<Strand>,
}
impl DefineIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name);
run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?;
run.set(key, self).await?;
// Remove the index data
let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name);
run.delp(key, u32::MAX).await?;
// Clear the cache
let key = crate::key::table::ix::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Release the transaction
drop(run);
// Force queries to run
let opt = &opt.new_with_force(true);
// Don't process field queries
let opt = &opt.new_with_fields(false);
// Don't process event queries
let opt = &opt.new_with_events(false);
// Don't process table queries
let opt = &opt.new_with_tables(false);
// Update the index data
let stm = UpdateStatement {
what: Values(vec![Value::Table(self.what.clone().into())]),
..UpdateStatement::default()
};
stm.compute(ctx, opt, txn, doc).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineIndexStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE INDEX {} ON {} FIELDS {}", self.name, self.what, self.cols)?;
if Index::Idx != self.index {
write!(f, " {}", self.index)?;
}
Ok(())
}
}
pub fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("INDEX")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
let (i, opts) = many0(index_opts)(i)?;
// Create the base statement
let mut res = DefineIndexStatement {
name,
what,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineIndexOption::Index(v) => {
res.index = v;
}
DefineIndexOption::Columns(v) => {
res.cols = v;
}
DefineIndexOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Check necessary options
if res.cols.is_empty() {
// TODO throw error
}
// Return the statement
Ok((i, res))
}
enum DefineIndexOption {
Index(Index),
Columns(Idioms),
Comment(Strand),
}
fn index_opts(i: &str) -> IResult<&str, DefineIndexOption> {
alt((index_kind, index_columns, index_comment))(i)
}
fn index_kind(i: &str) -> IResult<&str, DefineIndexOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = index::index(i)?;
Ok((i, DefineIndexOption::Index(v)))
}
fn index_columns(i: &str) -> IResult<&str, DefineIndexOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("COLUMNS"), tag_no_case("FIELDS")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = idiom::locals(i)?;
Ok((i, DefineIndexOption::Columns(v)))
}
fn index_comment(i: &str) -> IResult<&str, DefineIndexOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineIndexOption::Comment(v)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::Ident;
use crate::sql::Idiom;
use crate::sql::Idioms;
use crate::sql::Index;
use crate::sql::Part;
use crate::sql::Scoring;
#[test]
fn check_create_non_unique_index() {
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col";
let (_, idx) = index(sql).unwrap();
assert_eq!(
idx,
DefineIndexStatement {
name: Ident("my_index".to_string()),
what: Ident("my_table".to_string()),
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Idx,
comment: None,
}
);
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col");
}
#[test]
fn check_create_unique_index() {
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col UNIQUE";
let (_, idx) = index(sql).unwrap();
assert_eq!(
idx,
DefineIndexStatement {
name: Ident("my_index".to_string()),
what: Ident("my_table".to_string()),
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Uniq,
comment: None,
}
);
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col UNIQUE");
}
#[test]
fn check_create_search_index_with_highlights() {
let sql = "DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) ORDER 1000 HIGHLIGHTS";
let (_, idx) = index(sql).unwrap();
assert_eq!(
idx,
DefineIndexStatement {
name: Ident("my_index".to_string()),
what: Ident("my_table".to_string()),
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Search {
az: Ident("my_analyzer".to_string()),
hl: true,
sc: Scoring::Bm {
k1: 1.2,
b: 0.75,
},
order: 1000
},
comment: None,
}
);
assert_eq!(idx.to_string(), "DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH ANALYZER my_analyzer BM25(1.2,0.75) ORDER 1000 HIGHLIGHTS");
}
#[test]
fn check_create_search_index() {
let sql =
"DEFINE INDEX my_index ON TABLE my_table COLUMNS my_col SEARCH ANALYZER my_analyzer VS";
let (_, idx) = index(sql).unwrap();
assert_eq!(
idx,
DefineIndexStatement {
name: Ident("my_index".to_string()),
what: Ident("my_table".to_string()),
cols: Idioms(vec![Idiom(vec![Part::Field(Ident("my_col".to_string()))])]),
index: Index::Search {
az: Ident("my_analyzer".to_string()),
hl: false,
sc: Scoring::Vs,
order: 100
},
comment: None,
}
);
assert_eq!(
idx.to_string(),
"DEFINE INDEX my_index ON my_table FIELDS my_col SEARCH ANALYZER my_analyzer VS ORDER 100"
);
}
}

View file

@ -0,0 +1,135 @@
mod analyzer;
mod database;
mod event;
mod field;
mod function;
mod index;
mod namespace;
mod param;
mod scope;
mod table;
mod token;
mod user;
pub use analyzer::{analyzer, DefineAnalyzerStatement};
pub use database::{database, DefineDatabaseStatement};
pub use event::{event, DefineEventStatement};
pub use field::{field, DefineFieldStatement};
pub use function::{function, DefineFunctionStatement};
pub use index::{index, DefineIndexStatement};
pub use namespace::{namespace, DefineNamespaceStatement};
pub use param::{param, DefineParamStatement};
pub use scope::{scope, DefineScopeStatement};
pub use table::{table, DefineTableStatement};
pub use token::{token, DefineTokenStatement};
pub use user::{user, DefineUserStatement};
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::error::IResult;
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::combinator::map;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub enum DefineStatement {
Namespace(DefineNamespaceStatement),
Database(DefineDatabaseStatement),
Function(DefineFunctionStatement),
Analyzer(DefineAnalyzerStatement),
Token(DefineTokenStatement),
Scope(DefineScopeStatement),
Param(DefineParamStatement),
Table(DefineTableStatement),
Event(DefineEventStatement),
Field(DefineFieldStatement),
Index(DefineIndexStatement),
User(DefineUserStatement),
}
impl DefineStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
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::Token(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Scope(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Param(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Table(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Event(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Field(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Index(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Analyzer(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::User(ref v) => v.compute(ctx, opt, txn, doc).await,
}
}
}
impl Display for DefineStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Namespace(v) => Display::fmt(v, f),
Self::Database(v) => Display::fmt(v, f),
Self::Function(v) => Display::fmt(v, f),
Self::User(v) => Display::fmt(v, f),
Self::Token(v) => Display::fmt(v, f),
Self::Scope(v) => Display::fmt(v, f),
Self::Param(v) => Display::fmt(v, f),
Self::Table(v) => Display::fmt(v, f),
Self::Event(v) => Display::fmt(v, f),
Self::Field(v) => Display::fmt(v, f),
Self::Index(v) => Display::fmt(v, f),
Self::Analyzer(v) => Display::fmt(v, f),
}
}
}
pub fn define(i: &str) -> IResult<&str, DefineStatement> {
alt((
map(namespace, DefineStatement::Namespace),
map(database, DefineStatement::Database),
map(function, DefineStatement::Function),
map(user, DefineStatement::User),
map(token, DefineStatement::Token),
map(scope, DefineStatement::Scope),
map(param, DefineStatement::Param),
map(table, DefineStatement::Table),
map(event, DefineStatement::Event),
map(field, DefineStatement::Field),
map(index, DefineStatement::Index),
map(analyzer, DefineStatement::Analyzer),
))(i)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::Ident;
#[test]
fn check_define_serialize() {
let stm = DefineStatement::Namespace(DefineNamespaceStatement {
name: Ident::from("test"),
..Default::default()
});
let enc: Vec<u8> = stm.try_into().unwrap();
assert_eq!(11, enc.len());
}
}

View file

@ -0,0 +1,108 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineNamespaceStatement {
pub id: Option<u32>,
pub name: Ident,
pub comment: Option<Strand>,
}
impl DefineNamespaceStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Namespace, &Base::Root)?;
// Process the statement
let key = crate::key::root::ns::new(&self.name);
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Set the id
if self.id.is_none() {
let mut ns = self.clone();
ns.id = Some(run.get_next_ns_id().await?);
run.set(key, ns).await?;
} else {
run.set(key, self).await?;
}
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineNamespaceStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE NAMESPACE {}", self.name)?;
if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")?
}
Ok(())
}
}
pub fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(namespace_opts)(i)?;
// Create the base statement
let mut res = DefineNamespaceStatement {
name,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineNamespaceOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineNamespaceOption {
Comment(Strand),
}
fn namespace_opts(i: &str) -> IResult<&str, DefineNamespaceOption> {
namespace_comment(i)
}
fn namespace_comment(i: &str) -> IResult<&str, DefineNamespaceOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineNamespaceOption::Comment(v)))
}

View file

@ -0,0 +1,117 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{value, Value};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineParamStatement {
pub name: Ident,
pub value: Value,
pub comment: Option<Strand>,
}
impl DefineParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Parameter, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::pa::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 Display for DefineParamStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE PARAM ${} VALUE {}", self.name, self.value)
}
}
pub fn param(i: &str) -> IResult<&str, DefineParamStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("PARAM")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = char('$')(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(param_opts)(i)?;
// Create the base statement
let mut res = DefineParamStatement {
name,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineParamOption::Value(v) => {
res.value = v;
}
DefineParamOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Check necessary options
if res.value.is_none() {
// TODO throw error
}
// Return the statement
Ok((i, res))
}
enum DefineParamOption {
Value(Value),
Comment(Strand),
}
fn param_opts(i: &str) -> IResult<&str, DefineParamOption> {
alt((param_value, param_comment))(i)
}
fn param_value(i: &str) -> IResult<&str, DefineParamOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("VALUE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineParamOption::Value(v)))
}
fn param_comment(i: &str) -> IResult<&str, DefineParamOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineParamOption::Comment(v)))
}

View file

@ -0,0 +1,156 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::duration::{duration, Duration};
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{value, Value};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use rand::distributions::Alphanumeric;
use rand::Rng;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineScopeStatement {
pub name: Ident,
pub code: String,
pub session: Option<Duration>,
pub signup: Option<Value>,
pub signin: Option<Value>,
pub comment: Option<Strand>,
}
impl DefineScopeStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Scope, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::sc::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 Display for DefineScopeStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE SCOPE {}", self.name)?;
if let Some(ref v) = self.session {
write!(f, " SESSION {v}")?
}
if let Some(ref v) = self.signup {
write!(f, " SIGNUP {v}")?
}
if let Some(ref v) = self.signin {
write!(f, " SIGNIN {v}")?
}
Ok(())
}
}
pub fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SCOPE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(scope_opts)(i)?;
// Create the base statement
let mut res = DefineScopeStatement {
name,
code: rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(128)
.map(char::from)
.collect::<String>(),
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineScopeOption::Session(v) => {
res.session = Some(v);
}
DefineScopeOption::Signup(v) => {
res.signup = Some(v);
}
DefineScopeOption::Signin(v) => {
res.signin = Some(v);
}
DefineScopeOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineScopeOption {
Session(Duration),
Signup(Value),
Signin(Value),
Comment(Strand),
}
fn scope_opts(i: &str) -> IResult<&str, DefineScopeOption> {
alt((scope_session, scope_signup, scope_signin, scope_comment))(i)
}
fn scope_session(i: &str) -> IResult<&str, DefineScopeOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SESSION")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = duration(i)?;
Ok((i, DefineScopeOption::Session(v)))
}
fn scope_signup(i: &str) -> IResult<&str, DefineScopeOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SIGNUP")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineScopeOption::Signup(v)))
}
fn scope_signin(i: &str) -> IResult<&str, DefineScopeOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SIGNIN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = value(i)?;
Ok((i, DefineScopeOption::Signin(v)))
}
fn scope_comment(i: &str) -> IResult<&str, DefineScopeOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineScopeOption::Comment(v)))
}

View file

@ -0,0 +1,260 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::base::Base;
use crate::sql::changefeed::{changefeed, ChangeFeed};
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::fmt::is_pretty;
use crate::sql::fmt::pretty_indent;
use crate::sql::ident::{ident, Ident};
use crate::sql::permission::{permissions, Permissions};
use crate::sql::statements::UpdateStatement;
use crate::sql::strand::{strand, Strand};
use crate::sql::value::{Value, Values};
use crate::sql::view::{view, View};
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Write};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineTableStatement {
pub id: Option<u32>,
pub name: Ident,
pub drop: bool,
pub full: bool,
pub view: Option<View>,
pub permissions: Permissions,
pub changefeed: Option<ChangeFeed>,
pub comment: Option<Strand>,
}
impl DefineTableStatement {
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name);
let ns = run.add_ns(opt.ns(), opt.strict).await?;
let db = run.add_db(opt.ns(), opt.db(), opt.strict).await?;
if self.id.is_none() && ns.id.is_some() && db.id.is_some() {
let mut tb = self.clone();
tb.id = Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?);
run.set(key, tb).await?;
} else {
run.set(key, self).await?;
}
// Check if table is a view
if let Some(view) = &self.view {
// Remove the table data
let key = crate::key::table::all::new(opt.ns(), opt.db(), &self.name);
run.delp(key, u32::MAX).await?;
// Process each foreign table
for v in view.what.0.iter() {
// Save the view config
let key = crate::key::table::ft::new(opt.ns(), opt.db(), v, &self.name);
run.set(key, self).await?;
// Clear the cache
let key = crate::key::table::ft::prefix(opt.ns(), opt.db(), v);
run.clr(key).await?;
}
// Release the transaction
drop(run);
// Force queries to run
let opt = &opt.new_with_force(true);
// Don't process field queries
let opt = &opt.new_with_fields(false);
// Don't process event queries
let opt = &opt.new_with_events(false);
// Don't process index queries
let opt = &opt.new_with_indexes(false);
// Process each foreign table
for v in view.what.0.iter() {
// Process the view data
let stm = UpdateStatement {
what: Values(vec![Value::Table(v.clone())]),
..UpdateStatement::default()
};
stm.compute(ctx, opt, txn, doc).await?;
}
}
// Ok all good
Ok(Value::None)
}
}
impl Display for DefineTableStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TABLE {}", self.name)?;
if self.drop {
f.write_str(" DROP")?;
}
f.write_str(if self.full {
" SCHEMAFULL"
} else {
" SCHEMALESS"
})?;
if let Some(ref v) = self.view {
write!(f, " {v}")?
}
if let Some(ref v) = self.changefeed {
write!(f, " {v}")?;
}
if !self.permissions.is_full() {
let _indent = if is_pretty() {
Some(pretty_indent())
} else {
f.write_char(' ')?;
None
};
write!(f, "{}", self.permissions)?;
}
Ok(())
}
}
pub fn table(i: &str) -> IResult<&str, DefineTableStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TABLE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, opts) = many0(table_opts)(i)?;
// Create the base statement
let mut res = DefineTableStatement {
name,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineTableOption::Drop => {
res.drop = true;
}
DefineTableOption::Schemafull => {
res.full = true;
}
DefineTableOption::Schemaless => {
res.full = false;
}
DefineTableOption::View(v) => {
res.view = Some(v);
}
DefineTableOption::Comment(v) => {
res.comment = Some(v);
}
DefineTableOption::ChangeFeed(v) => {
res.changefeed = Some(v);
}
DefineTableOption::Permissions(v) => {
res.permissions = v;
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineTableOption {
Drop,
View(View),
Schemaless,
Schemafull,
Comment(Strand),
Permissions(Permissions),
ChangeFeed(ChangeFeed),
}
fn table_opts(i: &str) -> IResult<&str, DefineTableOption> {
alt((
table_drop,
table_view,
table_comment,
table_schemaless,
table_schemafull,
table_permissions,
table_changefeed,
))(i)
}
fn table_drop(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("DROP")(i)?;
Ok((i, DefineTableOption::Drop))
}
fn table_changefeed(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = changefeed(i)?;
Ok((i, DefineTableOption::ChangeFeed(v)))
}
fn table_view(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = view(i)?;
Ok((i, DefineTableOption::View(v)))
}
fn table_schemaless(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SCHEMALESS")(i)?;
Ok((i, DefineTableOption::Schemaless))
}
fn table_schemafull(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("SCHEMAFULL"), tag_no_case("SCHEMAFUL")))(i)?;
Ok((i, DefineTableOption::Schemafull))
}
fn table_comment(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineTableOption::Comment(v)))
}
fn table_permissions(i: &str) -> IResult<&str, DefineTableOption> {
let (i, _) = shouldbespace(i)?;
let (i, v) = permissions(i)?;
Ok((i, DefineTableOption::Permissions(v)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn define_table_with_changefeed() {
let sql = "DEFINE TABLE mytable SCHEMALESS CHANGEFEED 1h";
let res = table(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(sql, format!("{}", out));
let serialized: Vec<u8> = (&out).try_into().unwrap();
let deserialized = DefineTableStatement::try_from(&serialized).unwrap();
assert_eq!(out, deserialized);
}
}

View file

@ -0,0 +1,179 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::sql::algorithm::{algorithm, Algorithm};
use crate::sql::base::{base_or_scope, Base};
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::escape::quote_str;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, strand_raw, Strand};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineTokenStatement {
pub name: Ident,
pub base: Base,
pub kind: Algorithm,
pub code: String,
pub comment: Option<Strand>,
}
impl DefineTokenStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match &self.base {
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::namespace::tk::new(opt.ns(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?;
run.set(key, self).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::tk::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)
}
Base::Sc(sc) => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name);
run.add_ns(opt.ns(), opt.strict).await?;
run.add_db(opt.ns(), opt.db(), opt.strict).await?;
run.add_sc(opt.ns(), opt.db(), sc, opt.strict).await?;
run.set(key, self).await?;
// Ok all good
Ok(Value::None)
}
// Other levels are not supported
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for DefineTokenStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DEFINE TOKEN {} ON {} TYPE {} VALUE {}",
self.name,
self.base,
self.kind,
quote_str(&self.code)
)?;
if let Some(ref v) = self.comment {
write!(f, " COMMENT {v}")?
}
Ok(())
}
}
pub fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TOKEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base_or_scope(i)?;
let (i, opts) = many0(token_opts)(i)?;
// Create the base statement
let mut res = DefineTokenStatement {
name,
base,
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineTokenOption::Type(v) => {
res.kind = v;
}
DefineTokenOption::Value(v) => {
res.code = v;
}
DefineTokenOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Check necessary options
if res.code.is_empty() {
// TODO throw error
}
// Return the statement
Ok((i, res))
}
enum DefineTokenOption {
Type(Algorithm),
Value(String),
Comment(Strand),
}
fn token_opts(i: &str) -> IResult<&str, DefineTokenOption> {
alt((token_type, token_value, token_comment))(i)
}
fn token_type(i: &str) -> IResult<&str, DefineTokenOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TYPE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = algorithm(i)?;
Ok((i, DefineTokenOption::Type(v)))
}
fn token_value(i: &str) -> IResult<&str, DefineTokenOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("VALUE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand_raw(i)?;
Ok((i, DefineTokenOption::Value(v)))
}
fn token_comment(i: &str) -> IResult<&str, DefineTokenOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineTokenOption::Comment(v)))
}

View file

@ -0,0 +1,230 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::Action;
use crate::iam::ResourceKind;
use crate::iam::Role;
use crate::sql::base::{base, Base};
use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::error::Error as SqlError;
use crate::sql::error::IResult;
use crate::sql::escape::quote_str;
use crate::sql::fmt::Fmt;
use crate::sql::ident::{ident, Ident};
use crate::sql::strand::{strand, strand_raw, Strand};
use crate::sql::value::Value;
use argon2::password_hash::{PasswordHasher, SaltString};
use argon2::Argon2;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::many0;
use nom::multi::separated_list0;
use nom::Err::Failure;
use rand::distributions::Alphanumeric;
use rand::rngs::OsRng;
use rand::Rng;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct DefineUserStatement {
pub name: Ident,
pub base: Base,
pub hash: String,
pub code: String,
pub roles: Vec<Ident>,
pub comment: Option<Strand>,
}
impl From<(Base, &str, &str)> for DefineUserStatement {
fn from((base, user, pass): (Base, &str, &str)) -> Self {
DefineUserStatement {
base,
name: user.into(),
hash: Argon2::default()
.hash_password(pass.as_ref(), &SaltString::generate(&mut OsRng))
.unwrap()
.to_string(),
code: rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(128)
.map(char::from)
.collect::<String>(),
roles: vec!["owner".into()],
comment: None,
}
}
}
impl DefineUserStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match self.base {
Base::Root => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::root::us::new(&self.name);
run.set(key, self).await?;
// Ok all good
Ok(Value::None)
}
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::namespace::us::new(opt.ns(), &self.name);
run.add_ns(opt.ns(), opt.strict).await?;
run.set(key, self).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::database::us::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)
}
// Other levels are not supported
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for DefineUserStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"DEFINE USER {} ON {} PASSHASH {} ROLES {}",
self.name,
self.base,
quote_str(&self.hash),
Fmt::comma_separated(
&self.roles.iter().map(|r| r.to_string().to_uppercase()).collect::<Vec<String>>()
)
)
}
}
pub fn user(i: &str) -> IResult<&str, DefineUserStatement> {
let (i, _) = tag_no_case("DEFINE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("USER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base(i)?;
let (i, opts) = user_opts(i)?;
// Create the base statement
let mut res = DefineUserStatement {
name,
base,
roles: vec!["Viewer".into()], // New users get the viewer role by default
code: rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(128)
.map(char::from)
.collect::<String>(),
..Default::default()
};
// Assign any defined options
for opt in opts {
match opt {
DefineUserOption::Password(v) => {
res.hash = Argon2::default()
.hash_password(v.as_ref(), &SaltString::generate(&mut OsRng))
.unwrap()
.to_string()
}
DefineUserOption::Passhash(v) => {
res.hash = v;
}
DefineUserOption::Roles(v) => {
res.roles = v;
}
DefineUserOption::Comment(v) => {
res.comment = Some(v);
}
}
}
// Return the statement
Ok((i, res))
}
enum DefineUserOption {
Password(String),
Passhash(String),
Roles(Vec<Ident>),
Comment(Strand),
}
fn user_opts(i: &str) -> IResult<&str, Vec<DefineUserOption>> {
many0(alt((alt((user_pass, user_hash)), user_roles, user_comment)))(i)
}
fn user_pass(i: &str) -> IResult<&str, DefineUserOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("PASSWORD")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand_raw(i)?;
Ok((i, DefineUserOption::Password(v)))
}
fn user_hash(i: &str) -> IResult<&str, DefineUserOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("PASSHASH")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand_raw(i)?;
Ok((i, DefineUserOption::Passhash(v)))
}
fn user_comment(i: &str) -> IResult<&str, DefineUserOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("COMMENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, v) = strand(i)?;
Ok((i, DefineUserOption::Comment(v)))
}
fn user_roles(i: &str) -> IResult<&str, DefineUserOption> {
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ROLES")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, roles) = separated_list0(commas, |i| {
let (i, v) = ident(i)?;
// Verify the role is valid
Role::try_from(v.as_str()).map_err(|_| Failure(SqlError::Role(i, v.to_string())))?;
Ok((i, v))
})(i)?;
Ok((i, DefineUserOption::Roles(roles)))
}

View file

@ -77,12 +77,6 @@ impl InfoStatement {
tmp.insert(v.name.to_string(), v.to_string().into());
}
res.insert("databases".to_owned(), tmp.into());
// Process the logins
let mut tmp = Object::default();
for v in run.all_nl(opt.ns()).await?.iter() {
tmp.insert(v.name.to_string(), v.to_string().into());
}
res.insert("logins".to_owned(), tmp.into());
// Process the users
let mut tmp = Object::default();
for v in run.all_ns_users(opt.ns()).await?.iter() {
@ -105,12 +99,6 @@ impl InfoStatement {
let mut run = txn.lock().await;
// Create the result set
let mut res = Object::default();
// Process the logins
let mut tmp = Object::default();
for v in run.all_dl(opt.ns(), opt.db()).await?.iter() {
tmp.insert(v.name.to_string(), v.to_string().into());
}
res.insert("logins".to_owned(), tmp.into());
// Process the users
let mut tmp = Object::default();
for v in run.all_db_users(opt.ns(), opt.db()).await?.iter() {

View file

@ -53,7 +53,6 @@ pub use self::define::DefineEventStatement;
pub use self::define::DefineFieldStatement;
pub use self::define::DefineFunctionStatement;
pub use self::define::DefineIndexStatement;
pub use self::define::DefineLoginStatement;
pub use self::define::DefineNamespaceStatement;
pub use self::define::DefineParamStatement;
pub use self::define::DefineScopeStatement;
@ -67,7 +66,6 @@ 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;
pub use self::remove::RemoveParamStatement;
pub use self::remove::RemoveScopeStatement;

View file

@ -1,908 +0,0 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::{base, base_or_scope, Base};
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};
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub enum RemoveStatement {
Namespace(RemoveNamespaceStatement),
Database(RemoveDatabaseStatement),
Function(RemoveFunctionStatement),
Analyzer(RemoveAnalyzerStatement),
Login(RemoveLoginStatement),
Token(RemoveTokenStatement),
Scope(RemoveScopeStatement),
Param(RemoveParamStatement),
Table(RemoveTableStatement),
Event(RemoveEventStatement),
Field(RemoveFieldStatement),
Index(RemoveIndexStatement),
User(RemoveUserStatement),
}
impl RemoveStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
match self {
Self::Namespace(ref v) => v.compute(ctx, opt, txn).await,
Self::Database(ref v) => v.compute(ctx, opt, txn).await,
Self::Function(ref v) => v.compute(ctx, opt, txn).await,
Self::Login(ref v) => v.compute(ctx, opt, txn).await,
Self::Token(ref v) => v.compute(ctx, opt, txn).await,
Self::Scope(ref v) => v.compute(ctx, opt, txn).await,
Self::Param(ref v) => v.compute(ctx, opt, txn).await,
Self::Table(ref v) => v.compute(ctx, opt, txn).await,
Self::Event(ref v) => v.compute(ctx, opt, txn).await,
Self::Field(ref v) => v.compute(ctx, opt, txn).await,
Self::Index(ref v) => v.compute(ctx, opt, txn).await,
Self::Analyzer(ref v) => v.compute(ctx, opt, txn).await,
Self::User(ref v) => v.compute(ctx, opt, txn).await,
}
}
}
impl Display for RemoveStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
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),
Self::Param(v) => Display::fmt(v, f),
Self::Table(v) => Display::fmt(v, f),
Self::Event(v) => Display::fmt(v, f),
Self::Field(v) => Display::fmt(v, f),
Self::Index(v) => Display::fmt(v, f),
Self::Analyzer(v) => Display::fmt(v, f),
Self::User(v) => Display::fmt(v, f),
}
}
}
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),
map(param, RemoveStatement::Param),
map(table, RemoveStatement::Table),
map(event, RemoveStatement::Event),
map(field, RemoveStatement::Field),
map(index, RemoveStatement::Index),
map(analyzer, RemoveStatement::Analyzer),
map(user, RemoveStatement::User),
))(i)
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveNamespaceStatement {
pub name: Ident,
}
impl RemoveNamespaceStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Namespace, &Base::Root)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::root::ns::new(&self.name);
run.del(key).await?;
// Delete the resource data
let key = crate::key::namespace::all::new(&self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveNamespaceStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE NAMESPACE {}", self.name)
}
}
fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveNamespaceStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveDatabaseStatement {
pub name: Ident,
}
impl RemoveDatabaseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Database, &Base::Ns)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::namespace::db::new(opt.ns(), &self.name);
run.del(key).await?;
// Delete the resource data
let key = crate::key::database::all::new(opt.ns(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveDatabaseStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE DATABASE {}", self.name)
}
}
fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveDatabaseStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveFunctionStatement {
pub name: Ident,
}
impl RemoveFunctionStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl 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)]
#[revisioned(revision = 1)]
pub struct RemoveAnalyzerStatement {
pub name: Ident,
}
impl RemoveAnalyzerStatement {
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Analyzer, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// TODO Check that the analyzer is not used in any schema
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveAnalyzerStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE ANALYZER {}", self.name)
}
}
fn analyzer(i: &str) -> IResult<&str, RemoveAnalyzerStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ANALYZER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveAnalyzerStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveLoginStatement {
pub name: Ident,
pub base: Base,
}
impl RemoveLoginStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match self.base {
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::namespace::lg::new(opt.ns(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::lg::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for RemoveLoginStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE LOGIN {} ON {}", self.name, self.base)
}
}
fn login(i: &str) -> IResult<&str, RemoveLoginStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("LOGIN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base(i)?;
Ok((
i,
RemoveLoginStatement {
name,
base,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveTokenStatement {
pub name: Ident,
pub base: Base,
}
impl RemoveTokenStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match &self.base {
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::namespace::tk::new(opt.ns(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Sc(sc) => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for RemoveTokenStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE TOKEN {} ON {}", self.name, self.base)
}
}
fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TOKEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base_or_scope(i)?;
Ok((
i,
RemoveTokenStatement {
name,
base,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveScopeStatement {
pub name: Ident,
}
impl RemoveScopeStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Scope, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Remove the resource data
let key = crate::key::scope::all::new(opt.ns(), opt.db(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveScopeStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE SCOPE {}", self.name)
}
}
fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SCOPE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveScopeStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveParamStatement {
pub name: Ident,
}
impl RemoveParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Parameter, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveParamStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE PARAM {}", self.name)
}
}
fn param(i: &str) -> IResult<&str, RemoveParamStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("PARAM")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = char('$')(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveParamStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveTableStatement {
pub name: Ident,
}
impl RemoveTableStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Remove the resource data
let key = crate::key::table::all::new(opt.ns(), opt.db(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveTableStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE TABLE {}", self.name)
}
}
fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TABLE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveTableStatement {
name,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveEventStatement {
pub name: Ident,
pub what: Ident,
}
impl RemoveEventStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Event, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name);
run.del(key).await?;
// Clear the cache
let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveEventStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE EVENT {} ON {}", self.name, self.what)
}
}
fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("EVENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveEventStatement {
name,
what,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveFieldStatement {
pub name: Idiom,
pub what: Ident,
}
impl RemoveFieldStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let fd = self.name.to_string();
let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd);
run.del(key).await?;
// Clear the cache
let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveFieldStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE FIELD {} ON {}", self.name, self.what)
}
}
fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("FIELD")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = idiom::local(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveFieldStatement {
name,
what,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveIndexStatement {
pub name: Ident,
pub what: Ident,
}
impl RemoveIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name);
run.del(key).await?;
// Remove the index data
let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name);
run.delp(key, u32::MAX).await?;
// Clear the cache
let key = crate::key::table::ix::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveIndexStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE INDEX {} ON {}", self.name, self.what)
}
}
fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("INDEX")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveIndexStatement {
name,
what,
},
))
}
// --------------------------------------------------
// --------------------------------------------------
// --------------------------------------------------
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveUserStatement {
pub name: Ident,
pub base: Base,
}
impl RemoveUserStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match self.base {
Base::Root => {
// Claim transaction
let mut run = txn.lock().await;
// Process the statement
let key = crate::key::root::us::new(&self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::namespace::us::new(opt.ns(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Delete the definition
let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for RemoveUserStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE USER {} ON {}", self.name, self.base)
}
}
fn user(i: &str) -> IResult<&str, RemoveUserStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("USER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base(i)?;
Ok((
i,
RemoveUserStatement {
name,
base,
},
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_remove_serialize() {
let stm = RemoveStatement::Namespace(RemoveNamespaceStatement {
name: Ident::from("test"),
});
let enc: Vec<u8> = stm.try_into().unwrap();
assert_eq!(9, enc.len());
}
}

View file

@ -0,0 +1,63 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveAnalyzerStatement {
pub name: Ident,
}
impl RemoveAnalyzerStatement {
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Analyzer, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// TODO Check that the analyzer is not used in any schema
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveAnalyzerStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE ANALYZER {}", self.name)
}
}
pub fn analyzer(i: &str) -> IResult<&str, RemoveAnalyzerStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ANALYZER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveAnalyzerStatement {
name,
},
))
}

View file

@ -0,0 +1,67 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveDatabaseStatement {
pub name: Ident,
}
impl RemoveDatabaseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Database, &Base::Ns)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::namespace::db::new(opt.ns(), &self.name);
run.del(key).await?;
// Delete the resource data
let key = crate::key::database::all::new(opt.ns(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveDatabaseStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE DATABASE {}", self.name)
}
}
pub fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("DB"), tag_no_case("DATABASE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveDatabaseStatement {
name,
},
))
}

View file

@ -0,0 +1,75 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveEventStatement {
pub name: Ident,
pub what: Ident,
}
impl RemoveEventStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Event, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name);
run.del(key).await?;
// Clear the cache
let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveEventStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE EVENT {} ON {}", self.name, self.what)
}
}
pub fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("EVENT")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveEventStatement {
name,
what,
},
))
}

View file

@ -0,0 +1,78 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
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::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveFieldStatement {
pub name: Idiom,
pub what: Ident,
}
impl RemoveFieldStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let fd = self.name.to_string();
let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd);
run.del(key).await?;
// Clear the cache
let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveFieldStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE FIELD {} ON {}", self.name, self.what)
}
}
pub fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("FIELD")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = idiom::local(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveFieldStatement {
name,
what,
},
))
}

View file

@ -0,0 +1,75 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::{mightbespace, shouldbespace};
use crate::sql::error::IResult;
use crate::sql::ident;
use crate::sql::ident::Ident;
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use nom::combinator::opt;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveFunctionStatement {
pub name: Ident,
}
impl RemoveFunctionStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveFunctionStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "REMOVE FUNCTION fn::{}", self.name)
}
}
pub 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,
},
))
}

View file

@ -0,0 +1,78 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use nom::combinator::opt;
use nom::sequence::tuple;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveIndexStatement {
pub name: Ident,
pub what: Ident,
}
impl RemoveIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name);
run.del(key).await?;
// Remove the index data
let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name);
run.delp(key, u32::MAX).await?;
// Clear the cache
let key = crate::key::table::ix::prefix(opt.ns(), opt.db(), &self.what);
run.clr(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveIndexStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE INDEX {} ON {}", self.name, self.what)
}
}
pub fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("INDEX")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = opt(tuple((shouldbespace, tag_no_case("TABLE"))))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, what) = ident(i)?;
Ok((
i,
RemoveIndexStatement {
name,
what,
},
))
}

View file

@ -0,0 +1,135 @@
mod analyzer;
mod database;
mod event;
mod field;
mod function;
mod index;
mod namespace;
mod param;
mod scope;
mod table;
mod token;
mod user;
pub use analyzer::{analyzer, RemoveAnalyzerStatement};
pub use database::{database, RemoveDatabaseStatement};
pub use event::{event, RemoveEventStatement};
pub use field::{field, RemoveFieldStatement};
pub use function::{function, RemoveFunctionStatement};
pub use index::{index, RemoveIndexStatement};
pub use namespace::{namespace, RemoveNamespaceStatement};
pub use param::{param, RemoveParamStatement};
pub use scope::{scope, RemoveScopeStatement};
pub use table::{table, RemoveTableStatement};
pub use token::{token, RemoveTokenStatement};
pub use user::{user, RemoveUserStatement};
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::error::IResult;
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::combinator::map;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub enum RemoveStatement {
Namespace(RemoveNamespaceStatement),
Database(RemoveDatabaseStatement),
Function(RemoveFunctionStatement),
Analyzer(RemoveAnalyzerStatement),
Token(RemoveTokenStatement),
Scope(RemoveScopeStatement),
Param(RemoveParamStatement),
Table(RemoveTableStatement),
Event(RemoveEventStatement),
Field(RemoveFieldStatement),
Index(RemoveIndexStatement),
User(RemoveUserStatement),
}
impl RemoveStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
match self {
Self::Namespace(ref v) => v.compute(ctx, opt, txn).await,
Self::Database(ref v) => v.compute(ctx, opt, txn).await,
Self::Function(ref v) => v.compute(ctx, opt, txn).await,
Self::Token(ref v) => v.compute(ctx, opt, txn).await,
Self::Scope(ref v) => v.compute(ctx, opt, txn).await,
Self::Param(ref v) => v.compute(ctx, opt, txn).await,
Self::Table(ref v) => v.compute(ctx, opt, txn).await,
Self::Event(ref v) => v.compute(ctx, opt, txn).await,
Self::Field(ref v) => v.compute(ctx, opt, txn).await,
Self::Index(ref v) => v.compute(ctx, opt, txn).await,
Self::Analyzer(ref v) => v.compute(ctx, opt, txn).await,
Self::User(ref v) => v.compute(ctx, opt, txn).await,
}
}
}
impl Display for RemoveStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Namespace(v) => Display::fmt(v, f),
Self::Database(v) => Display::fmt(v, f),
Self::Function(v) => Display::fmt(v, f),
Self::Token(v) => Display::fmt(v, f),
Self::Scope(v) => Display::fmt(v, f),
Self::Param(v) => Display::fmt(v, f),
Self::Table(v) => Display::fmt(v, f),
Self::Event(v) => Display::fmt(v, f),
Self::Field(v) => Display::fmt(v, f),
Self::Index(v) => Display::fmt(v, f),
Self::Analyzer(v) => Display::fmt(v, f),
Self::User(v) => Display::fmt(v, f),
}
}
}
pub fn remove(i: &str) -> IResult<&str, RemoveStatement> {
alt((
map(namespace, RemoveStatement::Namespace),
map(database, RemoveStatement::Database),
map(function, RemoveStatement::Function),
map(token, RemoveStatement::Token),
map(scope, RemoveStatement::Scope),
map(param, RemoveStatement::Param),
map(table, RemoveStatement::Table),
map(event, RemoveStatement::Event),
map(field, RemoveStatement::Field),
map(index, RemoveStatement::Index),
map(analyzer, RemoveStatement::Analyzer),
map(user, RemoveStatement::User),
))(i)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::Ident;
#[test]
fn check_remove_serialize() {
let stm = RemoveStatement::Namespace(RemoveNamespaceStatement {
name: Ident::from("test"),
..Default::default()
});
let enc: Vec<u8> = stm.try_into().unwrap();
assert_eq!(9, enc.len());
}
}

View file

@ -0,0 +1,67 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveNamespaceStatement {
pub name: Ident,
}
impl RemoveNamespaceStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Namespace, &Base::Root)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::root::ns::new(&self.name);
run.del(key).await?;
// Delete the resource data
let key = crate::key::namespace::all::new(&self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveNamespaceStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE NAMESPACE {}", self.name)
}
}
pub fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = alt((tag_no_case("NS"), tag_no_case("NAMESPACE")))(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveNamespaceStatement {
name,
},
))
}

View file

@ -0,0 +1,65 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveParamStatement {
pub name: Ident,
}
impl RemoveParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Parameter, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveParamStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE PARAM {}", self.name)
}
}
pub fn param(i: &str) -> IResult<&str, RemoveParamStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("PARAM")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = char('$')(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveParamStatement {
name,
},
))
}

View file

@ -0,0 +1,66 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveScopeStatement {
pub name: Ident,
}
impl RemoveScopeStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Scope, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Remove the resource data
let key = crate::key::scope::all::new(opt.ns(), opt.db(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveScopeStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE SCOPE {}", self.name)
}
}
pub fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("SCOPE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveScopeStatement {
name,
},
))
}

View file

@ -0,0 +1,66 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::Base;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveTableStatement {
pub name: Ident,
}
impl RemoveTableStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?;
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Remove the resource data
let key = crate::key::table::all::new(opt.ns(), opt.db(), &self.name);
run.delp(key, u32::MAX).await?;
// Ok all good
Ok(Value::None)
}
}
impl Display for RemoveTableStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE TABLE {}", self.name)
}
}
pub fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TABLE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
Ok((
i,
RemoveTableStatement {
name,
},
))
}

View file

@ -0,0 +1,97 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::{base_or_scope, Base};
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveTokenStatement {
pub name: Ident,
pub base: Base,
}
impl RemoveTokenStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match &self.base {
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::namespace::tk::new(opt.ns(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Sc(sc) => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for RemoveTokenStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE TOKEN {} ON {}", self.name, self.base)
}
}
pub fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("TOKEN")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base_or_scope(i)?;
Ok((
i,
RemoveTokenStatement {
name,
base,
},
))
}

View file

@ -0,0 +1,97 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::base::{base, Base};
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::ident::{ident, Ident};
use crate::sql::value::Value;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
#[revisioned(revision = 1)]
pub struct RemoveUserStatement {
pub name: Ident,
pub base: Base,
}
impl RemoveUserStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
) -> Result<Value, Error> {
// Allowed to run?
opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
match self.base {
Base::Root => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Process the statement
let key = crate::key::root::us::new(&self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Ns => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::namespace::us::new(opt.ns(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
Base::Db => {
// Claim transaction
let mut run = txn.lock().await;
// Clear the cache
run.clear_cache();
// Delete the definition
let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
run.del(key).await?;
// Ok all good
Ok(Value::None)
}
_ => Err(Error::InvalidLevel(self.base.to_string())),
}
}
}
impl Display for RemoveUserStatement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "REMOVE USER {} ON {}", self.name, self.base)
}
}
pub fn user(i: &str) -> IResult<&str, RemoveUserStatement> {
let (i, _) = tag_no_case("REMOVE")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("USER")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, name) = ident(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("ON")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, base) = base(i)?;
Ok((
i,
RemoveUserStatement {
name,
base,
},
))
}

View file

@ -1,4 +1,3 @@
use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use nom::branch::alt;
@ -41,8 +40,5 @@ fn tokenizer(i: &str) -> IResult<&str, Tokenizer> {
}
pub(super) fn tokenizers(i: &str) -> IResult<&str, Vec<Tokenizer>> {
let (i, _) = tag_no_case("TOKENIZERS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, t) = separated_list1(commas, tokenizer)(i)?;
Ok((i, t))
separated_list1(commas, tokenizer)(i)
}

View file

@ -148,6 +148,7 @@ pub enum Value {
Edges(Box<Edges>),
Future(Box<Future>),
Constant(Constant),
// Closure(Box<Closure>),
Function(Box<Function>),
Subquery(Box<Subquery>),
Expression(Box<Expression>),

View file

@ -57,7 +57,6 @@ async fn define_statement_database() -> Result<(), Error> {
let val = Value::parse(
"{
databases: { test: 'DEFINE DATABASE test' },
logins: {},
tokens: {},
users: {},
}",
@ -87,7 +86,6 @@ async fn define_statement_function() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: { test: 'DEFINE FUNCTION fn::test($first: string, $last: string) { RETURN $first + $last; }' },
params: {},
@ -121,7 +119,6 @@ async fn define_statement_table_drop() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -153,7 +150,6 @@ async fn define_statement_table_schemaless() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -189,7 +185,6 @@ async fn define_statement_table_schemafull() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -221,7 +216,6 @@ async fn define_statement_table_schemaful() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -1075,7 +1069,6 @@ async fn define_statement_analyzer() -> Result<(), Error> {
autocomplete: 'DEFINE ANALYZER autocomplete FILTERS LOWERCASE,EDGENGRAM(2,10)',
english: 'DEFINE ANALYZER english TOKENIZERS BLANK,CLASS FILTERS LOWERCASE,SNOWBALL(ENGLISH)',
},
logins: {},
tokens: {},
functions: {},
params: {},
@ -1352,10 +1345,8 @@ async fn permissions_checks_define_db() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec![
"{ databases: { DB: 'DEFINE DATABASE DB' }, logins: { }, tokens: { }, users: { } }",
],
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec!["{ databases: { DB: 'DEFINE DATABASE DB' }, tokens: { }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -1396,8 +1387,8 @@ async fn permissions_checks_define_function() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { greet: \"DEFINE FUNCTION fn::greet() { RETURN 'Hello'; }\" }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { greet: \"DEFINE FUNCTION fn::greet() { RETURN 'Hello'; }\" }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1438,8 +1429,8 @@ async fn permissions_checks_define_analyzer() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { analyzer: 'DEFINE ANALYZER analyzer TOKENIZERS BLANK' }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { analyzer: 'DEFINE ANALYZER analyzer TOKENIZERS BLANK' }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1480,8 +1471,8 @@ async fn permissions_checks_define_token_ns() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { token: \"DEFINE TOKEN token ON NAMESPACE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"]
vec!["{ databases: { }, tokens: { token: \"DEFINE TOKEN token ON NAMESPACE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1522,8 +1513,8 @@ async fn permissions_checks_define_token_db() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { token: \"DEFINE TOKEN token ON DATABASE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { token: \"DEFINE TOKEN token ON DATABASE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1606,8 +1597,8 @@ async fn permissions_checks_define_user_ns() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { }, users: { user: \"DEFINE USER user ON NAMESPACE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"]
vec!["{ databases: { }, tokens: { }, users: { user: \"DEFINE USER user ON NAMESPACE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1648,8 +1639,8 @@ async fn permissions_checks_define_user_db() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { user: \"DEFINE USER user ON DATABASE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { user: \"DEFINE USER user ON DATABASE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1690,8 +1681,8 @@ async fn permissions_checks_define_scope() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { account: 'DEFINE SCOPE account SESSION 1h' }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { account: 'DEFINE SCOPE account SESSION 1h' }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1732,8 +1723,8 @@ async fn permissions_checks_define_param() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { param: \"DEFINE PARAM $param VALUE 'foo'\" }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { }, params: { param: \"DEFINE PARAM $param VALUE 'foo'\" }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [
@ -1771,8 +1762,8 @@ async fn permissions_checks_define_table() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { TB: 'DEFINE TABLE TB SCHEMALESS' }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { TB: 'DEFINE TABLE TB SCHEMALESS' }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"]
];
let test_cases = [

View file

@ -55,7 +55,7 @@ async fn info_for_ns() {
assert!(out.is_ok(), "Unexpected error: {:?}", out);
let output_regex = Regex::new(
r"\{ databases: \{ DB: .* \}, logins: \{ \}, tokens: \{ token: .* \}, users: \{ user: .* \} \}",
r"\{ databases: \{ DB: .* \}, tokens: \{ token: .* \}, users: \{ user: .* \} \}",
)
.unwrap();
let out_str = out.unwrap().to_string();
@ -88,7 +88,7 @@ async fn info_for_db() {
let out = res.pop().unwrap().output();
assert!(out.is_ok(), "Unexpected error: {:?}", out);
let output_regex = Regex::new(r"\{ analyzers: \{ analyzer: .* \}, functions: \{ greet: .* \}, logins: \{ \}, params: \{ param: .* \}, scopes: \{ account: .* \}, tables: \{ TB: .* \}, tokens: \{ token: .* \}, users: \{ user: .* \} \}").unwrap();
let output_regex = Regex::new(r"\{ analyzers: \{ analyzer: .* \}, functions: \{ greet: .* \}, params: \{ param: .* \}, scopes: \{ account: .* \}, tables: \{ TB: .* \}, tokens: \{ token: .* \}, users: \{ user: .* \} \}").unwrap();
let out_str = out.unwrap().to_string();
assert!(
output_regex.is_match(&out_str),
@ -276,8 +276,8 @@ async fn permissions_checks_info_ns() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -315,8 +315,8 @@ async fn permissions_checks_info_db() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
];
let test_cases = [

View file

@ -26,7 +26,6 @@ async fn define_global_param() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: { test: 'DEFINE PARAM $test VALUE 12345' },

View file

@ -37,7 +37,6 @@ async fn remove_statement_table() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -72,7 +71,6 @@ async fn remove_statement_analyzer() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},
@ -181,10 +179,8 @@ async fn permissions_checks_remove_db() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec![
"{ databases: { DB: 'DEFINE DATABASE DB' }, logins: { }, tokens: { }, users: { } }",
],
vec!["{ databases: { }, tokens: { }, users: { } }"],
vec!["{ databases: { DB: 'DEFINE DATABASE DB' }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -225,8 +221,8 @@ async fn permissions_checks_remove_function() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { greet: \"DEFINE FUNCTION fn::greet() { RETURN 'Hello'; }\" }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { greet: \"DEFINE FUNCTION fn::greet() { RETURN 'Hello'; }\" }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -267,8 +263,8 @@ async fn permissions_checks_remove_analyzer() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { analyzer: 'DEFINE ANALYZER analyzer TOKENIZERS BLANK' }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { analyzer: 'DEFINE ANALYZER analyzer TOKENIZERS BLANK' }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -309,8 +305,8 @@ async fn permissions_checks_remove_ns_token() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, logins: { }, tokens: { token: \"DEFINE TOKEN token ON NAMESPACE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, tokens: { token: \"DEFINE TOKEN token ON NAMESPACE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
];
let test_cases = [
@ -351,8 +347,8 @@ async fn permissions_checks_remove_db_token() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { token: \"DEFINE TOKEN token ON DATABASE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { token: \"DEFINE TOKEN token ON DATABASE TYPE HS512 VALUE 'secret'\" }, users: { } }"],
];
let test_cases = [
@ -435,8 +431,8 @@ async fn permissions_checks_remove_ns_user() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ databases: { }, logins: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, logins: { }, tokens: { }, users: { user: \"DEFINE USER user ON NAMESPACE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ databases: { }, tokens: { }, users: { } }"],
vec!["{ databases: { }, tokens: { }, users: { user: \"DEFINE USER user ON NAMESPACE PASSHASH 'secret' ROLES VIEWER\" } }"],
];
let test_cases = [
@ -477,8 +473,8 @@ async fn permissions_checks_remove_db_user() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { user: \"DEFINE USER user ON DATABASE PASSHASH 'secret' ROLES VIEWER\" } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { user: \"DEFINE USER user ON DATABASE PASSHASH 'secret' ROLES VIEWER\" } }"],
];
let test_cases = [
@ -519,8 +515,8 @@ async fn permissions_checks_remove_scope() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { account: 'DEFINE SCOPE account SESSION 1h' }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { account: 'DEFINE SCOPE account SESSION 1h' }, tables: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -561,8 +557,8 @@ async fn permissions_checks_remove_param() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { param: \"DEFINE PARAM $param VALUE 'foo'\" }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { param: \"DEFINE PARAM $param VALUE 'foo'\" }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
];
let test_cases = [
@ -603,8 +599,8 @@ async fn permissions_checks_remove_table() {
// Define the expected results for the check statement when the test statement succeeded and when it failed
let check_results = [
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, logins: { }, params: { }, scopes: { }, tables: { TB: 'DEFINE TABLE TB SCHEMALESS' }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { }, tokens: { }, users: { } }"],
vec!["{ analyzers: { }, functions: { }, params: { }, scopes: { }, tables: { TB: 'DEFINE TABLE TB SCHEMALESS' }, tokens: { }, users: { } }"],
];
let test_cases = [

View file

@ -242,7 +242,6 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
let val = Value::parse(
"{
databases: { test: 'DEFINE DATABASE test' },
logins: {},
tokens: {},
users: {},
}",
@ -253,7 +252,6 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
let val = Value::parse(
"{
analyzers: {},
logins: {},
tokens: {},
functions: {},
params: {},