diff --git a/src/common/function/src/scalars/timestamp.rs b/src/common/function/src/scalars/timestamp.rs index 17fb9a2443..475e1c150f 100644 --- a/src/common/function/src/scalars/timestamp.rs +++ b/src/common/function/src/scalars/timestamp.rs @@ -11,11 +11,17 @@ // 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::sync::Arc; +mod to_unixtime; + +use to_unixtime::ToUnixtimeFunction; use crate::scalars::function_registry::FunctionRegistry; pub(crate) struct TimestampFunction; impl TimestampFunction { - pub fn register(_registry: &FunctionRegistry) {} + pub fn register(registry: &FunctionRegistry) { + registry.register(Arc::new(ToUnixtimeFunction::default())); + } } diff --git a/src/common/function/src/scalars/timestamp/to_unixtime.rs b/src/common/function/src/scalars/timestamp/to_unixtime.rs new file mode 100644 index 0000000000..9922525164 --- /dev/null +++ b/src/common/function/src/scalars/timestamp/to_unixtime.rs @@ -0,0 +1,148 @@ +// Copyright 2023 Greptime Team +// +// 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 std::str::FromStr; +use std::sync::Arc; + +use common_query::error::{self, Result, UnsupportedInputDataTypeSnafu}; +use common_query::prelude::{Signature, Volatility}; +use common_time::timestamp::TimeUnit; +use common_time::Timestamp; +use datatypes::prelude::ConcreteDataType; +use datatypes::types::StringType; +use datatypes::vectors::{Int64Vector, StringVector, Vector, VectorRef}; +use snafu::ensure; + +use crate::scalars::function::{Function, FunctionContext}; + +#[derive(Clone, Debug, Default)] +pub struct ToUnixtimeFunction; + +const NAME: &str = "to_unixtime"; + +fn convert_to_seconds(arg: &str) -> Option { + match Timestamp::from_str(arg) { + Ok(ts) => { + let sec_mul = (TimeUnit::Second.factor() / ts.unit().factor()) as i64; + Some(ts.value().div_euclid(sec_mul)) + } + Err(_err) => None, + } +} + +impl Function for ToUnixtimeFunction { + fn name(&self) -> &str { + NAME + } + + fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result { + Ok(ConcreteDataType::timestamp_second_datatype()) + } + + fn signature(&self) -> Signature { + Signature::exact( + vec![ConcreteDataType::String(StringType)], + Volatility::Immutable, + ) + } + + fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result { + ensure!( + columns.len() == 1, + error::InvalidFuncArgsSnafu { + err_msg: format!( + "The length of the args is not correct, expect exactly one, have: {}", + columns.len() + ), + } + ); + + match columns[0].data_type() { + ConcreteDataType::String(_) => { + let array = columns[0].to_arrow_array(); + let vector = StringVector::try_from_arrow_array(&array).unwrap(); + Ok(Arc::new(Int64Vector::from( + (0..vector.len()) + .map(|i| convert_to_seconds(&vector.get(i).to_string())) + .collect::>(), + ))) + } + _ => UnsupportedInputDataTypeSnafu { + function: NAME, + datatypes: columns.iter().map(|c| c.data_type()).collect::>(), + } + .fail(), + } + } +} + +impl fmt::Display for ToUnixtimeFunction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TO_UNIXTIME") + } +} + +#[cfg(test)] +mod tests { + use common_query::prelude::TypeSignature; + use datatypes::prelude::ConcreteDataType; + use datatypes::types::StringType; + use datatypes::value::Value; + use datatypes::vectors::StringVector; + + use super::{ToUnixtimeFunction, *}; + use crate::scalars::Function; + + #[test] + fn test_to_unixtime() { + let f = ToUnixtimeFunction::default(); + assert_eq!("to_unixtime", f.name()); + assert_eq!( + ConcreteDataType::timestamp_second_datatype(), + f.return_type(&[]).unwrap() + ); + + assert!(matches!(f.signature(), + Signature { + type_signature: TypeSignature::Exact(valid_types), + volatility: Volatility::Immutable + } if valid_types == vec![ConcreteDataType::String(StringType)] + )); + + let times = vec![ + Some("2023-03-01T06:35:02Z"), + None, + Some("2022-06-30T23:59:60Z"), + Some("invalid_time_stamp"), + ]; + let results = vec![Some(1677652502), None, Some(1656633600), None]; + let args: Vec = vec![Arc::new(StringVector::from(times.clone()))]; + let vector = f.eval(FunctionContext::default(), &args).unwrap(); + assert_eq!(4, vector.len()); + for (i, _t) in times.iter().enumerate() { + let v = vector.get(i); + if i == 1 || i == 3 { + assert_eq!(Value::Null, v); + continue; + } + match v { + Value::Int64(ts) => { + assert_eq!(ts, (*results.get(i).unwrap()).unwrap()); + } + _ => unreachable!(), + } + } + } +} diff --git a/tests/README.md b/tests/README.md index 64f805542f..ffdc8eaef5 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,7 +3,7 @@ ## Sqlness manual ### Case file -Sqlness has three types of file +Sqlness has two types of file - `.sql`: test input, SQL only - `.result`: expected test output, SQL and its results diff --git a/tests/cases/standalone/common/select/dummy.result b/tests/cases/standalone/common/select/dummy.result index 82d04f8520..6fa669871b 100644 --- a/tests/cases/standalone/common/select/dummy.result +++ b/tests/cases/standalone/common/select/dummy.result @@ -34,3 +34,11 @@ select * where "a" = "A"; Error: 3000(PlanQuery), No field named 'a'. +select TO_UNIXTIME('2023-03-01T06:35:02Z'); + ++-------------------------------------------+ +| to_unixtime(Utf8("2023-03-01T06:35:02Z")) | ++-------------------------------------------+ +| 1677652502 | ++-------------------------------------------+ + diff --git a/tests/cases/standalone/common/select/dummy.sql b/tests/cases/standalone/common/select/dummy.sql index 97d975b2e2..9c2a3944cf 100644 --- a/tests/cases/standalone/common/select/dummy.sql +++ b/tests/cases/standalone/common/select/dummy.sql @@ -9,3 +9,5 @@ select "a"; select "A"; select * where "a" = "A"; + +select TO_UNIXTIME('2023-03-01T06:35:02Z');