Skip to content

Commit

Permalink
Implement SKIP function (gluesql#1325)
Browse files Browse the repository at this point in the history
Add skip Function to LIST Data Type
This function takes an integer n as an argument and returns a new LIST with the first n elements skipped.
  • Loading branch information
cl-kim committed Jul 25, 2023
1 parent 5d96a9a commit aaeedc0
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 0 deletions.
16 changes: 16 additions & 0 deletions core/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ pub enum Function {
expr: Expr,
value: Expr,
},
Skip {
expr: Expr,
size: Expr,
},
Take {
expr: Expr,
size: Expr,
Expand Down Expand Up @@ -384,6 +388,9 @@ impl ToSql for Function {
value = value.to_sql()
}
}
Function::Skip { expr, size } => {
format!("SKIP({}, {})", expr.to_sql(), size.to_sql())
}
Function::Sort { expr, order } => match order {
None => format!("SORT({})", expr.to_sql()),
Some(order) => {
Expand Down Expand Up @@ -1094,6 +1101,15 @@ mod tests {
.to_sql()
);

assert_eq!(
r#"SKIP("list", 2)"#,
&Expr::Function(Box::new(Function::Skip {
expr: Expr::Identifier("list".to_owned()),
size: Expr::Literal(AstLiteral::Number(BigDecimal::from_str("2").unwrap()))
}))
.to_sql()
);

assert_eq!(
r#"SORT("list")"#,
&Expr::Function(Box::new(Function::Sort {
Expand Down
23 changes: 23 additions & 0 deletions core/src/executor/evaluate/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,29 @@ pub fn prepend<'a>(expr: Evaluated<'_>, value: Evaluated<'_>) -> Result<Evaluate
}
}

pub fn skip<'a>(name: String, expr: Evaluated<'_>, size: Evaluated<'_>) -> Result<Evaluated<'a>> {
if expr.is_null() || size.is_null() {
return Ok(Evaluated::Value(Value::Null));
}
let expr: Value = expr.try_into()?;
let size: usize = match size.try_into()? {
Value::I64(number) => {
usize::try_from(number).map_err(|_| EvaluateError::FunctionRequiresUSizeValue(name))?
}
_ => {
return Err(EvaluateError::FunctionRequiresIntegerValue(name).into());
}
};

match expr {
Value::List(l) => {
let l = l.into_iter().skip(size).collect();
Ok(Evaluated::Value(Value::List(l)))
}
_ => Err(EvaluateError::ListTypeRequired.into()),
}
}

pub fn sort<'a>(expr: Evaluated<'_>, order: Evaluated<'_>) -> Result<Evaluated<'a>> {
let expr: Value = expr.try_into()?;
let order: Value = order.try_into()?;
Expand Down
5 changes: 5 additions & 0 deletions core/src/executor/evaluate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,11 @@ async fn evaluate_function<'a, 'b: 'a, 'c: 'a, T: GStore>(
let value = eval(value).await?;
f::prepend(expr, value)
}
Function::Skip { expr, size } => {
let expr = eval(expr).await?;
let size = eval(size).await?;
f::skip(name, expr, size)
}
Function::Sort { expr, order } => {
let expr = eval(expr).await?;
let order = match order {
Expand Down
2 changes: 2 additions & 0 deletions core/src/plan/expr/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ impl Function {
}
| Self::Append { expr, value: expr2 }
| Self::Prepend { expr, value: expr2 }
| Self::Skip { expr, size: expr2 }
| Self::Sort {
expr,
order: Some(expr2),
Expand Down Expand Up @@ -287,6 +288,7 @@ mod tests {
test("REPEAT(col || col2, 3)", &["col || col2", "3"]);
test("REPEAT(column, 2)", &["column", "2"]);
test(r#"UNWRAP(field, "foo.1")"#, &["field", r#""foo.1""#]);
test(r#"SKIP(list, 2)"#, &[r#""list""#, r#"2"#]);

// Triple
test(
Expand Down
7 changes: 7 additions & 0 deletions core/src/translate/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,13 @@ pub fn translate_function(sql_function: &SqlFunction) -> Result<Expr> {

Ok(Expr::Function(Box::new(Function::Prepend { expr, value })))
}
"SKIP" => {
check_len(name, args.len(), 2)?;
let expr = translate_expr(args[0])?;
let size = translate_expr(args[1])?;

Ok(Expr::Function(Box::new(Function::Skip { expr, size })))
}
"SORT" => {
check_len_range(name, args.len(), 1, 2)?;
let expr = translate_expr(args[0])?;
Expand Down
1 change: 1 addition & 0 deletions test-suite/src/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod replace;
pub mod reverse;
pub mod round;
pub mod sign;
pub mod skip;
pub mod sort;
pub mod sqrt_power;
pub mod substr;
Expand Down
58 changes: 58 additions & 0 deletions test-suite/src/function/skip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use {
crate::*,
gluesql_core::{executor::EvaluateError, prelude::Value::*},
};
test_case!(skip, async move {
run!(
"
CREATE TABLE Test (
id INTEGER,
list LIST
)"
);
run!("INSERT INTO Test (id, list) VALUES (1,'[1,2,3,4,5]')");

test! (
name: "skip function with normal usage",
sql : "SELECT SKIP(list, 2) as col1 FROM Test",
expected : Ok(select!(
col1
List;
vec![I64(3), I64(4), I64(5)]
))
);
test! (
name: "skip function with out of range index",
sql : "SELECT SKIP(list, 6) as col1 FROM Test",
expected : Ok(select!(
col1
List;
[].to_vec()
))
);
test! (
name: "skip function with null list",
sql : "SELECT SKIP(NULL, 2) as col1 FROM Test",
expected : Ok(select_with_null!(col1; Null))
);
test! (
name: "skip function with null size",
sql : "SELECT SKIP(list, NULL) as col1 FROM Test",
expected : Ok(select_with_null!(col1; Null))
);
test! (
name: "skip function with non integer parameter",
sql : "SELECT SKIP(list, 'd') as col1 FROM Test",
expected : Err(EvaluateError::FunctionRequiresIntegerValue("SKIP".to_owned()).into())
);
test! (
name: "skip function with non list",
sql : "SELECT SKIP(id, 2) as col1 FROM Test",
expected : Err(EvaluateError::ListTypeRequired.into())
);
test! (
name: "skip function with negative size",
sql : "SELECT SKIP(id, -2) as col1 FROM Test",
expected : Err(EvaluateError::FunctionRequiresUSizeValue("SKIP".to_owned()).into())
);
});
1 change: 1 addition & 0 deletions test-suite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ macro_rules! generate_store_tests {
glue!(function_exp, function::exp_log::exp);
glue!(function_now, function::now::now);
glue!(function_sign, function::sign::sign);
glue!(function_skip, function::skip::skip);
glue!(function_to_date, function::to_date::to_date);
glue!(function_ascii, function::ascii::ascii);
glue!(function_chr, function::chr::chr);
Expand Down

0 comments on commit aaeedc0

Please sign in to comment.