Skip to content

Commit

Permalink
Implement LAST_DAY function gluesql#1315 (gluesql#1323)
Browse files Browse the repository at this point in the history
Extract the last day of the month for the given date | timestamp
  • Loading branch information
cjy13753 committed Jul 28, 2023
1 parent 0ea13d0 commit a624028
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 0 deletions.
11 changes: 11 additions & 0 deletions core/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub enum Function {
Degrees(Expr),
Now(),
Pi(),
LastDay(Expr),
Ltrim {
expr: Expr,
chars: Option<Expr>,
Expand Down Expand Up @@ -307,6 +308,7 @@ impl ToSql for Function {
Function::Degrees(e) => format!("DEGREES({})", e.to_sql()),
Function::Now() => "NOW()".to_owned(),
Function::Pi() => "PI()".to_owned(),
Function::LastDay(expr) => format!("LAST_DAY({})", expr.to_sql()),
Function::Ltrim { expr, chars } => match chars {
None => format!("LTRIM({})", expr.to_sql()),
Some(chars) => format!("LTRIM({}, {})", expr.to_sql(), chars.to_sql()),
Expand Down Expand Up @@ -994,6 +996,15 @@ mod tests {
.to_sql()
);

assert_eq!(
"LAST_DAY(DATE '2022-10-12')",
&Expr::Function(Box::new(Function::LastDay(Expr::TypedString {
data_type: DataType::Date,
value: "2022-10-12".to_owned()
})))
.to_sql()
);

assert_eq!(
"TO_DATE('2022-10-12', '%Y-%m-%d')",
&Expr::Function(Box::new(Function::ToDate {
Expand Down
3 changes: 3 additions & 0 deletions core/src/executor/evaluate/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub enum EvaluateError {
#[error("function requires point value: {0}")]
FunctionRequiresPointValue(String),

#[error("function requires date or datetime value: {0}")]
FunctionRequiresDateOrDateTimeValue(String),

#[error("function requires one of string, list, map types: {0}")]
FunctionRequiresStrOrListOrMapValue(String),

Expand Down
13 changes: 13 additions & 0 deletions core/src/executor/evaluate/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use {
data::{Key, Point, Value, ValueError},
result::{Error, Result},
},
chrono::{Datelike, Duration, Months},
md5::{Digest, Md5},
rand::{rngs::StdRng, Rng, SeedableRng},
std::ops::ControlFlow,
Expand Down Expand Up @@ -673,6 +674,18 @@ pub fn format<'a>(
}
}

pub fn last_day<'a>(name: String, expr: Evaluated<'_>) -> Result<Evaluated<'a>> {
let date = match expr.try_into()? {
Value::Date(date) => date,
Value::Timestamp(timestamp) => timestamp.date(),
_ => return Err(EvaluateError::FunctionRequiresDateOrDateTimeValue(name).into()),
};

Ok(Evaluated::from(Value::Date(
date + Months::new(1) - Duration::days(date.day() as i64),
)))
}

pub fn to_date<'a>(
name: String,
expr: Evaluated<'_>,
Expand Down
4 changes: 4 additions & 0 deletions core/src/executor/evaluate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,10 @@ async fn evaluate_function<'a, 'b: 'a, 'c: 'a, T: GStore>(

f::lpad_or_rpad(name, expr, size, fill)
}
Function::LastDay(expr) => {
let expr = eval(expr).await?;
f::last_day(name, expr)
}
Function::Trim {
expr,
filter_chars,
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 @@ -45,6 +45,7 @@ impl Function {
| Self::Ascii(expr)
| Self::Chr(expr)
| Self::Md5(expr)
| Self::LastDay(expr)
| Self::Ltrim { expr, chars: None }
| Self::Rtrim { expr, chars: None }
| Self::Trim {
Expand Down Expand Up @@ -245,6 +246,7 @@ mod tests {
test("LOG2(16)", &["16"]);
test("LOG10(150 - 50)", &["150 - 50"]);
test("SQRT(144)", &["144"]);
test("LASTDAY(DATE '2020-01-01')", &[r#"DATE '2020-01-01'"#]);
test(r#"LTRIM(" hello")"#, &[r#"" hello""#]);
test(r#"RTRIM("world ")"#, &[r#""world ""#]);
test(r#"TRIM(" rust ")"#, &[r#"" rust ""#]);
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 @@ -367,6 +367,13 @@ pub fn translate_function(sql_function: &SqlFunction) -> Result<Expr> {

Ok(Expr::Function(Box::new(Function::Gcd { left, right })))
}
"LAST_DAY" => {
check_len(name, args.len(), 1)?;

let expr = translate_expr(args[0])?;

Ok(Expr::Function(Box::new(Function::LastDay(expr))))
}
"LCM" => {
check_len(name, args.len(), 2)?;

Expand Down
64 changes: 64 additions & 0 deletions test-suite/src/function/last_day.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use {
crate::*, chrono::NaiveDate, gluesql_core::executor::EvaluateError,
gluesql_core::prelude::Value::*,
};

test_case!(last_day, async move {
run!(
"CREATE TABLE LastDay (
id INTEGER,
date DATE,
timestamp TIMESTAMP,
);"
);

run!("INSERT INTO LastDay (id, date) VALUES (1, LAST_DAY(DATE '2017-12-15'));");
test! {
name: "Should insert the last day of the month that a given date belongs to",
sql: "SELECT date FROM LastDay WHERE id = 1;",
expected: Ok(select!(
date;
Date;
NaiveDate::from_ymd_opt(2017, 12, 31).unwrap()
))
};

run!("INSERT INTO LastDay (id, date) VALUES (2, DATE '2017-01-01');");
test! {
name: "Should return the last day of the month that a retrieved date belongs to",
sql: "SELECT LAST_DAY(date) as date FROM LastDay WHERE id = 2;",
expected: Ok(select!(
date;
Date;
NaiveDate::from_ymd_opt(2017, 1, 31).unwrap()
))
};

run!("INSERT INTO LastDay (id, date) VALUES (3, LAST_DAY(TIMESTAMP '2017-12-15 12:12:20'));");
test! {
name: "Should insert the last day of the month that a given timestamp belongs to",
sql: "SELECT date FROM LastDay WHERE id = 3;",
expected: Ok(select!(
date;
Date;
NaiveDate::from_ymd_opt(2017, 12, 31).unwrap()
))
};

run!("INSERT INTO LastDay (id, timestamp) VALUES (4, TIMESTAMP '2017-01-01 12:12:20');");
test! {
name: "Should return the last day of the month that a retrieved timestamp belongs to",
sql: "SELECT LAST_DAY(timestamp) as date FROM LastDay WHERE id = 4;",
expected: Ok(select!(
date;
Date;
NaiveDate::from_ymd_opt(2017, 1, 31).unwrap()
))
};

test! {
name: "Should only give date or timestamp value to LAST_DAY function",
sql: "VALUES (LAST_DAY('dfafsdf3243252454325342'));",
expected: Err(EvaluateError::FunctionRequiresDateOrDateTimeValue("LAST_DAY".to_owned()).into())
};
});
1 change: 1 addition & 0 deletions test-suite/src/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod geometry;
pub mod ifnull;
pub mod initcap;
pub mod is_empty;
pub mod last_day;
pub mod left_right;
pub mod length;
pub mod lpad_rpad;
Expand Down
1 change: 1 addition & 0 deletions test-suite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ macro_rules! generate_store_tests {
glue!(function_rand, function::rand::rand);
glue!(function_floor, function::floor::floor);
glue!(function_format, function::format::format);
glue!(function_last_day, function::last_day::last_day);
glue!(function_ln, function::exp_log::ln);
glue!(function_log, function::exp_log::log);
glue!(function_log2, function::exp_log::log2);
Expand Down

0 comments on commit a624028

Please sign in to comment.