diff --git a/lib/src/err/mod.rs b/lib/src/err/mod.rs index b558150a..cf3ce91f 100644 --- a/lib/src/err/mod.rs +++ b/lib/src/err/mod.rs @@ -234,6 +234,12 @@ pub enum Error { value: String, }, + /// The requested analyzer does not exist + #[error("The analyzer '{value}' does not exist")] + AzNotFound { + value: String, + }, + /// Unable to perform the realtime query #[error("Unable to perform the realtime query")] RealtimeDisabled, diff --git a/lib/src/key/az.rs b/lib/src/key/az.rs new file mode 100644 index 00000000..dbd15a98 --- /dev/null +++ b/lib/src/key/az.rs @@ -0,0 +1,64 @@ +use derive::Key; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Key)] +pub struct Az<'a> { + __: u8, + _a: u8, + pub ns: &'a str, + _b: u8, + pub db: &'a str, + _c: u8, + _d: u8, + _e: u8, + pub az: &'a str, +} + +pub fn new<'a>(ns: &'a str, db: &'a str, tb: &'a str) -> Az<'a> { + Az::new(ns, db, tb) +} + +pub fn prefix(ns: &str, db: &str) -> Vec { + let mut k = super::database::new(ns, db).encode().unwrap(); + k.extend_from_slice(&[0x21, 0x61, 0x7a, 0x00]); + k +} + +pub fn suffix(ns: &str, db: &str) -> Vec { + let mut k = super::database::new(ns, db).encode().unwrap(); + k.extend_from_slice(&[0x21, 0x61, 0x7a, 0xff]); + k +} + +impl<'a> Az<'a> { + pub fn new(ns: &'a str, db: &'a str, az: &'a str) -> Self { + Self { + __: 0x2f, // / + _a: 0x2a, // * + ns, + _b: 0x2a, // * + db, + _c: 0x21, // ! + _d: 0x61, // a + _e: 0x7a, // z + az, + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn key() { + use super::*; + #[rustfmt::skip] + let val = Az::new( + "ns", + "db", + "test", + ); + let enc = Az::encode(&val).unwrap(); + let dec = Az::decode(&enc).unwrap(); + assert_eq!(val, dec); + } +} diff --git a/lib/src/key/mod.rs b/lib/src/key/mod.rs index c197633b..71d75adf 100644 --- a/lib/src/key/mod.rs +++ b/lib/src/key/mod.rs @@ -8,6 +8,7 @@ /// DB /*{ns}!db{db} /// /// Database /*{ns}*{db} +/// AZ /*{ns}*{db}!az{az} /// DL /*{ns}*{db}!dl{us} /// DT /*{ns}*{db}!dt{tk} /// PA /*{ns}*{db}!pa{pa} @@ -31,6 +32,7 @@ /// /// Index /*{ns}*{db}*{tb}ยค{ix}{fd}{id} /// +pub mod az; // Stores a DEFINE ANALYZER config definition pub mod database; // Stores the key prefix for all keys under a database pub mod db; // Stores a DEFINE DATABASE config definition pub mod dl; // Stores a DEFINE LOGIN ON DATABASE config definition diff --git a/lib/src/kvs/cache.rs b/lib/src/kvs/cache.rs index 322a9330..8d3e7369 100644 --- a/lib/src/kvs/cache.rs +++ b/lib/src/kvs/cache.rs @@ -1,4 +1,5 @@ use crate::kvs::kv::Key; +use crate::sql::statements::DefineAnalyzerStatement; use crate::sql::statements::DefineDatabaseStatement; use crate::sql::statements::DefineEventStatement; use crate::sql::statements::DefineFieldStatement; @@ -21,6 +22,7 @@ pub enum Entry { Ns(Arc), Tb(Arc), // Multi definitions + Azs(Arc<[DefineAnalyzerStatement]>), Dbs(Arc<[DefineDatabaseStatement]>), Dls(Arc<[DefineLoginStatement]>), Dts(Arc<[DefineTokenStatement]>), diff --git a/lib/src/kvs/tx.rs b/lib/src/kvs/tx.rs index 5939ad5f..141fb625 100644 --- a/lib/src/kvs/tx.rs +++ b/lib/src/kvs/tx.rs @@ -14,6 +14,7 @@ use crate::sql::thing::Thing; use crate::sql::Value; use channel::Sender; use sql::permission::Permissions; +use sql::statements::DefineAnalyzerStatement; use sql::statements::DefineDatabaseStatement; use sql::statements::DefineEventStatement; use sql::statements::DefineFieldStatement; @@ -1086,6 +1087,28 @@ impl Transaction { val }) } + /// Retrieve all analyzer definitions for a specific database. + pub async fn all_az( + &mut self, + ns: &str, + db: &str, + ) -> Result, Error> { + let key = crate::key::az::prefix(ns, db); + Ok(if let Some(e) = self.cache.get(&key) { + if let Entry::Azs(v) = e { + v + } else { + unreachable!(); + } + } else { + let beg = crate::key::az::prefix(ns, db); + let end = crate::key::az::suffix(ns, db); + let val = self.getr(beg..end, u32::MAX).await?; + let val = val.convert().into(); + self.cache.set(key, Entry::Azs(Arc::clone(&val))); + val + }) + } /// Retrieve a specific namespace definition. pub async fn get_ns(&mut self, ns: &str) -> Result { @@ -1221,7 +1244,19 @@ impl Transaction { })?; Ok(val.into()) } - + /// Retrieve a specific analyzer definition. + pub async fn get_az( + &mut self, + ns: &str, + db: &str, + az: &str, + ) -> Result { + let key = crate::key::az::new(ns, db, az); + let val = self.get(key).await?.ok_or(Error::AzNotFound { + value: az.to_owned(), + })?; + Ok(val.into()) + } /// Add a namespace with a default configuration, only if we are in dynamic mode. pub async fn add_ns( &mut self, @@ -1610,6 +1645,20 @@ impl Transaction { chn.send(bytes!("")).await?; } } + // Output ANALYZERS + { + let azs = self.all_az(ns, db).await?; + if !azs.is_empty() { + chn.send(bytes!("-- ------------------------------")).await?; + chn.send(bytes!("-- ANALYZERS")).await?; + chn.send(bytes!("-- ------------------------------")).await?; + chn.send(bytes!("")).await?; + for az in azs.iter() { + chn.send(bytes!(format!("{az};"))).await?; + } + chn.send(bytes!("")).await?; + } + } // Output TABLES { let tbs = self.all_tb(ns, db).await?; diff --git a/lib/src/sql/array.rs b/lib/src/sql/array.rs index f2454b74..8419eae9 100644 --- a/lib/src/sql/array.rs +++ b/lib/src/sql/array.rs @@ -3,7 +3,7 @@ use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; use crate::sql::comment::mightbespace; -use crate::sql::common::commas; +use crate::sql::common::{closebracket, commas, openbracket}; use crate::sql::error::IResult; use crate::sql::fmt::{pretty_indent, Fmt, Pretty}; use crate::sql::number::Number; @@ -355,13 +355,11 @@ impl Uniq for Array { // ------------------------------ pub fn array(i: &str) -> IResult<&str, Array> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list0(commas, value)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, Array(v))) } diff --git a/lib/src/sql/block.rs b/lib/src/sql/block.rs index 0a0a6bf2..51bbac71 100644 --- a/lib/src/sql/block.rs +++ b/lib/src/sql/block.rs @@ -4,7 +4,7 @@ use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; use crate::sql::comment::{comment, mightbespace}; -use crate::sql::common::colons; +use crate::sql::common::{closebraces, colons, openbraces}; use crate::sql::error::IResult; use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty}; use crate::sql::statements::create::{create, CreateStatement}; @@ -18,7 +18,6 @@ use crate::sql::statements::set::{set, SetStatement}; use crate::sql::statements::update::{update, UpdateStatement}; use crate::sql::value::{value, Value}; use nom::branch::alt; -use nom::character::complete::char; use nom::combinator::map; use nom::multi::many0; use nom::multi::separated_list1; @@ -159,12 +158,10 @@ impl Display for Block { } pub fn block(i: &str) -> IResult<&str, Block> { - let (i, _) = char('{')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbraces(i)?; let (i, v) = separated_list1(colons, entry)(i)?; let (i, _) = many0(alt((colons, comment)))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char('}')(i)?; + let (i, _) = closebraces(i)?; Ok((i, Block(v))) } diff --git a/lib/src/sql/common.rs b/lib/src/sql/common.rs index d2c2d1cc..57a4d673 100644 --- a/lib/src/sql/common.rs +++ b/lib/src/sql/common.rs @@ -36,6 +36,54 @@ pub fn commasorspace(i: &str) -> IResult<&str, ()> { alt((commas, shouldbespace))(i) } +pub fn openparentheses(i: &str) -> IResult<&str, ()> { + let (i, _) = char('(')(i)?; + let (i, _) = mightbespace(i)?; + Ok((i, ())) +} + +pub fn closeparentheses(i: &str) -> IResult<&str, ()> { + let (i, _) = mightbespace(i)?; + let (i, _) = char(')')(i)?; + Ok((i, ())) +} + +pub fn openbraces(i: &str) -> IResult<&str, ()> { + let (i, _) = char('{')(i)?; + let (i, _) = mightbespace(i)?; + Ok((i, ())) +} + +pub fn closebraces(i: &str) -> IResult<&str, ()> { + let (i, _) = mightbespace(i)?; + let (i, _) = char('}')(i)?; + Ok((i, ())) +} + +pub fn openbracket(i: &str) -> IResult<&str, ()> { + let (i, _) = char('[')(i)?; + let (i, _) = mightbespace(i)?; + Ok((i, ())) +} + +pub fn closebracket(i: &str) -> IResult<&str, ()> { + let (i, _) = mightbespace(i)?; + let (i, _) = char(']')(i)?; + Ok((i, ())) +} + +pub fn openchevron(i: &str) -> IResult<&str, ()> { + let (i, _) = char('<')(i)?; + let (i, _) = mightbespace(i)?; + Ok((i, ())) +} + +pub fn closechevron(i: &str) -> IResult<&str, ()> { + let (i, _) = mightbespace(i)?; + let (i, _) = char('>')(i)?; + Ok((i, ())) +} + #[inline] pub fn is_hex(chr: char) -> bool { chr.is_ascii_hexdigit() diff --git a/lib/src/sql/edges.rs b/lib/src/sql/edges.rs index 486f23d6..3a03431b 100644 --- a/lib/src/sql/edges.rs +++ b/lib/src/sql/edges.rs @@ -1,4 +1,4 @@ -use crate::sql::comment::mightbespace; +use crate::sql::common::{closeparentheses, openparentheses}; use crate::sql::dir::{dir, Dir}; use crate::sql::error::IResult; use crate::sql::table::{table, tables, Tables}; @@ -48,11 +48,9 @@ fn simple(i: &str) -> IResult<&str, Tables> { } fn custom(i: &str) -> IResult<&str, Tables> { - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, w) = alt((any, tables))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, w)) } diff --git a/lib/src/sql/filter.rs b/lib/src/sql/filter.rs new file mode 100644 index 00000000..671d50fc --- /dev/null +++ b/lib/src/sql/filter.rs @@ -0,0 +1,62 @@ +use crate::sql::comment::shouldbespace; +use crate::sql::common::{closeparentheses, commas, openparentheses}; +use crate::sql::error::IResult; +use crate::sql::language::{language, Language}; +use crate::sql::number::number; +use crate::sql::Number; +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::multi::separated_list1; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +pub enum Filter { + EdgeNgram(Number, Number), + Lowercase, + Snowball(Language), +} + +impl Display for Filter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::EdgeNgram(min, max) => write!(f, "EDGENGRAM({},{})", min, max), + Self::Lowercase => f.write_str("LOWERCASE"), + Self::Snowball(lang) => write!(f, "SNOWBALL({})", lang), + } + } +} + +fn edgengram(i: &str) -> IResult<&str, Filter> { + let (i, _) = tag_no_case("EDGENGRAM")(i)?; + let (i, _) = openparentheses(i)?; + let (i, min) = number(i)?; + let (i, _) = commas(i)?; + let (i, max) = number(i)?; + let (i, _) = closeparentheses(i)?; + Ok((i, Filter::EdgeNgram(min, max))) +} + +fn snowball(i: &str) -> IResult<&str, Filter> { + let (i, _) = tag_no_case("SNOWBALL")(i)?; + let (i, _) = openparentheses(i)?; + let (i, language) = language(i)?; + let (i, _) = closeparentheses(i)?; + Ok((i, Filter::Snowball(language))) +} + +fn lowercase(i: &str) -> IResult<&str, Filter> { + let (i, _) = tag_no_case("LOWERCASE")(i)?; + Ok((i, Filter::Lowercase)) +} + +fn filter(i: &str) -> IResult<&str, Filter> { + alt((edgengram, lowercase, snowball))(i) +} + +pub(super) fn filters(i: &str) -> IResult<&str, Vec> { + let (i, _) = tag_no_case("FILTERS")(i)?; + let (i, _) = shouldbespace(i)?; + separated_list1(commas, filter)(i) +} diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index 85d584c2..92a82abb 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -4,8 +4,8 @@ use crate::dbs::Transaction; use crate::err::Error; use crate::fnc; use crate::sql::comment::mightbespace; -use crate::sql::common::commas; use crate::sql::common::val_char; +use crate::sql::common::{closeparentheses, commas, openparentheses}; use crate::sql::error::IResult; use crate::sql::fmt::Fmt; use crate::sql::idiom::Idiom; @@ -229,11 +229,9 @@ pub fn function(i: &str) -> IResult<&str, Function> { pub fn normal(i: &str) -> IResult<&str, Function> { let (i, s) = function_names(i)?; - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, a) = separated_list0(commas, value)(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, Function::Normal(s.to_string(), a))) } @@ -250,11 +248,10 @@ pub fn custom(i: &str) -> IResult<&str, Function> { fn script(i: &str) -> IResult<&str, Function> { let (i, _) = tag("function")(i)?; - let (i, _) = tag("(")(i)?; + let (i, _) = openparentheses(i)?; let (i, _) = mightbespace(i)?; let (i, a) = separated_list0(commas, value)(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = tag(")")(i)?; + let (i, _) = closeparentheses(i)?; let (i, _) = mightbespace(i)?; let (i, _) = char('{')(i)?; let (i, v) = func(i)?; diff --git a/lib/src/sql/future.rs b/lib/src/sql/future.rs index a030c56b..08903024 100644 --- a/lib/src/sql/future.rs +++ b/lib/src/sql/future.rs @@ -4,10 +4,10 @@ use crate::dbs::Transaction; use crate::err::Error; use crate::sql::block::{block, Block}; use crate::sql::comment::mightbespace; +use crate::sql::common::{closechevron, openchevron}; use crate::sql::error::IResult; use crate::sql::value::Value; use nom::bytes::complete::tag; -use nom::character::complete::char; use serde::{Deserialize, Serialize}; use std::fmt; @@ -49,9 +49,9 @@ impl fmt::Display for Future { } pub fn future(i: &str) -> IResult<&str, Future> { - let (i, _) = char('<')(i)?; + let (i, _) = openchevron(i)?; let (i, _) = tag("future")(i)?; - let (i, _) = char('>')(i)?; + let (i, _) = closechevron(i)?; let (i, _) = mightbespace(i)?; let (i, v) = block(i)?; Ok((i, Future(v))) diff --git a/lib/src/sql/geometry.rs b/lib/src/sql/geometry.rs index 93eb3f27..e039ea34 100644 --- a/lib/src/sql/geometry.rs +++ b/lib/src/sql/geometry.rs @@ -1,7 +1,7 @@ #![allow(clippy::derived_hash_with_manual_eq)] use crate::sql::comment::mightbespace; -use crate::sql::common::commas; +use crate::sql::common::{closebracket, closeparentheses, commas, openbracket, openparentheses}; use crate::sql::error::IResult; use crate::sql::fmt::Fmt; use geo::algorithm::contains::Contains; @@ -538,13 +538,11 @@ pub fn geometry(i: &str) -> IResult<&str, Geometry> { } fn simple(i: &str) -> IResult<&str, Geometry> { - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, x) = double(i)?; let (i, _) = commas(i)?; let (i, y) = double(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, Geometry::Point((x, y).into()))) } @@ -695,78 +693,64 @@ fn point_vals(i: &str) -> IResult<&str, Point> { } fn line_vals(i: &str) -> IResult<&str, LineString> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list1(commas, coordinate)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v.into())) } fn polygon_vals(i: &str) -> IResult<&str, Polygon> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, e) = line_vals(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; let (i, v) = separated_list0(commas, |i| { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = line_vals(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v)) })(i)?; Ok((i, Polygon::new(e, v))) } fn multipoint_vals(i: &str) -> IResult<&str, Vec>> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list1(commas, point_vals)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v)) } fn multiline_vals(i: &str) -> IResult<&str, Vec>> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list1(commas, line_vals)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v)) } fn multipolygon_vals(i: &str) -> IResult<&str, Vec>> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list1(commas, polygon_vals)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v)) } fn collection_vals(i: &str) -> IResult<&str, Vec> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, v) = separated_list1(commas, geometry)(i)?; let (i, _) = mightbespace(i)?; let (i, _) = opt(char(','))(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, v)) } @@ -775,15 +759,13 @@ fn collection_vals(i: &str) -> IResult<&str, Vec> { // fn coordinate(i: &str) -> IResult<&str, (f64, f64)> { - let (i, _) = char('[')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openbracket(i)?; let (i, x) = double(i)?; let (i, _) = mightbespace(i)?; let (i, _) = char(',')(i)?; let (i, _) = mightbespace(i)?; let (i, y) = double(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, (x, y))) } diff --git a/lib/src/sql/graph.rs b/lib/src/sql/graph.rs index 719b0db0..1cd8f5c2 100644 --- a/lib/src/sql/graph.rs +++ b/lib/src/sql/graph.rs @@ -1,5 +1,5 @@ -use crate::sql::comment::mightbespace; use crate::sql::comment::shouldbespace; +use crate::sql::common::{closeparentheses, openparentheses}; use crate::sql::cond::{cond, Cond}; use crate::sql::dir::{dir, Dir}; use crate::sql::error::IResult; @@ -106,8 +106,7 @@ fn simple(i: &str) -> IResult<&str, (Tables, Option, Option)> { } fn custom(i: &str) -> IResult<&str, (Tables, Option, Option)> { - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, w) = alt((any, tables))(i)?; let (i, c) = opt(|i| { let (i, _) = shouldbespace(i)?; @@ -121,8 +120,7 @@ fn custom(i: &str) -> IResult<&str, (Tables, Option, Option)> { let (i, v) = idiom(i)?; Ok((i, v)) })(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, (w, c, a))) } diff --git a/lib/src/sql/kind.rs b/lib/src/sql/kind.rs index c925ade4..bd32734d 100644 --- a/lib/src/sql/kind.rs +++ b/lib/src/sql/kind.rs @@ -1,6 +1,6 @@ use crate::sql::comment::mightbespace; -use crate::sql::common::commas; use crate::sql::common::verbar; +use crate::sql::common::{closeparentheses, commas, openparentheses}; use crate::sql::error::IResult; use crate::sql::fmt::Fmt; use crate::sql::table::{table, Table}; @@ -136,9 +136,9 @@ fn record(i: &str) -> IResult<&str, Kind> { let (i, v) = opt(alt(( |i| { let (i, _) = mightbespace(i)?; - let (i, _) = char('(')(i)?; + let (i, _) = openparentheses(i)?; let (i, v) = separated_list1(commas, table)(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, v)) }, |i| { diff --git a/lib/src/sql/language.rs b/lib/src/sql/language.rs new file mode 100644 index 00000000..4fcc7c93 --- /dev/null +++ b/lib/src/sql/language.rs @@ -0,0 +1,24 @@ +use crate::sql::error::IResult; +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +pub enum Language { + English, +} + +impl Display for Language { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::English => "ENGLISH", + }) + } +} + +pub(super) fn language(i: &str) -> IResult<&str, Language> { + alt((map(tag_no_case("ENGLISH"), |_| Language::English),))(i) +} diff --git a/lib/src/sql/mod.rs b/lib/src/sql/mod.rs index 857a7b42..17024ef2 100644 --- a/lib/src/sql/mod.rs +++ b/lib/src/sql/mod.rs @@ -20,6 +20,7 @@ pub(crate) mod escape; pub(crate) mod expression; pub(crate) mod fetch; pub(crate) mod field; +pub(crate) mod filter; pub(crate) mod fmt; pub(crate) mod function; pub(crate) mod future; @@ -30,6 +31,7 @@ pub(crate) mod id; pub(crate) mod ident; pub(crate) mod idiom; pub(crate) mod kind; +pub(crate) mod language; pub(crate) mod limit; pub(crate) mod model; pub(crate) mod number; @@ -56,6 +58,7 @@ pub(crate) mod subquery; pub(crate) mod table; pub(crate) mod thing; pub(crate) mod timeout; +pub(crate) mod tokenizer; pub(crate) mod uuid; pub(crate) mod value; pub(crate) mod version; diff --git a/lib/src/sql/part.rs b/lib/src/sql/part.rs index c4bebf55..483ab152 100644 --- a/lib/src/sql/part.rs +++ b/lib/src/sql/part.rs @@ -1,4 +1,5 @@ use crate::sql::comment::shouldbespace; +use crate::sql::common::{closebracket, openbracket}; use crate::sql::ending::ident as ending; use crate::sql::error::IResult; use crate::sql::fmt::Fmt; @@ -159,9 +160,9 @@ pub fn all(i: &str) -> IResult<&str, Part> { Ok((i, ())) }, |i| { - let (i, _) = char('[')(i)?; + let (i, _) = openbracket(i)?; let (i, _) = char('*')(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, ())) }, ))(i)?; @@ -169,16 +170,16 @@ pub fn all(i: &str) -> IResult<&str, Part> { } pub fn last(i: &str) -> IResult<&str, Part> { - let (i, _) = char('[')(i)?; + let (i, _) = openbracket(i)?; let (i, _) = char('$')(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, Part::Last)) } pub fn index(i: &str) -> IResult<&str, Part> { - let (i, _) = char('[')(i)?; + let (i, _) = openbracket(i)?; let (i, v) = number(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, Part::Index(v))) } @@ -190,11 +191,11 @@ pub fn field(i: &str) -> IResult<&str, Part> { } pub fn filter(i: &str) -> IResult<&str, Part> { - let (i, _) = char('[')(i)?; + let (i, _) = openbracket(i)?; let (i, _) = alt((tag_no_case("WHERE"), tag("?")))(i)?; let (i, _) = shouldbespace(i)?; let (i, v) = value::value(i)?; - let (i, _) = char(']')(i)?; + let (i, _) = closebracket(i)?; Ok((i, Part::Where(v))) } diff --git a/lib/src/sql/statements/define.rs b/lib/src/sql/statements/define.rs index aa9211d8..c7f0265c 100644 --- a/lib/src/sql/statements/define.rs +++ b/lib/src/sql/statements/define.rs @@ -11,6 +11,7 @@ use crate::sql::common::commas; use crate::sql::duration::{duration, Duration}; use crate::sql::error::IResult; use crate::sql::escape::escape_str; +use crate::sql::filter::{filters, Filter}; use crate::sql::fmt::is_pretty; use crate::sql::fmt::pretty_indent; use crate::sql::ident; @@ -21,6 +22,7 @@ use crate::sql::kind::{kind, Kind}; use crate::sql::permission::{permissions, Permissions}; use crate::sql::statements::UpdateStatement; use crate::sql::strand::strand_raw; +use crate::sql::tokenizer::{tokenizers, Tokenizer}; use crate::sql::value::{value, values, Value, Values}; use crate::sql::view::{view, View}; use argon2::password_hash::{PasswordHasher, SaltString}; @@ -46,6 +48,7 @@ pub enum DefineStatement { Namespace(DefineNamespaceStatement), Database(DefineDatabaseStatement), Function(DefineFunctionStatement), + Analyzer(DefineAnalyzerStatement), Login(DefineLoginStatement), Token(DefineTokenStatement), Scope(DefineScopeStatement), @@ -77,11 +80,12 @@ impl DefineStatement { 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, } } } -impl fmt::Display for DefineStatement { +impl Display for DefineStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Namespace(v) => Display::fmt(v, f), @@ -95,6 +99,7 @@ impl fmt::Display for DefineStatement { 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), } } } @@ -112,6 +117,7 @@ pub fn define(i: &str) -> IResult<&str, DefineStatement> { map(event, DefineStatement::Event), map(field, DefineStatement::Field), map(index, DefineStatement::Index), + map(analyzer, DefineStatement::Analyzer), ))(i) } @@ -202,7 +208,7 @@ impl DefineDatabaseStatement { } } -impl fmt::Display for DefineDatabaseStatement { +impl Display for DefineDatabaseStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DEFINE DATABASE {}", self.name) } @@ -312,6 +318,80 @@ fn function(i: &str) -> IResult<&str, DefineFunctionStatement> { // -------------------------------------------------- // -------------------------------------------------- +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)] +pub struct DefineAnalyzerStatement { + pub name: Ident, + pub tokenizers: Option>, + pub filters: Option>, +} + +impl DefineAnalyzerStatement { + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&Value>, + ) -> Result { + // Selected DB? + opt.needs(Level::Db)?; + // Allowed to run? + opt.check(Level::Db)?; + // Clone transaction + let run = txn.clone(); + // Claim transaction + let mut run = run.lock().await; + // Process the statement + let key = crate::key::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); + // 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(tokenizers) = &self.tokenizers { + let tokens: Vec = tokenizers.iter().map(|f| f.to_string()).collect(); + write!(f, " TOKENIZERS {}", tokens.join(","))?; + } + if let Some(filters) = &self.filters { + let tokens: Vec = filters.iter().map(|f| f.to_string()).collect(); + write!(f, " FILTERS {}", tokens.join(","))?; + } + Ok(()) + } +} + +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, _) = shouldbespace(i)?; + let (i, tokenizers) = opt(tokenizers)(i)?; + let (i, _) = mightbespace(i)?; + let (i, filters) = opt(filters)(i)?; + Ok(( + i, + DefineAnalyzerStatement { + name, + tokenizers, + filters, + }, + )) +} + +// -------------------------------------------------- +// -------------------------------------------------- +// -------------------------------------------------- + #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)] #[format(Named)] pub struct DefineLoginStatement { @@ -369,7 +449,7 @@ impl DefineLoginStatement { } } -impl fmt::Display for DefineLoginStatement { +impl Display for DefineLoginStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DEFINE LOGIN {} ON {} PASSHASH {}", self.name, self.base, escape_str(&self.hash)) } @@ -512,7 +592,7 @@ impl DefineTokenStatement { } } -impl fmt::Display for DefineTokenStatement { +impl Display for DefineTokenStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, @@ -595,7 +675,7 @@ impl DefineScopeStatement { } } -impl fmt::Display for DefineScopeStatement { +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 { @@ -716,7 +796,7 @@ impl DefineParamStatement { } } -impl fmt::Display for DefineParamStatement { +impl Display for DefineParamStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DEFINE PARAM ${} VALUE {}", self.name, self.value) } @@ -816,7 +896,7 @@ impl DefineTableStatement { } } -impl fmt::Display for DefineTableStatement { +impl Display for DefineTableStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DEFINE TABLE {}", self.name)?; if self.drop { @@ -971,7 +1051,7 @@ impl DefineEventStatement { } } -impl fmt::Display for DefineEventStatement { +impl Display for DefineEventStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, @@ -1059,7 +1139,7 @@ impl DefineFieldStatement { } } -impl fmt::Display for DefineFieldStatement { +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 { diff --git a/lib/src/sql/statements/info.rs b/lib/src/sql/statements/info.rs index 0c1deb06..7bd84a17 100644 --- a/lib/src/sql/statements/info.rs +++ b/lib/src/sql/statements/info.rs @@ -133,6 +133,12 @@ impl InfoStatement { tmp.insert(v.name.to_string(), v.to_string().into()); } res.insert("tb".to_owned(), tmp.into()); + // Process the analyzers + let mut tmp = Object::default(); + for v in run.all_az(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("az".to_owned(), tmp.into()); // Ok all good Value::from(res).ok() } diff --git a/lib/src/sql/statements/mod.rs b/lib/src/sql/statements/mod.rs index 5c6d4871..ca2d73ad 100644 --- a/lib/src/sql/statements/mod.rs +++ b/lib/src/sql/statements/mod.rs @@ -37,6 +37,7 @@ pub use self::set::SetStatement; pub use self::update::UpdateStatement; pub use self::yuse::UseStatement; +pub use self::define::DefineAnalyzerStatement; pub use self::define::DefineDatabaseStatement; pub use self::define::DefineEventStatement; pub use self::define::DefineFieldStatement; diff --git a/lib/src/sql/statements/remove.rs b/lib/src/sql/statements/remove.rs index dad0c800..e5739321 100644 --- a/lib/src/sql/statements/remove.rs +++ b/lib/src/sql/statements/remove.rs @@ -27,6 +27,7 @@ pub enum RemoveStatement { Namespace(RemoveNamespaceStatement), Database(RemoveDatabaseStatement), Function(RemoveFunctionStatement), + Analyzer(RemoveAnalyzerStatement), Login(RemoveLoginStatement), Token(RemoveTokenStatement), Scope(RemoveScopeStatement), @@ -58,6 +59,7 @@ impl RemoveStatement { 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, } } } @@ -76,6 +78,7 @@ impl Display for RemoveStatement { 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), } } } @@ -93,6 +96,7 @@ pub fn remove(i: &str) -> IResult<&str, RemoveStatement> { map(event, RemoveStatement::Event), map(field, RemoveStatement::Field), map(index, RemoveStatement::Index), + map(analyzer, RemoveStatement::Analyzer), ))(i) } @@ -134,8 +138,8 @@ impl RemoveNamespaceStatement { } } -impl fmt::Display for RemoveNamespaceStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveNamespaceStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE NAMESPACE {}", self.name) } } @@ -192,8 +196,8 @@ impl RemoveDatabaseStatement { } } -impl fmt::Display for RemoveDatabaseStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveDatabaseStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE DATABASE {}", self.name) } } @@ -279,6 +283,60 @@ fn function(i: &str) -> IResult<&str, RemoveFunctionStatement> { // -------------------------------------------------- // -------------------------------------------------- +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)] +pub struct RemoveAnalyzerStatement { + pub name: Ident, +} + +impl RemoveAnalyzerStatement { + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&Value>, + ) -> Result { + // Selected DB? + opt.needs(Level::Db)?; + // Allowed to run? + opt.check(Level::Db)?; + // Clone transaction + let run = txn.clone(); + // Claim transaction + let mut run = run.lock().await; + // Delete the definition + let key = crate::key::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)] #[format(Named)] pub struct RemoveLoginStatement { @@ -331,8 +389,8 @@ impl RemoveLoginStatement { } } -impl fmt::Display for RemoveLoginStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveLoginStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE LOGIN {} ON {}", self.name, self.base) } } @@ -427,8 +485,8 @@ impl RemoveTokenStatement { } } -impl fmt::Display for RemoveTokenStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveTokenStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE TOKEN {} ON {}", self.name, self.base) } } @@ -490,8 +548,8 @@ impl RemoveScopeStatement { } } -impl fmt::Display for RemoveScopeStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveScopeStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE SCOPE {}", self.name) } } @@ -545,8 +603,8 @@ impl RemoveParamStatement { } } -impl fmt::Display for RemoveParamStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveParamStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE PARAM {}", self.name) } } @@ -604,8 +662,8 @@ impl RemoveTableStatement { } } -impl fmt::Display for RemoveTableStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveTableStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE TABLE {}", self.name) } } @@ -663,8 +721,8 @@ impl RemoveEventStatement { } } -impl fmt::Display for RemoveEventStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveEventStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE EVENT {} ON {}", self.name, self.what) } } @@ -729,8 +787,8 @@ impl RemoveFieldStatement { } } -impl fmt::Display for RemoveFieldStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveFieldStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE FIELD {} ON {}", self.name, self.what) } } @@ -798,8 +856,8 @@ impl RemoveIndexStatement { } } -impl fmt::Display for RemoveIndexStatement { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Display for RemoveIndexStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "REMOVE INDEX {} ON {}", self.name, self.what) } } diff --git a/lib/src/sql/subquery.rs b/lib/src/sql/subquery.rs index 4638075f..2cfba8d2 100644 --- a/lib/src/sql/subquery.rs +++ b/lib/src/sql/subquery.rs @@ -2,7 +2,7 @@ use crate::ctx::Context; use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; -use crate::sql::comment::mightbespace; +use crate::sql::common::{closeparentheses, openparentheses}; use crate::sql::ending::subquery as ending; use crate::sql::error::IResult; use crate::sql::statements::create::{create, CreateStatement}; @@ -15,7 +15,6 @@ use crate::sql::statements::select::{select, SelectStatement}; use crate::sql::statements::update::{update, UpdateStatement}; use crate::sql::value::{value, Value}; use nom::branch::alt; -use nom::character::complete::char; use nom::combinator::map; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; @@ -237,22 +236,18 @@ fn subquery_ifelse(i: &str) -> IResult<&str, Subquery> { } fn subquery_value(i: &str) -> IResult<&str, Subquery> { - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, v) = map(value, Subquery::Value)(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, v)) } fn subquery_other(i: &str) -> IResult<&str, Subquery> { alt(( |i| { - let (i, _) = char('(')(i)?; - let (i, _) = mightbespace(i)?; + let (i, _) = openparentheses(i)?; let (i, v) = subquery_inner(i)?; - let (i, _) = mightbespace(i)?; - let (i, _) = char(')')(i)?; + let (i, _) = closeparentheses(i)?; Ok((i, v)) }, |i| { diff --git a/lib/src/sql/tokenizer.rs b/lib/src/sql/tokenizer.rs new file mode 100644 index 00000000..c7139598 --- /dev/null +++ b/lib/src/sql/tokenizer.rs @@ -0,0 +1,41 @@ +use crate::sql::comment::shouldbespace; +use crate::sql::common::commas; +use crate::sql::error::IResult; +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::multi::separated_list1; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +pub enum Tokenizer { + Case, + Space, + // Add new variants here +} + +impl Display for Tokenizer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::Case => "CASE", + Self::Space => "SPACE", + }) + } +} + +fn tokenizer(i: &str) -> IResult<&str, Tokenizer> { + let (i, t) = alt(( + map(tag_no_case("CASE"), |_| Tokenizer::Case), + map(tag_no_case("SPACE"), |_| Tokenizer::Space), + ))(i)?; + Ok((i, t)) +} + +pub(super) fn tokenizers(i: &str) -> IResult<&str, Vec> { + let (i, _) = tag_no_case("TOKENIZERS")(i)?; + let (i, _) = shouldbespace(i)?; + let (i, t) = separated_list1(commas, tokenizer)(i)?; + Ok((i, t)) +} diff --git a/lib/tests/define.rs b/lib/tests/define.rs index 3a944d8f..59db285e 100644 --- a/lib/tests/define.rs +++ b/lib/tests/define.rs @@ -76,6 +76,7 @@ async fn define_statement_function() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: { test: 'DEFINE FUNCTION fn::test($first: string, $last: string) { RETURN $first + $last; }' }, @@ -108,6 +109,7 @@ async fn define_statement_table_drop() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {}, @@ -138,6 +140,7 @@ async fn define_statement_table_schemaless() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {}, @@ -172,6 +175,7 @@ async fn define_statement_table_schemafull() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {}, @@ -202,6 +206,7 @@ async fn define_statement_table_schemaful() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {}, @@ -927,3 +932,41 @@ async fn define_statement_index_multiple_unique_existing() -> Result<(), Error> // Ok(()) } + +#[tokio::test] +async fn define_statement_analyzer() -> Result<(), Error> { + let sql = " + DEFINE ANALYZER english TOKENIZERS space,case FILTERS lowercase,snowball(english); + DEFINE ANALYZER autocomplete FILTERS lowercase,edgengram(2,10); + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + az: { + autocomplete: 'DEFINE ANALYZER autocomplete FILTERS LOWERCASE,EDGENGRAM(2,10)', + english: 'DEFINE ANALYZER english TOKENIZERS SPACE,CASE FILTERS LOWERCASE,SNOWBALL(ENGLISH)', + }, + dl: {}, + dt: {}, + fc: {}, + pa: {}, + sc: {}, + tb: {} + }", + ); + assert_eq!(tmp, val); + Ok(()) +} diff --git a/lib/tests/param.rs b/lib/tests/param.rs index b28f2a4c..cf007171 100644 --- a/lib/tests/param.rs +++ b/lib/tests/param.rs @@ -25,6 +25,7 @@ async fn define_global_param() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {}, diff --git a/lib/tests/remove.rs b/lib/tests/remove.rs new file mode 100644 index 00000000..d193c6eb --- /dev/null +++ b/lib/tests/remove.rs @@ -0,0 +1,75 @@ +mod parse; + +use parse::Parse; +use surrealdb::dbs::Session; +use surrealdb::err::Error; +use surrealdb::kvs::Datastore; +use surrealdb::sql::Value; + +#[tokio::test] +async fn remove_statement_table() -> Result<(), Error> { + let sql = " + DEFINE TABLE test SCHEMALESS; + REMOVE TABLE test; + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + az: {}, + dl: {}, + dt: {}, + fc: {}, + pa: {}, + sc: {}, + tb: {} + }", + ); + assert_eq!(tmp, val); + Ok(()) +} + +#[tokio::test] +async fn remove_statement_analyzer() -> Result<(), Error> { + let sql = " + DEFINE ANALYZER english TOKENIZERS space,case FILTERS lowercase,snowball(english); + REMOVE ANALYZER english; + INFO FOR DB; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 3); + // Analyzer is defined + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // Analyzer is removed + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // Check infos output + let tmp = res.remove(0).result?; + let val = Value::parse( + "{ + az: {}, + dl: {}, + dt: {}, + fc: {}, + pa: {}, + sc: {}, + tb: {} + }", + ); + assert_eq!(tmp, val); + Ok(()) +} diff --git a/lib/tests/strict.rs b/lib/tests/strict.rs index 079ec89a..d8b3554d 100644 --- a/lib/tests/strict.rs +++ b/lib/tests/strict.rs @@ -250,6 +250,7 @@ async fn loose_mode_all_ok() -> Result<(), Error> { let tmp = res.remove(0).result?; let val = Value::parse( "{ + az: {}, dl: {}, dt: {}, fc: {},