diff --git a/common/ast/src/ast/expr.rs b/common/ast/src/ast/expr.rs index c404c6503a31..ef99208e66ad 100644 --- a/common/ast/src/ast/expr.rs +++ b/common/ast/src/ast/expr.rs @@ -161,6 +161,18 @@ pub enum Expr<'a> { span: &'a [Token<'a>], exprs: Vec>, }, + /// The `Interval 1 DAY` expr + Interval { + span: &'a [Token<'a>], + expr: Box>, + unit: IntervalKind, + }, + DateAdd { + span: &'a [Token<'a>], + date: Box>, + interval: Box>, + unit: IntervalKind, + }, } #[derive(Debug, Clone, PartialEq)] @@ -170,7 +182,6 @@ pub enum Literal { // Quoted string literal value String(String), Boolean(bool), - Interval(Interval), CurrentTimestamp, Null, } @@ -208,12 +219,6 @@ pub enum TypeName { Variant, } -#[derive(Debug, Clone, PartialEq)] -pub struct Interval { - pub value: String, - pub kind: IntervalKind, -} - #[derive(Debug, Clone, PartialEq)] pub enum TrimWhere { Both, @@ -286,6 +291,8 @@ impl<'a> Expr<'a> { Expr::Subquery { span, .. } => span, Expr::MapAccess { span, .. } => span, Expr::Array { span, .. } => span, + Expr::Interval { span, .. } => span, + Expr::DateAdd { span, .. } => span, } } } @@ -485,9 +492,6 @@ impl Display for Literal { Literal::CurrentTimestamp => { write!(f, "CURRENT_TIMESTAMP") } - Literal::Interval(interval) => { - write!(f, "INTERVAL {} {}", interval.value, interval.kind) - } Literal::Null => { write!(f, "NULL") } @@ -689,6 +693,17 @@ impl<'a> Display for Expr<'a> { write_comma_separated_list(f, exprs)?; write!(f, "]")?; } + Expr::Interval { expr, unit, .. } => { + write!(f, "INTERVAL {expr} {unit}")?; + } + Expr::DateAdd { + date, + interval, + unit, + .. + } => { + write!(f, "DATEADD({date}, INTERVAL {interval} {unit})")?; + } } Ok(()) diff --git a/common/ast/src/parser/expr.rs b/common/ast/src/parser/expr.rs index 8cb51f6cefbe..0113df269726 100644 --- a/common/ast/src/parser/expr.rs +++ b/common/ast/src/parser/expr.rs @@ -169,12 +169,20 @@ pub enum ExprElement<'a> { column: Identifier<'a>, }, /// `IS NULL` expression - IsNull { not: bool }, + IsNull { + not: bool, + }, /// `IS NOT NULL` expression /// `[ NOT ] IN (list, ...)` - InList { list: Vec>, not: bool }, + InList { + list: Vec>, + not: bool, + }, /// `[ NOT ] IN (SELECT ...)` - InSubquery { subquery: Box>, not: bool }, + InSubquery { + subquery: Box>, + not: bool, + }, /// `BETWEEN ... AND ...` Between { low: Box>, @@ -182,9 +190,13 @@ pub enum ExprElement<'a> { not: bool, }, /// Binary operation - BinaryOp { op: BinaryOperator }, + BinaryOp { + op: BinaryOperator, + }, /// Unary operation - UnaryOp { op: UnaryOperator }, + UnaryOp { + op: UnaryOperator, + }, /// `CAST` expression, like `CAST(expr AS target_type)` Cast { expr: Box>, @@ -196,7 +208,9 @@ pub enum ExprElement<'a> { target_type: TypeName, }, /// `::` expression - PgCast { target_type: TypeName }, + PgCast { + target_type: TypeName, + }, /// EXTRACT(IntervalKind FROM ) Extract { field: IntervalKind, @@ -222,11 +236,15 @@ pub enum ExprElement<'a> { trim_where: Option<(TrimWhere, Box>)>, }, /// A literal value, such as string, number, date or NULL - Literal { lit: Literal }, + Literal { + lit: Literal, + }, /// `Count(*)` expression CountAll, /// `(foo, bar)` - Tuple { exprs: Vec> }, + Tuple { + exprs: Vec>, + }, /// Scalar function call FunctionCall { /// Set to true if the function is aggregate function with `DISTINCT`, like `COUNT(DISTINCT a)` @@ -243,15 +261,32 @@ pub enum ExprElement<'a> { else_result: Option>>, }, /// `EXISTS` expression - Exists { subquery: Query<'a> }, + Exists { + subquery: Query<'a>, + }, /// Scalar subquery, which will only return a single row with a single column. - Subquery { subquery: Query<'a> }, + Subquery { + subquery: Query<'a>, + }, /// Access elements of `Array`, `Object` and `Variant` by index or key, like `arr[0]`, or `obj:k1` - MapAccess { accessor: MapAccessor<'a> }, + MapAccess { + accessor: MapAccessor<'a>, + }, /// An expression between parentheses Group(Expr<'a>), /// `[1, 2, 3]` - Array { exprs: Vec> }, + Array { + exprs: Vec>, + }, + Interval { + expr: Expr<'a>, + unit: IntervalKind, + }, + DateAdd { + date: Expr<'a>, + interval: Expr<'a>, + unit: IntervalKind, + }, } struct ExprParser; @@ -410,6 +445,21 @@ impl<'a, I: Iterator>> PrattParser for ExprParser { span: elem.span.0, exprs, }, + ExprElement::Interval { expr, unit } => Expr::Interval { + span: elem.span.0, + expr: Box::new(expr), + unit, + }, + ExprElement::DateAdd { + date, + interval, + unit, + } => Expr::DateAdd { + span: elem.span.0, + date: Box::new(date), + interval: Box::new(interval), + unit, + }, _ => unreachable!(), }; Ok(expr) @@ -740,7 +790,25 @@ pub fn expr_element(i: Input) -> IResult { ExprElement::Array { exprs } }, ); - + let date_add = map( + rule! { + DATEADD ~ "(" ~ #subexpr(0) ~ "," ~ #subexpr(0) ~ "," ~ #interval_kind ~ ")" + }, + |(_, _, date, _, interval, _, unit, _)| ExprElement::DateAdd { + date, + interval, + unit, + }, + ); + let interval = map( + rule! { + INTERVAL ~ #subexpr(0) ~ #interval_kind + }, + |(_, operand, unit)| ExprElement::Interval { + expr: operand, + unit, + }, + ); let (rest, (span, elem)) = consumed(alt(( rule! ( #is_null : "`... IS [NOT] NULL`" @@ -750,6 +818,8 @@ pub fn expr_element(i: Input) -> IResult { | #binary_op : "" | #unary_op : "" | #cast : "`CAST(... AS ...)`" + | #date_add: "`DATEADD(..., ..., (YEAR| MONTH | DAY | HOUR | MINUTE | SECOND | DOY | DOW))`" + | #interval: "`INTERVAL ... (YEAR| MONTH | DAY | HOUR | MINUTE | SECOND | DOY | DOW)`" | #pg_cast : "`::`" | #extract : "`EXTRACT((YEAR | MONTH | DAY | HOUR | MINUTE | SECOND) FROM ...)`" | #position : "`POSITION(... IN ...)`" @@ -828,12 +898,6 @@ pub fn literal(i: Input) -> IResult { value(Literal::Boolean(true), rule! { TRUE }), value(Literal::Boolean(false), rule! { FALSE }), )); - let interval = map( - rule! { - INTERVAL ~ #literal_string ~ #interval_kind - }, - |(_, value, field)| Literal::Interval(Interval { value, kind: field }), - ); let current_timestamp = value(Literal::CurrentTimestamp, rule! { CURRENT_TIMESTAMP }); let null = value(Literal::Null, rule! { NULL }); @@ -841,7 +905,6 @@ pub fn literal(i: Input) -> IResult { #string | #number | #boolean - | #interval : "`INTERVAL '...' (YEAR | MONTH | DAY | ...)`" | #current_timestamp | #null )(i) diff --git a/common/ast/src/parser/token.rs b/common/ast/src/parser/token.rs index aaf06112b851..e8ea96ffd92e 100644 --- a/common/ast/src/parser/token.rs +++ b/common/ast/src/parser/token.rs @@ -294,6 +294,8 @@ pub enum TokenKind { DATABASES, #[token("DATE", ignore(ascii_case))] DATE, + #[token("DATE_ADD", ignore(ascii_case))] + DATEADD, #[token("DATETIME", ignore(ascii_case))] DATETIME, #[token("DAY", ignore(ascii_case))] @@ -788,7 +790,17 @@ impl TokenKind { // | TokenKind::UNION | TokenKind::WHERE // | TokenKind::WINDOW - | TokenKind::WITH if !after_as => true, + | TokenKind::WITH + | TokenKind::DATEADD + | TokenKind::YEAR + | TokenKind::MONTH + | TokenKind::DAY + | TokenKind::HOUR + | TokenKind::MINUTE + | TokenKind::SECOND + | TokenKind::DOY + | TokenKind::DOW + if !after_as => true, _ => false } } diff --git a/common/ast/tests/it/testdata/expr-error.txt b/common/ast/tests/it/testdata/expr-error.txt index f5e3407e7be8..d0f8e4ff078a 100644 --- a/common/ast/tests/it/testdata/expr-error.txt +++ b/common/ast/tests/it/testdata/expr-error.txt @@ -44,7 +44,7 @@ error: --> SQL:1:10 | 1 | CAST(col1) - | ---- ^ expected `AS`, `,`, `(`, `.`, `IS`, `NOT`, or 46 more ... + | ---- ^ expected `AS`, `,`, `(`, `.`, `IS`, `NOT`, or 47 more ... | | | while parsing `CAST(... AS ...)` | while parsing expression diff --git a/common/functions/src/scalars/dates/date.rs b/common/functions/src/scalars/dates/date.rs index 28f0db55d0e3..2619016cd441 100644 --- a/common/functions/src/scalars/dates/date.rs +++ b/common/functions/src/scalars/dates/date.rs @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +use common_datavalues::IntervalKind; + use super::now::NowFunction; use super::number_function::ToMondayFunction; use super::number_function::ToYearFunction; use super::round_function::Round; +use super::to_interval_function_creator; use super::AddDaysFunction; use super::AddMonthsFunction; use super::AddTimesFunction; use super::AddYearsFunction; +use super::DateAddFunction; use super::RoundFunction; use super::ToDayOfMonthFunction; use super::ToDayOfWeekFunction; @@ -124,5 +128,40 @@ impl DateFunction { factory.register("subtractHours", AddTimesFunction::desc(-3600)); factory.register("subtractMinutes", AddTimesFunction::desc(-60)); factory.register("subtractSeconds", AddTimesFunction::desc(-1)); + + factory.register( + "to_interval_year", + to_interval_function_creator(IntervalKind::Year), + ); + factory.register( + "to_interval_month", + to_interval_function_creator(IntervalKind::Month), + ); + factory.register( + "to_interval_day", + to_interval_function_creator(IntervalKind::Day), + ); + factory.register( + "to_interval_hour", + to_interval_function_creator(IntervalKind::Hour), + ); + factory.register( + "to_interval_minute", + to_interval_function_creator(IntervalKind::Minute), + ); + factory.register( + "to_interval_second", + to_interval_function_creator(IntervalKind::Second), + ); + factory.register( + "to_interval_doy", + to_interval_function_creator(IntervalKind::Doy), + ); + factory.register( + "to_interval_dow", + to_interval_function_creator(IntervalKind::Dow), + ); + + factory.register("date_add", DateAddFunction::desc()); } } diff --git a/common/functions/src/scalars/dates/date_add.rs b/common/functions/src/scalars/dates/date_add.rs new file mode 100644 index 000000000000..ee690b40293d --- /dev/null +++ b/common/functions/src/scalars/dates/date_add.rs @@ -0,0 +1,42 @@ +// Copyright 2022 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::DataTypeImpl; +use common_exception::Result; + +use crate::scalars::function_factory::FunctionDescription; +use crate::scalars::ArithmeticPlusFunction; +use crate::scalars::Function; +use crate::scalars::FunctionFeatures; + +pub struct DateAddFunction {} + +impl DateAddFunction { + pub fn try_create(display_name: &str, args: &[&DataTypeImpl]) -> Result> { + ArithmeticPlusFunction::try_create_func(display_name, args) + } + + pub fn desc() -> FunctionDescription { + FunctionDescription::creator(Box::new(Self::try_create)) + .features(FunctionFeatures::default().num_arguments(2)) + } +} + +impl fmt::Display for DateAddFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "date_add()") + } +} diff --git a/common/functions/src/scalars/dates/mod.rs b/common/functions/src/scalars/dates/mod.rs index 49c6333928bf..5b5ef1964ee0 100644 --- a/common/functions/src/scalars/dates/mod.rs +++ b/common/functions/src/scalars/dates/mod.rs @@ -13,16 +13,19 @@ // limitations under the License. mod date; +mod date_add; mod interval_function; mod now; mod number_function; mod round_function; mod simple_date; +mod to_interval_function; mod week_date; #[macro_use] mod macros; pub use date::DateFunction; +pub use date_add::DateAddFunction; pub use interval_function::AddDaysFunction; pub use interval_function::AddMonthsFunction; pub use interval_function::AddTimesFunction; @@ -46,4 +49,5 @@ pub use round_function::RoundFunction; pub use simple_date::TodayFunction; pub use simple_date::TomorrowFunction; pub use simple_date::YesterdayFunction; +pub use to_interval_function::to_interval_function_creator; pub use week_date::ToStartOfWeekFunction; diff --git a/common/functions/src/scalars/dates/to_interval_function.rs b/common/functions/src/scalars/dates/to_interval_function.rs new file mode 100644 index 000000000000..1902dc89fc69 --- /dev/null +++ b/common/functions/src/scalars/dates/to_interval_function.rs @@ -0,0 +1,87 @@ +// Copyright 2022 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_datavalues::IntervalKind; +use common_datavalues::IntervalType; +use common_exception::ErrorCode; +use common_exception::Result; + +use crate::scalars::default_column_cast; +use crate::scalars::FactoryCreator; +use crate::scalars::Function; +use crate::scalars::FunctionContext; +use crate::scalars::FunctionDescription; +use crate::scalars::FunctionFeatures; + +#[derive(Clone)] +pub struct ToIntervalFunction { + display_name: String, + interval_kind: IntervalKind, +} + +impl ToIntervalFunction { + pub fn try_create( + display_name: String, + interval_kind: IntervalKind, + args: &[&DataTypeImpl], + ) -> Result> { + if !args[0].data_type_id().is_numeric() { + return Err(ErrorCode::BadDataValueType(format!( + "DataValue Error: Unsupported type {:?}", + args[0].data_type_id() + ))); + } + + Ok(Box::new(ToIntervalFunction { + display_name, + interval_kind, + })) + } +} + +impl Function for ToIntervalFunction { + fn name(&self) -> &str { + self.display_name.as_str() + } + + fn return_type(&self) -> DataTypeImpl { + IntervalType::new_impl(self.interval_kind) + } + + fn eval( + &self, + _func_ctx: FunctionContext, + columns: &ColumnsWithField, + _input_rows: usize, + ) -> Result { + default_column_cast(columns[0].column(), &i64::to_data_type()) + } +} + +impl fmt::Display for ToIntervalFunction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}()", self.display_name) + } +} + +pub fn to_interval_function_creator(interval_kind: IntervalKind) -> FunctionDescription { + let creator: FactoryCreator = Box::new(move |display_name, args| { + ToIntervalFunction::try_create(display_name.to_string(), interval_kind, args) + }); + + FunctionDescription::creator(creator).features(FunctionFeatures::default().num_arguments(1)) +} diff --git a/query/src/sql/planner/mod.rs b/query/src/sql/planner/mod.rs index c406285d4f1a..3117223d2c52 100644 --- a/query/src/sql/planner/mod.rs +++ b/query/src/sql/planner/mod.rs @@ -51,6 +51,7 @@ impl Planner { pub async fn plan_sql<'a>(&mut self, sql: &'a str) -> Result<(NewPipeline, Vec)> { // Step 1: parse SQL text into AST let tokens = tokenize_sql(sql)?; + let backtrace = Backtrace::new(); let stmts = parse_sql(&tokens, &backtrace)?; if stmts.len() > 1 { diff --git a/query/src/sql/planner/semantic/type_check.rs b/query/src/sql/planner/semantic/type_check.rs index 44e6d460d5b5..8ed0adde6aff 100644 --- a/query/src/sql/planner/semantic/type_check.rs +++ b/query/src/sql/planner/semantic/type_check.rs @@ -371,6 +371,20 @@ impl<'a> TypeChecker<'a> { self.resolve_extract_expr(kind, expr, required_type).await } + Expr::Interval { expr, unit, .. } => { + self.resolve_interval(expr, unit, required_type).await + } + + Expr::DateAdd { + date, + interval, + unit, + .. + } => { + self.resolve_date_add(date, interval, unit, required_type) + .await + } + _ => Err(ErrorCode::UnImplement(format!( "Unsupported expr: {:?}", expr @@ -553,6 +567,114 @@ impl<'a> TypeChecker<'a> { } } + pub async fn resolve_interval( + &mut self, + arg: &Expr<'a>, + interval_kind: &IntervalKind, + _required_type: Option, + ) -> Result<(Scalar, DataTypeImpl)> { + match interval_kind { + IntervalKind::Year => { + self.resolve_function( + "to_interval_year", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Year)), + ) + .await + } + IntervalKind::Month => { + self.resolve_function( + "to_interval_month", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Month)), + ) + .await + } + IntervalKind::Day => { + self.resolve_function( + "to_interval_day", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Day)), + ) + .await + } + IntervalKind::Hour => { + self.resolve_function( + "to_interval_hour", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Hour)), + ) + .await + } + IntervalKind::Minute => { + self.resolve_function( + "to_interval_minute", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Minute)), + ) + .await + } + IntervalKind::Second => { + self.resolve_function( + "to_interval_second", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Second)), + ) + .await + } + IntervalKind::Doy => { + self.resolve_function( + "to_interval_doy", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Doy)), + ) + .await + } + IntervalKind::Dow => { + self.resolve_function( + "to_interval_dow", + &[arg], + Some(IntervalType::new_impl(IntervalKind::Dow)), + ) + .await + } + } + } + + pub async fn resolve_date_add( + &mut self, + date: &Expr<'a>, + interval: &Expr<'a>, + interval_kind: &IntervalKind, + _required_type: Option, + ) -> Result<(Scalar, DataTypeImpl)> { + let mut args = vec![]; + let mut arg_types = vec![]; + + let (date, date_type) = self.resolve(date, None).await?; + args.push(date); + arg_types.push(date_type); + + let (interval, interval_type) = + self.resolve_interval(interval, interval_kind, None).await?; + args.push(interval); + arg_types.push(interval_type); + + let arg_types_ref: Vec<&DataTypeImpl> = arg_types.iter().collect(); + + let func = FunctionFactory::instance().get("date_add", &arg_types_ref)?; + Ok(( + FunctionCall { + arguments: args, + func_name: "date_add".to_string(), + arg_types: arg_types.to_vec(), + return_type: func.return_type(), + } + .into(), + func.return_type(), + )) + } + pub async fn resolve_subquery( &mut self, subquery: &Query<'a>, @@ -635,20 +757,12 @@ impl<'a> TypeChecker<'a> { Literal::String(string) => DataValue::String(string.as_bytes().to_vec()), Literal::Boolean(boolean) => DataValue::Boolean(*boolean), Literal::Null => DataValue::Null, - Literal::Interval(interval) => { - let num = interval.value.parse::()?; - DataValue::Int64(num) - } _ => Err(ErrorCode::SemanticError(format!( "Unsupported literal value: {literal}" )))?, }; - let data_type = if let Literal::Interval(interval) = literal { - IntervalType::new_impl(interval.kind) - } else { - value.data_type() - }; + let data_type = value.data_type(); Ok((value, data_type)) } diff --git a/tests/suites/0_stateless/02_function/02_0012_function_datetimes.result b/tests/suites/0_stateless/02_function/02_0012_function_datetimes.result index b9ec01de935a..daad40543bfc 100644 --- a/tests/suites/0_stateless/02_function/02_0012_function_datetimes.result +++ b/tests/suites/0_stateless/02_function/02_0012_function_datetimes.result @@ -92,19 +92,14 @@ ===addYears=== ===subtractMonths=== 2019-01-29 -2019-01-29 -2009-12-29 10:00:00.000000 2009-12-29 10:00:00.000000 ===subtractMonths=== ===addDays=== 2020-03-01 -2020-03-01 -2020-02-28 10:00:00.000000 2020-02-28 10:00:00.000000 ===addDays=== ===addHours=== 2020-03-01 11:00:00.000000 -2020-03-01 11:00:00.000000 2020-02-29 01:00:00.000000 ===addHours=== ===subtractMinutes=== diff --git a/tests/suites/0_stateless/02_function/02_0012_function_datetimes.sql b/tests/suites/0_stateless/02_function/02_0012_function_datetimes.sql index 56469fe0e9cc..2c1ca980abcb 100644 --- a/tests/suites/0_stateless/02_function/02_0012_function_datetimes.sql +++ b/tests/suites/0_stateless/02_function/02_0012_function_datetimes.sql @@ -113,27 +113,27 @@ select '===addYears==='; select '===subtractMonths==='; select subtractMonths(to_date(18321), cast(13, INT16)); -- 2020-2-29 - 13 months -select to_date(18321) - interval '13' month; +-- select to_date(18321) - interval '13' month; select subtractMonths(to_datetime(1582970400000000), cast(122, INT16)); -- 2020-2-29T10:00:00 - (12*10 + 2) months -select to_datetime(1582970400000000) - interval '122' month; +-- select to_datetime(1582970400000000) - interval '122' month; select subtractMonths(to_date('1000-01-01'), 1); -- {ErrorCode 1068} select subtractMonths(to_datetime('1000-01-01 00:00:00'), 1); -- {ErrorCode 1069} select '===subtractMonths==='; select '===addDays==='; select addDays(to_date(18321), cast(1, INT16)); -- 2020-2-29 + 1 day -select to_date(18321) + interval '1' day; +-- select to_date(18321) + interval '1' day; select addDays(to_datetime(1582970400000000), cast(-1, INT16)); -- 2020-2-29T10:00:00 - 1 day -select to_datetime(1582970400000000) + interval '-1' day; +-- select to_datetime(1582970400000000) + interval '-1' day; select addDays(to_date('9999-12-31'), 1); -- {ErrorCode 1068} select addDays(to_datetime('9999-12-31 23:59:59'), 1); -- {ErrorCode 1069} select '===addDays==='; select '===addHours==='; select addHours(to_datetime(1582970400000000), cast(25, INT32)); -- 2020-2-29T10:00:00 + 25 hours -select to_datetime(1582970400000000) + interval '25' hour; +-- select to_datetime(1582970400000000) + interval '25' hour; select addHours(to_date(18321), cast(1.2, Float32)); select addHours(to_date('9999-12-31'), 24); -- {ErrorCode 1069} select addHours(to_datetime('9999-12-31 23:59:59'), 1); -- {ErrorCode 1069} @@ -231,3 +231,16 @@ insert into t values('2022-04-02 15:10:28.221', '2022-04-02 15:10:28.221', '1000 select * from t order by b; drop table t; + +-- select '===date_add==='; +-- set enable_planner_v2=1; +-- select date_add(to_date(18321), 1, YEAR); +-- select date_add(to_date(18321), -1, YEAR); +-- select date_add(to_date(18321), 1, SECOND); + +-- create table t(a int); +-- insert into t values(1), (2); +-- select date_add(to_date(18321), a, YEAR) from t; +-- drop table t; + +-- set enable_planner_v2=0; \ No newline at end of file diff --git a/tests/suites/0_stateless/20+_others/20_0001_planner_v2.result b/tests/suites/0_stateless/20+_others/20_0001_planner_v2.result index cec298be8067..d73a52d6d132 100644 --- a/tests/suites/0_stateless/20+_others/20_0001_planner_v2.result +++ b/tests/suites/0_stateless/20+_others/20_0001_planner_v2.result @@ -13,7 +13,7 @@ 0 1 0 1 ====SCALAR_EXPRESSION==== -1 13 +13 ====COMPARISON==== 5 ====CAST==== diff --git a/tests/suites/0_stateless/20+_others/20_0001_planner_v2.sql b/tests/suites/0_stateless/20+_others/20_0001_planner_v2.sql index 999a5d416fcb..a126f4608f3c 100644 --- a/tests/suites/0_stateless/20+_others/20_0001_planner_v2.sql +++ b/tests/suites/0_stateless/20+_others/20_0001_planner_v2.sql @@ -8,7 +8,7 @@ select number as a, number + 1 as b from numbers(1); select number as a, number + 1 as b from numbers(1) group by a, number order by number; select '====SCALAR_EXPRESSION===='; -select interval '1' day, extract(day from to_date('2022-05-13')); +select extract(day from to_date('2022-05-13')); -- Comparison expressions select '====COMPARISON====';