feat: Implements SELECT/EXPLAIN FULL (#2258)
This commit is contained in:
parent
ac213d69bb
commit
1e30eb4aa1
14 changed files with 399 additions and 74 deletions
100
lib/src/dbs/explanation.rs
Normal file
100
lib/src/dbs/explanation.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use crate::dbs::Iterable;
|
||||
use crate::sql::{Explain, Object, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct Explanation(Vec<ExplainItem>);
|
||||
|
||||
impl Explanation {
|
||||
pub(super) fn new(e: Option<&Explain>, iterables: &Vec<Iterable>) -> (bool, Option<Self>) {
|
||||
match e {
|
||||
None => (true, None),
|
||||
Some(e) => {
|
||||
let mut exp = Self::default();
|
||||
for i in iterables {
|
||||
exp.add_iter(i);
|
||||
}
|
||||
(e.0, Some(exp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_iter(&mut self, iter: &Iterable) {
|
||||
self.0.push(ExplainItem::new_iter(iter));
|
||||
}
|
||||
|
||||
pub(super) fn add_fetch(&mut self, count: usize) {
|
||||
self.0.push(ExplainItem::new_fetch(count));
|
||||
}
|
||||
|
||||
pub(super) fn output(self, results: &mut Vec<Value>) {
|
||||
for e in self.0 {
|
||||
results.push(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExplainItem {
|
||||
name: Value,
|
||||
details: Vec<(&'static str, Value)>,
|
||||
}
|
||||
|
||||
impl ExplainItem {
|
||||
fn new_fetch(count: usize) -> Self {
|
||||
Self {
|
||||
name: "Fetch".into(),
|
||||
details: vec![("count", count.into())],
|
||||
}
|
||||
}
|
||||
|
||||
fn new_iter(iter: &Iterable) -> Self {
|
||||
match iter {
|
||||
Iterable::Value(v) => Self {
|
||||
name: "Iterate Value".into(),
|
||||
details: vec![("value", v.to_owned())],
|
||||
},
|
||||
Iterable::Table(t) => Self {
|
||||
name: "Iterate Table".into(),
|
||||
details: vec![("table", Value::from(t.0.to_owned()))],
|
||||
},
|
||||
Iterable::Thing(t) => Self {
|
||||
name: "Iterate Thing".into(),
|
||||
details: vec![("thing", Value::Thing(t.to_owned()))],
|
||||
},
|
||||
Iterable::Range(r) => Self {
|
||||
name: "Iterate Range".into(),
|
||||
details: vec![("table", Value::from(r.tb.to_owned()))],
|
||||
},
|
||||
Iterable::Edges(e) => Self {
|
||||
name: "Iterate Edges".into(),
|
||||
details: vec![("from", Value::Thing(e.from.to_owned()))],
|
||||
},
|
||||
Iterable::Mergeable(t, v) => Self {
|
||||
name: "Iterate Mergeable".into(),
|
||||
details: vec![("thing", Value::Thing(t.to_owned())), ("value", v.to_owned())],
|
||||
},
|
||||
Iterable::Relatable(t1, t2, t3) => Self {
|
||||
name: "Iterate Relatable".into(),
|
||||
details: vec![
|
||||
("thing-1", Value::Thing(t1.to_owned())),
|
||||
("thing-2", Value::Thing(t2.to_owned())),
|
||||
("thing-3", Value::Thing(t3.to_owned())),
|
||||
],
|
||||
},
|
||||
Iterable::Index(t, p) => Self {
|
||||
name: "Iterate Index".into(),
|
||||
details: vec![("table", Value::from(t.0.to_owned())), ("plan", p.explain())],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExplainItem> for Value {
|
||||
fn from(i: ExplainItem) -> Self {
|
||||
let explain = Object::from(HashMap::from([
|
||||
("operation", i.name),
|
||||
("detail", Value::Object(Object::from(HashMap::from_iter(i.details)))),
|
||||
]));
|
||||
Value::from(explain)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use crate::ctx::Canceller;
|
||||
use crate::ctx::Context;
|
||||
use crate::dbs::explanation::Explanation;
|
||||
use crate::dbs::Statement;
|
||||
use crate::dbs::{Options, Transaction};
|
||||
use crate::doc::CursorDoc;
|
||||
|
@ -14,11 +15,10 @@ use crate::sql::range::Range;
|
|||
use crate::sql::table::Table;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Object;
|
||||
use async_recursion::async_recursion;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
|
||||
pub(crate) enum Iterable {
|
||||
|
@ -88,8 +88,11 @@ impl Iterator {
|
|||
self.setup_limit(&cancel_ctx, opt, txn, stm).await?;
|
||||
// Process the query START clause
|
||||
self.setup_start(&cancel_ctx, opt, txn, stm).await?;
|
||||
// Process any EXPLAIN clause
|
||||
if !self.output_explain(&cancel_ctx, opt, txn, stm)? {
|
||||
|
||||
// Extract the expected behaviour depending on the presence of EXPLAIN with or without FULL
|
||||
let (do_iterate, mut explanation) = Explanation::new(stm.explain(), &self.entries);
|
||||
|
||||
if do_iterate {
|
||||
// Process prepared values
|
||||
self.iterate(&cancel_ctx, opt, txn, stm).await?;
|
||||
// Return any document errors
|
||||
|
@ -106,9 +109,21 @@ impl Iterator {
|
|||
self.output_start(ctx, opt, txn, stm).await?;
|
||||
// Process any LIMIT clause
|
||||
self.output_limit(ctx, opt, txn, stm).await?;
|
||||
// Process any FETCH clause
|
||||
self.output_fetch(ctx, opt, txn, stm).await?;
|
||||
|
||||
if let Some(e) = &mut explanation {
|
||||
e.add_fetch(self.results.len());
|
||||
self.results.clear();
|
||||
} else {
|
||||
// Process any FETCH clause
|
||||
self.output_fetch(ctx, opt, txn, stm).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Output the explanation if any
|
||||
if let Some(e) = explanation {
|
||||
e.output(&mut self.results);
|
||||
}
|
||||
|
||||
// Output the results
|
||||
Ok(mem::take(&mut self.results).into())
|
||||
}
|
||||
|
@ -352,58 +367,6 @@ impl Iterator {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn output_explain(
|
||||
&mut self,
|
||||
_ctx: &Context<'_>,
|
||||
_opt: &Options,
|
||||
_txn: &Transaction,
|
||||
stm: &Statement<'_>,
|
||||
) -> Result<bool, Error> {
|
||||
if !stm.explain() {
|
||||
return Ok(false);
|
||||
}
|
||||
for iter in &self.entries {
|
||||
let (operation, detail) = match iter {
|
||||
Iterable::Value(v) => ("Iterate Value", vec![("value", v.to_owned())]),
|
||||
Iterable::Table(t) => {
|
||||
("Iterate Table", vec![("table", Value::from(t.0.to_owned()))])
|
||||
}
|
||||
Iterable::Thing(t) => {
|
||||
("Iterate Thing", vec![("thing", Value::Thing(t.to_owned()))])
|
||||
}
|
||||
Iterable::Range(r) => {
|
||||
("Iterate Range", vec![("table", Value::from(r.tb.to_owned()))])
|
||||
}
|
||||
Iterable::Edges(e) => {
|
||||
("Iterate Edges", vec![("from", Value::Thing(e.from.to_owned()))])
|
||||
}
|
||||
Iterable::Mergeable(t, v) => (
|
||||
"Iterate Mergeable",
|
||||
vec![("thing", Value::Thing(t.to_owned())), ("value", v.to_owned())],
|
||||
),
|
||||
Iterable::Relatable(t1, t2, t3) => (
|
||||
"Iterate Relatable",
|
||||
vec![
|
||||
("thing-1", Value::Thing(t1.to_owned())),
|
||||
("thing-2", Value::Thing(t2.to_owned())),
|
||||
("thing-3", Value::Thing(t3.to_owned())),
|
||||
],
|
||||
),
|
||||
Iterable::Index(t, p) => (
|
||||
"Iterate Index",
|
||||
vec![("table", Value::from(t.0.to_owned())), ("plan", p.explain())],
|
||||
),
|
||||
};
|
||||
let explain = Object::from(HashMap::from([
|
||||
("operation", Value::from(operation)),
|
||||
("detail", Value::Object(Object::from(HashMap::from_iter(detail)))),
|
||||
]));
|
||||
self.results.push(Value::Object(explain));
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[async_recursion(?Send)]
|
||||
async fn iterate(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! and executors to process the operations. This module also gives a `context` to the transaction.
|
||||
mod auth;
|
||||
mod executor;
|
||||
mod explanation;
|
||||
mod iterator;
|
||||
mod notification;
|
||||
mod options;
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::sql::statements::relate::RelateStatement;
|
|||
use crate::sql::statements::select::SelectStatement;
|
||||
use crate::sql::statements::show::ShowStatement;
|
||||
use crate::sql::statements::update::UpdateStatement;
|
||||
use crate::sql::Explain;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -211,10 +212,10 @@ impl<'a> Statement<'a> {
|
|||
}
|
||||
/// Returns any EXPLAIN clause if specified
|
||||
#[inline]
|
||||
pub fn explain(&self) -> bool {
|
||||
pub fn explain(&self) -> Option<&Explain> {
|
||||
match self {
|
||||
Statement::Select(v) => v.explain,
|
||||
_ => false,
|
||||
Statement::Select(v) => v.explain.as_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
lib/src/sql/explain.rs
Normal file
52
lib/src/sql/explain.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::error::IResult;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::sequence::tuple;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
pub struct Explain(pub bool);
|
||||
|
||||
impl fmt::Display for Explain {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("EXPLAIN")?;
|
||||
if self.0 {
|
||||
f.write_str(" FULL")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn explain(i: &str) -> IResult<&str, Explain> {
|
||||
let (i, _) = tag_no_case("EXPLAIN")(i)?;
|
||||
let (i, full) = opt(tuple((shouldbespace, tag_no_case("FULL"))))(i)?;
|
||||
Ok((i, Explain(full.is_some())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn explain_statement() {
|
||||
let sql = "EXPLAIN";
|
||||
let res = explain(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(out, Explain(false));
|
||||
assert_eq!("EXPLAIN", format!("{}", out));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explain_full_statement() {
|
||||
let sql = "EXPLAIN FULL";
|
||||
let res = explain(sql);
|
||||
assert!(res.is_ok());
|
||||
let out = res.unwrap().1;
|
||||
assert_eq!(out, Explain(true));
|
||||
assert_eq!("EXPLAIN FULL", format!("{}", out));
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ pub(crate) mod edges;
|
|||
pub(crate) mod ending;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod escape;
|
||||
pub(crate) mod explain;
|
||||
pub(crate) mod expression;
|
||||
pub(crate) mod fetch;
|
||||
pub(crate) mod field;
|
||||
|
@ -89,6 +90,7 @@ pub use self::dir::Dir;
|
|||
pub use self::duration::Duration;
|
||||
pub use self::edges::Edges;
|
||||
pub use self::error::Error;
|
||||
pub use self::explain::Explain;
|
||||
pub use self::expression::Expression;
|
||||
pub use self::fetch::Fetch;
|
||||
pub use self::fetch::Fetchs;
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::idx::planner::QueryPlanner;
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::cond::{cond, Cond};
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::explain::{explain, Explain};
|
||||
use crate::sql::fetch::{fetch, Fetchs};
|
||||
use crate::sql::field::{fields, Field, Fields};
|
||||
use crate::sql::group::{group, Groups};
|
||||
|
@ -44,7 +45,7 @@ pub struct SelectStatement {
|
|||
pub version: Option<Version>,
|
||||
pub timeout: Option<Timeout>,
|
||||
pub parallel: bool,
|
||||
pub explain: bool,
|
||||
pub explain: Option<Explain>,
|
||||
}
|
||||
|
||||
impl SelectStatement {
|
||||
|
@ -172,6 +173,9 @@ impl fmt::Display for SelectStatement {
|
|||
if self.parallel {
|
||||
f.write_str(" PARALLEL")?
|
||||
}
|
||||
if let Some(ref v) = self.explain {
|
||||
write!(f, " {v}")?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +201,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
|||
let (i, version) = opt(preceded(shouldbespace, version))(i)?;
|
||||
let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
|
||||
let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
|
||||
let (i, explain) = opt(preceded(shouldbespace, tag_no_case("EXPLAIN")))(i)?;
|
||||
let (i, explain) = opt(preceded(shouldbespace, explain))(i)?;
|
||||
Ok((
|
||||
i,
|
||||
SelectStatement {
|
||||
|
@ -213,7 +217,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
|||
version,
|
||||
timeout,
|
||||
parallel: parallel.is_some(),
|
||||
explain: explain.is_some(),
|
||||
explain,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
1
lib/src/sql/value/serde/ser/explain/mod.rs
Normal file
1
lib/src/sql/value/serde/ser/explain/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub(super) mod opt;
|
74
lib/src/sql/value/serde/ser/explain/opt.rs
Normal file
74
lib/src/sql/value/serde/ser/explain/opt.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Explain;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Option<Explain>;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Option<Explain>, Error>;
|
||||
type SerializeTuple = Impossible<Option<Explain>, Error>;
|
||||
type SerializeTupleStruct = Impossible<Option<Explain>, Error>;
|
||||
type SerializeTupleVariant = Impossible<Option<Explain>, Error>;
|
||||
type SerializeMap = Impossible<Option<Explain>, Error>;
|
||||
type SerializeStruct = Impossible<Option<Explain>, Error>;
|
||||
type SerializeStructVariant = Impossible<Option<Explain>, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an `Option<Explain>`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self.wrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
Ok(Some(Explain(value.serialize(ser::primitive::bool::Serializer.wrap())?)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ser::Serializer as _;
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
let option: Option<Explain> = None;
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some_default() {
|
||||
let option = Some(Explain::default());
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn some_full() {
|
||||
let option = Some(Explain(true));
|
||||
let serialized = option.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(option, serialized);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ mod decimal;
|
|||
mod dir;
|
||||
mod duration;
|
||||
mod edges;
|
||||
mod explain;
|
||||
mod expression;
|
||||
mod fetch;
|
||||
mod field;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::explain::Explain;
|
||||
use crate::sql::statements::SelectStatement;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Cond;
|
||||
|
@ -57,7 +58,7 @@ pub struct SerializeSelectStatement {
|
|||
version: Option<Version>,
|
||||
timeout: Option<Timeout>,
|
||||
parallel: Option<bool>,
|
||||
explain: Option<bool>,
|
||||
explain: Option<Explain>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStruct for SerializeSelectStatement {
|
||||
|
@ -106,7 +107,7 @@ impl serde::ser::SerializeStruct for SerializeSelectStatement {
|
|||
self.parallel = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||
}
|
||||
"explain" => {
|
||||
self.explain = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||
self.explain = value.serialize(ser::explain::opt::Serializer.wrap())?;
|
||||
}
|
||||
key => {
|
||||
return Err(Error::custom(format!("unexpected field `SelectStatement::{key}`")));
|
||||
|
@ -116,12 +117,12 @@ impl serde::ser::SerializeStruct for SerializeSelectStatement {
|
|||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Error> {
|
||||
match (self.expr, self.what, self.parallel, self.explain) {
|
||||
(Some(expr), Some(what), Some(parallel), Some(explain)) => Ok(SelectStatement {
|
||||
match (self.expr, self.what, self.parallel) {
|
||||
(Some(expr), Some(what), Some(parallel)) => Ok(SelectStatement {
|
||||
expr,
|
||||
what,
|
||||
parallel,
|
||||
explain,
|
||||
explain: self.explain,
|
||||
cond: self.cond,
|
||||
split: self.split,
|
||||
group: self.group,
|
||||
|
@ -237,4 +238,14 @@ mod tests {
|
|||
let value: SelectStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(value, stmt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_explain() {
|
||||
let stmt = SelectStatement {
|
||||
explain: Some(Default::default()),
|
||||
..Default::default()
|
||||
};
|
||||
let value: SelectStatement = stmt.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(value, stmt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2937,7 +2937,7 @@ mod tests {
|
|||
assert_eq!(24, std::mem::size_of::<crate::sql::idiom::Idiom>());
|
||||
assert_eq!(24, std::mem::size_of::<crate::sql::table::Table>());
|
||||
assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>());
|
||||
assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>());
|
||||
assert_eq!(40, std::mem::size_of::<crate::sql::model::Model>());
|
||||
assert_eq!(16, std::mem::size_of::<crate::sql::regex::Regex>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::range::Range>>());
|
||||
assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>());
|
||||
|
|
|
@ -59,7 +59,7 @@ async fn select_where_matches_without_using_index_iterator() -> Result<(), Error
|
|||
CREATE blog:2 SET title = 'Foo Bar!';
|
||||
DEFINE ANALYZER simple TOKENIZERS blank,class FILTERS lowercase;
|
||||
DEFINE INDEX blog_title ON blog FIELDS title SEARCH ANALYZER simple BM25(1.2,0.75) HIGHLIGHTS;
|
||||
SELECT id FROM blog WHERE (title @0@ 'hello' AND identifier > 0) OR (title @1@ 'world' AND identifier < 99) EXPLAIN;
|
||||
SELECT id FROM blog WHERE (title @0@ 'hello' AND identifier > 0) OR (title @1@ 'world' AND identifier < 99) EXPLAIN FULL;
|
||||
SELECT id,search::highlight('<em>', '</em>', 1) AS title FROM blog WHERE (title @0@ 'hello' AND identifier > 0) OR (title @1@ 'world' AND identifier < 99);
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
|
@ -79,7 +79,13 @@ async fn select_where_matches_without_using_index_iterator() -> Result<(), Error
|
|||
table: 'blog',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
}
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
count: 1,
|
||||
},
|
||||
operation: 'Fetch'
|
||||
},
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
|
|
|
@ -72,11 +72,12 @@ async fn select_expression_value() -> Result<(), Error> {
|
|||
CREATE thing:b SET number = -5, boolean = false;
|
||||
SELECT VALUE -number FROM thing;
|
||||
SELECT VALUE !boolean FROM thing;
|
||||
SELECT VALUE !boolean FROM thing EXPLAIN FULL;
|
||||
";
|
||||
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).await?;
|
||||
assert_eq!(res.len(), 4);
|
||||
assert_eq!(res.len(), 5);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
|
@ -120,6 +121,25 @@ async fn select_expression_value() -> Result<(), Error> {
|
|||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
detail: {
|
||||
table: 'thing',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
count: 2,
|
||||
},
|
||||
operation: 'Fetch'
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -267,11 +287,12 @@ async fn select_where_field_is_thing_and_with_index() -> Result<(), Error> {
|
|||
CREATE post:1 SET author = person:tobie;
|
||||
CREATE post:2 SET author = person:tobie;
|
||||
SELECT * FROM post WHERE author = person:tobie EXPLAIN;
|
||||
SELECT * FROM post WHERE author = person:tobie EXPLAIN FULL;
|
||||
SELECT * FROM post WHERE author = person:tobie;";
|
||||
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).await?;
|
||||
assert_eq!(res.len(), 6);
|
||||
assert_eq!(res.len(), 7);
|
||||
//
|
||||
let _ = res.remove(0).result?;
|
||||
let _ = res.remove(0).result?;
|
||||
|
@ -297,6 +318,30 @@ async fn select_where_field_is_thing_and_with_index() -> Result<(), Error> {
|
|||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
detail: {
|
||||
plan: {
|
||||
index: 'author',
|
||||
operator: '=',
|
||||
value: person:tobie
|
||||
},
|
||||
table: 'post',
|
||||
},
|
||||
operation: 'Iterate Index'
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
count: 2,
|
||||
},
|
||||
operation: 'Fetch'
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
|
@ -455,3 +500,67 @@ async fn select_where_and_with_fulltext_index() -> Result<(), Error> {
|
|||
assert_eq!(tmp, val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn select_where_explain() -> Result<(), Error> {
|
||||
let sql = "
|
||||
CREATE person:tobie SET name = 'Tobie';
|
||||
CREATE person:jaime SET name = 'Jaime';
|
||||
CREATE software:surreal SET name = 'SurrealDB';
|
||||
SELECT * FROM person,software EXPLAIN;
|
||||
SELECT * FROM person,software EXPLAIN FULL;";
|
||||
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).await?;
|
||||
assert_eq!(res.len(), 5);
|
||||
//
|
||||
let _ = res.remove(0).result?;
|
||||
let _ = res.remove(0).result?;
|
||||
let _ = res.remove(0).result?;
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
detail: {
|
||||
table: 'person',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
table: 'software',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
detail: {
|
||||
table: 'person',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
table: 'software',
|
||||
},
|
||||
operation: 'Iterate Table'
|
||||
},
|
||||
{
|
||||
detail: {
|
||||
count: 3,
|
||||
},
|
||||
operation: 'Fetch'
|
||||
},
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue