Skip to content

Commit

Permalink
Add toInterval suppport
Browse files Browse the repository at this point in the history
  • Loading branch information
junli1026 committed Sep 7, 2021
1 parent 4965ffc commit db1ff10
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 0 deletions.
1 change: 1 addition & 0 deletions common/datavalues/src/data_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ impl From<&DataType> for DataValue {
DataType::List(f) => DataValue::List(None, f.data_type().clone()),
DataType::Struct(_) => DataValue::Struct(vec![]),
DataType::String => DataValue::String(None),
DataType::Interval(_) => DataValue::Float64(None),
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions common/datavalues/src/types/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub enum DataType {
/// in seconds, it's physical type is UInt32
DateTime32,

/// 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,
Expand Down Expand Up @@ -75,6 +78,7 @@ impl DataType {
ArrowDataType::Struct(arrows_fields)
}
String => ArrowDataType::LargeBinary,
Interval(_) => ArrowDataType::Float64,
}
}
}
Expand Down Expand Up @@ -125,3 +129,29 @@ 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,
}
}
}
1 change: 1 addition & 0 deletions common/datavalues/src/types/physical_data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ impl From<DataType> for PhysicalDataType {
DataType::List(x) => List(x),
DataType::Struct(x) => Struct(x),
DataType::String => String,
DataType::Interval(_) => Float64,
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions common/datavalues/src/types/serializations/interval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2020 Datafuse Labs.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use common_exception::Result;

use crate::prelude::DataColumn;
use crate::DataValue;
use crate::DataValueArithmeticOperator;
use crate::IntervalUnit;
use crate::NumberSerializer;
use crate::TypeSerializer;

pub struct IntervalSerializer {
unit: IntervalUnit,
}

impl IntervalSerializer {
pub fn new(unit: IntervalUnit) -> Self {
Self { unit }
}
}

impl TypeSerializer for IntervalSerializer {
fn serialize_strings(&self, column: &DataColumn) -> Result<Vec<String>> {
let seconds_per_unit = IntervalUnit::avg_seconds(self.unit.clone());
let seconds = DataColumn::Constant(DataValue::Int32(Some(seconds_per_unit)), 1);
let interval = column.arithmetic(DataValueArithmeticOperator::Div, &seconds)?;
NumberSerializer::<f64>::default().serialize_strings(&interval)
}
}
3 changes: 3 additions & 0 deletions common/datavalues/src/types/serializations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ use crate::DataType;
mod boolean;
mod date;
mod date_time;
mod interval;
mod null;
mod number;
mod string;

pub use boolean::*;
pub use date::*;
pub use date_time::*;
pub use interval::*;
pub use null::*;
pub use number::*;
pub use string::*;
Expand Down Expand Up @@ -54,6 +56,7 @@ impl DataType {
DataType::Date32 => Box::new(DateSerializer::<u32>::default()),
DataType::DateTime32 => Box::new(DateTimeSerializer::<u32>::default()),
DataType::String => Box::new(StringSerializer {}),
DataType::Interval(unit) => Box::new(IntervalSerializer::new(unit.clone())),
DataType::List(_) => todo!(),
DataType::Struct(_) => todo!(),
}
Expand Down
18 changes: 18 additions & 0 deletions common/functions/src/scalars/dates/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ use common_exception::Result;

use super::now::NowFunction;
use super::ToYYYYMMFunction;
use super::IntervalDayFunction;
use super::IntervalHourFunction;
use super::IntervalMinuteFunction;
use super::IntervalMonthFunction;
use super::IntervalSecondFunction;
use super::IntervalYearFunction;
use super::TodayFunction;
use super::TomorrowFunction;
use super::YesterdayFunction;
Expand All @@ -32,6 +38,18 @@ impl DateFunction {
map.insert("tomorrow".into(), TomorrowFunction::try_create);
map.insert("now".into(), NowFunction::try_create);
map.insert("toYYYYMM".into(), ToYYYYMMFunction::try_create);
map.insert("toIntervalYear".into(), IntervalYearFunction::try_create);
map.insert("toIntervalMonth".into(), IntervalMonthFunction::try_create);
map.insert("toIntervalDay".into(), IntervalDayFunction::try_create);
map.insert("toIntervalHour".into(), IntervalHourFunction::try_create);
map.insert(
"toIntervalMinute".into(),
IntervalMinuteFunction::try_create,
);
map.insert(
"toIntervalSecond".into(),
IntervalSecondFunction::try_create,
);

Ok(())
}
Expand Down
118 changes: 118 additions & 0 deletions common/functions/src/scalars/dates/interval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2020 Datafuse Labs.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;

use common_datavalues::prelude::*;
use common_exception::Result;

use crate::scalars::Function;

macro_rules! define_interval_struct {
($interval_type: ident) => {
#[derive(Clone)]
pub struct $interval_type {
display_name: String,
}
};
}

define_interval_struct!(IntervalYearFunction);
define_interval_struct!(IntervalMonthFunction);
define_interval_struct!(IntervalDayFunction);
define_interval_struct!(IntervalHourFunction);
define_interval_struct!(IntervalMinuteFunction);
define_interval_struct!(IntervalSecondFunction);

macro_rules! impl_function {
($interval_type: ident, $interval_unit: expr) => {
impl Function for $interval_type {
fn name(&self) -> &str {
self.display_name.as_str()
}

fn return_type(&self, _args: &[DataType]) -> Result<DataType> {
Ok(DataType::Interval($interval_unit))
}

fn nullable(&self, _input_schema: &DataSchema) -> Result<bool> {
Ok(false)
}

fn num_arguments(&self) -> usize {
1
}

fn eval(
&self,
columns: &DataColumnsWithField,
input_rows: usize,
) -> Result<DataColumn> {
let interval_num = columns[0].column();
let seconds_per_unit = IntervalUnit::avg_seconds($interval_unit);
let seconds = DataColumn::Constant(
DataValue::Float64(Some(seconds_per_unit as f64)),
input_rows,
);
let total_seconds =
interval_num.arithmetic(DataValueArithmeticOperator::Mul, &seconds);
println!("total seconds: {:?}", total_seconds);
total_seconds
}
}
};
}

impl_function!(IntervalYearFunction, IntervalUnit::Year);
impl_function!(IntervalMonthFunction, IntervalUnit::Month);
impl_function!(IntervalDayFunction, IntervalUnit::Day);
impl_function!(IntervalHourFunction, IntervalUnit::Hour);
impl_function!(IntervalMinuteFunction, IntervalUnit::Minute);
impl_function!(IntervalSecondFunction, IntervalUnit::Second);

macro_rules! define_try_create {
($interval_type: ident) => {
impl $interval_type {
pub fn try_create(display_name: &str) -> Result<Box<dyn Function>> {
Ok(Box::new($interval_type {
display_name: display_name.to_string(),
}))
}
}
};
}

define_try_create!(IntervalYearFunction);
define_try_create!(IntervalMonthFunction);
define_try_create!(IntervalDayFunction);
define_try_create!(IntervalHourFunction);
define_try_create!(IntervalMinuteFunction);
define_try_create!(IntervalSecondFunction);

macro_rules! define_display {
($interval_type: ident, $content: expr) => {
impl fmt::Display for $interval_type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $content)
}
}
};
}

define_display!(IntervalYearFunction, "toIntervalYear()");
define_display!(IntervalMonthFunction, "toIntervalMonth()");
define_display!(IntervalDayFunction, "toIntervalDay()");
define_display!(IntervalHourFunction, "toIntervalHour()");
define_display!(IntervalMinuteFunction, "toIntervalMinute()");
define_display!(IntervalSecondFunction, "toIntervalSecond()");
7 changes: 7 additions & 0 deletions common/functions/src/scalars/dates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@
mod date;
#[cfg(test)]
mod date_function_test;
mod interval;
mod now;
mod number_function;
mod simple_date;

pub use date::DateFunction;
pub use number_function::ToYYYYMMFunction;
pub use interval::IntervalDayFunction;
pub use interval::IntervalHourFunction;
pub use interval::IntervalMinuteFunction;
pub use interval::IntervalMonthFunction;
pub use interval::IntervalSecondFunction;
pub use interval::IntervalYearFunction;
pub use simple_date::TodayFunction;
pub use simple_date::TomorrowFunction;
pub use simple_date::YesterdayFunction;
9 changes: 9 additions & 0 deletions query/src/servers/clickhouse/writers/query_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ pub fn to_clickhouse_block(block: DataBlock) -> Result<Block> {
.collect();
result.column(name, vs)
}
DataType::Interval(unit) => {
let seconds = IntervalUnit::avg_seconds(unit.clone()) as f64;
let vs: Vec<f64> = column
.f64()?
.into_no_null_iter()
.map(|c| *c / seconds)
.collect();
result.column(name, vs)
}
_ => {
return Err(ErrorCode::BadDataValueType(format!(
"Unsupported column type:{:?}",
Expand Down
1 change: 1 addition & 0 deletions query/src/servers/mysql/writers/query_result_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl<'a, W: std::io::Write> DFQueryResultWriter<'a, W> {
DataType::Date16 | DataType::Date32 => Ok(ColumnType::MYSQL_TYPE_DATE),
DataType::DateTime32 => Ok(ColumnType::MYSQL_TYPE_DATETIME),
DataType::Null => Ok(ColumnType::MYSQL_TYPE_NULL),
DataType::Interval(_) => Ok(ColumnType::MYSQL_TYPE_DOUBLE),
_ => Err(ErrorCode::UnImplement(format!(
"Unsupported column type:{:?}",
field.data_type()
Expand Down
Loading

0 comments on commit db1ff10

Please sign in to comment.