use crate::core::{Result, Value};
use crate::functions::{
FunctionDataType, FunctionInfo, FunctionSignature, FunctionType, WindowFunction,
};
#[derive(Default)]
pub struct FirstValueFunction;
impl WindowFunction for FirstValueFunction {
fn name(&self) -> &str {
"FIRST_VALUE"
}
fn info(&self) -> FunctionInfo {
FunctionInfo::new(
"FIRST_VALUE",
FunctionType::Window,
"Returns the first value in the window frame",
FunctionSignature::new(FunctionDataType::Any, vec![FunctionDataType::Any], 1, 1),
)
}
fn process(
&self,
partition: &[Value],
_order_by: &[Value],
_current_row: usize,
) -> Result<Value> {
if partition.is_empty() {
return Ok(Value::null_unknown());
}
Ok(partition[0].clone())
}
fn clone_box(&self) -> Box<dyn WindowFunction> {
Box::new(FirstValueFunction)
}
}
#[derive(Default)]
pub struct LastValueFunction;
impl WindowFunction for LastValueFunction {
fn name(&self) -> &str {
"LAST_VALUE"
}
fn info(&self) -> FunctionInfo {
FunctionInfo::new(
"LAST_VALUE",
FunctionType::Window,
"Returns the last value in the window frame",
FunctionSignature::new(FunctionDataType::Any, vec![FunctionDataType::Any], 1, 1),
)
}
fn process(
&self,
partition: &[Value],
_order_by: &[Value],
_current_row: usize,
) -> Result<Value> {
if partition.is_empty() {
return Ok(Value::null_unknown());
}
Ok(partition[partition.len() - 1].clone())
}
fn clone_box(&self) -> Box<dyn WindowFunction> {
Box::new(LastValueFunction)
}
}
#[derive(Default)]
pub struct NthValueFunction {
n: usize,
}
impl NthValueFunction {
pub fn new(n: usize) -> Self {
Self { n }
}
}
impl WindowFunction for NthValueFunction {
fn name(&self) -> &str {
"NTH_VALUE"
}
fn info(&self) -> FunctionInfo {
FunctionInfo::new(
"NTH_VALUE",
FunctionType::Window,
"Returns the value at the specified position in the window frame",
FunctionSignature::new(
FunctionDataType::Any,
vec![FunctionDataType::Any, FunctionDataType::Integer],
2,
2,
),
)
}
fn process(
&self,
partition: &[Value],
_order_by: &[Value],
_current_row: usize,
) -> Result<Value> {
if self.n == 0 || partition.is_empty() {
return Ok(Value::null_unknown());
}
let index = self.n - 1;
if index >= partition.len() {
return Ok(Value::null_unknown());
}
Ok(partition[index].clone())
}
fn clone_box(&self) -> Box<dyn WindowFunction> {
Box::new(NthValueFunction::new(self.n))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_value_basic() {
let f = FirstValueFunction;
let partition = vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
Value::Integer(40),
];
let order_by = vec![];
assert_eq!(
f.process(&partition, &order_by, 0).unwrap(),
Value::Integer(10)
);
assert_eq!(
f.process(&partition, &order_by, 1).unwrap(),
Value::Integer(10)
);
assert_eq!(
f.process(&partition, &order_by, 2).unwrap(),
Value::Integer(10)
);
assert_eq!(
f.process(&partition, &order_by, 3).unwrap(),
Value::Integer(10)
);
}
#[test]
fn test_first_value_empty() {
let f = FirstValueFunction;
let partition = vec![];
let order_by = vec![];
assert!(f.process(&partition, &order_by, 0).unwrap().is_null());
}
#[test]
fn test_first_value_strings() {
let f = FirstValueFunction;
let partition = vec![
Value::text("apple"),
Value::text("banana"),
Value::text("cherry"),
];
let order_by = vec![];
assert_eq!(
f.process(&partition, &order_by, 2).unwrap(),
Value::text("apple")
);
}
#[test]
fn test_last_value_basic() {
let f = LastValueFunction;
let partition = vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
Value::Integer(40),
];
let order_by = vec![];
assert_eq!(
f.process(&partition, &order_by, 0).unwrap(),
Value::Integer(40)
);
assert_eq!(
f.process(&partition, &order_by, 1).unwrap(),
Value::Integer(40)
);
assert_eq!(
f.process(&partition, &order_by, 2).unwrap(),
Value::Integer(40)
);
assert_eq!(
f.process(&partition, &order_by, 3).unwrap(),
Value::Integer(40)
);
}
#[test]
fn test_last_value_empty() {
let f = LastValueFunction;
let partition = vec![];
let order_by = vec![];
assert!(f.process(&partition, &order_by, 0).unwrap().is_null());
}
#[test]
fn test_last_value_strings() {
let f = LastValueFunction;
let partition = vec![
Value::text("apple"),
Value::text("banana"),
Value::text("cherry"),
];
let order_by = vec![];
assert_eq!(
f.process(&partition, &order_by, 0).unwrap(),
Value::text("cherry")
);
}
#[test]
fn test_nth_value_basic() {
let partition = vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
Value::Integer(40),
];
let order_by = vec![];
let f1 = NthValueFunction::new(1);
assert_eq!(
f1.process(&partition, &order_by, 0).unwrap(),
Value::Integer(10)
);
let f2 = NthValueFunction::new(2);
assert_eq!(
f2.process(&partition, &order_by, 0).unwrap(),
Value::Integer(20)
);
let f3 = NthValueFunction::new(3);
assert_eq!(
f3.process(&partition, &order_by, 0).unwrap(),
Value::Integer(30)
);
let f4 = NthValueFunction::new(4);
assert_eq!(
f4.process(&partition, &order_by, 0).unwrap(),
Value::Integer(40)
);
}
#[test]
fn test_nth_value_out_of_bounds() {
let f = NthValueFunction::new(5);
let partition = vec![
Value::Integer(10),
Value::Integer(20),
Value::Integer(30),
Value::Integer(40),
];
let order_by = vec![];
assert!(f.process(&partition, &order_by, 0).unwrap().is_null());
}
#[test]
fn test_nth_value_zero() {
let f = NthValueFunction::new(0);
let partition = vec![Value::Integer(10), Value::Integer(20)];
let order_by = vec![];
assert!(f.process(&partition, &order_by, 0).unwrap().is_null());
}
#[test]
fn test_nth_value_empty() {
let f = NthValueFunction::new(1);
let partition = vec![];
let order_by = vec![];
assert!(f.process(&partition, &order_by, 0).unwrap().is_null());
}
#[test]
fn test_nth_value_strings() {
let f = NthValueFunction::new(2);
let partition = vec![
Value::text("apple"),
Value::text("banana"),
Value::text("cherry"),
];
let order_by = vec![];
assert_eq!(
f.process(&partition, &order_by, 0).unwrap(),
Value::text("banana")
);
}
}