diff --git a/lib/fuzz/fuzz_targets/fuzz_executor.dict b/lib/fuzz/fuzz_targets/fuzz_executor.dict index 54d345b3..fac72df1 100644 --- a/lib/fuzz/fuzz_targets/fuzz_executor.dict +++ b/lib/fuzz/fuzz_targets/fuzz_executor.dict @@ -151,6 +151,7 @@ "array::all(" "array::any(" "array::append(" +"array::at(" "array::boolean_and(" "array::boolean_not(" "array::boolean_or(" diff --git a/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict b/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict index 53fa0f48..21965e32 100644 --- a/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict +++ b/lib/fuzz/fuzz_targets/fuzz_sql_parser.dict @@ -150,6 +150,7 @@ "array::add(" "array::all(" "array::any(" +"array::at(" "array::append(" "array::boolean_and(" "array::boolean_not(" diff --git a/lib/src/fnc/array.rs b/lib/src/fnc/array.rs index e75688f6..406fc386 100644 --- a/lib/src/fnc/array.rs +++ b/lib/src/fnc/array.rs @@ -45,6 +45,14 @@ pub fn append((mut array, value): (Array, Value)) -> Result { Ok(array.into()) } +pub fn at((array, i): (Array, i64)) -> Result { + let mut idx = i as usize; + if i < 0 { + idx = (array.len() as i64 + i) as usize; + } + Ok(array.get(idx).cloned().unwrap_or_default()) +} + pub fn boolean_and((lh, rh): (Array, Array)) -> Result { let longest_length = lh.len().max(rh.len()); let mut results = Array::with_capacity(longest_length); @@ -391,7 +399,7 @@ pub mod sort { #[cfg(test)] mod tests { - use super::{first, join, last, slice}; + use super::{at, first, join, last, slice}; use crate::sql::{Array, Value}; #[test] @@ -455,4 +463,13 @@ mod tests { test(vec!["hello", "world"].into(), "world".into()); test(Array::new(), Value::None); } + + #[test] + fn array_at() { + fn test(arr: Array, i: i64, expected: Value) { + assert_eq!(at((arr, i)).unwrap(), expected); + } + test(vec!["hello", "world"].into(), -2, "hello".into()); + test(vec!["hello", "world"].into(), -3, Value::None); + } } diff --git a/lib/src/fnc/mod.rs b/lib/src/fnc/mod.rs index 7a3c2f57..abf47231 100644 --- a/lib/src/fnc/mod.rs +++ b/lib/src/fnc/mod.rs @@ -82,6 +82,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec) -> Result array::all, "array::any" => array::any, "array::append" => array::append, + "array::at" => array::at, "array::boolean_and" => array::boolean_and, "array::boolean_not" => array::boolean_not, "array::boolean_or" => array::boolean_or, diff --git a/lib/src/fnc/script/modules/surrealdb/functions/array.rs b/lib/src/fnc/script/modules/surrealdb/functions/array.rs index 804354dd..e2aaef18 100644 --- a/lib/src/fnc/script/modules/surrealdb/functions/array.rs +++ b/lib/src/fnc/script/modules/surrealdb/functions/array.rs @@ -10,6 +10,7 @@ impl_module_def!( "add" => run, "all" => run, "any" => run, + "at" => run, "append" => run, "boolean_and" => run, "boolean_not" => run, diff --git a/lib/src/sql/function.rs b/lib/src/sql/function.rs index 8a93d0f9..7c99dac7 100644 --- a/lib/src/sql/function.rs +++ b/lib/src/sql/function.rs @@ -281,6 +281,7 @@ fn function_array(i: &str) -> IResult<&str, &str> { tag("all"), tag("any"), tag("append"), + tag("at"), tag("boolean_and"), tag("boolean_not"), tag("boolean_or"), diff --git a/lib/tests/function.rs b/lib/tests/function.rs index 1dc1bf4e..250261a0 100644 --- a/lib/tests/function.rs +++ b/lib/tests/function.rs @@ -200,6 +200,48 @@ async fn function_array_append() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn function_array_at() -> Result<(), Error> { + let sql = r#" + RETURN array::at(["hello", "world"], 0); + RETURN array::at(["hello", "world"], -1); + RETURN array::at(["hello", "world"], 3); + RETURN array::at(["hello", "world"], -3); + RETURN array::at([], 0); + RETURN array::at([], 3); + RETURN array::at([], -3); + "#; + 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(), 7); + // + let tmp = res.remove(0).result?; + let val = Value::Strand("hello".into()); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::Strand("world".into()); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + assert_eq!(tmp, Value::None); + // + let tmp = res.remove(0).result?; + assert_eq!(tmp, Value::None); + // + let tmp = res.remove(0).result?; + assert_eq!(tmp, Value::None); + // + let tmp = res.remove(0).result?; + assert_eq!(tmp, Value::None); + // + let tmp = res.remove(0).result?; + assert_eq!(tmp, Value::None); + // + Ok(()) +} + #[tokio::test] async fn function_array_boolean_and() -> Result<(), Error> { test_queries(