change datetime functions to accept AsValueRef and not registers

This commit is contained in:
pedrocarlo
2025-11-10 22:07:26 -03:00
parent 505a6ba5ea
commit 98d268cdc6
4 changed files with 132 additions and 110 deletions

View File

@@ -1,40 +1,66 @@
use crate::types::AsValueRef;
use crate::types::Value;
use crate::LimboError::InvalidModifier;
use crate::Result;
use crate::{types::Value, vdbe::Register};
use crate::{Result, ValueRef};
use chrono::{
DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeDelta, TimeZone, Timelike, Utc,
};
/// Execution of date/time/datetime functions
#[inline(always)]
pub fn exec_date(values: &[Register]) -> Value {
pub fn exec_date<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
exec_datetime(values, DateTimeOutput::Date)
}
#[inline(always)]
pub fn exec_time(values: &[Register]) -> Value {
pub fn exec_time<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
exec_datetime(values, DateTimeOutput::Time)
}
#[inline(always)]
pub fn exec_datetime_full(values: &[Register]) -> Value {
pub fn exec_datetime_full<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
exec_datetime(values, DateTimeOutput::DateTime)
}
#[inline(always)]
pub fn exec_strftime(values: &[Register]) -> Value {
if values.is_empty() {
pub fn exec_strftime<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut values = values.into_iter();
if values.len() == 0 {
return Value::Null;
}
let value = &values[0].get_value();
let format_str = if matches!(value, Value::Text(_) | Value::Integer(_) | Value::Float(_)) {
let value = values.next().unwrap();
let value = value.as_value_ref();
let format_str = if matches!(
value,
ValueRef::Text(_) | ValueRef::Integer(_) | ValueRef::Float(_)
) {
format!("{value}")
} else {
return Value::Null;
};
exec_datetime(&values[1..], DateTimeOutput::StrfTime(format_str))
exec_datetime(values, DateTimeOutput::StrfTime(format_str))
}
enum DateTimeOutput {
@@ -46,14 +72,22 @@ enum DateTimeOutput {
JuliaDay,
}
fn exec_datetime(values: &[Register], output_type: DateTimeOutput) -> Value {
if values.is_empty() {
fn exec_datetime<I, E, V>(values: I, output_type: DateTimeOutput) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let values = values.into_iter();
if values.len() == 0 {
let now = parse_naive_date_time(&Value::build_text("now")).unwrap();
return format_dt(now, output_type, false);
}
if let Some(mut dt) = parse_naive_date_time(values[0].get_value()) {
let mut values = values.peekable();
let first = values.peek().unwrap();
if let Some(mut dt) = parse_naive_date_time(first) {
// if successful, treat subsequent entries as modifiers
modify_dt(&mut dt, &values[1..], output_type)
modify_dt(&mut dt, values.skip(1), output_type)
} else {
// if the first argument is NOT a valid date/time, treat the entire set of values as modifiers.
let mut dt = chrono::Local::now().to_utc().naive_utc();
@@ -61,11 +95,17 @@ fn exec_datetime(values: &[Register], output_type: DateTimeOutput) -> Value {
}
}
fn modify_dt(dt: &mut NaiveDateTime, mods: &[Register], output_type: DateTimeOutput) -> Value {
fn modify_dt<I, E, V>(dt: &mut NaiveDateTime, mods: I, output_type: DateTimeOutput) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mods = mods.into_iter();
let mut n_floor: i64 = 0;
let mut subsec_requested = false;
for modifier in mods {
if let Value::Text(ref text_rc) = modifier.get_value() {
if let ValueRef::Text(ref text_rc) = modifier.as_value_ref() {
// TODO: to prevent double conversion and properly support 'utc'/'localtime', we also
// need to keep track of the current timezone and apply it to the modifier.
let parsed = parse_modifier(text_rc.as_str());
@@ -339,7 +379,12 @@ fn last_day_in_month(year: i32, month: u32) -> u32 {
28
}
pub fn exec_julianday(values: &[Register]) -> Value {
pub fn exec_julianday<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
exec_datetime(values, DateTimeOutput::JuliaDay)
}
@@ -392,11 +437,12 @@ fn get_unixepoch_from_naive_datetime(value: NaiveDateTime) -> i64 {
value.and_utc().timestamp()
}
fn parse_naive_date_time(time_value: &Value) -> Option<NaiveDateTime> {
fn parse_naive_date_time(time_value: impl AsValueRef) -> Option<NaiveDateTime> {
let time_value = time_value.as_value_ref();
match time_value {
Value::Text(s) => get_date_time_from_time_value_string(s.as_str()),
Value::Integer(i) => get_date_time_from_time_value_integer(*i),
Value::Float(f) => get_date_time_from_time_value_float(*f),
ValueRef::Text(s) => get_date_time_from_time_value_string(s.as_str()),
ValueRef::Integer(i) => get_date_time_from_time_value_integer(i),
ValueRef::Float(f) => get_date_time_from_time_value_float(f),
_ => None,
}
}
@@ -767,13 +813,19 @@ fn parse_modifier(modifier: &str) -> Result<Modifier> {
}
}
pub fn exec_timediff(values: &[Register]) -> Value {
pub fn exec_timediff<I, E, V>(values: I) -> Value
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut values = values.into_iter();
if values.len() < 2 {
return Value::Null;
}
let start = parse_naive_date_time(values[0].get_value());
let end = parse_naive_date_time(values[1].get_value());
let start = parse_naive_date_time(values.next().unwrap());
let end = parse_naive_date_time(values.next().unwrap());
match (start, end) {
(Some(start), Some(end)) => {
@@ -931,7 +983,7 @@ mod tests {
];
for (input, expected) in test_cases {
let result = exec_date(&[Register::Value(input.clone())]);
let result = exec_date(&[input.clone()]);
assert_eq!(
result,
Value::build_text(expected.to_string()),
@@ -971,7 +1023,7 @@ mod tests {
];
for case in invalid_cases.iter() {
let result = exec_date(&[Register::Value(case.clone())]);
let result = exec_date(&[case]);
match result {
Value::Text(ref result_str) if result_str.value.is_empty() => (),
_ => panic!("Expected empty string for input: {case:?}, but got: {result:?}"),
@@ -1063,7 +1115,7 @@ mod tests {
];
for (input, expected) in test_cases {
let result = exec_time(&[Register::Value(input)]);
let result = exec_time(&[input]);
if let Value::Text(result_str) = result {
assert_eq!(result_str.as_str(), expected);
} else {
@@ -1103,7 +1155,7 @@ mod tests {
];
for case in invalid_cases {
let result = exec_time(&[Register::Value(case.clone())]);
let result = exec_time(&[case.clone()]);
match result {
Value::Text(ref result_str) if result_str.value.is_empty() => (),
_ => panic!("Expected empty string for input: {case:?}, but got: {result:?}"),
@@ -1425,8 +1477,8 @@ mod tests {
assert_eq!(dt, create_datetime(2023, 6, 15, 0, 0, 0));
}
fn text(value: &str) -> Register {
Register::Value(Value::build_text(value.to_string()))
fn text(value: &str) -> Value {
Value::build_text(value.to_string())
}
fn format(dt: NaiveDateTime) -> String {
@@ -1444,7 +1496,7 @@ mod tests {
&[text("2023-06-15 12:30:45"), text("-1 day")],
DateTimeOutput::DateTime,
);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1459,7 +1511,7 @@ mod tests {
],
DateTimeOutput::DateTime,
);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1486,7 +1538,7 @@ mod tests {
],
DateTimeOutput::DateTime,
);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1505,7 +1557,7 @@ mod tests {
],
DateTimeOutput::DateTime,
);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1525,7 +1577,7 @@ mod tests {
],
DateTimeOutput::DateTime,
);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1537,13 +1589,12 @@ mod tests {
);
assert_eq!(
result_local,
*text(
text(
&dt.and_utc()
.with_timezone(&chrono::Local)
.format("%Y-%m-%d %H:%M:%S")
.to_string()
)
.get_value()
);
// TODO: utc modifier assumes time given is not already utc
// add test when fixed in the future
@@ -1581,7 +1632,7 @@ mod tests {
.unwrap();
let expected = format(max);
let result = exec_datetime(&[text("9999-12-31 23:59:59")], DateTimeOutput::DateTime);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
// leap second
@@ -1593,7 +1644,7 @@ mod tests {
.unwrap();
let expected = String::new(); // SQLite ignores leap seconds
let result = exec_datetime(&[text(&leap_second.to_string())], DateTimeOutput::DateTime);
assert_eq!(result, *text(&expected).get_value());
assert_eq!(result, text(&expected));
}
#[test]
@@ -1816,61 +1867,40 @@ mod tests {
let start = Value::build_text("12:00:00");
let end = Value::build_text("14:30:45");
let expected = Value::build_text("-0000-00-00 02:30:45.000");
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::build_text("14:30:45");
let end = Value::build_text("12:00:00");
let expected = Value::build_text("+0000-00-00 02:30:45.000");
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::build_text("12:00:01.300");
let end = Value::build_text("12:00:00.500");
let expected = Value::build_text("+0000-00-00 00:00:00.800");
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::build_text("13:30:00");
let end = Value::build_text("16:45:30");
let expected = Value::build_text("-0000-00-00 03:15:30.000");
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::build_text("2023-05-10 23:30:00");
let end = Value::build_text("2023-05-11 01:15:00");
let expected = Value::build_text("-0000-00-00 01:45:00.000");
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::Null;
let end = Value::build_text("12:00:00");
let expected = Value::Null;
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end]), expected);
let start = Value::build_text("not a time");
let end = Value::build_text("12:00:00");
let expected = Value::Null;
assert_eq!(
exec_timediff(&[Register::Value(start), Register::Value(end)]),
expected
);
assert_eq!(exec_timediff(&[start, end.clone()]), expected);
let start = Value::build_text("12:00:00");
let expected = Value::Null;
assert_eq!(exec_timediff(&[Register::Value(start)]), expected);
assert_eq!(exec_timediff(&[start, end]), expected);
}
}

View File

@@ -4304,23 +4304,32 @@ pub fn emit_literal(
}
ast::Literal::CurrentDate => {
program.emit_insn(Insn::String8 {
value: datetime::exec_date(&[]).to_string(),
value: datetime::exec_date::<&[_; 0], std::slice::Iter<'_, Value>, &Value>(&[])
.to_string(),
dest: target_register,
});
Ok(target_register)
}
ast::Literal::CurrentTime => {
program.emit_insn(Insn::String8 {
value: datetime::exec_time(&[]).to_string(),
value: datetime::exec_time::<&[_; 0], std::slice::Iter<'_, Value>, &Value>(&[])
.to_string(),
dest: target_register,
});
Ok(target_register)
}
ast::Literal::CurrentTimestamp => {
program.emit_insn(Insn::String8 {
value: datetime::exec_datetime_full(&[]).to_string(),
dest: target_register,
});
program.emit_insn(
Insn::String8 {
value: datetime::exec_datetime_full::<
&[_; 0],
std::slice::Iter<'_, Value>,
&Value,
>(&[])
.to_string(),
dest: target_register,
},
);
Ok(target_register)
}
}

View File

@@ -297,13 +297,6 @@ impl<'b> AsValueRef for ValueRef<'b> {
}
}
impl<'b> AsValueRef for &ValueRef<'b> {
#[inline]
fn as_value_ref<'a>(&'a self) -> ValueRef<'a> {
**self
}
}
impl AsValueRef for Value {
#[inline]
fn as_value_ref<'a>(&'a self) -> ValueRef<'a> {
@@ -311,13 +304,6 @@ impl AsValueRef for Value {
}
}
impl AsValueRef for &Value {
#[inline]
fn as_value_ref<'a>(&'a self) -> ValueRef<'a> {
self.as_ref()
}
}
impl AsValueRef for &mut Value {
#[inline]
fn as_value_ref<'a>(&'a self) -> ValueRef<'a> {
@@ -339,17 +325,9 @@ where
}
}
impl<V1, V2> AsValueRef for &Either<V1, V2>
where
V1: AsValueRef,
V2: AsValueRef,
{
#[inline]
impl<'b, V: AsValueRef> AsValueRef for &'b V {
fn as_value_ref<'a>(&'a self) -> ValueRef<'a> {
match self {
Either::Left(left) => left.as_value_ref(),
Either::Right(right) => right.as_value_ref(),
}
(*self).as_value_ref()
}
}

View File

@@ -4942,11 +4942,14 @@ pub fn op_function(
state.registers[*dest] = Register::Value(result);
}
ScalarFunc::Date => {
let result = exec_date(&state.registers[*start_reg..*start_reg + arg_count]);
let values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let result = exec_date(values);
state.registers[*dest] = Register::Value(result);
}
ScalarFunc::Time => {
let values = &state.registers[*start_reg..*start_reg + arg_count];
let values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let result = exec_time(values);
state.registers[*dest] = Register::Value(result);
}
@@ -4954,13 +4957,10 @@ pub fn op_function(
if arg_count != 2 {
state.registers[*dest] = Register::Value(Value::Null);
} else {
let start = state.registers[*start_reg].get_value().clone();
let end = state.registers[*start_reg + 1].get_value().clone();
let start = state.registers[*start_reg].get_value();
let end = state.registers[*start_reg + 1].get_value();
let result = crate::functions::datetime::exec_timediff(&[
Register::Value(start),
Register::Value(end),
]);
let result = crate::functions::datetime::exec_timediff(&[start, end]);
state.registers[*dest] = Register::Value(result);
}
@@ -4971,12 +4971,15 @@ pub fn op_function(
state.registers[*dest] = Register::Value(Value::Integer(total_changes));
}
ScalarFunc::DateTime => {
let result =
exec_datetime_full(&state.registers[*start_reg..*start_reg + arg_count]);
let values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let result = exec_datetime_full(values);
state.registers[*dest] = Register::Value(result);
}
ScalarFunc::JulianDay => {
let result = exec_julianday(&state.registers[*start_reg..*start_reg + arg_count]);
let values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let result = exec_julianday(values);
state.registers[*dest] = Register::Value(result);
}
ScalarFunc::UnixEpoch => {
@@ -5042,7 +5045,9 @@ pub fn op_function(
program.connection.load_extension(ext)?;
}
ScalarFunc::StrfTime => {
let result = exec_strftime(&state.registers[*start_reg..*start_reg + arg_count]);
let values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let result = exec_strftime(values);
state.registers[*dest] = Register::Value(result);
}
ScalarFunc::Printf => {