diff --git a/COMPAT.md b/COMPAT.md index ebf8ab655..421b1e9d0 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -143,7 +143,7 @@ This document describes the SQLite compatibility status of Limbo: | time() | Partial | partially supports modifiers | | datetime() | No | | | julianday() | No | | -| unixepoch() | No | | +| unixepoch() | Partial | does not support modifiers | | strftime() | No | | | timediff() | No | | diff --git a/core/function.rs b/core/function.rs index 9bd7bdc3a..ae1ab0966 100644 --- a/core/function.rs +++ b/core/function.rs @@ -63,6 +63,7 @@ pub enum ScalarFunc { Time, Unicode, Quote, + UnixEpoch, } impl ToString for ScalarFunc { @@ -91,6 +92,7 @@ impl ToString for ScalarFunc { ScalarFunc::Time => "time".to_string(), ScalarFunc::Unicode => "unicode".to_string(), ScalarFunc::Quote => "quote".to_string(), + ScalarFunc::UnixEpoch => "unixepoch".to_string(), } } } @@ -137,6 +139,7 @@ impl Func { "unicode" => Ok(Func::Scalar(ScalarFunc::Unicode)), "quote" => Ok(Func::Scalar(ScalarFunc::Quote)), "json" => Ok(Func::Json(JsonFunc::JSON)), + "unixepoch" => Ok(Func::Scalar(ScalarFunc::UnixEpoch)), _ => Err(()), } } diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 9eba86073..b834f2a97 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -941,7 +941,30 @@ pub fn translate_expr( dest: target_register, func: crate::vdbe::Func::Scalar(ScalarFunc::Substring), }); - + Ok(target_register) + } + ScalarFunc::UnixEpoch => { + let mut start_reg = 0; + if let Some(args) = args { + if args.len() > 1 { + crate::bail_parse_error!("epoch function with > 1 arguments. Modifiers are not yet supported."); + } else if args.len() == 1 { + let arg_reg = program.alloc_register(); + let _ = translate_expr( + program, + referenced_tables, + &args[0], + arg_reg, + cursor_hint, + )?; + start_reg = arg_reg; + } + } + program.emit_insn(Insn::Function { + start_reg, + dest: target_register, + func: crate::vdbe::Func::Scalar(srf), + }); Ok(target_register) } ScalarFunc::Time => { diff --git a/core/vdbe/datetime.rs b/core/vdbe/datetime.rs index a56abff33..8f9c91a2b 100644 --- a/core/vdbe/datetime.rs +++ b/core/vdbe/datetime.rs @@ -94,6 +94,18 @@ fn apply_modifier(dt: &mut NaiveDateTime, modifier: &str) -> Result<()> { Ok(()) } +pub fn exec_unixepoch(time_value: &OwnedValue) -> Result { + let dt = parse_naive_date_time(time_value); + match dt { + Some(dt) => Ok(get_unixepoch_from_naive_datetime(dt)), + None => Ok(String::new()), + } +} + +fn get_unixepoch_from_naive_datetime(value: NaiveDateTime) -> String { + return value.and_utc().timestamp().to_string(); +} + fn parse_naive_date_time(time_value: &OwnedValue) -> Option { match time_value { OwnedValue::Text(s) => get_date_time_from_time_value_string(s), diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index d31cb636c..aefe6c271 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -33,7 +33,7 @@ use crate::storage::{btree::BTreeCursor, pager::Pager}; use crate::types::{AggContext, Cursor, CursorResult, OwnedRecord, OwnedValue, Record}; use crate::Result; -use datetime::{exec_date, exec_time}; +use datetime::{exec_date, exec_time, exec_unixepoch}; use rand::distributions::{Distribution, Uniform}; use rand::{thread_rng, Rng}; @@ -1365,6 +1365,28 @@ impl Program { state.registers[*dest] = result; state.pc += 1; } + Func::Scalar(ScalarFunc::UnixEpoch) => { + if *start_reg == 0 { + let unixepoch: String = + exec_unixepoch(&OwnedValue::Text(Rc::new("now".to_string())))?; + state.registers[*dest] = OwnedValue::Text(Rc::new(unixepoch)); + } else { + let datetime_value = &state.registers[*start_reg]; + let unixepoch = exec_unixepoch(datetime_value); + match unixepoch { + Ok(time) => { + state.registers[*dest] = OwnedValue::Text(Rc::new(time)) + } + Err(e) => { + return Err(LimboError::ParseError(format!( + "Error encountered while parsing datetime value: {}", + e + ))); + } + } + } + state.pc += 1 + } Func::Scalar(ScalarFunc::Unicode) => { let reg_value = state.registers[*start_reg].borrow_mut(); state.registers[*dest] = exec_unicode(reg_value); diff --git a/testing/scalar-functions-datetime.test b/testing/scalar-functions-datetime.test index 13cbf9a01..8cf53a316 100755 --- a/testing/scalar-functions-datetime.test +++ b/testing/scalar-functions-datetime.test @@ -123,3 +123,23 @@ do_execsql_test time-with-invalid-modifier { SELECT time('2023-05-18 15:30:45', '+1 hour', 'invalid modifier'); } {{}} + +do_execsql_test unixepoch-at-start { + SELECT unixepoch('1970-01-01'); +} {0} + +do_execsql_test unixepoch-at-1-second-before-epochtime { + SELECT unixepoch('1969-12-31 23:59:59'); +} {-1} + +do_execsql_test unixepoch-at-future { + SELECT unixepoch('9999-12-31 23:59:59'); +} {253402300799} + +do_execsql_test unixepoch-at-start-of-time { + SELECT unixepoch('0000-01-01 00:00:00'); +} {-62167219200} + +do_execsql_test unixepoch-at-millisecond-precision-input-produces-seconds-precision-output { + SELECT unixepoch('2022-01-27 12:59:28.052'); +} {1643288368} \ No newline at end of file diff --git a/testing/scalar-functions.test b/testing/scalar-functions.test index 2113eae79..07d54a1f9 100755 --- a/testing/scalar-functions.test +++ b/testing/scalar-functions.test @@ -441,4 +441,4 @@ do_execsql_test quote-null { do_execsql_test quote-integer { SELECT quote(123) -} {123} +} {123} \ No newline at end of file