diff --git a/src/query/ast/src/ast/expr.rs b/src/query/ast/src/ast/expr.rs index 53662edc0695..c52c8b7b1c9d 100644 --- a/src/query/ast/src/ast/expr.rs +++ b/src/query/ast/src/ast/expr.rs @@ -40,6 +40,7 @@ pub enum IntervalKind { Minute, Second, Doy, + Week, Dow, } @@ -143,6 +144,12 @@ pub enum Expr { kind: IntervalKind, expr: Box, }, + /// DATE_PART(IntervalKind, ) + DatePart { + span: Span, + kind: IntervalKind, + expr: Box, + }, /// POSITION( IN ) Position { span: Span, @@ -503,6 +510,7 @@ impl Expr { | Expr::Cast { span, .. } | Expr::TryCast { span, .. } | Expr::Extract { span, .. } + | Expr::DatePart { span, .. } | Expr::Position { span, .. } | Expr::Substring { span, .. } | Expr::Trim { span, .. } @@ -536,6 +544,7 @@ impl Display for IntervalKind { IntervalKind::Second => "SECOND", IntervalKind::Doy => "DOY", IntervalKind::Dow => "DOW", + IntervalKind::Week => "WEEK", }) } } @@ -1027,6 +1036,11 @@ impl Display for Expr { } => { write!(f, "EXTRACT({field} FROM {expr})")?; } + Expr::DatePart { + kind: field, expr, .. + } => { + write!(f, "DATE_PART({field}, {expr})")?; + } Expr::Position { substr_expr, str_expr, diff --git a/src/query/ast/src/ast/format/syntax/expr.rs b/src/query/ast/src/ast/format/syntax/expr.rs index a1a8659a864e..bd4a8a8101a7 100644 --- a/src/query/ast/src/ast/format/syntax/expr.rs +++ b/src/query/ast/src/ast/format/syntax/expr.rs @@ -173,6 +173,15 @@ pub(crate) fn pretty_expr(expr: Expr) -> RcDoc<'static> { .append(RcDoc::space()) .append(pretty_expr(*expr)) .append(RcDoc::text(")")), + Expr::DatePart { + kind: field, expr, .. + } => RcDoc::text("DATE_PART(") + .append(RcDoc::text(field.to_string())) + .append(RcDoc::space()) + .append(RcDoc::text(",")) + .append(RcDoc::space()) + .append(pretty_expr(*expr)) + .append(RcDoc::text(")")), Expr::Position { substr_expr, str_expr, diff --git a/src/query/ast/src/parser/expr.rs b/src/query/ast/src/parser/expr.rs index c78cd533e8cb..e1b41f238291 100644 --- a/src/query/ast/src/parser/expr.rs +++ b/src/query/ast/src/parser/expr.rs @@ -254,6 +254,11 @@ pub enum ExprElement { field: IntervalKind, expr: Box, }, + /// DATE_PART(IntervalKind, ) + DatePart { + field: IntervalKind, + expr: Box, + }, /// POSITION( IN ) Position { substr_expr: Box, @@ -452,6 +457,11 @@ impl<'a, I: Iterator>> PrattParser for ExprP kind: field, expr, }, + ExprElement::DatePart { field, expr } => Expr::DatePart { + span: transform_span(elem.span.0), + kind: field, + expr, + }, ExprElement::Position { substr_expr, str_expr, @@ -730,6 +740,15 @@ pub fn expr_element(i: Input) -> IResult> { }, |(_, target_type)| ExprElement::PgCast { target_type }, ); + let date_part = map( + rule! { + DATE_PART ~ "(" ~ ^#interval_kind ~ "," ~ ^#subexpr(0) ~ ^")" + }, + |(_, _, field, _, expr, _)| ExprElement::DatePart { + field, + expr: Box::new(expr), + }, + ); let extract = map( rule! { EXTRACT ~ "(" ~ ^#interval_kind ~ ^FROM ~ ^#subexpr(0) ~ ^")" @@ -1044,7 +1063,8 @@ pub fn expr_element(i: Input) -> IResult> { | #timestamp_expr: "`TIMESTAMP `" | #interval: "`INTERVAL ... (YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | SECOND | DOY | DOW)`" | #pg_cast : "`::`" - | #extract : "`EXTRACT((YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | SECOND) FROM ...)`" + | #extract : "`EXTRACT((YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | SECOND | WEEK) FROM ...)`" + | #date_part : "`DATE_PART((YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | SECOND | WEEK), ...)`" ), rule!( #position : "`POSITION(... IN ...)`" @@ -1398,6 +1418,7 @@ pub fn interval_kind(i: Input) -> IResult { value(IntervalKind::Second, rule! { SECOND }), value(IntervalKind::Doy, rule! { DOY }), value(IntervalKind::Dow, rule! { DOW }), + value(IntervalKind::Week, rule! { WEEK }), value( IntervalKind::Year, rule! { #literal_string_eq_ignore_case("YEAR") }, @@ -1434,6 +1455,10 @@ pub fn interval_kind(i: Input) -> IResult { IntervalKind::Dow, rule! { #literal_string_eq_ignore_case("DOW") }, ), + value( + IntervalKind::Week, + rule! { #literal_string_eq_ignore_case("WEEK") }, + ), ))(i) } diff --git a/src/query/ast/src/parser/token.rs b/src/query/ast/src/parser/token.rs index 693c97d3e92e..ac65bd4dfc92 100644 --- a/src/query/ast/src/parser/token.rs +++ b/src/query/ast/src/parser/token.rs @@ -415,6 +415,8 @@ pub enum TokenKind { DATE, #[token("DATE_ADD", ignore(ascii_case))] DATE_ADD, + #[token("DATE_PART", ignore(ascii_case))] + DATE_PART, #[token("DATE_SUB", ignore(ascii_case))] DATE_SUB, #[token("DATE_TRUNC", ignore(ascii_case))] @@ -449,6 +451,8 @@ pub enum TokenKind { DOUBLE, #[token("DOW", ignore(ascii_case))] DOW, + #[token("WEEK", ignore(ascii_case))] + WEEK, #[token("DOY", ignore(ascii_case))] DOY, #[token("DOWNLOAD", ignore(ascii_case))] @@ -960,8 +964,6 @@ pub enum TokenKind { VIEW, #[token("VIRTUAL", ignore(ascii_case))] VIRTUAL, - #[token("WEEK", ignore(ascii_case))] - WEEK, #[token("WHEN", ignore(ascii_case))] WHEN, #[token("WHERE", ignore(ascii_case))] @@ -1125,6 +1127,7 @@ impl TokenKind { | TokenKind::END | TokenKind::EXISTS | TokenKind::EXTRACT + | TokenKind::DATE_PART | TokenKind::FALSE | TokenKind::FLOAT // | TokenKind::FOREIGN diff --git a/src/query/ast/src/visitors/walk.rs b/src/query/ast/src/visitors/walk.rs index 943655f8ac41..d1c24b5aaffd 100644 --- a/src/query/ast/src/visitors/walk.rs +++ b/src/query/ast/src/visitors/walk.rs @@ -68,6 +68,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expr: &'a Expr) { target_type, } => visitor.visit_try_cast(*span, expr, target_type), Expr::Extract { span, kind, expr } => visitor.visit_extract(*span, kind, expr), + Expr::DatePart { span, kind, expr } => visitor.visit_extract(*span, kind, expr), Expr::Position { span, substr_expr, diff --git a/src/query/ast/src/visitors/walk_mut.rs b/src/query/ast/src/visitors/walk_mut.rs index 1c66fcbebd72..84cc45c3f62c 100644 --- a/src/query/ast/src/visitors/walk_mut.rs +++ b/src/query/ast/src/visitors/walk_mut.rs @@ -68,6 +68,7 @@ pub fn walk_expr_mut(visitor: &mut V, expr: &mut Expr) { target_type, } => visitor.visit_try_cast(*span, expr, target_type), Expr::Extract { span, kind, expr } => visitor.visit_extract(*span, kind, expr), + Expr::DatePart { span, kind, expr } => visitor.visit_extract(*span, kind, expr), Expr::Position { span, substr_expr, diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index 337ed1c7cba5..cc465937cf99 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -687,6 +687,7 @@ fn test_expr() { r#"TRY_CAST(col1 AS TUPLE(BIGINT UNSIGNED NULL, BOOLEAN))"#, r#"trim(leading 'abc' from 'def')"#, r#"extract(year from d)"#, + r#"date_part(year, d)"#, r#"position('a' in str)"#, r#"substring(a from b for c)"#, r#"substring(a, b, c)"#, diff --git a/src/query/ast/tests/it/testdata/expr-error.txt b/src/query/ast/tests/it/testdata/expr-error.txt index 2786abb32f68..c6d61d2b16c3 100644 --- a/src/query/ast/tests/it/testdata/expr-error.txt +++ b/src/query/ast/tests/it/testdata/expr-error.txt @@ -53,7 +53,7 @@ error: --> SQL:1:10 | 1 | CAST(col1) - | ---- ^ expected `AS`, `,`, `(`, `.`, `IS`, `NOT`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `POSITION`, `SUBSTRING`, `SUBSTR`, `TRIM`, `COUNT`, , , or 16 more ... + | ---- ^ expected `AS`, `,`, `(`, `.`, `IS`, `NOT`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `DATE_PART`, `POSITION`, `SUBSTRING`, `SUBSTR`, `TRIM`, `COUNT`, , or 17 more ... | | | while parsing `CAST(... AS ...)` | while parsing expression diff --git a/src/query/ast/tests/it/testdata/expr.txt b/src/query/ast/tests/it/testdata/expr.txt index f26ca37ec495..d4740b28f6f2 100644 --- a/src/query/ast/tests/it/testdata/expr.txt +++ b/src/query/ast/tests/it/testdata/expr.txt @@ -1574,6 +1574,35 @@ Extract { } +---------- Input ---------- +date_part(year, d) +---------- Output --------- +DATE_PART(YEAR, d) +---------- AST ------------ +DatePart { + span: Some( + 0..18, + ), + kind: Year, + expr: ColumnRef { + span: Some( + 16..17, + ), + database: None, + table: None, + column: Name( + Identifier { + name: "d", + quote: None, + span: Some( + 16..17, + ), + }, + ), + }, +} + + ---------- Input ---------- position('a' in str) ---------- Output --------- diff --git a/src/query/ast/tests/it/testdata/statement-error.txt b/src/query/ast/tests/it/testdata/statement-error.txt index 725e47ba9fc6..1bc5b7022818 100644 --- a/src/query/ast/tests/it/testdata/statement-error.txt +++ b/src/query/ast/tests/it/testdata/statement-error.txt @@ -383,7 +383,7 @@ error: --> SQL:1:41 | 1 | SELECT * FROM t GROUP BY GROUPING SETS () - | ------ ^ expected `(`, `IS`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `NOT`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `POSITION`, `SUBSTRING`, `SUBSTR`, `TRIM`, `COUNT`, , , `CASE`, `ColumnPosition`, `[`, or 14 more ... + | ------ ^ expected `(`, `IS`, `IN`, `EXISTS`, `BETWEEN`, `+`, `-`, `*`, `/`, `//`, `DIV`, `%`, `||`, `<->`, `>`, `<`, `>=`, `<=`, `=`, `<>`, `!=`, `^`, `AND`, `OR`, `XOR`, `LIKE`, `NOT`, `REGEXP`, `RLIKE`, `SOUNDS`, , , , , , , , , , , `CAST`, `TRY_CAST`, `DATE_ADD`, `DATE_SUB`, `DATE_TRUNC`, `DATE`, `TIMESTAMP`, `INTERVAL`, `::`, `EXTRACT`, `DATE_PART`, `POSITION`, `SUBSTRING`, `SUBSTR`, `TRIM`, `COUNT`, , , `CASE`, `ColumnPosition`, or 15 more ... | | | while parsing `SELECT ...` diff --git a/src/query/service/src/interpreters/common/query_log.rs b/src/query/service/src/interpreters/common/query_log.rs index 5bd163c77379..3084111c4d6b 100644 --- a/src/query/service/src/interpreters/common/query_log.rs +++ b/src/query/service/src/interpreters/common/query_log.rs @@ -14,9 +14,7 @@ use std::fmt::Write; use std::sync::Arc; -use std::time::Duration; use std::time::SystemTime; -use std::time::UNIX_EPOCH; use common_config::GlobalConfig; use common_exception::ErrorCode; @@ -28,6 +26,7 @@ use log::error; use log::info; use serde_json; +use crate::sessions::convert_query_log_timestamp; use crate::sessions::QueryContext; use crate::sessions::TableContext; @@ -187,6 +186,7 @@ impl InterpreterQueryLog { } pub fn log_finish(ctx: &QueryContext, now: SystemTime, err: Option) -> Result<()> { + ctx.set_finish_time(now); // User. let handler_type = ctx.get_current_session().get_type().to_string(); let tenant_id = GlobalConfig::instance().query.tenant_id.clone(); @@ -206,7 +206,7 @@ impl InterpreterQueryLog { let event_time = convert_query_log_timestamp(now); let event_date = (event_time / (24 * 3_600_000_000)) as i32; let query_start_time = convert_query_log_timestamp(ctx.get_created_time()); - let query_duration_ms = (event_time - query_start_time) / 1_000; + let query_duration_ms = ctx.get_query_duration_ms(); let data_metrics = ctx.get_data_metrics(); let written_rows = ctx.get_write_progress_value().rows as u64; @@ -316,9 +316,3 @@ impl InterpreterQueryLog { }) } } - -fn convert_query_log_timestamp(time: SystemTime) -> i64 { - time.duration_since(UNIX_EPOCH) - .unwrap_or(Duration::new(0, 0)) - .as_micros() as i64 -} diff --git a/src/query/service/src/servers/http/v1/http_query_handlers.rs b/src/query/service/src/servers/http/v1/http_query_handlers.rs index b9666b3929e4..d96fa1fdaf8d 100644 --- a/src/query/service/src/servers/http/v1/http_query_handlers.rs +++ b/src/query/service/src/servers/http/v1/http_query_handlers.rs @@ -83,7 +83,7 @@ impl QueryError { pub struct QueryStats { #[serde(flatten)] pub progresses: Progresses, - pub running_time_ms: f64, + pub running_time_ms: i64, } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/query/service/src/servers/http/v1/query/execute_state.rs b/src/query/service/src/servers/http/v1/query/execute_state.rs index f87e63042771..7d2c5c35726b 100644 --- a/src/query/service/src/servers/http/v1/query/execute_state.rs +++ b/src/query/service/src/servers/http/v1/query/execute_state.rs @@ -13,8 +13,6 @@ // limitations under the License. use std::sync::Arc; -use std::time::Duration; -use std::time::Instant; use std::time::SystemTime; use common_base::base::tokio::sync::RwLock; @@ -111,12 +109,11 @@ pub struct ExecuteStopped { pub stats: Progresses, pub affect: Option, pub reason: Result<()>, - pub stop_time: Instant, + pub query_duration_ms: i64, } pub struct Executor { pub query_id: String, - pub start_time: Instant, pub state: ExecuteState, } @@ -137,10 +134,12 @@ impl Executor { } } - pub fn elapsed(&self) -> Duration { + pub fn get_query_duration_ms(&self) -> i64 { match &self.state { - Starting(_) | Running(_) => Instant::now() - self.start_time, - Stopped(f) => f.stop_time - self.start_time, + Starting(ExecuteStarting { ctx }) | Running(ExecuteRunning { ctx, .. }) => { + ctx.get_query_duration_ms() + } + Stopped(f) => f.query_duration_ms, } } @@ -179,7 +178,7 @@ impl Executor { guard.state = Stopped(Box::new(ExecuteStopped { stats: Default::default(), reason, - stop_time: Instant::now(), + query_duration_ms: s.ctx.get_query_duration_ms(), affect: Default::default(), })) } @@ -198,7 +197,7 @@ impl Executor { guard.state = Stopped(Box::new(ExecuteStopped { stats: Progresses::from_context(&r.ctx), reason, - stop_time: Instant::now(), + query_duration_ms: r.ctx.get_query_duration_ms(), affect: r.ctx.get_affect(), })) } diff --git a/src/query/service/src/servers/http/v1/query/http_query.rs b/src/query/service/src/servers/http/v1/query/http_query.rs index e97489cfd41b..6ffa4b90554b 100644 --- a/src/query/service/src/servers/http/v1/query/http_query.rs +++ b/src/query/service/src/servers/http/v1/query/http_query.rs @@ -172,7 +172,7 @@ pub struct StageAttachmentConf { #[derive(Debug, Clone)] pub struct ResponseState { - pub running_time_ms: f64, + pub running_time_ms: i64, pub progresses: Progresses, pub state: ExecuteStateKind, pub affect: Option, @@ -315,10 +315,8 @@ impl HttpQuery { }; let (block_sender, block_receiver) = sized_spsc(request.pagination.max_rows_in_buffer); - let start_time = Instant::now(); let state = Arc::new(RwLock::new(Executor { query_id: query_id.clone(), - start_time, state: ExecuteState::Starting(ExecuteStarting { ctx: ctx.clone() }), })); let block_sender_closer = block_sender.closer(); @@ -349,7 +347,7 @@ impl HttpQuery { let state = ExecuteStopped { stats: Progresses::default(), reason: Err(e.clone()), - stop_time: Instant::now(), + query_duration_ms: ctx_clone.get_query_duration_ms(), affect: ctx_clone.get_affect(), }; info!( @@ -424,7 +422,7 @@ impl HttpQuery { let state = self.state.read().await; let (exe_state, err) = state.state.extract(); ResponseState { - running_time_ms: state.elapsed().as_secs_f64() * 1000.0, + running_time_ms: state.get_query_duration_ms(), progresses: state.get_progress(), state: exe_state, error: err, diff --git a/src/query/service/src/sessions/mod.rs b/src/query/service/src/sessions/mod.rs index cf78ad82f172..9225460b5bf5 100644 --- a/src/query/service/src/sessions/mod.rs +++ b/src/query/service/src/sessions/mod.rs @@ -26,6 +26,7 @@ mod session_type; pub use common_catalog::table_context::TableContext; pub use query_affect::QueryAffect; +pub use query_ctx::convert_query_log_timestamp; pub use query_ctx::QueryContext; pub use query_ctx_shared::short_sql; pub use query_ctx_shared::QueryContextShared; diff --git a/src/query/service/src/sessions/query_ctx.rs b/src/query/service/src/sessions/query_ctx.rs index 01f4a726e164..e1f46a129c5b 100644 --- a/src/query/service/src/sessions/query_ctx.rs +++ b/src/query/service/src/sessions/query_ctx.rs @@ -23,8 +23,10 @@ use std::str::FromStr; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::sync::Arc; +use std::time::Duration; use std::time::Instant; use std::time::SystemTime; +use std::time::UNIX_EPOCH; use chrono_tz::Tz; use common_base::base::tokio::task::JoinHandle; @@ -263,10 +265,22 @@ impl QueryContext { ua.clone() } + pub fn get_query_duration_ms(&self) -> i64 { + let query_start_time = convert_query_log_timestamp(self.shared.created_time); + let finish_time = *self.shared.finish_time.read(); + let finish_time = finish_time.unwrap_or_else(SystemTime::now); + let finish_time = convert_query_log_timestamp(finish_time); + (finish_time - query_start_time) / 1_000 + } + pub fn get_created_time(&self) -> SystemTime { self.shared.created_time } + pub fn set_finish_time(&self, time: SystemTime) { + *self.shared.finish_time.write() = Some(time) + } + pub fn evict_table_from_cache(&self, catalog: &str, database: &str, table: &str) -> Result<()> { self.shared.evict_table_from_cache(catalog, database, table) } @@ -783,3 +797,9 @@ impl std::fmt::Debug for QueryContext { write!(f, "{:?}", self.get_current_user()) } } + +pub fn convert_query_log_timestamp(time: SystemTime) -> i64 { + time.duration_since(UNIX_EPOCH) + .unwrap_or(Duration::new(0, 0)) + .as_micros() as i64 +} diff --git a/src/query/service/src/sessions/query_ctx_shared.rs b/src/query/service/src/sessions/query_ctx_shared.rs index cc1a4148d2e7..e09586816793 100644 --- a/src/query/service/src/sessions/query_ctx_shared.rs +++ b/src/query/service/src/sessions/query_ctx_shared.rs @@ -81,6 +81,8 @@ pub struct QueryContextShared { pub(in crate::sessions) executor: Arc>>, pub(in crate::sessions) stage_attachment: Arc>>, pub(in crate::sessions) created_time: SystemTime, + // now it is only set in query_log::log_query_finished + pub(in crate::sessions) finish_time: RwLock>, // DashMap> // We use this field to count maximum of one error found per data file. #[allow(clippy::type_complexity)] @@ -126,6 +128,7 @@ impl QueryContextShared { executor: Arc::new(RwLock::new(Weak::new())), stage_attachment: Arc::new(RwLock::new(None)), created_time: SystemTime::now(), + finish_time: Default::default(), on_error_map: Arc::new(RwLock::new(None)), on_error_mode: Arc::new(RwLock::new(None)), copy_status: Arc::new(Default::default()), diff --git a/src/query/service/tests/it/servers/http/http_query_handlers.rs b/src/query/service/tests/it/servers/http/http_query_handlers.rs index 5ef40a3edde6..952f7c0d4bef 100644 --- a/src/query/service/tests/it/servers/http/http_query_handlers.rs +++ b/src/query/service/tests/it/servers/http/http_query_handlers.rs @@ -49,7 +49,6 @@ use jwt_simple::algorithms::RSAKeyPairLike; use jwt_simple::claims::JWTClaims; use jwt_simple::claims::NoCustomClaims; use jwt_simple::prelude::Clock; -use num::ToPrimitive; use poem::http::header; use poem::http::Method; use poem::http::StatusCode; @@ -559,8 +558,6 @@ async fn test_insert() -> Result<()> { Ok(()) } -// Wait for https://github.com/datafuselabs/databend/issues/7831 to be fixed, then remove ignore -#[ignore] #[tokio::test(flavor = "current_thread")] async fn test_query_log() -> Result<()> { let config = ConfigBuilder::create().build(); @@ -574,23 +571,37 @@ async fn test_query_log() -> Result<()> { .with(session_middleware); let sql = "create table t1(a int)"; - let (status, result) = post_sql_to_endpoint(&ep, sql, 3).await?; + let (status, result) = post_sql_to_endpoint(&ep, sql, 10).await?; assert_eq!(status, StatusCode::OK, "{:?}", result); assert!(result.error.is_none(), "{:?}", result); - assert!(result.next_uri.is_none(), "{:?}", result); + assert_eq!(result.state, ExecuteStateKind::Succeeded); assert!(result.data.is_empty(), "{:?}", result); + let result_type_2 = result; let (status, result) = post_sql_to_endpoint(&ep, sql, 3).await?; assert_eq!(status, StatusCode::OK, "{:?}", result); assert!(result.error.is_some(), "{:?}", result); - assert!(result.next_uri.is_none(), "{:?}", result); + assert_eq!(result.state, ExecuteStateKind::Failed); + let result_type_3 = result; + + let sql = "select query_text, query_duration_ms from system.query_log where log_type=2"; + let (status, result) = post_sql_to_endpoint(&ep, sql, 3).await?; + assert_eq!(status, StatusCode::OK, "{:?}", result); + assert_eq!( + result.data[0][1].as_str().unwrap(), + result_type_2.stats.running_time_ms.to_string(), + ); - let sql = "select query_text, exception_code, exception_text, stack_trace from system.query_log where log_type=3"; + let sql = "select query_text, exception_code, exception_text, stack_trace, query_duration_ms from system.query_log where log_type=3"; let (status, result) = post_sql_to_endpoint(&ep, sql, 3).await?; assert_eq!(status, StatusCode::OK, "{:?}", result); assert_eq!(result.data.len(), 1, "{:?}", result); assert!( - result.data[0][0].as_str().unwrap().contains("create table"), + result.data[0][0] + .as_str() + .unwrap() + .to_lowercase() + .contains("create table"), "{:?}", result ); @@ -604,22 +615,23 @@ async fn test_query_log() -> Result<()> { result ); assert_eq!( - result.data[0][1].as_u64().unwrap(), - ErrorCode::TableAlreadyExists("").code().to_u64().unwrap(), + result.data[0][1].as_str().unwrap(), + ErrorCode::TableAlreadyExists("").code().to_string(), "{:?}", result ); - assert!( - result.data[0][3].as_str().unwrap().to_lowercase().contains( - if std::env::var("RUST_BACKTRACE").is_ok() { - "backtrace" - } else { - "" - } - ), + assert_eq!( + result.data[0][4].as_str().unwrap(), + result_type_3.stats.running_time_ms.to_string(), "{:?}", result ); + Ok(()) +} +#[tokio::test(flavor = "current_thread")] +async fn test_query_log_killed() -> Result<()> { + let config = ConfigBuilder::create().build(); + let _guard = TestGlobalServices::setup(config.clone()).await?; let session_middleware = HTTPSessionMiddleware::create(HttpHandlerKind::Query, AuthMgr::instance()); @@ -637,7 +649,7 @@ async fn test_query_log() -> Result<()> { let response = get_uri(&ep, result.kill_uri.as_ref().unwrap()).await; assert_eq!(response.status(), StatusCode::OK, "{:?}", result); - let sql = "select query_text, exception_code, exception_text, stack_trace from system.query_log where log_type=4"; + let sql = "select query_text, exception_code, exception_text, stack_trace, query_duration_ms from system.query_log where log_type=4"; let (status, result) = post_sql_to_endpoint(&ep, sql, 3).await?; assert_eq!(status, StatusCode::OK, "{:?}", result); assert_eq!(result.data.len(), 1, "{:?}", result); @@ -656,8 +668,8 @@ async fn test_query_log() -> Result<()> { result ); assert_eq!( - result.data[0][1].as_u64().unwrap(), - ErrorCode::AbortedQuery("").code().to_u64().unwrap(), + result.data[0][1].as_str().unwrap(), + ErrorCode::AbortedQuery("").code().to_string(), "{:?}", result ); diff --git a/src/query/sql/src/planner/semantic/type_check.rs b/src/query/sql/src/planner/semantic/type_check.rs index aca75048b2d6..6867c87a51cd 100644 --- a/src/query/sql/src/planner/semantic/type_check.rs +++ b/src/query/sql/src/planner/semantic/type_check.rs @@ -1042,6 +1042,10 @@ impl<'a> TypeChecker<'a> { span, kind, expr, .. } => self.resolve_extract_expr(*span, kind, expr).await?, + Expr::DatePart { + span, kind, expr, .. + } => self.resolve_extract_expr(*span, kind, expr).await?, + Expr::Interval { span, .. } => { return Err(ErrorCode::SemanticError( "Unsupported interval expression yet".to_string(), @@ -1962,6 +1966,10 @@ impl<'a> TypeChecker<'a> { self.resolve_function(span, "to_day_of_week", vec![], &[arg]) .await } + ASTIntervalKind::Week => { + self.resolve_function(span, "to_week_of_year", vec![], &[arg]) + .await + } } } @@ -3374,6 +3382,13 @@ impl<'a> TypeChecker<'a> { self.clone_expr_with_replacement(expr.as_ref(), replacement_fn)?, ), }), + Expr::DatePart { span, kind, expr } => Ok(Expr::DatePart { + span: *span, + kind: *kind, + expr: Box::new( + self.clone_expr_with_replacement(expr.as_ref(), replacement_fn)?, + ), + }), Expr::Position { span, substr_expr, diff --git a/src/tests/sqlsmith/src/sql_gen/expr.rs b/src/tests/sqlsmith/src/sql_gen/expr.rs index 9290cd72ba6f..c539e1112f69 100644 --- a/src/tests/sqlsmith/src/sql_gen/expr.rs +++ b/src/tests/sqlsmith/src/sql_gen/expr.rs @@ -482,7 +482,7 @@ impl<'a, R: Rng> SqlGenerator<'a, R> { DataType::Timestamp }; let expr = self.gen_expr(&expr_ty); - let kind = match self.rng.gen_range(0..=6) { + let kind = match self.rng.gen_range(0..=9) { 0 => IntervalKind::Year, 1 => IntervalKind::Quarter, 2 => IntervalKind::Month, @@ -492,6 +492,7 @@ impl<'a, R: Rng> SqlGenerator<'a, R> { 6 => IntervalKind::Second, 7 => IntervalKind::Doy, 8 => IntervalKind::Dow, + 9 => IntervalKind::Week, _ => unreachable!(), }; Expr::Extract { diff --git a/tests/sqllogictests/suites/base/20+_others/20_0001_planner b/tests/sqllogictests/suites/base/20+_others/20_0001_planner index 91664f56e139..a0732f1c3a95 100644 --- a/tests/sqllogictests/suites/base/20+_others/20_0001_planner +++ b/tests/sqllogictests/suites/base/20+_others/20_0001_planner @@ -30,6 +30,20 @@ select extract(day from to_date('2022-05-13')) ---- 13 +query I +select date_part(day, to_date('2022-05-13')) +---- +13 + +query I +select extract(week from to_timestamp('2016-01-02T23:39:20.123-07:00')) +---- +53 + +query I +select date_part(week, to_timestamp('2016-01-02T23:39:20.123-07:00')) +---- +53 query T select date_trunc(month, to_date('2022-07-07')) @@ -964,6 +978,11 @@ select number from temp statement ok drop view temp +statement ok +drop table if exists t1; + +statement ok +drop table if exists t2; statement ok create table t1(a int, b int)