From 95526a95a8c00e261adabc1944ce504d05a7e734 Mon Sep 17 00:00:00 2001 From: Mihai Budiu Date: Sun, 24 May 2026 20:06:03 -0700 Subject: [PATCH] [SQL] Support for TIME/DATE/TIMESTAMP constructors Signed-off-by: Mihai Budiu --- crates/sqllib/src/lib.rs | 236 +++++++++++++----- crates/sqllib/src/timestamp.rs | 137 +++++++++- docs.feldera.com/docs/sql/datetime.md | 55 ++-- docs.feldera.com/docs/sql/function-index.md | 3 + .../compiler/frontend/ExpressionCompiler.java | 80 +++++- .../calciteCompiler/CalciteFunctions.java | 5 +- .../calciteCompiler/CustomFunctions.java | 45 ++++ .../sql/postgres/PostgresTimestampTests.java | 11 +- .../sql/simple/DateArithmeticTests.java | 110 ++++++++ .../compiler/sql/simple/RegressionTests.java | 4 +- .../compiler/sql/simple/TimeTests.java | 53 +++- 11 files changed, 625 insertions(+), 114 deletions(-) diff --git a/crates/sqllib/src/lib.rs b/crates/sqllib/src/lib.rs index 31ca87e5215..87b089e9cae 100644 --- a/crates/sqllib/src/lib.rs +++ b/crates/sqllib/src/lib.rs @@ -230,10 +230,10 @@ pub type WSet = OrdZSet; #[doc(hidden)] pub type IndexedWSet = OrdIndexedZSet; -// Macro to create variants of a function with 1 argument -// If there exists a function is f_(x: T) -> S, this creates a function -// fN(x: Option) -> Option, defined as -// fN(x) { let x = x?; Some(f_(x)) }. +/// Macro to create variants of a function with 1 argument +/// If there exists a function is f_(x: T) -> S, this creates a function +/// fN(x: Option) -> Option, defined as +/// fN(x) { let x = x?; Some(f_(x)) }. macro_rules! some_function1 { ($func_name:ident, $arg_type:ty, $ret_type:ty) => { ::paste::paste! { @@ -248,10 +248,10 @@ macro_rules! some_function1 { pub(crate) use some_function1; -// Macro to create variants of a function with 1 argument -// If there exists a function is f_type(x: T) -> S, this creates a function -// f_typeN(x: Option) -> Option -// { let x = x?; Some(f_type(x)) }. +/// Macro to create variants of a function with 1 argument +/// If there exists a function is f_type(x: T) -> S, this creates a function +/// f_typeN(x: Option) -> Option +/// { let x = x?; Some(f_type(x)) }. macro_rules! some_polymorphic_function1 { ($func_name:ident $(< $( const $var : ident : $ty: ty),* >)?, $type_name: ident, $arg_type:ty, $ret_type:ty) => { ::paste::paste! { @@ -266,13 +266,14 @@ macro_rules! some_polymorphic_function1 { pub(crate) use some_polymorphic_function1; -// Macro to create variants of a function with 2 arguments -// If there exists a function is f__(x: T, y: S) -> U, this creates -// three functions: -// - f_N(x: T, y: Option) -> Option -// - fN_(x: Option, y: S) -> Option -// - fNN(x: Option, y: Option) -> Option -// The resulting functions return Some only if all arguments are 'Some'. +/// Macro to create variants of a function with 2 arguments +/// If there exists a function is f__(x: T, y: S) -> U, this creates +/// three functions: +/// - f_N(x: T, y: Option) -> Option +/// - fN_(x: Option, y: S) -> Option +/// - fNN(x: Option, y: Option) -> Option +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_function2 { ($func_name:ident, $arg_type0:ty, $arg_type1:ty, $ret_type:ty) => { ::paste::paste! { @@ -300,13 +301,14 @@ macro_rules! some_function2 { pub(crate) use some_function2; -// Macro to create variants of a polymorphic function with 2 arguments -// If there exists a function is f_type1_type2(x: T, y: S) -> U, this -// creates three functions: -// - f_type1_type2N(x: T, y: Option) -> Option -// - f_type1N_type2(x: Option, y: S) -> Option -// - f_type1N_type2N(x: Option, y: Option) -> Option -// The resulting functions return Some only if all arguments are 'Some'. +/// Macro to create variants of a polymorphic function with 2 arguments +/// If there exists a function is f_type1_type2(x: T, y: S) -> U, this +/// creates three functions: +/// - f_type1_type2N(x: T, y: Option) -> Option +/// - f_type1N_type2(x: Option, y: S) -> Option +/// - f_type1N_type2N(x: Option, y: Option) -> Option +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_polymorphic_function2 { ($func_name:ident $(< $( const $var:ident : $ty: ty),* >)?, $type_name0: ident, $arg_type0:ty, $type_name1: ident, $arg_type1:ty, $ret_type:ty) => { ::paste::paste! { @@ -334,12 +336,13 @@ macro_rules! some_polymorphic_function2 { pub(crate) use some_polymorphic_function2; -// If there exists a function is f_t1_t2_t3(x: T, y: S, z: V) -> U, this creates -// seven functions: -// - f_t1_t2_t3N(x: T, y: S, z: Option) -> Option -// - f_t1_t2N_t2(x: T, y: Option, z: V) -> Option -// - etc. -// The resulting functions return Some only if all arguments are 'Some'. +/// If there exists a function is f_t1_t2_t3(x: T, y: S, z: V) -> U, this creates +/// seven functions: +/// - f_t1_t2_t3N(x: T, y: S, z: Option) -> Option +/// - f_t1_t2N_t2(x: T, y: Option, z: V) -> Option +/// - etc. +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_polymorphic_function3 { ($func_name:ident, $type_name0: ident, $arg_type0:ty, @@ -427,12 +430,106 @@ macro_rules! some_polymorphic_function3 { pub(crate) use some_polymorphic_function3; -// If there exists a function is f___(x: T, y: S, z: V) -> U, this creates -// seven functions: -// - f__N(x: T, y: S, z: Option) -> Option -// - f_N_(x: T, y: Option, z: V) -> Option -// - etc. -// The resulting functions return Some only if all arguments are 'Some'. +/// If there exists a function is f_t1_t2_t3(x: T, y: S, z: V) -> Option, this creates +/// seven functions: +/// - f_t1_t2_t3N(x: T, y: S, z: Option) -> Option +/// - f_t1_t2N_t2(x: T, y: Option, z: V) -> Option +/// - etc. +// This is like some_polymorphic_function3, but the result is always Option. +macro_rules! some_polymorphic_null_function3 { + ($func_name:ident $(< $( const $var : ident : $ty: ty),* >)?, + $type_name0: ident, $arg_type0:ty, + $type_name1: ident, $arg_type1:ty, + $type_name2: ident, $arg_type2: ty, + $ret_type:ty) => { + ::paste::paste! { + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 _ $type_name1 _ $type_name2 N>] $(< $( const $var : $ty ),* >)? ( + arg0: $arg_type0, + arg1: $arg_type1, + arg2: Option<$arg_type2> + ) -> Option<$ret_type> { + let arg2 = arg2?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 _ $type_name1 N _ $type_name2>] $(< $( const $var : $ty ),* >)? ( + arg0: $arg_type0, + arg1: Option<$arg_type1>, + arg2: $arg_type2 + ) -> Option<$ret_type> { + let arg1 = arg1?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 _ $type_name1 N _ $type_name2 N>] $(< $( const $var : $ty ),* >)? ( + arg0: $arg_type0, + arg1: Option<$arg_type1>, + arg2: Option<$arg_type2> + ) -> Option<$ret_type> { + let arg1 = arg1?; + let arg2 = arg2?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 N _ $type_name1 _ $type_name2>] $(< $( const $var : $ty ),* >)? ( + arg0: Option<$arg_type0>, + arg1: $arg_type1, + arg2: $arg_type2 + ) -> Option<$ret_type> { + let arg0 = arg0?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 N _ $type_name1 _ $type_name2 N>] $(< $( const $var : $ty ),* >)? ( + arg0: Option<$arg_type0>, + arg1: $arg_type1, + arg2: Option<$arg_type2> + ) -> Option<$ret_type> { + let arg0 = arg0?; + let arg2 = arg2?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 N _ $type_name1 N _ $type_name2>] $(< $( const $var : $ty ),* >)? ( + arg0: Option<$arg_type0>, + arg1: Option<$arg_type1>, + arg2: $arg_type2 + ) -> Option<$ret_type> { + let arg0 = arg0?; + let arg1 = arg1?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + + #[doc(hidden)] + pub fn [<$func_name _ $type_name0 N _ $type_name1 N _ $type_name2 N>] $(< $( const $var : $ty ),* >)? ( + arg0: Option<$arg_type0>, + arg1: Option<$arg_type1>, + arg2: Option<$arg_type2> + ) -> Option<$ret_type> { + let arg0 = arg0?; + let arg1 = arg1?; + let arg2 = arg2?; + [<$func_name _ $type_name0 _ $type_name1 _ $type_name2>] $(:: < $($var),* >)? (arg0, arg1, arg2) + } + } + }; +} + +pub(crate) use some_polymorphic_null_function3; + +/// If there exists a function is f___(x: T, y: S, z: V) -> U, this creates +/// seven functions: +/// - f__N(x: T, y: S, z: Option) -> Option +/// - f_N_(x: T, y: Option, z: V) -> Option +/// - etc. +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_function3 { ($func_name:ident, $arg_type0:ty, $arg_type1:ty, $arg_type2: ty, $ret_type:ty) => { ::paste::paste! { @@ -488,13 +585,14 @@ macro_rules! some_function3 { pub(crate) use some_function3; -// Macro to create variants of a function with 4 arguments -// If there exists a function is f____(x: T, y: S, z: V, w: W) -> U, this -// creates fifteen functions: -// - f___N(x: T, y: S, z: V, w: Option) -> Option -// - f__N_(x: T, y: S, z: Option, w: W) -> Option -// - etc. -// The resulting functions return Some only if all arguments are 'Some'. +/// Macro to create variants of a function with 4 arguments +/// If there exists a function is f____(x: T, y: S, z: V, w: W) -> U, this +/// creates fifteen functions: +/// - f___N(x: T, y: S, z: V, w: Option) -> Option +/// - f__N_(x: T, y: S, z: Option, w: W) -> Option +/// - etc. +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_function4 { ($func_name:ident, $arg_type0:ty, $arg_type1:ty, $arg_type2: ty, $arg_type3: ty, $ret_type:ty) => { ::paste::paste! { @@ -610,14 +708,15 @@ macro_rules! some_function4 { pub(crate) use some_function4; -// Macro to create variants of a function with 2 arguments -// optimized for the implementation of arithmetic operators. -// Assuming there exists a function is f__(x: T, y: T) -> U, this creates -// three functions: -// - f_tN_t(x: T, y: Option) -> Option -// - f_t_tN(x: Option, y: T) -> Option -// - f_tN_tN(x: Option, y: Option) -> Option -// The resulting functions return Some only if all arguments are 'Some'. +/// Macro to create variants of a function with 2 arguments +/// optimized for the implementation of arithmetic operators. +/// Assuming there exists a function is f__(x: T, y: T) -> U, this creates +/// three functions: +/// - f_tN_t(x: T, y: Option) -> Option +/// - f_t_tN(x: Option, y: T) -> Option +/// - f_tN_tN(x: Option, y: Option) -> Option +/// +/// The resulting functions return Some only if all arguments are 'Some'. macro_rules! some_existing_operator { ($func_name: ident $(< $( const $var:ident : $ty: ty),* >)?, $short_name: ident, $arg_type: ty, $ret_type: ty) => { ::paste::paste! { @@ -648,21 +747,22 @@ macro_rules! some_existing_operator { pub(crate) use some_existing_operator; -// Macro to create variants of a function with 2 arguments -// optimized for the implementation of arithmetic operators. -// Assuming there exists a function is f(x: T, y: T) -> U, this creates -// four functions: -// - f_t_t(x: T, y: T) -> U -// - f_tN_t(x: T, y: Option) -> Option -// - f_t_tN(x: Option, y: T) -> Option -// - f_tN_tN(x: Option, y: Option) -> Option -// The resulting functions return Some only if all arguments are 'Some'. -// -// Has two variants: -// - Takes the name of the existing function, the generated functions will have -// this prefix -// - Takes the name of the existing function, and the prefix for the generated -// functions +/// Macro to create variants of a function with 2 arguments +/// optimized for the implementation of arithmetic operators. +/// Assuming there exists a function is f(x: T, y: T) -> U, this creates +/// four functions: +/// - f_t_t(x: T, y: T) -> U +/// - f_tN_t(x: T, y: Option) -> Option +/// - f_t_tN(x: Option, y: T) -> Option +/// - f_tN_tN(x: Option, y: Option) -> Option +/// +/// The resulting functions return Some only if all arguments are 'Some'. +/// +/// Has two variants: +/// - Takes the name of the existing function, the generated functions will have +/// this prefix +/// - Takes the name of the existing function, and the prefix for the generated +/// functions macro_rules! some_operator { ($func_name: ident $(< $( const $var:ident : $ty: ty),* >)?, $short_name: ident, $arg_type: ty, $ret_type: ty) => { ::paste::paste! { @@ -1414,10 +1514,10 @@ where } } -// Semigroup for the an aggregate which computes nothing -// Useful when the compiler removes all aggregates from an aggregate operator -// (This is currently never used, because the compiler generates a linear aggregate -// for this case) +/// Semigroup for the an aggregate which computes nothing +/// Useful when the compiler removes all aggregates from an aggregate operator +/// (This is currently never used, because the compiler generates a linear aggregate +/// for this case) #[derive(Clone)] #[doc(hidden)] pub struct EmptySemigroup; diff --git a/crates/sqllib/src/timestamp.rs b/crates/sqllib/src/timestamp.rs index 567c2e34074..3f61740b821 100644 --- a/crates/sqllib/src/timestamp.rs +++ b/crates/sqllib/src/timestamp.rs @@ -2,7 +2,7 @@ use crate::error::{SqlResult, SqlRuntimeError}; use crate::{ - FromInteger, SqlString, ToInteger, + DynamicDecimal, F64, FromInteger, SqlDecimal, SqlString, ToInteger, array::Array, casts::*, interval::{LongInterval, ShortInterval}, @@ -15,7 +15,7 @@ use chrono::{ }; use chrono_tz::Tz; use core::fmt::Formatter; -use dbsp::num_entries_scalar; +use dbsp::{algebra::HasZero, num_entries_scalar}; use feldera_macros::IsNone; use feldera_types::serde_with_context::{ DateFormat, DeserializeWithContext, SerializeWithContext, SqlSerdeConfig, TimeFormat, @@ -33,7 +33,7 @@ use std::{ use crate::{ operators::{eq, gt, gte, lt, lte, neq}, some_existing_operator, some_function2, some_operator, some_polymorphic_function1, - some_polymorphic_function2, some_polymorphic_function3, + some_polymorphic_function2, some_polymorphic_function3, some_polymorphic_null_function3, }; /// Represents a date and a time without timezone information. @@ -604,6 +604,15 @@ pub fn now() -> Timestamp { Timestamp::from_dateTime(Utc::now()) } +#[doc(hidden)] +pub fn make_timestampNN(date: Option, time: Option