Add ONLY
keyword for single result outputs (#2624)
This commit is contained in:
parent
e008745a54
commit
e316498e96
19 changed files with 418 additions and 288 deletions
|
@ -23,7 +23,7 @@ args = ["doc", "--open", "--no-deps", "--package", "surrealdb", "--features", "r
|
||||||
[tasks.test]
|
[tasks.test]
|
||||||
category = "LOCAL USAGE"
|
category = "LOCAL USAGE"
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["test", "--workspace"]
|
args = ["test", "--workspace", "--no-fail-fast"]
|
||||||
|
|
||||||
# Check
|
# Check
|
||||||
[tasks.cargo-check]
|
[tasks.cargo-check]
|
||||||
|
|
|
@ -428,6 +428,10 @@ pub enum Error {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Can not execute CREATE statement using the specified value
|
||||||
|
#[error("Expected a single result output when using the ONLY keyword")]
|
||||||
|
SingleOnlyOutput,
|
||||||
|
|
||||||
/// The permissions do not allow this query to be run on this table
|
/// The permissions do not allow this query to be run on this table
|
||||||
#[error("You don't have permission to run this query on the `{table}` table")]
|
#[error("You don't have permission to run this query on the `{table}` table")]
|
||||||
TablePermissions {
|
TablePermissions {
|
||||||
|
|
|
@ -21,8 +21,10 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 2)]
|
||||||
pub struct CreateStatement {
|
pub struct CreateStatement {
|
||||||
|
#[revision(start = 2)]
|
||||||
|
pub only: bool,
|
||||||
pub what: Values,
|
pub what: Values,
|
||||||
pub data: Option<Data>,
|
pub data: Option<Data>,
|
||||||
pub output: Option<Output>,
|
pub output: Option<Output>,
|
||||||
|
@ -35,15 +37,6 @@ impl CreateStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match self.what.len() {
|
|
||||||
1 if self.what[0].is_object() => true,
|
|
||||||
1 if self.what[0].is_thing() => true,
|
|
||||||
1 if self.what[0].is_table() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -73,13 +66,27 @@ impl CreateStatement {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
match i.output(ctx, opt, txn, &stm).await? {
|
||||||
|
// This is a single record result
|
||||||
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
|
// There was exactly one result
|
||||||
|
v if v == 1 => Ok(a.remove(0)),
|
||||||
|
// There were no results
|
||||||
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
|
},
|
||||||
|
// This is standard query result
|
||||||
|
v => Ok(v),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CreateStatement {
|
impl fmt::Display for CreateStatement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "CREATE {}", self.what)?;
|
write!(f, "CREATE")?;
|
||||||
|
if self.only {
|
||||||
|
f.write_str(" ONLY")?
|
||||||
|
}
|
||||||
|
write!(f, " {}", self.what)?;
|
||||||
if let Some(ref v) = self.data {
|
if let Some(ref v) = self.data {
|
||||||
write!(f, " {v}")?
|
write!(f, " {v}")?
|
||||||
}
|
}
|
||||||
|
@ -98,6 +105,7 @@ impl fmt::Display for CreateStatement {
|
||||||
|
|
||||||
pub fn create(i: &str) -> IResult<&str, CreateStatement> {
|
pub fn create(i: &str) -> IResult<&str, CreateStatement> {
|
||||||
let (i, _) = tag_no_case("CREATE")(i)?;
|
let (i, _) = tag_no_case("CREATE")(i)?;
|
||||||
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = whats(i)?;
|
let (i, what) = whats(i)?;
|
||||||
let (i, (data, output, timeout, parallel)) = cut(|i| {
|
let (i, (data, output, timeout, parallel)) = cut(|i| {
|
||||||
|
@ -110,6 +118,7 @@ pub fn create(i: &str) -> IResult<&str, CreateStatement> {
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
CreateStatement {
|
CreateStatement {
|
||||||
|
only: only.is_some(),
|
||||||
what,
|
what,
|
||||||
data,
|
data,
|
||||||
output,
|
output,
|
||||||
|
|
|
@ -16,14 +16,15 @@ use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::cut;
|
use nom::combinator::cut;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::terminated;
|
|
||||||
use revision::revisioned;
|
use revision::revisioned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 2)]
|
||||||
pub struct DeleteStatement {
|
pub struct DeleteStatement {
|
||||||
|
#[revision(start = 2)]
|
||||||
|
pub only: bool,
|
||||||
pub what: Values,
|
pub what: Values,
|
||||||
pub cond: Option<Cond>,
|
pub cond: Option<Cond>,
|
||||||
pub output: Option<Output>,
|
pub output: Option<Output>,
|
||||||
|
@ -36,14 +37,6 @@ impl DeleteStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match self.what.len() {
|
|
||||||
1 if self.what[0].is_object() => true,
|
|
||||||
1 if self.what[0].is_thing() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -73,13 +66,27 @@ impl DeleteStatement {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
match i.output(ctx, opt, txn, &stm).await? {
|
||||||
|
// This is a single record result
|
||||||
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
|
// There was exactly one result
|
||||||
|
v if v == 1 => Ok(a.remove(0)),
|
||||||
|
// There were no results
|
||||||
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
|
},
|
||||||
|
// This is standard query result
|
||||||
|
v => Ok(v),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DeleteStatement {
|
impl fmt::Display for DeleteStatement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "DELETE {}", self.what)?;
|
write!(f, "DELETE")?;
|
||||||
|
if self.only {
|
||||||
|
f.write_str(" ONLY")?
|
||||||
|
}
|
||||||
|
write!(f, " {}", self.what)?;
|
||||||
if let Some(ref v) = self.cond {
|
if let Some(ref v) = self.cond {
|
||||||
write!(f, " {v}")?
|
write!(f, " {v}")?
|
||||||
}
|
}
|
||||||
|
@ -98,8 +105,9 @@ impl fmt::Display for DeleteStatement {
|
||||||
|
|
||||||
pub fn delete(i: &str) -> IResult<&str, DeleteStatement> {
|
pub fn delete(i: &str) -> IResult<&str, DeleteStatement> {
|
||||||
let (i, _) = tag_no_case("DELETE")(i)?;
|
let (i, _) = tag_no_case("DELETE")(i)?;
|
||||||
|
let (i, _) = opt(preceded(shouldbespace, tag_no_case("FROM")))(i)?;
|
||||||
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = opt(terminated(tag_no_case("FROM"), shouldbespace))(i)?;
|
|
||||||
let (i, what) = whats(i)?;
|
let (i, what) = whats(i)?;
|
||||||
let (i, (cond, output, timeout, parallel)) = cut(|i| {
|
let (i, (cond, output, timeout, parallel)) = cut(|i| {
|
||||||
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
|
||||||
|
@ -111,6 +119,7 @@ pub fn delete(i: &str) -> IResult<&str, DeleteStatement> {
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
DeleteStatement {
|
DeleteStatement {
|
||||||
|
only: only.is_some(),
|
||||||
what,
|
what,
|
||||||
cond,
|
cond,
|
||||||
output,
|
output,
|
||||||
|
|
|
@ -41,14 +41,6 @@ impl InsertStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match &self.data {
|
|
||||||
Data::SingleExpression(v) if v.is_object() => true,
|
|
||||||
Data::ValuesExpression(v) if v.len() == 1 => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -30,8 +30,10 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 2)]
|
||||||
pub struct RelateStatement {
|
pub struct RelateStatement {
|
||||||
|
#[revision(start = 2)]
|
||||||
|
pub only: bool,
|
||||||
pub kind: Value,
|
pub kind: Value,
|
||||||
pub from: Value,
|
pub from: Value,
|
||||||
pub with: Value,
|
pub with: Value,
|
||||||
|
@ -47,16 +49,6 @@ impl RelateStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match (&self.from, &self.with) {
|
|
||||||
(v, w) if v.is_object() && w.is_object() => true,
|
|
||||||
(v, w) if v.is_object() && w.is_thing() => true,
|
|
||||||
(v, w) if v.is_thing() && w.is_object() => true,
|
|
||||||
(v, w) if v.is_thing() && w.is_thing() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -183,13 +175,27 @@ impl RelateStatement {
|
||||||
// Assign the statement
|
// Assign the statement
|
||||||
let stm = Statement::from(self);
|
let stm = Statement::from(self);
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
match i.output(ctx, opt, txn, &stm).await? {
|
||||||
|
// This is a single record result
|
||||||
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
|
// There was exactly one result
|
||||||
|
v if v == 1 => Ok(a.remove(0)),
|
||||||
|
// There were no results
|
||||||
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
|
},
|
||||||
|
// This is standard query result
|
||||||
|
v => Ok(v),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RelateStatement {
|
impl fmt::Display for RelateStatement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "RELATE {} -> {} -> {}", self.from, self.kind, self.with)?;
|
write!(f, "RELATE")?;
|
||||||
|
if self.only {
|
||||||
|
f.write_str(" ONLY")?
|
||||||
|
}
|
||||||
|
write!(f, " {} -> {} -> {}", self.from, self.kind, self.with)?;
|
||||||
if self.uniq {
|
if self.uniq {
|
||||||
f.write_str(" UNIQUE")?
|
f.write_str(" UNIQUE")?
|
||||||
}
|
}
|
||||||
|
@ -211,6 +217,7 @@ impl fmt::Display for RelateStatement {
|
||||||
|
|
||||||
pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
||||||
let (i, _) = tag_no_case("RELATE")(i)?;
|
let (i, _) = tag_no_case("RELATE")(i)?;
|
||||||
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, path) = relate_oi(i)?;
|
let (i, path) = relate_oi(i)?;
|
||||||
let (i, uniq) = opt(preceded(shouldbespace, tag_no_case("UNIQUE")))(i)?;
|
let (i, uniq) = opt(preceded(shouldbespace, tag_no_case("UNIQUE")))(i)?;
|
||||||
|
@ -221,6 +228,7 @@ pub fn relate(i: &str) -> IResult<&str, RelateStatement> {
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
RelateStatement {
|
RelateStatement {
|
||||||
|
only: only.is_some(),
|
||||||
kind: path.0,
|
kind: path.0,
|
||||||
from: path.1,
|
from: path.1,
|
||||||
with: path.2,
|
with: path.2,
|
||||||
|
|
|
@ -36,10 +36,12 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 2)]
|
||||||
pub struct SelectStatement {
|
pub struct SelectStatement {
|
||||||
pub expr: Fields,
|
pub expr: Fields,
|
||||||
pub omit: Option<Idioms>,
|
pub omit: Option<Idioms>,
|
||||||
|
#[revision(start = 2)]
|
||||||
|
pub only: bool,
|
||||||
pub what: Values,
|
pub what: Values,
|
||||||
pub with: Option<With>,
|
pub with: Option<With>,
|
||||||
pub cond: Option<Cond>,
|
pub cond: Option<Cond>,
|
||||||
|
@ -72,14 +74,6 @@ impl SelectStatement {
|
||||||
}
|
}
|
||||||
self.cond.as_ref().map_or(false, |v| v.writeable())
|
self.cond.as_ref().map_or(false, |v| v.writeable())
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match self.what.len() {
|
|
||||||
1 if self.what[0].is_object() => true,
|
|
||||||
1 if self.what[0].is_thing() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -131,17 +125,25 @@ impl SelectStatement {
|
||||||
v => i.ingest(Iterable::Value(v)),
|
v => i.ingest(Iterable::Value(v)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// Create a new context
|
||||||
|
let mut ctx = Context::new(ctx);
|
||||||
// Assign the statement
|
// Assign the statement
|
||||||
let stm = Statement::from(self);
|
let stm = Statement::from(self);
|
||||||
// Add query executors if any
|
// Add query executors if any
|
||||||
if planner.has_executors() {
|
if planner.has_executors() {
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
ctx.set_query_planner(&planner);
|
ctx.set_query_planner(&planner);
|
||||||
|
}
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(&ctx, opt, txn, &stm).await
|
match i.output(&ctx, opt, txn, &stm).await? {
|
||||||
} else {
|
// This is a single record result
|
||||||
// Output the results
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
i.output(ctx, opt, txn, &stm).await
|
// There was exactly one result
|
||||||
|
v if v == 1 => Ok(a.remove(0)),
|
||||||
|
// There were no results
|
||||||
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
|
},
|
||||||
|
// This is standard query result
|
||||||
|
v => Ok(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +154,11 @@ impl fmt::Display for SelectStatement {
|
||||||
if let Some(ref v) = self.omit {
|
if let Some(ref v) = self.omit {
|
||||||
write!(f, " OMIT {v}")?
|
write!(f, " OMIT {v}")?
|
||||||
}
|
}
|
||||||
write!(f, " FROM {}", self.what)?;
|
write!(f, " FROM")?;
|
||||||
|
if self.only {
|
||||||
|
f.write_str(" ONLY")?
|
||||||
|
}
|
||||||
|
write!(f, " {}", self.what)?;
|
||||||
if let Some(ref v) = self.with {
|
if let Some(ref v) = self.with {
|
||||||
write!(f, " {v}")?
|
write!(f, " {v}")?
|
||||||
}
|
}
|
||||||
|
@ -200,6 +206,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
||||||
let (i, omit) = opt(preceded(shouldbespace, omit))(i)?;
|
let (i, omit) = opt(preceded(shouldbespace, omit))(i)?;
|
||||||
let (i, _) = cut(shouldbespace)(i)?;
|
let (i, _) = cut(shouldbespace)(i)?;
|
||||||
let (i, _) = cut(tag_no_case("FROM"))(i)?;
|
let (i, _) = cut(tag_no_case("FROM"))(i)?;
|
||||||
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = cut(shouldbespace)(i)?;
|
let (i, _) = cut(shouldbespace)(i)?;
|
||||||
let (i, what) = cut(selects)(i)?;
|
let (i, what) = cut(selects)(i)?;
|
||||||
let (i, with) = opt(preceded(shouldbespace, with))(i)?;
|
let (i, with) = opt(preceded(shouldbespace, with))(i)?;
|
||||||
|
@ -222,6 +229,7 @@ pub fn select(i: &str) -> IResult<&str, SelectStatement> {
|
||||||
SelectStatement {
|
SelectStatement {
|
||||||
expr,
|
expr,
|
||||||
omit,
|
omit,
|
||||||
|
only: only.is_some(),
|
||||||
what,
|
what,
|
||||||
with,
|
with,
|
||||||
cond,
|
cond,
|
||||||
|
|
|
@ -21,8 +21,10 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
||||||
#[revisioned(revision = 1)]
|
#[revisioned(revision = 2)]
|
||||||
pub struct UpdateStatement {
|
pub struct UpdateStatement {
|
||||||
|
#[revision(start = 2)]
|
||||||
|
pub only: bool,
|
||||||
pub what: Values,
|
pub what: Values,
|
||||||
pub data: Option<Data>,
|
pub data: Option<Data>,
|
||||||
pub cond: Option<Cond>,
|
pub cond: Option<Cond>,
|
||||||
|
@ -36,14 +38,6 @@ impl UpdateStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
/// Check if this statement is for a single record
|
|
||||||
pub(crate) fn single(&self) -> bool {
|
|
||||||
match self.what.len() {
|
|
||||||
1 if self.what[0].is_object() => true,
|
|
||||||
1 if self.what[0].is_thing() => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Process this type returning a computed simple Value
|
/// Process this type returning a computed simple Value
|
||||||
pub(crate) async fn compute(
|
pub(crate) async fn compute(
|
||||||
&self,
|
&self,
|
||||||
|
@ -73,13 +67,27 @@ impl UpdateStatement {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
match i.output(ctx, opt, txn, &stm).await? {
|
||||||
|
// This is a single record result
|
||||||
|
Value::Array(mut a) if self.only => match a.len() {
|
||||||
|
// There was exactly one result
|
||||||
|
v if v == 1 => Ok(a.remove(0)),
|
||||||
|
// There were no results
|
||||||
|
_ => Err(Error::SingleOnlyOutput),
|
||||||
|
},
|
||||||
|
// This is standard query result
|
||||||
|
v => Ok(v),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for UpdateStatement {
|
impl fmt::Display for UpdateStatement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "UPDATE {}", self.what)?;
|
write!(f, "UPDATE")?;
|
||||||
|
if self.only {
|
||||||
|
f.write_str(" ONLY")?
|
||||||
|
}
|
||||||
|
write!(f, " {}", self.what)?;
|
||||||
if let Some(ref v) = self.data {
|
if let Some(ref v) = self.data {
|
||||||
write!(f, " {v}")?
|
write!(f, " {v}")?
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,7 @@ impl fmt::Display for UpdateStatement {
|
||||||
|
|
||||||
pub fn update(i: &str) -> IResult<&str, UpdateStatement> {
|
pub fn update(i: &str) -> IResult<&str, UpdateStatement> {
|
||||||
let (i, _) = tag_no_case("UPDATE")(i)?;
|
let (i, _) = tag_no_case("UPDATE")(i)?;
|
||||||
|
let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, what) = whats(i)?;
|
let (i, what) = whats(i)?;
|
||||||
let (i, data) = opt(preceded(shouldbespace, data))(i)?;
|
let (i, data) = opt(preceded(shouldbespace, data))(i)?;
|
||||||
|
@ -111,6 +120,7 @@ pub fn update(i: &str) -> IResult<&str, UpdateStatement> {
|
||||||
Ok((
|
Ok((
|
||||||
i,
|
i,
|
||||||
UpdateStatement {
|
UpdateStatement {
|
||||||
|
only: only.is_some(),
|
||||||
what,
|
what,
|
||||||
data,
|
data,
|
||||||
cond,
|
cond,
|
||||||
|
|
|
@ -75,145 +75,25 @@ impl Subquery {
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
doc: Option<&CursorDoc<'_>>,
|
doc: Option<&CursorDoc<'_>>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
|
// Duplicate context
|
||||||
|
let mut ctx = Context::new(ctx);
|
||||||
|
// Add parent document
|
||||||
|
if let Some(doc) = doc {
|
||||||
|
ctx.add_value("parent", doc.doc.as_ref());
|
||||||
|
}
|
||||||
// Process the subquery
|
// Process the subquery
|
||||||
match self {
|
match self {
|
||||||
Self::Value(ref v) => v.compute(ctx, opt, txn, doc).await,
|
Self::Value(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
Self::Ifelse(ref v) => v.compute(ctx, opt, txn, doc).await,
|
Self::Ifelse(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
Self::Output(ref v) => v.compute(ctx, opt, txn, doc).await,
|
Self::Output(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
Self::Define(ref v) => v.compute(ctx, opt, txn, doc).await,
|
Self::Define(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
Self::Remove(ref v) => v.compute(ctx, opt, txn, doc).await,
|
Self::Remove(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
Self::Select(ref v) => {
|
Self::Select(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
// Is this a single output?
|
Self::Create(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
let one = v.single();
|
Self::Update(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
// Duplicate context
|
Self::Delete(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
let mut ctx = Context::new(ctx);
|
Self::Relate(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
// Add parent document
|
Self::Insert(ref v) => v.compute(&ctx, opt, txn, doc).await,
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Create(ref v) => {
|
|
||||||
// Is this a single output?
|
|
||||||
let one = v.single();
|
|
||||||
// Duplicate context
|
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
// Add parent document
|
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Update(ref v) => {
|
|
||||||
// Is this a single output?
|
|
||||||
let one = v.single();
|
|
||||||
// Duplicate context
|
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
// Add parent document
|
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Delete(ref v) => {
|
|
||||||
// Is this a single output?
|
|
||||||
let one = v.single();
|
|
||||||
// Duplicate context
|
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
// Add parent document
|
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Relate(ref v) => {
|
|
||||||
// Is this a single output?
|
|
||||||
let one = v.single();
|
|
||||||
// Duplicate context
|
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
// Add parent document
|
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Insert(ref v) => {
|
|
||||||
// Is this a single output?
|
|
||||||
let one = v.single();
|
|
||||||
// Duplicate context
|
|
||||||
let mut ctx = Context::new(ctx);
|
|
||||||
// Add parent document
|
|
||||||
if let Some(doc) = doc {
|
|
||||||
ctx.add_value("parent", doc.doc.as_ref());
|
|
||||||
}
|
|
||||||
// Process subquery
|
|
||||||
match v.compute(&ctx, opt, txn, doc).await? {
|
|
||||||
// This is a single record result
|
|
||||||
Value::Array(mut a) if one => match a.len() {
|
|
||||||
// There was at least one result
|
|
||||||
v if v > 0 => Ok(a.remove(0)),
|
|
||||||
// There were no results
|
|
||||||
_ => Ok(Value::None),
|
|
||||||
},
|
|
||||||
// This is standard query result
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl ser::Serializer for Serializer {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SerializeCreateStatement {
|
pub struct SerializeCreateStatement {
|
||||||
|
only: Option<bool>,
|
||||||
what: Option<Values>,
|
what: Option<Values>,
|
||||||
data: Option<Data>,
|
data: Option<Data>,
|
||||||
output: Option<Output>,
|
output: Option<Output>,
|
||||||
|
@ -55,6 +56,9 @@ impl serde::ser::SerializeStruct for SerializeCreateStatement {
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
match key {
|
match key {
|
||||||
|
"only" => {
|
||||||
|
self.only = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||||
|
}
|
||||||
"what" => {
|
"what" => {
|
||||||
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
||||||
}
|
}
|
||||||
|
@ -82,6 +86,7 @@ impl serde::ser::SerializeStruct for SerializeCreateStatement {
|
||||||
fn end(self) -> Result<Self::Ok, Error> {
|
fn end(self) -> Result<Self::Ok, Error> {
|
||||||
match (self.what, self.parallel) {
|
match (self.what, self.parallel) {
|
||||||
(Some(what), Some(parallel)) => Ok(CreateStatement {
|
(Some(what), Some(parallel)) => Ok(CreateStatement {
|
||||||
|
only: self.only.is_some_and(|v| v),
|
||||||
what,
|
what,
|
||||||
parallel,
|
parallel,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
|
|
|
@ -38,6 +38,7 @@ impl ser::Serializer for Serializer {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SerializeDeleteStatement {
|
pub struct SerializeDeleteStatement {
|
||||||
|
only: Option<bool>,
|
||||||
what: Option<Values>,
|
what: Option<Values>,
|
||||||
cond: Option<Cond>,
|
cond: Option<Cond>,
|
||||||
output: Option<Output>,
|
output: Option<Output>,
|
||||||
|
@ -54,6 +55,9 @@ impl serde::ser::SerializeStruct for SerializeDeleteStatement {
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
match key {
|
match key {
|
||||||
|
"only" => {
|
||||||
|
self.only = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||||
|
}
|
||||||
"what" => {
|
"what" => {
|
||||||
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
||||||
}
|
}
|
||||||
|
@ -79,6 +83,7 @@ impl serde::ser::SerializeStruct for SerializeDeleteStatement {
|
||||||
fn end(self) -> Result<Self::Ok, Error> {
|
fn end(self) -> Result<Self::Ok, Error> {
|
||||||
match (self.what, self.parallel) {
|
match (self.what, self.parallel) {
|
||||||
(Some(what), Some(parallel)) => Ok(DeleteStatement {
|
(Some(what), Some(parallel)) => Ok(DeleteStatement {
|
||||||
|
only: self.only.is_some_and(|v| v),
|
||||||
what,
|
what,
|
||||||
parallel,
|
parallel,
|
||||||
cond: self.cond,
|
cond: self.cond,
|
||||||
|
|
|
@ -38,6 +38,7 @@ impl ser::Serializer for Serializer {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SerializeRelateStatement {
|
pub struct SerializeRelateStatement {
|
||||||
|
only: Option<bool>,
|
||||||
kind: Option<Value>,
|
kind: Option<Value>,
|
||||||
from: Option<Value>,
|
from: Option<Value>,
|
||||||
with: Option<Value>,
|
with: Option<Value>,
|
||||||
|
@ -57,6 +58,9 @@ impl serde::ser::SerializeStruct for SerializeRelateStatement {
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
match key {
|
match key {
|
||||||
|
"only" => {
|
||||||
|
self.only = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||||
|
}
|
||||||
"kind" => {
|
"kind" => {
|
||||||
self.kind = Some(value.serialize(ser::value::Serializer.wrap())?);
|
self.kind = Some(value.serialize(ser::value::Serializer.wrap())?);
|
||||||
}
|
}
|
||||||
|
@ -92,6 +96,7 @@ impl serde::ser::SerializeStruct for SerializeRelateStatement {
|
||||||
match (self.kind, self.from, self.with, self.uniq, self.parallel) {
|
match (self.kind, self.from, self.with, self.uniq, self.parallel) {
|
||||||
(Some(kind), Some(from), Some(with), Some(uniq), Some(parallel)) => {
|
(Some(kind), Some(from), Some(with), Some(uniq), Some(parallel)) => {
|
||||||
Ok(RelateStatement {
|
Ok(RelateStatement {
|
||||||
|
only: self.only.is_some_and(|v| v),
|
||||||
kind,
|
kind,
|
||||||
from,
|
from,
|
||||||
with,
|
with,
|
||||||
|
|
|
@ -50,6 +50,7 @@ impl ser::Serializer for Serializer {
|
||||||
pub struct SerializeSelectStatement {
|
pub struct SerializeSelectStatement {
|
||||||
expr: Option<Fields>,
|
expr: Option<Fields>,
|
||||||
omit: Option<Idioms>,
|
omit: Option<Idioms>,
|
||||||
|
only: Option<bool>,
|
||||||
what: Option<Values>,
|
what: Option<Values>,
|
||||||
with: Option<With>,
|
with: Option<With>,
|
||||||
cond: Option<Cond>,
|
cond: Option<Cond>,
|
||||||
|
@ -80,6 +81,9 @@ impl serde::ser::SerializeStruct for SerializeSelectStatement {
|
||||||
"omit" => {
|
"omit" => {
|
||||||
self.omit = value.serialize(ser::idiom::vec::opt::Serializer.wrap())?.map(Idioms);
|
self.omit = value.serialize(ser::idiom::vec::opt::Serializer.wrap())?.map(Idioms);
|
||||||
}
|
}
|
||||||
|
"only" => {
|
||||||
|
self.only = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||||
|
}
|
||||||
"what" => {
|
"what" => {
|
||||||
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
||||||
}
|
}
|
||||||
|
@ -131,6 +135,7 @@ impl serde::ser::SerializeStruct for SerializeSelectStatement {
|
||||||
(Some(expr), Some(what), Some(parallel)) => Ok(SelectStatement {
|
(Some(expr), Some(what), Some(parallel)) => Ok(SelectStatement {
|
||||||
expr,
|
expr,
|
||||||
omit: self.omit,
|
omit: self.omit,
|
||||||
|
only: self.only.is_some_and(|v| v),
|
||||||
what,
|
what,
|
||||||
with: self.with,
|
with: self.with,
|
||||||
parallel,
|
parallel,
|
||||||
|
|
|
@ -40,6 +40,7 @@ impl ser::Serializer for Serializer {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SerializeUpdateStatement {
|
pub struct SerializeUpdateStatement {
|
||||||
|
only: Option<bool>,
|
||||||
what: Option<Values>,
|
what: Option<Values>,
|
||||||
data: Option<Data>,
|
data: Option<Data>,
|
||||||
cond: Option<Cond>,
|
cond: Option<Cond>,
|
||||||
|
@ -57,6 +58,9 @@ impl serde::ser::SerializeStruct for SerializeUpdateStatement {
|
||||||
T: ?Sized + Serialize,
|
T: ?Sized + Serialize,
|
||||||
{
|
{
|
||||||
match key {
|
match key {
|
||||||
|
"only" => {
|
||||||
|
self.only = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?);
|
||||||
|
}
|
||||||
"what" => {
|
"what" => {
|
||||||
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
self.what = Some(Values(value.serialize(ser::value::vec::Serializer.wrap())?));
|
||||||
}
|
}
|
||||||
|
@ -87,6 +91,7 @@ impl serde::ser::SerializeStruct for SerializeUpdateStatement {
|
||||||
fn end(self) -> Result<Self::Ok, Error> {
|
fn end(self) -> Result<Self::Ok, Error> {
|
||||||
match (self.what, self.parallel) {
|
match (self.what, self.parallel) {
|
||||||
(Some(what), Some(parallel)) => Ok(UpdateStatement {
|
(Some(what), Some(parallel)) => Ok(UpdateStatement {
|
||||||
|
only: self.only.is_some_and(|v| v),
|
||||||
what,
|
what,
|
||||||
parallel,
|
parallel,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
|
|
|
@ -173,7 +173,7 @@ async fn create_with_id() -> Result<(), Error> {
|
||||||
async fn create_with_custom_function() -> Result<(), Error> {
|
async fn create_with_custom_function() -> Result<(), Error> {
|
||||||
let sql = "
|
let sql = "
|
||||||
DEFINE FUNCTION fn::record::create($data: any) {
|
DEFINE FUNCTION fn::record::create($data: any) {
|
||||||
RETURN CREATE person:ulid() CONTENT { data: $data } RETURN AFTER;
|
RETURN CREATE ONLY person:ulid() CONTENT { data: $data } RETURN AFTER;
|
||||||
};
|
};
|
||||||
RETURN fn::record::create({ test: true, name: 'Tobie' });
|
RETURN fn::record::create({ test: true, name: 'Tobie' });
|
||||||
RETURN fn::record::create({ test: true, name: 'Jaime' });
|
RETURN fn::record::create({ test: true, name: 'Jaime' });
|
||||||
|
|
161
lib/tests/return.rs
Normal file
161
lib/tests/return.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
mod parse;
|
||||||
|
use parse::Parse;
|
||||||
|
mod helpers;
|
||||||
|
use helpers::new_ds;
|
||||||
|
use surrealdb::dbs::Session;
|
||||||
|
use surrealdb::err::Error;
|
||||||
|
use surrealdb::sql::Value;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn return_subquery_only() -> Result<(), Error> {
|
||||||
|
let sql = "
|
||||||
|
CREATE person:tobie SET name = 'Tobie';
|
||||||
|
CREATE person:jaime SET name = 'Jaime';
|
||||||
|
LET $single = person:tobie;
|
||||||
|
--
|
||||||
|
SELECT name FROM person;
|
||||||
|
SELECT VALUE name FROM person;
|
||||||
|
SELECT name FROM ONLY person;
|
||||||
|
SELECT VALUE name FROM ONLY person;
|
||||||
|
SELECT name FROM person:tobie;
|
||||||
|
SELECT VALUE name FROM person:tobie;
|
||||||
|
SELECT name FROM ONLY person:tobie;
|
||||||
|
SELECT VALUE name FROM ONLY person:tobie;
|
||||||
|
SELECT name FROM $single;
|
||||||
|
SELECT VALUE name FROM $single;
|
||||||
|
SELECT name FROM ONLY $single;
|
||||||
|
SELECT VALUE name FROM ONLY $single;
|
||||||
|
--
|
||||||
|
RETURN SELECT name FROM person;
|
||||||
|
RETURN SELECT VALUE name FROM person;
|
||||||
|
RETURN SELECT name FROM ONLY person;
|
||||||
|
RETURN SELECT VALUE name FROM ONLY person;
|
||||||
|
RETURN SELECT name FROM person:tobie;
|
||||||
|
RETURN SELECT VALUE name FROM person:tobie;
|
||||||
|
RETURN SELECT name FROM ONLY person:tobie;
|
||||||
|
RETURN SELECT VALUE name FROM ONLY person:tobie;
|
||||||
|
RETURN SELECT name FROM $single;
|
||||||
|
RETURN SELECT VALUE name FROM $single;
|
||||||
|
RETURN SELECT name FROM ONLY $single;
|
||||||
|
RETURN SELECT VALUE name FROM ONLY $single;
|
||||||
|
";
|
||||||
|
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(), 27);
|
||||||
|
//
|
||||||
|
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;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Jaime' }, { name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Jaime', 'Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Expected a single result output when using the ONLY keyword"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Expected a single result output when using the ONLY keyword"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("{ name: 'Tobie' }");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::from("Tobie");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("{ name: 'Tobie' }");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::from("Tobie");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Jaime' }, { name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Jaime', 'Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Expected a single result output when using the ONLY keyword"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Expected a single result output when using the ONLY keyword"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("{ name: 'Tobie' }");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::from("Tobie");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("[{ name: 'Tobie' }]");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("['Tobie']");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse("{ name: 'Tobie' }");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::from("Tobie");
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -252,7 +252,7 @@ async fn script_query_from_script_select() -> Result<(), Error> {
|
||||||
async fn script_query_from_script() -> Result<(), Error> {
|
async fn script_query_from_script() -> Result<(), Error> {
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
RETURN function() {
|
RETURN function() {
|
||||||
return await surrealdb.query(`CREATE article:test SET name = "The daily news", issue_number = 3`)
|
return await surrealdb.query(`CREATE ONLY article:test SET name = "The daily news", issue_number = 3`)
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let dbs = new_ds().await?;
|
let dbs = new_ds().await?;
|
||||||
|
@ -289,7 +289,7 @@ async fn script_query_from_script() -> Result<(), Error> {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn script_value_function_params() -> Result<(), Error> {
|
async fn script_value_function_params() -> Result<(), Error> {
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
LET $test = CREATE article:test SET name = "The daily news", issue_number = 3;
|
LET $test = CREATE ONLY article:test SET name = "The daily news", issue_number = 3;
|
||||||
RETURN function() {
|
RETURN function() {
|
||||||
return await surrealdb.value(`$test.name`)
|
return await surrealdb.value(`$test.name`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,9 +370,11 @@ async fn select_writeable_subqueries() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
id: tester:test
|
id: tester:test
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -380,7 +382,7 @@ async fn select_writeable_subqueries() -> Result<(), Error> {
|
||||||
assert!(tmp.is_ok());
|
assert!(tmp.is_ok());
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse("tester:test");
|
let val = Value::parse("[tester:test]");
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result;
|
let tmp = res.remove(0).result;
|
||||||
|
|
|
@ -73,9 +73,11 @@ async fn subquery_select() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
adult: true
|
adult: true
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -146,16 +148,18 @@ async fn subquery_ifelse_set() -> Result<(), Error> {
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::None;
|
let val = Value::parse("[]");
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -165,24 +169,28 @@ async fn subquery_ifelse_set() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
count: 1,
|
count: 1,
|
||||||
id: person:test,
|
id: person:test,
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -192,25 +200,29 @@ async fn subquery_ifelse_set() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
count: 1,
|
count: 1,
|
||||||
id: person:test,
|
id: person:test,
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -261,16 +273,18 @@ async fn subquery_ifelse_array() -> Result<(), Error> {
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::None;
|
let val = Value::parse("[]");
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -280,24 +294,28 @@ async fn subquery_ifelse_array() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
count: 1,
|
count: 1,
|
||||||
id: person:test,
|
id: person:test,
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
@ -307,26 +325,30 @@ async fn subquery_ifelse_array() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
count: 1,
|
count: 1,
|
||||||
id: person:test,
|
id: person:test,
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"{
|
"[
|
||||||
|
{
|
||||||
sport: [
|
sport: [
|
||||||
'basketball',
|
'basketball',
|
||||||
'football',
|
'football',
|
||||||
'football',
|
'football',
|
||||||
]
|
]
|
||||||
}",
|
}
|
||||||
|
]",
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in a new issue