Support FOR statements inside code blocks (#2664)

This commit is contained in:
Tobie Morgan Hitchcock 2023-09-10 12:30:30 +01:00 committed by GitHub
parent a4856327cf
commit c3acab4614
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 5 deletions

View file

@ -9,6 +9,7 @@ use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
use crate::sql::statements::create::{create, CreateStatement};
use crate::sql::statements::define::{define, DefineStatement};
use crate::sql::statements::delete::{delete, DeleteStatement};
use crate::sql::statements::foreach::{foreach, ForeachStatement};
use crate::sql::statements::ifelse::{ifelse, IfelseStatement};
use crate::sql::statements::insert::{insert, InsertStatement};
use crate::sql::statements::output::{output, OutputStatement};
@ -88,6 +89,9 @@ impl Block {
// Always errors immediately
v.compute(&ctx, opt, txn, doc).await?;
}
Entry::Foreach(v) => {
v.compute(&ctx, opt, txn, doc).await?;
}
Entry::Ifelse(v) => {
v.compute(&ctx, opt, txn, doc).await?;
}
@ -210,6 +214,7 @@ pub enum Entry {
Throw(ThrowStatement),
Break(BreakStatement),
Continue(ContinueStatement),
Foreach(ForeachStatement),
}
impl PartialOrd for Entry {
@ -238,6 +243,7 @@ impl Entry {
Self::Throw(v) => v.writeable(),
Self::Break(v) => v.writeable(),
Self::Continue(v) => v.writeable(),
Self::Foreach(v) => v.writeable(),
}
}
}
@ -260,6 +266,7 @@ impl Display for Entry {
Self::Throw(v) => write!(f, "{v}"),
Self::Break(v) => write!(f, "{v}"),
Self::Continue(v) => write!(f, "{v}"),
Self::Foreach(v) => write!(f, "{v}"),
}
}
}
@ -282,6 +289,7 @@ pub fn entry(i: &str) -> IResult<&str, Entry> {
map(throw, Entry::Throw),
map(r#break, Entry::Break),
map(r#continue, Entry::Continue),
map(foreach, Entry::Foreach),
map(value, Entry::Value),
)),
mightbespace,

View file

@ -7,6 +7,7 @@ use crate::sql::comment::{mightbespace, shouldbespace};
use crate::sql::error::{expect_tag_no_case, IResult};
use crate::sql::param::{param, Param};
use crate::sql::value::{value, Value};
use async_recursion::async_recursion;
use derive::Store;
use nom::bytes::complete::tag_no_case;
use nom::combinator::cut;
@ -28,12 +29,14 @@ impl ForeachStatement {
self.range.writeable() || self.block.writeable()
}
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
doc: Option<&'async_recursion CursorDoc<'_>>,
) -> Result<Value, Error> {
// Check the loop data
match &self.range.compute(ctx, opt, txn, doc).await? {
@ -58,6 +61,7 @@ impl ForeachStatement {
Entry::Value(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Break(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Continue(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Foreach(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Ifelse(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Select(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Create(v) => v.compute(&ctx, opt, txn, doc).await,

View file

@ -7,23 +7,23 @@ use surrealdb::err::Error;
use surrealdb::sql::Value;
#[tokio::test]
async fn foreach() -> Result<(), Error> {
async fn foreach_simple() -> Result<(), Error> {
let sql = "
FOR $test in [1, 2, 3] {
FOR $test IN [1, 2, 3] {
IF $test == 2 {
BREAK;
};
UPDATE type::thing('person', $test) SET test = $test;
};
SELECT * FROM person;
FOR $test in [4, 5, 6] {
FOR $test IN [4, 5, 6] {
IF $test == 5 {
CONTINUE;
};
UPDATE type::thing('person', $test) SET test = $test;
};
SELECT * FROM person;
FOR $test in <future> { [7, 8, 9] } {
FOR $test IN <future> { [7, 8, 9] } {
IF $test > 8 {
THROW 'This is an error';
};
@ -96,3 +96,106 @@ async fn foreach() -> Result<(), Error> {
//
Ok(())
}
#[tokio::test]
async fn foreach_nested() -> Result<(), Error> {
let sql = "
FOR $i IN [1,2,3,4,5] {
FOR $j IN [6,7,8,9,0] {
CREATE type::thing('person', [$i, $j]);
}
};
SELECT * FROM person;
";
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(), 2);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:[1, 0]
},
{
id: person:[1, 6]
},
{
id: person:[1, 7]
},
{
id: person:[1, 8]
},
{
id: person:[1, 9]
},
{
id: person:[2, 0]
},
{
id: person:[2, 6]
},
{
id: person:[2, 7]
},
{
id: person:[2, 8]
},
{
id: person:[2, 9]
},
{
id: person:[3, 0]
},
{
id: person:[3, 6]
},
{
id: person:[3, 7]
},
{
id: person:[3, 8]
},
{
id: person:[3, 9]
},
{
id: person:[4, 0]
},
{
id: person:[4, 6]
},
{
id: person:[4, 7]
},
{
id: person:[4, 8]
},
{
id: person:[4, 9]
},
{
id: person:[5, 0]
},
{
id: person:[5, 6]
},
{
id: person:[5, 7]
},
{
id: person:[5, 8]
},
{
id: person:[5, 9]
}
]",
);
assert_eq!(tmp, val);
//
Ok(())
}