Cleanup Fetch resolution (#4312)
Co-authored-by: Gerard Guillemas Martos <gguillemas@users.noreply.github.com>
This commit is contained in:
parent
6a5dca9214
commit
968b1714dc
15 changed files with 316 additions and 89 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -4785,12 +4785,24 @@ name = "revision"
|
|||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4df61cfb2522f24fd6aa90ce3489c2c8660a181075e7bac3ae7bdf22287d238f"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"revision-derive 0.7.0",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "revision"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98b99dba8f2787c9af2e46b17ff38437d213d46c8970b550e6b79b862bf7629"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"geo 0.26.0",
|
||||
"regex",
|
||||
"revision-derive",
|
||||
"revision-derive 0.8.0",
|
||||
"roaring",
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
|
@ -4811,6 +4823,19 @@ dependencies = [
|
|||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "revision-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3721b4a8e52f9e52c54f74f482a4f550601f5c44cb7876606a2ab79cb09469c1"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rexie"
|
||||
version = "0.4.2"
|
||||
|
@ -5896,7 +5921,7 @@ dependencies = [
|
|||
"rand 0.8.5",
|
||||
"rcgen",
|
||||
"reqwest",
|
||||
"revision",
|
||||
"revision 0.8.0",
|
||||
"rmp-serde",
|
||||
"rmpv",
|
||||
"rustyline",
|
||||
|
@ -5953,7 +5978,7 @@ dependencies = [
|
|||
"reblessive",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"revision",
|
||||
"revision 0.8.0",
|
||||
"ring 0.17.8",
|
||||
"rust_decimal",
|
||||
"rustls 0.21.11",
|
||||
|
@ -6042,7 +6067,7 @@ dependencies = [
|
|||
"regex",
|
||||
"regex-syntax 0.8.3",
|
||||
"reqwest",
|
||||
"revision",
|
||||
"revision 0.8.0",
|
||||
"ring 0.17.8",
|
||||
"rmpv",
|
||||
"roaring",
|
||||
|
@ -6159,7 +6184,7 @@ dependencies = [
|
|||
"lru",
|
||||
"parking_lot",
|
||||
"quick_cache",
|
||||
"revision",
|
||||
"revision 0.7.1",
|
||||
"sha2",
|
||||
"tokio",
|
||||
"vart",
|
||||
|
|
|
@ -81,7 +81,7 @@ reqwest = { version = "0.11.22", default-features = false, features = [
|
|||
"blocking",
|
||||
"gzip",
|
||||
] }
|
||||
revision = { version = "0.7.0", features = [
|
||||
revision = { version = "0.8.0", features = [
|
||||
"chrono",
|
||||
"geo",
|
||||
"roaring",
|
||||
|
@ -96,9 +96,9 @@ serde = { version = "1.0.193", features = ["derive"] }
|
|||
serde_json = "1.0.108"
|
||||
serde_pack = { version = "1.1.2", package = "rmp-serde" }
|
||||
surrealdb = { version = "2", path = "lib", features = [
|
||||
"protocol-http",
|
||||
"protocol-ws",
|
||||
"rustls",
|
||||
"protocol-http",
|
||||
"protocol-ws",
|
||||
"rustls",
|
||||
] }
|
||||
tempfile = "3.8.1"
|
||||
thiserror = "1.0.50"
|
||||
|
|
|
@ -122,7 +122,7 @@ reqwest = { version = "0.11.22", default-features = false, features = [
|
|||
"stream",
|
||||
"multipart",
|
||||
], optional = true }
|
||||
revision = { version = "0.7.0", features = ["chrono", "geo", "roaring", "regex", "rust_decimal", "uuid"] }
|
||||
revision = { version = "0.8.0", features = ["chrono", "geo", "roaring", "regex", "rust_decimal", "uuid"] }
|
||||
rmpv = "1.0.1"
|
||||
roaring = { version = "0.10.2", features = ["serde"] }
|
||||
rocksdb = { version = "0.21.0", features = ["lz4", "snappy"], optional = true }
|
||||
|
|
|
@ -16,9 +16,6 @@ use crate::sql::range::Range;
|
|||
use crate::sql::table::Table;
|
||||
use crate::sql::thing::Thing;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Idiom;
|
||||
use crate::sql::Part;
|
||||
use reblessive::tree::Stk;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use reblessive::TreeStack;
|
||||
|
@ -446,23 +443,11 @@ impl Iterator {
|
|||
stm: &Statement<'_>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(fetchs) = stm.fetch() {
|
||||
let mut idioms = Vec::with_capacity(fetchs.0.len());
|
||||
for fetch in fetchs.iter() {
|
||||
let i: &Idiom;
|
||||
let new_idiom: Idiom;
|
||||
if let Value::Idiom(idiom) = &fetch.0 {
|
||||
i = idiom;
|
||||
} else if let Value::Param(param) = &fetch.0 {
|
||||
let p = param.compute(stk, ctx, opt, None).await?;
|
||||
if let Value::Strand(s) = p {
|
||||
let p: Part = Part::Field(Ident(s.0));
|
||||
new_idiom = Idiom(vec![p]);
|
||||
i = &new_idiom;
|
||||
} else {
|
||||
return Err(Error::Thrown("Parameter should be a string".to_string()));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::Thrown("Invalid field".to_string()));
|
||||
}
|
||||
fetch.compute(stk, ctx, opt, &mut idioms).await?;
|
||||
}
|
||||
for i in &idioms {
|
||||
let mut values = self.results.take()?;
|
||||
// Loop over each result value
|
||||
for obj in &mut values {
|
||||
|
|
|
@ -166,6 +166,12 @@ pub enum Error {
|
|||
field: String,
|
||||
},
|
||||
|
||||
/// The FETCH clause accepts idioms, strings and fields.
|
||||
#[error("Found {value} on FETCH CLAUSE, but FETCH expects an idiom, a string or fields")]
|
||||
InvalidFetch {
|
||||
value: Value,
|
||||
},
|
||||
|
||||
#[error("Found '{field}' in SPLIT ON clause on line {line}, but field is not present in SELECT expression")]
|
||||
InvalidSplit {
|
||||
line: usize,
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::err::Error;
|
||||
use crate::sql::fmt::Fmt;
|
||||
use crate::sql::statements::info::InfoStructure;
|
||||
use crate::sql::Value;
|
||||
use crate::sql::{Idiom, Value};
|
||||
use crate::syn;
|
||||
use reblessive::tree::Stk;
|
||||
use revision::revisioned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
@ -39,22 +44,111 @@ impl InfoStructure for Fetchs {
|
|||
}
|
||||
}
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[revisioned(revision = 2)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
pub struct Fetch(pub Value);
|
||||
pub struct Fetch(
|
||||
#[revision(end = 2, convert_fn = "convert_fetch_idiom")] pub Idiom,
|
||||
#[revision(start = 2)] pub Value,
|
||||
);
|
||||
|
||||
impl Fetch {
|
||||
fn convert_fetch_idiom(&mut self, _revision: u16, old: Idiom) -> Result<(), revision::Error> {
|
||||
self.1 = if old.is_empty() {
|
||||
Value::None
|
||||
} else {
|
||||
Value::Idiom(old)
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn compute(
|
||||
&self,
|
||||
stk: &mut Stk,
|
||||
ctx: &Context<'_>,
|
||||
opt: &Options,
|
||||
idioms: &mut Vec<Idiom>,
|
||||
) -> Result<(), Error> {
|
||||
let strand_or_idiom = |v: Value| match v {
|
||||
Value::Strand(s) => Ok(Idiom::from(s.0)),
|
||||
Value::Idiom(i) => Ok(i.to_owned()),
|
||||
v => Err(Error::InvalidFetch {
|
||||
value: v,
|
||||
}),
|
||||
};
|
||||
match &self.1 {
|
||||
Value::Idiom(idiom) => {
|
||||
idioms.push(idiom.to_owned());
|
||||
Ok(())
|
||||
}
|
||||
Value::Param(param) => {
|
||||
let v = param.compute(stk, ctx, opt, None).await?;
|
||||
idioms.push(strand_or_idiom(v)?);
|
||||
Ok(())
|
||||
}
|
||||
Value::Function(f) => {
|
||||
if f.name() == Some("type::field") {
|
||||
let v = match f.args().first().unwrap() {
|
||||
Value::Param(v) => v.compute(stk, ctx, opt, None).await?,
|
||||
v => v.to_owned(),
|
||||
};
|
||||
idioms.push(strand_or_idiom(v)?);
|
||||
Ok(())
|
||||
} else if f.name() == Some("type::fields") {
|
||||
// Get the first argument which is guaranteed to exist
|
||||
let args = match f.args().first().unwrap() {
|
||||
Value::Param(v) => v.compute(stk, ctx, opt, None).await?,
|
||||
v => v.to_owned(),
|
||||
};
|
||||
// This value is always an array, so we can convert it
|
||||
let args: Vec<Value> = args.try_into()?;
|
||||
// This value is always an array, so we can convert it
|
||||
for v in args.into_iter() {
|
||||
let i = match v {
|
||||
Value::Param(v) => {
|
||||
strand_or_idiom(v.compute(stk, ctx, opt, None).await?)?
|
||||
}
|
||||
Value::Strand(s) => syn::idiom(s.as_str())?,
|
||||
Value::Idiom(i) => i,
|
||||
v => {
|
||||
return Err(Error::InvalidFetch {
|
||||
value: v,
|
||||
})
|
||||
}
|
||||
};
|
||||
idioms.push(i);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidFetch {
|
||||
value: Value::Function(f.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
v => Err(Error::InvalidFetch {
|
||||
value: v.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for Fetch {
|
||||
fn from(value: Value) -> Self {
|
||||
Self(Idiom(vec![]), value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Fetch {
|
||||
type Target = Value;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Fetch {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
Display::fmt(&self.1, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::doc::CursorDoc;
|
||||
use crate::err::Error;
|
||||
use crate::sql::fetch::Fetchs;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::{Ident, Idiom};
|
||||
use crate::{ctx::Context, sql::Part};
|
||||
use derive::Store;
|
||||
use reblessive::tree::Stk;
|
||||
use revision::revisioned;
|
||||
|
@ -39,23 +38,11 @@ impl OutputStatement {
|
|||
let mut value = self.what.compute(stk, ctx, opt, doc).await?;
|
||||
// Fetch any
|
||||
if let Some(fetchs) = &self.fetch {
|
||||
let mut idioms = Vec::with_capacity(fetchs.0.len());
|
||||
for fetch in fetchs.iter() {
|
||||
let i: &Idiom;
|
||||
let new_idiom: Idiom;
|
||||
if let Value::Idiom(idiom) = &fetch.0 {
|
||||
i = idiom;
|
||||
} else if let Value::Param(param) = &fetch.0 {
|
||||
let p = param.compute(stk, ctx, opt, None).await?;
|
||||
if let Value::Strand(s) = p {
|
||||
let p: Part = Part::Field(Ident(s.0));
|
||||
new_idiom = Idiom(vec![p]);
|
||||
i = &new_idiom;
|
||||
} else {
|
||||
return Err(Error::Thrown("Parameter should be a string".to_string()));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::Thrown("Invalid field".to_string()));
|
||||
}
|
||||
fetch.compute(stk, ctx, opt, &mut idioms).await?
|
||||
}
|
||||
for i in &idioms {
|
||||
value.fetch(stk, ctx, opt, i).await?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,84 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::{Fetch, Idiom, Value};
|
||||
use ser::Serializer as _;
|
||||
use serde::ser::Error as _;
|
||||
use serde::ser::Impossible;
|
||||
use serde::Serialize;
|
||||
|
||||
pub(super) mod vec;
|
||||
|
||||
struct FetchSerializer;
|
||||
|
||||
impl ser::Serializer for FetchSerializer {
|
||||
type Ok = Fetch;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Fetch, Error>;
|
||||
type SerializeTuple = Impossible<Fetch, Error>;
|
||||
type SerializeTupleStruct = SerializeFetch;
|
||||
type SerializeTupleVariant = Impossible<Fetch, Error>;
|
||||
type SerializeMap = Impossible<Fetch, Error>;
|
||||
type SerializeStruct = Impossible<Fetch, Error>;
|
||||
type SerializeStructVariant = Impossible<Fetch, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "a `Fetch`";
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Error> {
|
||||
Ok(SerializeFetch::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SerializeFetch {
|
||||
index: usize,
|
||||
idiom: Option<Idiom>,
|
||||
value: Option<Value>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleStruct for SerializeFetch {
|
||||
type Ok = Fetch;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
match self.index {
|
||||
0 => {
|
||||
self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?));
|
||||
}
|
||||
1 => {
|
||||
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
|
||||
}
|
||||
index => {
|
||||
return Err(Error::custom(format!("unexpected `Fetch` index `{index}`")));
|
||||
}
|
||||
}
|
||||
self.index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
match (self.idiom, self.value) {
|
||||
(Some(idiom), Some(value)) => Ok(Fetch(idiom, value)),
|
||||
_ => Err(ser::Error::custom("`Fetch` missing required value(s)")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fetch() {
|
||||
let fetch = Fetch::default();
|
||||
let serialized = fetch.serialize(FetchSerializer.wrap()).unwrap();
|
||||
assert_eq!(fetch, serialized);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ pub mod opt;
|
|||
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::value::serde::ser::fetch::FetchSerializer;
|
||||
use crate::sql::Fetch;
|
||||
use ser::Serializer as _;
|
||||
use serde::ser::Impossible;
|
||||
|
@ -52,7 +53,7 @@ impl serde::ser::SerializeSeq for SerializeFetchVec {
|
|||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
self.0.push(Fetch(value.serialize(ser::value::Serializer.wrap())?));
|
||||
self.0.push(value.serialize(FetchSerializer.wrap())?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ impl Parser<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses a list of idioms seperated by a `,`
|
||||
/// Parses a list of idioms separated by a `,`
|
||||
pub async fn parse_idiom_list(&mut self, ctx: &mut Stk) -> ParseResult<Vec<Idiom>> {
|
||||
let mut res = vec![self.parse_plain_idiom(ctx).await?];
|
||||
while self.eat(t!(",")) {
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
use reblessive::Stk;
|
||||
|
||||
use crate::sql::Fetch;
|
||||
use crate::{
|
||||
sql::{
|
||||
changefeed::ChangeFeed,
|
||||
index::{Distance, VectorType},
|
||||
Base, Cond, Data, Duration, Fetch, Fetchs, Field, Fields, Group, Groups, Ident, Idiom,
|
||||
Output, Permission, Permissions, Tables, Timeout, Value, View,
|
||||
Base, Cond, Data, Duration, Fetchs, Field, Fields, Group, Groups, Ident, Idiom, Output,
|
||||
Permission, Permissions, Tables, Timeout, Value, View,
|
||||
},
|
||||
syn::{
|
||||
parser::{
|
||||
|
@ -108,18 +109,39 @@ impl Parser<'_> {
|
|||
if !self.eat(t!("FETCH")) {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut fetchs = vec![Fetch(self.try_parse_param_or_idiom(ctx).await?)];
|
||||
let mut fetchs = self.try_parse_param_or_idiom_or_fields(ctx).await?;
|
||||
while self.eat(t!(",")) {
|
||||
fetchs.push(Fetch(self.try_parse_param_or_idiom(ctx).await?));
|
||||
fetchs.append(&mut self.try_parse_param_or_idiom_or_fields(ctx).await?);
|
||||
}
|
||||
Ok(Some(Fetchs(fetchs)))
|
||||
}
|
||||
|
||||
pub async fn try_parse_param_or_idiom(&mut self, ctx: &mut Stk) -> ParseResult<Value> {
|
||||
if self.peek().kind == t!("$param") {
|
||||
Ok(Value::Param(self.next_token_value()?))
|
||||
} else {
|
||||
Ok(Value::Idiom(self.parse_plain_idiom(ctx).await?))
|
||||
pub async fn try_parse_param_or_idiom_or_fields(
|
||||
&mut self,
|
||||
ctx: &mut Stk,
|
||||
) -> ParseResult<Vec<Fetch>> {
|
||||
match self.peek().kind {
|
||||
t!("$param") => Ok(vec![Value::Param(self.next_token_value()?).into()]),
|
||||
t!("TYPE") => {
|
||||
let fields = self.parse_fields(ctx).await?;
|
||||
let fetches = fields
|
||||
.0
|
||||
.into_iter()
|
||||
.filter_map(|f| {
|
||||
if let Field::Single {
|
||||
expr,
|
||||
..
|
||||
} = f
|
||||
{
|
||||
Some(expr.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(fetches)
|
||||
}
|
||||
_ => Ok(vec![Value::Idiom(self.parse_plain_idiom(ctx).await?).into()]),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1771,9 +1771,10 @@ SELECT bar as foo,[1,2],bar OMIT bar FROM ONLY a,1
|
|||
start: Some(Start(Value::Object(Object(
|
||||
[("a".to_owned(), Value::Bool(true))].into_iter().collect()
|
||||
)))),
|
||||
fetch: Some(Fetchs(vec![Fetch(Value::Idiom(Idiom(vec![Part::Field(Ident(
|
||||
"foo".to_owned()
|
||||
))])))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(
|
||||
Idiom(vec![]),
|
||||
Value::Idiom(Idiom(vec![Part::Field(Ident("foo".to_owned()))]))
|
||||
)])),
|
||||
version: Some(Version(Datetime(expected_datetime))),
|
||||
timeout: None,
|
||||
parallel: false,
|
||||
|
@ -2035,11 +2036,14 @@ fn parse_live() {
|
|||
assert_eq!(
|
||||
stmt.fetch,
|
||||
Some(Fetchs(vec![
|
||||
Fetch(Value::Idiom(Idiom(vec![
|
||||
Part::Field(Ident("a".to_owned())),
|
||||
Part::Where(Value::Idiom(Idiom(vec![Part::Field(Ident("foo".to_owned()))]))),
|
||||
]))),
|
||||
Fetch(Value::Idiom(Idiom(vec![Part::Field(Ident("b".to_owned()))]))),
|
||||
Fetch(
|
||||
Idiom(vec![]),
|
||||
Value::Idiom(Idiom(vec![
|
||||
Part::Field(Ident("a".to_owned())),
|
||||
Part::Where(Value::Idiom(Idiom(vec![Part::Field(Ident("foo".to_owned()))]))),
|
||||
]))
|
||||
),
|
||||
Fetch(Idiom(vec![]), Value::Idiom(Idiom(vec![Part::Field(Ident("b".to_owned()))]))),
|
||||
])),
|
||||
)
|
||||
}
|
||||
|
@ -2063,9 +2067,10 @@ fn parse_return() {
|
|||
res,
|
||||
Statement::Output(OutputStatement {
|
||||
what: Value::Idiom(Idiom(vec![Part::Field(Ident("RETRUN".to_owned()))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(Value::Idiom(Idiom(vec![Part::Field(
|
||||
Ident("RETURN".to_owned()).to_owned()
|
||||
)])))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(
|
||||
Idiom(vec![]),
|
||||
Value::Idiom(Idiom(vec![Part::Field(Ident("RETURN".to_owned()).to_owned())]))
|
||||
)])),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -503,9 +503,10 @@ fn statements() -> Vec<Statement> {
|
|||
start: Some(Start(Value::Object(Object(
|
||||
[("a".to_owned(), Value::Bool(true))].into_iter().collect(),
|
||||
)))),
|
||||
fetch: Some(Fetchs(vec![Fetch(Value::Idiom(Idiom(vec![Part::Field(Ident(
|
||||
"foo".to_owned(),
|
||||
))])))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(
|
||||
Idiom(vec![]),
|
||||
Value::Idiom(Idiom(vec![Part::Field(Ident("foo".to_owned()))])),
|
||||
)])),
|
||||
version: Some(Version(Datetime(expected_datetime))),
|
||||
timeout: None,
|
||||
parallel: false,
|
||||
|
@ -593,9 +594,10 @@ fn statements() -> Vec<Statement> {
|
|||
}),
|
||||
Statement::Output(OutputStatement {
|
||||
what: Value::Idiom(Idiom(vec![Part::Field(Ident("RETRUN".to_owned()))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(Value::Idiom(Idiom(vec![Part::Field(
|
||||
Ident("RETURN".to_owned()).to_owned(),
|
||||
)])))])),
|
||||
fetch: Some(Fetchs(vec![Fetch(
|
||||
Idiom(vec![]),
|
||||
Value::Idiom(Idiom(vec![Part::Field(Ident("RETURN".to_owned()).to_owned())])),
|
||||
)])),
|
||||
}),
|
||||
Statement::Relate(RelateStatement {
|
||||
only: true,
|
||||
|
|
|
@ -82,7 +82,7 @@ reqwest = { version = "0.11.22", default-features = false, features = [
|
|||
"stream",
|
||||
"multipart",
|
||||
], optional = true }
|
||||
revision = { version = "0.7.0", features = [
|
||||
revision = { version = "0.8.0", features = [
|
||||
"chrono",
|
||||
"geo",
|
||||
"roaring",
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
mod parse;
|
||||
use parse::Parse;
|
||||
mod helpers;
|
||||
use crate::helpers::skip_ok;
|
||||
use helpers::new_ds;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::err::Error;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb_core::sql::Number;
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_relate_select() -> Result<(), Error> {
|
||||
|
@ -21,13 +23,17 @@ async fn create_relate_select() -> Result<(), Error> {
|
|||
SELECT *, ->bought->product.* AS products FROM user;
|
||||
SELECT *, ->bought AS products FROM user FETCH products;
|
||||
SELECT *, ->(bought AS purchases) FROM user FETCH purchases, purchases.out;
|
||||
LET $param1 = \"purchases\";
|
||||
LET $param1 = 'purchases';
|
||||
SELECT *, ->(bought AS purchases) FROM user FETCH $param1, purchases.out;
|
||||
SELECT *, ->(bought AS purchases) FROM user FETCH type::field('purchases'), purchases.out;
|
||||
SELECT *, ->(bought AS purchases) FROM user FETCH type::fields([$param1, 'purchases.out']);
|
||||
LET $faultyparam = 1.0f;
|
||||
SELECT *, ->(bought AS purchases) FROM user FETCH $faultyparam, purchases.out;
|
||||
";
|
||||
let dbs = new_ds().await?;
|
||||
let ses = Session::owner().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||
assert_eq!(res.len(), 14);
|
||||
assert_eq!(res.len(), 18);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
|
@ -278,12 +284,13 @@ async fn create_relate_select() -> Result<(), Error> {
|
|||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
// Skip the LET $param statements
|
||||
skip_ok(res, 1)?;
|
||||
//
|
||||
// Remove LET statement result
|
||||
res.remove(0);
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
for i in 0..3 {
|
||||
let tmp = res.remove(0).result.unwrap_or_else(|e| panic!("{i} {e}"));
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: user:jaime,
|
||||
name: 'Jaime',
|
||||
|
@ -324,7 +331,17 @@ async fn create_relate_select() -> Result<(), Error> {
|
|||
]
|
||||
}
|
||||
]",
|
||||
);
|
||||
);
|
||||
assert_eq!(tmp, val, "{i}");
|
||||
}
|
||||
// Ignore LET statement result
|
||||
res.remove(0);
|
||||
match res.remove(0).result {
|
||||
Err(Error::InvalidFetch {
|
||||
value: Value::Number(Number::Float(1.0)),
|
||||
}) => {}
|
||||
found => panic!("Expected Err(Error::InvalidFetch), found '{found:?}'"),
|
||||
};
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue