Skip to content

Commit

Permalink
Add Interval type
Browse files Browse the repository at this point in the history
  • Loading branch information
junli1026 committed Sep 10, 2021
1 parent dc297e9 commit 94fb8e3
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 240 deletions.
73 changes: 45 additions & 28 deletions common/datavalues/src/types/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,31 @@ pub enum DataType {
/// Option<String> indicates the timezone, if it's None, it's UTC
DateTime32(Option<String>),

/// Representing the elapsed time or duration in seconds. You can use Interval-type values in arithmetical operations with DateTime, but not Date, which represents elapsed time in days.
Interval(IntervalUnit),

List(Box<DataField>),
Struct(Vec<DataField>),
String,
}

#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub enum IntervalUnit {
YearMonth,
DayTime,
}

impl fmt::Display for IntervalUnit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let str = match self {
IntervalUnit::YearMonth => "YearMonth",
IntervalUnit::DayTime => "DayTime",
};
write!(f, "{}", str)
}
}

impl DataType {
pub fn to_physical_type(&self) -> PhysicalDataType {
self.clone().into()
Expand Down Expand Up @@ -83,7 +100,11 @@ impl DataType {
ArrowDataType::Struct(arrows_fields)
}
String => ArrowDataType::LargeBinary,
Interval(_unit) => ArrowDataType::Int64,
Interval(unit) => ArrowDataType::Extension(
"Interval".to_string(),
Box::new(ArrowDataType::Int64),
Some(unit.to_string()),
),
}
}
}
Expand Down Expand Up @@ -120,6 +141,25 @@ impl From<&ArrowDataType> for DataType {
ArrowDataType::Timestamp(_, tz) => DataType::DateTime32(tz.clone()),
ArrowDataType::Date32 => DataType::Date16,
ArrowDataType::Date64 => DataType::Date32,

ArrowDataType::Extension(name, _arrow_type, extra) => match name.as_str() {
"Date16" => DataType::Date16,
"Date32" => DataType::Date32,
"DateTime32" => DataType::DateTime32(extra.clone()),
"Interval" => {
if let Some(unit) = extra {
match unit.as_str() {
"YearMonth" => DataType::Interval(IntervalUnit::YearMonth),
"DayTime" => DataType::Interval(IntervalUnit::DayTime),
_ => unreachable!(),
}
} else {
unreachable!()
}
}
_ => unimplemented!("data_type: {}", dt),
},

// this is safe, because we define the datatype firstly
_ => {
unimplemented!("data_type: {}", dt)
Expand Down Expand Up @@ -163,6 +203,9 @@ impl fmt::Debug for DataType {
Self::List(arg0) => f.debug_tuple("List").field(arg0).finish(),
Self::Struct(arg0) => f.debug_tuple("Struct").field(arg0).finish(),
Self::String => write!(f, "String"),
Self::Interval(unit) => {
write!(f, "Interval({})", unit.to_string())
}
}
}
}
Expand All @@ -171,29 +214,3 @@ impl fmt::Display for DataType {
write!(f, "{:?}", self)
}
}

#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
pub enum IntervalUnit {
Year,
Month,
Day,
Hour,
Minute,
Second,
}

impl IntervalUnit {
pub fn avg_seconds(unit: IntervalUnit) -> i32 {
// the average seconds number is from clickhouse: https://github.com/ClickHouse/ClickHouse/blob/9f5cd35a6963cc556a51218b46b0754dcac7306a/src/Common/IntervalKind.cpp
match unit {
IntervalUnit::Second => 1,
IntervalUnit::Minute => 60,
IntervalUnit::Hour => 3600,
IntervalUnit::Day => 86400,
IntervalUnit::Month => 2629746, // Exactly 1/12 of a year.
IntervalUnit::Year => 31556952, // The average length of a Gregorian year is equal to 365.2425 days,
}
}
}
36 changes: 35 additions & 1 deletion common/datavalues/src/types/data_type_coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub fn is_numeric(dt: &DataType) -> bool {
)
}

pub fn is_interval(dt: &DataType) -> bool {
matches!(dt, DataType::Interval(_))
}

fn next_size(size: usize) -> usize {
if size < 8_usize {
return size * 2;
Expand Down Expand Up @@ -273,7 +277,7 @@ pub fn datetime_arithmetic_coercion(
DataValueArithmeticOperator::Plus => Ok(a),

DataValueArithmeticOperator::Minus => {
if is_numeric(&b) {
if is_numeric(&b) || is_interval(&b) {
Ok(a)
} else {
// Date minus Date or DateTime minus DateTime
Expand All @@ -284,6 +288,36 @@ pub fn datetime_arithmetic_coercion(
}
}

#[inline]
pub fn interval_arithmetic_coercion(
op: &DataValueArithmeticOperator,
lhs_type: &DataType,
rhs_type: &DataType,
) -> Result<DataType> {
let e = Result::Err(ErrorCode::BadDataValueType(format!(
"DataValue Error: Unsupported date coercion ({:?}) {} ({:?})",
lhs_type, op, rhs_type
)));

// only allow date/datetime [+/-] interval
if !(is_date_or_date_time(lhs_type) && is_interval(rhs_type)
|| is_date_or_date_time(rhs_type) && is_interval(lhs_type))
{
return e;
}

match op {
DataValueArithmeticOperator::Plus | DataValueArithmeticOperator::Minus => {
if is_date_or_date_time(lhs_type) {
Ok(lhs_type.clone())
} else {
Ok(rhs_type.clone())
}
}
_ => e,
}
}

#[inline]
pub fn numerical_signed_coercion(val_type: &DataType) -> Result<DataType> {
// error on any non-numeric type
Expand Down
41 changes: 0 additions & 41 deletions common/datavalues/src/types/serializations/interval.rs

This file was deleted.

2 changes: 0 additions & 2 deletions common/datavalues/src/types/serializations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ use crate::DataType;
mod boolean;
mod date;
mod date_time;
mod null;
mod number;
mod string;

pub use boolean::*;
pub use date::*;
pub use date_time::*;
pub use null::*;
pub use number::*;
pub use string::*;

Expand Down
13 changes: 12 additions & 1 deletion common/functions/src/scalars/arithmetics/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ impl Function for ArithmeticFunction {
if args.len() == 1 {
return Ok(args[0].clone());
}

if is_interval(&args[0]) || is_interval(&args[1]) {
return interval_arithmetic_coercion(&self.op, &args[0], &args[1]);
}
if is_date_or_date_time(&args[0]) || is_date_or_date_time(&args[1]) {
return datetime_arithmetic_coercion(&self.op, &args[0], &args[1]);
}
Expand All @@ -74,14 +78,21 @@ impl Function for ArithmeticFunction {
}

fn eval(&self, columns: &DataColumnsWithField, _input_rows: usize) -> Result<DataColumn> {
let has_date_or_date_time = columns.iter().any(|c| is_date_or_date_time(c.data_type()));
let has_interval = columns.iter().any(|c| is_interval(c.data_type()));

if has_date_or_date_time && has_interval {
//TODO(Jun): implement data/datatime +/- interval
unimplemented!()
}

let result = match columns.len() {
1 => std::ops::Neg::neg(columns[0].column()),
_ => columns[0]
.column()
.arithmetic(self.op.clone(), columns[1].column()),
}?;

let has_date_or_date_time = columns.iter().any(|c| is_date_or_date_time(c.data_type()));
if has_date_or_date_time {
let args = columns
.iter()
Expand Down
119 changes: 0 additions & 119 deletions common/functions/src/scalars/dates/interval.rs

This file was deleted.

1 change: 0 additions & 1 deletion common/functions/src/scalars/dates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ mod date_function_test;
#[cfg(test)]
mod date_test;

mod interval;
mod now;
mod number_function;
mod round_function;
Expand Down
Loading

0 comments on commit 94fb8e3

Please sign in to comment.