Implements analyzer definition (#1705)

This commit is contained in:
Emmanuel Keller 2023-05-10 03:08:09 +01:00 committed by GitHub
parent 19b0920e15
commit 848be4dafb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 652 additions and 120 deletions

View file

@ -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,

64
lib/src/key/az.rs Normal file
View file

@ -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<u8> {
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<u8> {
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);
}
}

View file

@ -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

View file

@ -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<DefineNamespaceStatement>),
Tb(Arc<DefineTableStatement>),
// Multi definitions
Azs(Arc<[DefineAnalyzerStatement]>),
Dbs(Arc<[DefineDatabaseStatement]>),
Dls(Arc<[DefineLoginStatement]>),
Dts(Arc<[DefineTokenStatement]>),

View file

@ -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<Arc<[DefineAnalyzerStatement]>, 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<DefineNamespaceStatement, Error> {
@ -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<DefineAnalyzerStatement, Error> {
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?;

View file

@ -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<Array> 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)))
}

View file

@ -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)))
}

View file

@ -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()

View file

@ -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))
}

62
lib/src/sql/filter.rs Normal file
View file

@ -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<Filter>> {
let (i, _) = tag_no_case("FILTERS")(i)?;
let (i, _) = shouldbespace(i)?;
separated_list1(commas, filter)(i)
}

View file

@ -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)?;

View file

@ -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)))

View file

@ -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<f64>> {
}
fn line_vals(i: &str) -> IResult<&str, LineString<f64>> {
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<f64>> {
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<Point<f64>>> {
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<LineString<f64>>> {
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<Polygon<f64>>> {
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<Geometry>> {
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<Geometry>> {
//
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)))
}

View file

@ -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<Cond>, Option<Idiom>)> {
}
fn custom(i: &str) -> IResult<&str, (Tables, Option<Cond>, Option<Idiom>)> {
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<Cond>, Option<Idiom>)> {
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)))
}

View file

@ -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| {

24
lib/src/sql/language.rs Normal file
View file

@ -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)
}

View file

@ -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;

View file

@ -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)))
}

View file

@ -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<Vec<Tokenizer>>,
pub filters: Option<Vec<Filter>>,
}
impl DefineAnalyzerStatement {
pub(crate) async fn compute(
&self,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
_doc: Option<&Value>,
) -> Result<Value, Error> {
// Selected DB?
opt.needs(Level::Db)?;
// Allowed to run?
opt.check(Level::Db)?;
// Clone transaction
let run = txn.clone();
// Claim transaction
let mut run = run.lock().await;
// Process the statement
let key = crate::key::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<String> = tokenizers.iter().map(|f| f.to_string()).collect();
write!(f, " TOKENIZERS {}", tokens.join(","))?;
}
if let Some(filters) = &self.filters {
let tokens: Vec<String> = 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 {

View file

@ -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()
}

View file

@ -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;

View file

@ -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<Value, Error> {
// Selected DB?
opt.needs(Level::Db)?;
// Allowed to run?
opt.check(Level::Db)?;
// Clone transaction
let run = txn.clone();
// Claim transaction
let mut run = run.lock().await;
// Delete the definition
let key = crate::key::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)
}
}

View file

@ -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| {

41
lib/src/sql/tokenizer.rs Normal file
View file

@ -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<Tokenizer>> {
let (i, _) = tag_no_case("TOKENIZERS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, t) = separated_list1(commas, tokenizer)(i)?;
Ok((i, t))
}

View file

@ -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(())
}

View file

@ -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: {},

75
lib/tests/remove.rs Normal file
View file

@ -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(())
}

View file

@ -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: {},