diff --git a/src/query/ast/src/ast/expr.rs b/src/query/ast/src/ast/expr.rs index 83a016dd82a26..a159c16d5f35d 100644 --- a/src/query/ast/src/ast/expr.rs +++ b/src/query/ast/src/ast/expr.rs @@ -832,6 +832,7 @@ impl Display for Weekday { #[derive(Debug, Copy, Clone, PartialEq, Eq, Drive, DriveMut)] pub enum IntervalKind { + ISOYear, Year, Quarter, Month, @@ -849,6 +850,7 @@ pub enum IntervalKind { impl Display for IntervalKind { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str(match self { + IntervalKind::ISOYear => "ISOYEAR", IntervalKind::Year => "YEAR", IntervalKind::Quarter => "QUARTER", IntervalKind::Month => "MONTH", diff --git a/src/query/ast/src/parser/expr.rs b/src/query/ast/src/parser/expr.rs index 805290405de1e..7e2fd567aa897 100644 --- a/src/query/ast/src/parser/expr.rs +++ b/src/query/ast/src/parser/expr.rs @@ -1670,7 +1670,7 @@ pub fn type_name(i: Input) -> IResult { TypeName::Int64, rule! { ( INT64 | SIGNED | BIGINT ) ~ ( "(" ~ ^#literal_u64 ~ ^")" )? }, ); - let ty_float32 = value(TypeName::Float32, rule! { FLOAT32 | FLOAT }); + let ty_float32 = value(TypeName::Float32, rule! { FLOAT32 | FLOAT | REAL }); let ty_float64 = value( TypeName::Float64, rule! { (FLOAT64 | DOUBLE) ~ PRECISION? }, @@ -1692,6 +1692,14 @@ pub fn type_name(i: Input) -> IResult { }) }, ); + let ty_numeric = value( + TypeName::Decimal { + precision: 18, + scale: 3, + }, + rule! { NUMERIC }, + ); + let ty_array = map( rule! { ARRAY ~ "(" ~ #type_name ~ ")" }, |(_, _, item_type, _)| TypeName::Array(Box::new(item_type)), @@ -1769,6 +1777,7 @@ pub fn type_name(i: Input) -> IResult { ( #ty_date | #ty_datetime | #ty_interval + | #ty_numeric | #ty_binary | #ty_string | #ty_variant @@ -1834,6 +1843,7 @@ pub fn weekday(i: Input) -> IResult { } pub fn interval_kind(i: Input) -> IResult { + let iso_year = value(IntervalKind::ISOYear, rule! { ISOYEAR }); let year = value(IntervalKind::Year, rule! { YEAR }); let quarter = value(IntervalKind::Quarter, rule! { QUARTER }); let month = value(IntervalKind::Month, rule! { MONTH }); @@ -1846,6 +1856,11 @@ pub fn interval_kind(i: Input) -> IResult { let week = value(IntervalKind::Week, rule! { WEEK }); let epoch = value(IntervalKind::Epoch, rule! { EPOCH }); let microsecond = value(IntervalKind::MicroSecond, rule! { MICROSECOND }); + + let iso_year_str = value( + IntervalKind::ISOYear, + rule! { #literal_string_eq_ignore_case("ISOYEAR") }, + ); let year_str = value( IntervalKind::Year, rule! { #literal_string_eq_ignore_case("YEAR") }, @@ -1897,6 +1912,7 @@ pub fn interval_kind(i: Input) -> IResult { alt(( rule!( #year + | #iso_year | #quarter | #month | #day @@ -1911,6 +1927,7 @@ pub fn interval_kind(i: Input) -> IResult { ), rule!( #year_str + | #iso_year_str | #quarter_str | #month_str | #day_str diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index ccc84cb51c40b..425f99094180c 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -856,6 +856,8 @@ pub enum TokenKind { NULL, #[token("NULLABLE", ignore(ascii_case))] NULLABLE, + #[token("NUMERIC", ignore(ascii_case))] + NUMERIC, #[token("OBJECT", ignore(ascii_case))] OBJECT, #[token("OF", ignore(ascii_case))] @@ -990,6 +992,8 @@ pub enum TokenKind { ROW_TAG, #[token("GRANT", ignore(ascii_case))] GRANT, + #[token("REAL", ignore(ascii_case))] + REAL, #[token("REPEAT", ignore(ascii_case))] REPEAT, #[token("ROLE", ignore(ascii_case))] diff --git a/src/query/ast/tests/it/testdata/expr-error.txt b/src/query/ast/tests/it/testdata/expr-error.txt index 547e74f758c94..afb6f20ed2aac 100644 --- a/src/query/ast/tests/it/testdata/expr-error.txt +++ b/src/query/ast/tests/it/testdata/expr-error.txt @@ -29,7 +29,7 @@ error: --> SQL:1:14 | 1 | CAST(col1 AS foo) - | ---- ^^^ unexpected `foo`, expecting `BOOL`, `FLOAT`, `BOOLEAN`, `FLOAT32`, `FLOAT64`, `BLOB`, `JSON`, `DOUBLE`, `LONGBLOB`, `GEOMETRY`, `GEOGRAPHY`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `VARBINARY`, `MEDIUMBLOB`, `TINYBLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, or `NULLABLE` + | ---- ^^^ unexpected `foo`, expecting `BOOL`, `FLOAT`, `BOOLEAN`, `FLOAT32`, `FLOAT64`, `BLOB`, `JSON`, `DOUBLE`, `LONGBLOB`, `GEOMETRY`, `GEOGRAPHY`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `REAL`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `VARBINARY`, `MEDIUMBLOB`, `TINYBLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, or `NULLABLE` | | | while parsing `CAST(... AS ...)` | while parsing expression diff --git a/src/query/ast/tests/it/testdata/stmt-error.txt b/src/query/ast/tests/it/testdata/stmt-error.txt index 68df5507d3c67..efb9edf951aa3 100644 --- a/src/query/ast/tests/it/testdata/stmt-error.txt +++ b/src/query/ast/tests/it/testdata/stmt-error.txt @@ -29,7 +29,7 @@ error: --> SQL:1:19 | 1 | create table a (c varch) - | ------ - ^^^^^ unexpected `varch`, expecting `VARCHAR`, `CHAR`, `VARIANT`, `CHARACTER`, `VARBINARY`, `ARRAY`, `BINARY`, `GEOGRAPHY`, `MAP`, `DATE`, `STRING`, `FLOAT32`, `FLOAT64`, `DECIMAL`, `SMALLINT`, `DATETIME`, `INTERVAL`, `NULLABLE`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT`, `DOUBLE`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `TEXT`, `JSON`, or `GEOMETRY` + | ------ - ^^^^^ unexpected `varch`, expecting `VARCHAR`, `CHAR`, `VARIANT`, `CHARACTER`, `VARBINARY`, `ARRAY`, `BINARY`, `GEOGRAPHY`, `MAP`, `DATE`, `STRING`, `FLOAT32`, `FLOAT64`, `DECIMAL`, `NUMERIC`, `SMALLINT`, `DATETIME`, `INTERVAL`, `NULLABLE`, `REAL`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT`, `DOUBLE`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `TEXT`, `JSON`, or `GEOMETRY` | | | | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` | while parsing `CREATE [OR REPLACE] TABLE [IF NOT EXISTS] [.] [] []` @@ -42,7 +42,7 @@ error: --> SQL:1:25 | 1 | create table a (c tuple()) - | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, `NULLABLE`, , , or `IDENTIFIER` + | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `REAL`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, `NULLABLE`, , , or `IDENTIFIER` | | | | | | | while parsing type name | | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` @@ -70,7 +70,7 @@ error: --> SQL:1:38 | 1 | create table a (b tuple(c int, uint64)); - | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` + | ------ - ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `REAL`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` | | | | | | | while parsing TUPLE( , ...) | | | while parsing type name @@ -963,7 +963,7 @@ error: --> SQL:1:40 | 1 | CREATE FUNCTION my_agg (INT) STATE { s STRIN } RETURNS BOOLEAN LANGUAGE javascript ADDRESS = 'http://0.0.0.0:8815'; - | ------ - ^^^^^ unexpected `STRIN`, expecting `STRING`, `SIGNED`, `INTERVAL`, `TINYINT`, `VARIANT`, `SMALLINT`, `TINYBLOB`, `VARBINARY`, `INT8`, `JSON`, `INT16`, `INT32`, `INT64`, `UINT8`, `BIGINT`, `UINT16`, `UINT32`, `UINT64`, `BINARY`, `INTEGER`, `DATETIME`, `TIMESTAMP`, `UNSIGNED`, `DATE`, `CHAR`, `TEXT`, `ARRAY`, `TUPLE`, `BOOLEAN`, `DECIMAL`, `VARCHAR`, `LONGBLOB`, `NULLABLE`, `CHARACTER`, `GEOGRAPHY`, `MEDIUMBLOB`, `BITMAP`, `}`, `BOOL`, `INT`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `MAP`, `BLOB`, or `GEOMETRY` + | ------ - ^^^^^ unexpected `STRIN`, expecting `STRING`, `SIGNED`, `INTERVAL`, `TINYINT`, `VARIANT`, `SMALLINT`, `TINYBLOB`, `VARBINARY`, `INT8`, `JSON`, `INT16`, `INT32`, `INT64`, `UINT8`, `BIGINT`, `UINT16`, `UINT32`, `UINT64`, `BINARY`, `INTEGER`, `DATETIME`, `NUMERIC`, `TIMESTAMP`, `UNSIGNED`, `REAL`, `DATE`, `CHAR`, `TEXT`, `ARRAY`, `TUPLE`, `BOOLEAN`, `DECIMAL`, `VARCHAR`, `LONGBLOB`, `NULLABLE`, `CHARACTER`, `GEOGRAPHY`, `MEDIUMBLOB`, `BITMAP`, `}`, `BOOL`, `INT`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `MAP`, `BLOB`, or `GEOMETRY` | | | | | while parsing (, ...) STATE {, ...} RETURNS LANGUAGE { ADDRESS= | AS } | while parsing `CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] [DESC = ]` @@ -1051,7 +1051,7 @@ error: | ------ while parsing `CREATE [OR REPLACE] DICTIONARY [IF NOT EXISTS] [(, ...)] PRIMARY KEY [, ...] SOURCE ( ([])) [COMMENT ] ` 2 | ( 3 | user_name tuple(), - | --------- ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, `NULLABLE`, , , or `IDENTIFIER` + | --------- ----- ^ unexpected `)`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `REAL`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, `NULLABLE`, , , or `IDENTIFIER` | | | | | while parsing type name | while parsing ` [DEFAULT ] [AS () VIRTUAL] [AS () STORED] [COMMENT '']` @@ -1102,7 +1102,7 @@ error: --> SQL:1:19 | 1 | drop procedure p1(a int) - | ---- ^ unexpected `a`, expecting `DATE`, `ARRAY`, `VARCHAR`, `VARIANT`, `SMALLINT`, `DATETIME`, `VARBINARY`, `CHARACTER`, `)`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `MAP`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `CHAR`, `TEXT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` + | ---- ^ unexpected `a`, expecting `DATE`, `ARRAY`, `VARCHAR`, `VARIANT`, `SMALLINT`, `DATETIME`, `VARBINARY`, `CHARACTER`, `)`, `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `REAL`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `MAP`, `BITMAP`, `TUPLE`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `CHAR`, `TEXT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` | | | while parsing `DROP PROCEDURE ()` @@ -1134,7 +1134,7 @@ error: --> SQL:1:44 | 1 | create PROCEDURE p1() returns table(string not null, int null) language sql comment = 'test' as $$ - | ------ ----- ^^^ unexpected `not`, expecting `INT8`, `INT16`, `INT32`, `INT64`, `UINT16`, `UINT32`, `UINT64`, `INTEGER`, `FLOAT32`, `FLOAT64`, `INTERVAL`, `GEOMETRY`, `INT`, `BOOL`, `DATE`, `BLOB`, `TEXT`, `JSON`, `UINT8`, `FLOAT`, `TUPLE`, `DOUBLE`, `BITMAP`, `BINARY`, `STRING`, `BOOLEAN`, `UNSIGNED`, `DATETIME`, `NULLABLE`, `TIMESTAMP`, `GEOGRAPHY`, `TINYINT`, `LONGBLOB`, `TINYBLOB`, `SMALLINT`, `BIGINT`, `SIGNED`, `DECIMAL`, `ARRAY`, `MAP`, `VARBINARY`, `MEDIUMBLOB`, `VARCHAR`, `CHAR`, `CHARACTER`, or `VARIANT` + | ------ ----- ^^^ unexpected `not`, expecting `INT8`, `INT16`, `INT32`, `INT64`, `UINT16`, `UINT32`, `UINT64`, `INTEGER`, `FLOAT32`, `FLOAT64`, `INTERVAL`, `GEOMETRY`, `INT`, `BOOL`, `DATE`, `BLOB`, `TEXT`, `JSON`, `UINT8`, `FLOAT`, `TUPLE`, `DOUBLE`, `BITMAP`, `BINARY`, `STRING`, `BOOLEAN`, `NUMERIC`, `UNSIGNED`, `DATETIME`, `NULLABLE`, `TIMESTAMP`, `GEOGRAPHY`, `TINYINT`, `LONGBLOB`, `TINYBLOB`, `SMALLINT`, `BIGINT`, `SIGNED`, `REAL`, `DECIMAL`, `ARRAY`, `MAP`, `VARBINARY`, `MEDIUMBLOB`, `VARCHAR`, `CHAR`, `CHARACTER`, or `VARIANT` | | | | | while parsing TABLE( , ...) | while parsing `CREATE [ OR REPLACE ] PROCEDURE () RETURNS { [ NOT NULL ] | TABLE( , ...)} LANGUAGE SQL [ COMMENT = '' ] AS ` @@ -1155,7 +1155,7 @@ error: --> SQL:1:24 | 1 | create PROCEDURE p1(int, string) returns table(string not null, int null) language sql comment = 'test' as $$ - | ------ - ^ unexpected `,`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` + | ------ - ^ unexpected `,`, expecting `BOOLEAN`, `BOOL`, `UINT8`, `TINYINT`, `UINT16`, `SMALLINT`, `UINT32`, `INT`, `INTEGER`, `UINT64`, `UNSIGNED`, `BIGINT`, `INT8`, `INT16`, `INT32`, `INT64`, `SIGNED`, `FLOAT32`, `FLOAT`, `REAL`, `FLOAT64`, `DOUBLE`, `DECIMAL`, `ARRAY`, `MAP`, `BITMAP`, `TUPLE`, `DATE`, `DATETIME`, `TIMESTAMP`, `INTERVAL`, `NUMERIC`, `BINARY`, `VARBINARY`, `LONGBLOB`, `MEDIUMBLOB`, `TINYBLOB`, `BLOB`, `STRING`, `VARCHAR`, `CHAR`, `CHARACTER`, `TEXT`, `VARIANT`, `JSON`, `GEOMETRY`, `GEOGRAPHY`, or `NULLABLE` | | | | | while parsing ( , ...) | while parsing `CREATE [ OR REPLACE ] PROCEDURE () RETURNS { [ NOT NULL ] | TABLE( , ...)} LANGUAGE SQL [ COMMENT = '' ] AS ` diff --git a/src/query/expression/src/utils/date_helper.rs b/src/query/expression/src/utils/date_helper.rs index b339e56293c10..656599355f3d6 100644 --- a/src/query/expression/src/utils/date_helper.rs +++ b/src/query/expression/src/utils/date_helper.rs @@ -355,6 +355,7 @@ pub struct ToYYYYMMDD; pub struct ToYYYYMMDDHH; pub struct ToYYYYMMDDHHMMSS; pub struct ToYear; +pub struct ToISOYear; pub struct ToQuarter; pub struct ToMonth; pub struct ToDayOfYear; @@ -411,6 +412,12 @@ impl ToNumber for ToYear { } } +impl ToNumber for ToISOYear { + fn to_number(dt: &Zoned) -> u16 { + dt.date().iso_week_date().year() as _ + } +} + impl ToNumber for ToQuarter { fn to_number(dt: &Zoned) -> u8 { // begin with 0 @@ -582,10 +589,11 @@ pub struct ToStartOfMonth; pub struct ToStartOfQuarter; pub struct ToStartOfYear; pub struct ToStartOfISOYear; + +pub struct ToLastOfYear; pub struct ToLastOfWeek; pub struct ToLastOfMonth; pub struct ToLastOfQuarter; -pub struct ToLastOfYear; pub struct ToPreviousMonday; pub struct ToPreviousTuesday; pub struct ToPreviousWednesday; diff --git a/src/query/functions/src/scalars/other.rs b/src/query/functions/src/scalars/other.rs index 7bbc7acf86160..e6aab5c8a88bc 100644 --- a/src/query/functions/src/scalars/other.rs +++ b/src/query/functions/src/scalars/other.rs @@ -495,18 +495,6 @@ pub fn compute_grouping(cols: &[usize], grouping_id: u32) -> u32 { } // this implementation comes from https://github.com/joshuaclayton/jaro_winkler pub(crate) mod jaro_winkler { - #![deny(missing_docs)] - - //! `jaro_winkler` is a crate for calculating Jaro-Winkler distance of two strings. - //! - //! # Examples - //! - //! ``` - //! assert_eq!(jaro_winkler("martha", "marhta"), 0.9611111111111111); - //! assert_eq!(jaro_winkler("", "words"), 0.0); - //! assert_eq!(jaro_winkler("same", "same"), 1.0); - //! ``` - enum DataWrapper { Vec(Vec), Bitwise(u128), diff --git a/src/query/functions/src/scalars/timestamp/src/datetime.rs b/src/query/functions/src/scalars/timestamp/src/datetime.rs index 118cf76a6d62e..5a54c321adb15 100644 --- a/src/query/functions/src/scalars/timestamp/src/datetime.rs +++ b/src/query/functions/src/scalars/timestamp/src/datetime.rs @@ -1451,6 +1451,21 @@ fn register_to_number_functions(registry: &mut FunctionRegistry) { } }), ); + + registry.register_passthrough_nullable_1_arg::( + "to_iso_year", + |_, _| FunctionDomain::Full, + vectorize_with_builder_1_arg::(|val, output, ctx| { + match ToNumberImpl::eval_date::(val, ctx.func_ctx.tz.clone()) { + Ok(t) => output.push(t), + Err(e) => { + ctx.set_error(output.len(), format!("cannot parse to type `Date`. {}", e)); + output.push(0); + } + } + }), + ); + registry.register_passthrough_nullable_1_arg::( "to_quarter", |_, _| FunctionDomain::Full, @@ -1565,6 +1580,13 @@ fn register_to_number_functions(registry: &mut FunctionRegistry) { ToNumberImpl::eval_timestamp::(val, ctx.func_ctx.tz.clone()) }), ); + registry.register_passthrough_nullable_1_arg::( + "to_iso_year", + |_, _| FunctionDomain::Full, + vectorize_1_arg::(|val, ctx| { + ToNumberImpl::eval_timestamp::(val, ctx.func_ctx.tz.clone()) + }), + ); registry.register_passthrough_nullable_1_arg::( "to_quarter", |_, _| FunctionDomain::Full, diff --git a/src/query/functions/src/scalars/timestamp/src/interval.rs b/src/query/functions/src/scalars/timestamp/src/interval.rs index e9c80e5b4e535..9a26d478155e8 100644 --- a/src/query/functions/src/scalars/timestamp/src/interval.rs +++ b/src/query/functions/src/scalars/timestamp/src/interval.rs @@ -24,6 +24,7 @@ use databend_common_expression::types::Int64Type; use databend_common_expression::types::IntervalType; use databend_common_expression::types::StringType; use databend_common_expression::types::TimestampType; +use databend_common_expression::vectorize_2_arg; use databend_common_expression::vectorize_with_builder_1_arg; use databend_common_expression::vectorize_with_builder_2_arg; use databend_common_expression::EvalContext; @@ -37,7 +38,7 @@ pub fn register(registry: &mut FunctionRegistry) { register_string_to_interval(registry); register_interval_to_string(registry); // data/timestamp/interval +/- interval - register_interval_add_sub(registry); + register_interval_add_sub_mul(registry); register_number_to_interval(registry); } @@ -87,7 +88,7 @@ fn register_interval_to_string(registry: &mut FunctionRegistry) { ); } -fn register_interval_add_sub(registry: &mut FunctionRegistry) { +fn register_interval_add_sub_mul(registry: &mut FunctionRegistry) { registry.register_passthrough_nullable_2_arg::( "plus", |_, _, _| FunctionDomain::MayThrow, @@ -178,6 +179,30 @@ fn register_interval_add_sub(registry: &mut FunctionRegistry) { }, ), ); + + registry.register_passthrough_nullable_2_arg::( + "multiply", + |_, _, _| FunctionDomain::Full, + vectorize_2_arg::(|a, b, _ctx| { + months_days_micros::new( + b.months() * (a as i32), + b.days() * (a as i32), + b.microseconds() * a, + ) + }), + ); + + registry.register_passthrough_nullable_2_arg::( + "multiply", + |_, _, _| FunctionDomain::Full, + vectorize_2_arg::(|b, a, _ctx| { + months_days_micros::new( + b.months() * (a as i32), + b.days() * (a as i32), + b.microseconds() * a, + ) + }), + ); } fn register_number_to_interval(registry: &mut FunctionRegistry) { diff --git a/src/query/functions/tests/it/scalars/testdata/function_list.txt b/src/query/functions/tests/it/scalars/testdata/function_list.txt index 8499c21ecf863..634c06ed01ef8 100644 --- a/src/query/functions/tests/it/scalars/testdata/function_list.txt +++ b/src/query/functions/tests/it/scalars/testdata/function_list.txt @@ -2923,6 +2923,10 @@ Functions overloads: 198 multiply(Float64 NULL, Float32 NULL) :: Float64 NULL 199 multiply(Float64, Float64) :: Float64 200 multiply(Float64 NULL, Float64 NULL) :: Float64 NULL +201 multiply(Int64, Interval) :: Interval +202 multiply(Int64 NULL, Interval NULL) :: Interval NULL +203 multiply(Interval, Int64) :: Interval +204 multiply(Interval NULL, Int64 NULL) :: Interval NULL 0 not(Boolean) :: Boolean 1 not(Boolean NULL) :: Boolean NULL 0 noteq(Variant, Variant) :: Boolean @@ -3760,6 +3764,10 @@ Functions overloads: 25 to_int8(Boolean NULL) :: Int8 NULL 0 to_interval(String) :: Interval 1 to_interval(String NULL) :: Interval NULL +0 to_iso_year(Date) :: UInt16 +1 to_iso_year(Date NULL) :: UInt16 NULL +2 to_iso_year(Timestamp) :: UInt16 +3 to_iso_year(Timestamp NULL) :: UInt16 NULL 0 to_last_of_month(Date) :: Date 1 to_last_of_month(Date NULL) :: Date NULL 2 to_last_of_month(Timestamp) :: Date diff --git a/src/query/sql/src/planner/semantic/type_check.rs b/src/query/sql/src/planner/semantic/type_check.rs index 51c52360f13b9..2f9c4eaa62872 100644 --- a/src/query/sql/src/planner/semantic/type_check.rs +++ b/src/query/sql/src/planner/semantic/type_check.rs @@ -1061,11 +1061,41 @@ impl<'a> TypeChecker<'a> { span, kind, expr, .. } => self.resolve_extract_expr(*span, kind, expr)?, - Expr::Interval { span, .. } => { - return Err(ErrorCode::SemanticError( - "Unsupported interval expression yet".to_string(), - ) - .set_span(*span)); + Expr::Interval { span, expr, unit } => { + let ex = Expr::Cast { + span: *span, + expr: Box::new(expr.as_ref().clone()), + target_type: TypeName::String, + pg_style: false, + }; + let ex = Expr::FunctionCall { + span: *span, + func: ASTFunctionCall { + name: Identifier::from_name(None, "concat".to_string()), + args: vec![ex, Expr::Literal { + span: *span, + value: Literal::String(format!(" {}", unit)), + }], + params: vec![], + distinct: false, + order_by: vec![], + window: None, + lambda: None, + }, + }; + let ex = Expr::FunctionCall { + span: *span, + func: ASTFunctionCall { + name: Identifier::from_name(None, "to_interval".to_string()), + args: vec![ex], + params: vec![], + distinct: false, + order_by: vec![], + window: None, + lambda: None, + }, + }; + self.resolve(&ex)? } Expr::DateAdd { span, @@ -3065,6 +3095,7 @@ impl<'a> TypeChecker<'a> { arg: &Expr, ) -> Result> { match interval_kind { + ASTIntervalKind::ISOYear => self.resolve_function(span, "to_iso_year", vec![], &[arg]), ASTIntervalKind::Year => self.resolve_function(span, "to_year", vec![], &[arg]), ASTIntervalKind::Quarter => self.resolve_function(span, "to_quarter", vec![], &[arg]), ASTIntervalKind::Month => self.resolve_function(span, "to_month", vec![], &[arg]), @@ -3124,6 +3155,13 @@ impl<'a> TypeChecker<'a> { &[date], ) } + ASTIntervalKind::ISOYear => { + self.resolve_function( + span, + "to_start_of_iso_year", vec![], + &[date], + ) + } ASTIntervalKind::Quarter => { self.resolve_function( span, diff --git a/src/tests/sqlsmith/src/sql_gen/expr.rs b/src/tests/sqlsmith/src/sql_gen/expr.rs index 1e3f573929ff9..9c3532e4c02b6 100644 --- a/src/tests/sqlsmith/src/sql_gen/expr.rs +++ b/src/tests/sqlsmith/src/sql_gen/expr.rs @@ -514,6 +514,7 @@ impl SqlGenerator<'_, R> { 8 => IntervalKind::Dow, 9 => IntervalKind::Week, 10 => IntervalKind::Epoch, + 11 => IntervalKind::ISOYear, _ => unreachable!(), }; Expr::Extract { @@ -567,6 +568,7 @@ impl SqlGenerator<'_, R> { 4 => IntervalKind::Hour, 5 => IntervalKind::Minute, 6 => IntervalKind::Second, + 7 => IntervalKind::ISOYear, _ => unreachable!(), }; let interval_ty = DataType::Number(NumberDataType::Int64); diff --git a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test index 4ecd034d33340..73707d7e12e4e 100644 --- a/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test +++ b/tests/sqllogictests/suites/query/functions/02_0012_function_datetimes_tz.test @@ -846,9 +846,9 @@ select to_monday(to_date('1919-04-13','%Y-%m-%d')); 1919-04-07 query T -select to_year(to_date('1919-04-13','%Y-%m-%d')); +select to_date('1919-04-13','%Y-%m-%d') d, to_year(d), to_iso_year(d), EXTRACT(YEAR from d), EXTRACT(ISOYEAR from d); ---- -1919 +1919-04-13 1919 1919 1919 1919 statement ok set timezone='Asia/Shanghai'; diff --git a/tests/sqllogictests/suites/query/functions/02_0079_function_interval.test b/tests/sqllogictests/suites/query/functions/02_0079_function_interval.test index c21f3d1b54add..0b23b3e7261c5 100644 --- a/tests/sqllogictests/suites/query/functions/02_0079_function_interval.test +++ b/tests/sqllogictests/suites/query/functions/02_0079_function_interval.test @@ -4,6 +4,15 @@ select interval '1 month 1 hour 1 microsecond ago' ---- -1 month -1:00:00.000001 +onlyif http +query T +select interval (number::string) month a , a * 3, -3 * a from numbers(4) where number > 0; +---- +1 month 3 months -3 months +2 months 6 months -6 months +3 months 9 months -9 months + + onlyif http statement ok create or replace table t(c1 interval, c2 interval); diff --git a/tests/sqllogictests/suites/query/functions/cast.test b/tests/sqllogictests/suites/query/functions/cast.test index 435ceba9843e8..efd419272c387 100644 --- a/tests/sqllogictests/suites/query/functions/cast.test +++ b/tests/sqllogictests/suites/query/functions/cast.test @@ -113,6 +113,6 @@ select CAST(10249.5500000000000000 * POW(10, 2) AS UNSIGNED), '29.55'::Int, '29. 1024955 30 29 query T -select to_uint64(1024954.98046875::double) +select to_uint64(1024954.98046875::double) a, a::numeric, a::real ---- -1024955 +1024955 1024955.000 1024955.0