Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ rmp-serde = "1.1.1"
roaring = { version = "^0.10", features = ["serde"] }
rotbl = { version = "0.2.9", features = [] }
rust_decimal = "1.26"
rustix = "0.38.37"
rustix = { version = "0.38.37", features = ["fs"] }
rustls = { version = "0.23.27", features = ["ring", "tls12"], default-features = false }
rustls-pemfile = "2"
rustls-pki-types = "1"
Expand Down
30 changes: 30 additions & 0 deletions src/query/ast/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,34 @@ pub fn expr_element(i: Input) -> IResult<WithSpan<ExprElement>> {
|(_, not, _, _)| ExprElement::IsDistinctFrom { not: not.is_some() },
);

let current_date = map(consumed(rule! { CURRENT_DATE }), |(span, _)| {
ExprElement::FunctionCall {
func: FunctionCall {
distinct: false,
name: Identifier::from_name(transform_span(span.tokens), "current_date"),
args: vec![],
params: vec![],
order_by: vec![],
window: None,
lambda: None,
},
}
});

let current_time = map(consumed(rule! { CURRENT_TIME }), |(span, _)| {
ExprElement::FunctionCall {
func: FunctionCall {
distinct: false,
name: Identifier::from_name(transform_span(span.tokens), "current_time"),
args: vec![],
params: vec![],
order_by: vec![],
window: None,
lambda: None,
},
}
});

let current_timestamp = map(consumed(rule! { CURRENT_TIMESTAMP }), |(span, _)| {
ExprElement::FunctionCall {
func: FunctionCall {
Expand Down Expand Up @@ -1573,6 +1601,8 @@ pub fn expr_element(i: Input) -> IResult<WithSpan<ExprElement>> {
| #dot_access : "<dot_access>"
| #map_access : "[<key>] | .<key> | :<key>"
| #literal : "<literal>"
| #current_date: "CURRENT_DATE"
| #current_time: "CURRENT_TIME"
| #current_timestamp: "CURRENT_TIMESTAMP"
| #array : "`[<expr>, ...]`"
| #map_expr : "`{ <literal> : <expr>, ... }`"
Expand Down
8 changes: 6 additions & 2 deletions src/query/ast/src/parser/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,10 +497,14 @@ pub enum TokenKind {
CROSS,
#[token("CSV", ignore(ascii_case))]
CSV,
#[token("CURRENT", ignore(ascii_case))]
CURRENT,
#[token("CURRENT_DATE", ignore(ascii_case))]
CURRENT_DATE,
#[token("CURRENT_TIME", ignore(ascii_case))]
CURRENT_TIME,
#[token("CURRENT_TIMESTAMP", ignore(ascii_case))]
CURRENT_TIMESTAMP,
#[token("CURRENT", ignore(ascii_case))]
CURRENT,
#[token("DATABASE", ignore(ascii_case))]
DATABASE,
#[token("DATABASES", ignore(ascii_case))]
Expand Down
60 changes: 60 additions & 0 deletions src/query/functions/src/scalars/timestamp/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ use databend_common_expression::vectorize_with_builder_1_arg;
use databend_common_expression::vectorize_with_builder_2_arg;
use databend_common_expression::vectorize_with_builder_4_arg;
use databend_common_expression::EvalContext;
use databend_common_expression::FunctionContext;
use databend_common_expression::FunctionDomain;
use databend_common_expression::FunctionProperty;
use databend_common_expression::FunctionRegistry;
Expand Down Expand Up @@ -2101,13 +2102,50 @@ fn register_between_functions(registry: &mut FunctionRegistry) {
]);
}

fn normalize_time_precision(raw: i64) -> Result<u8, String> {
if (0..=9).contains(&raw) {
Ok(raw as u8)
} else {
Err(format!(
"Invalid fractional seconds precision `{raw}` for `current_time` (expect 0-9)"
))
}
}

fn current_time_string(func_ctx: &FunctionContext, precision: Option<u8>) -> String {
let datetime = func_ctx.now.with_time_zone(func_ctx.tz.clone()).datetime();
let nanos = datetime.subsec_nanosecond() as u32;
let mut value = format!(
"{:02}:{:02}:{:02}",
datetime.hour(),
datetime.minute(),
datetime.second()
);

let precision = precision.unwrap_or(9).min(9);
if precision > 0 {
let divisor = 10_u32.pow(9 - precision as u32);
let truncated = nanos / divisor;
let frac = format!("{:0width$}", truncated, width = precision as usize);
value.push('.');
value.push_str(&frac);
}

value
}

fn register_real_time_functions(registry: &mut FunctionRegistry) {
registry.register_aliases("now", &["current_timestamp"]);
registry.register_aliases("today", &["current_date"]);

registry.properties.insert(
"now".to_string(),
FunctionProperty::default().non_deterministic(),
);
registry.properties.insert(
"current_time".to_string(),
FunctionProperty::default().non_deterministic(),
);
registry.properties.insert(
"today".to_string(),
FunctionProperty::default().non_deterministic(),
Expand Down Expand Up @@ -2153,6 +2191,28 @@ fn register_real_time_functions(registry: &mut FunctionRegistry) {
|ctx| Value::Scalar(ctx.func_ctx.now.timestamp().as_microsecond()),
);

registry.register_0_arg_core::<StringType, _, _>(
"current_time",
|_| FunctionDomain::Full,
|ctx| Value::Scalar(current_time_string(ctx.func_ctx, None)),
);

registry.register_passthrough_nullable_1_arg::<Int64Type, StringType, _, _>(
"current_time",
|_, _| FunctionDomain::MayThrow,
vectorize_with_builder_1_arg::<Int64Type, StringType>(|precision, output, ctx| {
match normalize_time_precision(precision) {
Ok(valid_precision) => {
output.put_and_commit(current_time_string(ctx.func_ctx, Some(valid_precision)));
}
Err(err) => {
ctx.set_error(output.len(), err);
output.commit_row();
}
}
}),
);

registry.register_0_arg_core::<DateType, _, _>(
"today",
|_| FunctionDomain::Full,
Expand Down
28 changes: 28 additions & 0 deletions src/query/functions/tests/it/scalars/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@
// limitations under the License.

use std::io::Write;
use std::str::FromStr;

use databend_common_expression::types::*;
use databend_common_expression::FromData;
use databend_common_expression::FunctionContext;
use goldenfile::Mint;
use jiff::tz::TimeZone;
use jiff::Timestamp;

use super::run_ast;
use super::run_ast_with_context;
use super::TestContext;

#[test]
fn test_datetime() {
Expand All @@ -36,6 +42,7 @@ fn test_datetime() {
test_to_number(file);
test_rounder_functions(file);
test_date_date_diff(file);
test_current_time(file);
}

fn test_to_timestamp(file: &mut impl Write) {
Expand Down Expand Up @@ -742,3 +749,24 @@ fn test_date_date_diff(file: &mut impl Write) {
&[],
);
}

fn test_current_time(file: &mut impl Write) {
let tz = TimeZone::UTC;
let now = Timestamp::from_str("2024-02-03T04:05:06.789123Z")
.unwrap()
.to_zoned(tz.clone());
let func_ctx = FunctionContext {
tz: tz.clone(),
now,
..FunctionContext::default()
};
let ctx = TestContext {
func_ctx,
..TestContext::default()
};

run_ast_with_context(file, "typeof(current_time())", ctx.clone());
run_ast_with_context(file, "current_time()", ctx.clone());
run_ast_with_context(file, "current_time(3)", ctx.clone());
run_ast_with_context(file, "current_time(10)", ctx);
}
38 changes: 38 additions & 0 deletions src/query/functions/tests/it/scalars/testdata/datetime.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3835,3 +3835,41 @@ output domain : {-31622400..=-31622400}
output : -31622400


ast : typeof(current_time())
raw expr : typeof(current_time())
checked expr : typeof<T0=String><T0>(current_time<>())
optimized expr : "VARCHAR"
func ctx : (modified)
output type : String
output domain : {"VARCHAR"..="VARCHAR"}
output : 'VARCHAR'


ast : current_time()
raw expr : current_time()
checked expr : current_time<>()
optimized expr : "04:05:06.789123000"
func ctx : (modified)
output type : String
output domain : {"04:05:06.789123000"..="04:05:06.789123000"}
output : '04:05:06.789123000'


ast : current_time(3)
raw expr : current_time(3)
checked expr : current_time<Int64>(CAST<UInt8>(3_u8 AS Int64))
optimized expr : "04:05:06.789"
func ctx : (modified)
output type : String
output domain : {"04:05:06.789"..="04:05:06.789"}
output : '04:05:06.789'


error:
--> SQL:1:1
|
1 | current_time(10)
| ^^^^^^^^^^^^^^^^ Invalid fractional seconds precision `10` for `current_time` (expect 0-9) while evaluating function `current_time(10)` in expr `current_time(CAST(10 AS Int64))`



Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ceiling -> ceil
char_length -> length
character_length -> length
chr -> char
current_date -> today
current_timestamp -> now
date -> to_date
date_format -> to_string
Expand Down Expand Up @@ -1360,6 +1361,9 @@ Functions overloads:
1 cot(Float64 NULL) :: Float64 NULL
0 crc32(String) :: UInt32
1 crc32(String NULL) :: UInt32 NULL
0 current_time() :: String
1 current_time(Int64) :: String
2 current_time(Int64 NULL) :: String NULL
0 date_add_months(Date, Int64) :: Date
1 date_add_months(Date NULL, Int64 NULL) :: Date NULL
2 date_add_months(Timestamp, Int64) :: Timestamp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,21 @@ select typeof(now() - now())
----
BIGINT

query TTT
select typeof(CURRENT_DATE()), typeof(CURRENT_TIME()), typeof(CURRENT_TIMESTAMP())
----
DATE VARCHAR TIMESTAMP

query B
select regexp_like(CURRENT_TIME(), '^[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?$')
----
1

query B
select regexp_like(CURRENT_TIME(3), '^[0-9]{2}:[0-9]{2}:[0-9]{2}\.\d{3}$')
----
1

query B
select typeof(to_unix_timestamp('2023-04-06 04:06:23.231808'))
----
Expand Down
Loading