Skip to content

Commit 09c3a18

Browse files
authored
feat: support ANSI CURRENT_DATE/CURRENT_TIME keywords (#18902)
* Support CURRENT_DATE/TIME keywords * align current_time with ANSI TIME semantics
1 parent fc3040b commit 09c3a18

File tree

8 files changed

+182
-3
lines changed

8 files changed

+182
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ rmp-serde = "1.1.1"
465465
roaring = { version = "^0.10", features = ["serde"] }
466466
rotbl = { version = "0.2.9", features = [] }
467467
rust_decimal = "1.26"
468-
rustix = "0.38.37"
468+
rustix = { version = "0.38.37", features = ["fs"] }
469469
rustls = { version = "0.23.27", features = ["ring", "tls12"], default-features = false }
470470
rustls-pemfile = "2"
471471
rustls-pki-types = "1"

src/query/ast/src/parser/expr.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,34 @@ pub fn expr_element(i: Input) -> IResult<WithSpan<ExprElement>> {
14991499
|(_, not, _, _)| ExprElement::IsDistinctFrom { not: not.is_some() },
15001500
);
15011501

1502+
let current_date = map(consumed(rule! { CURRENT_DATE }), |(span, _)| {
1503+
ExprElement::FunctionCall {
1504+
func: FunctionCall {
1505+
distinct: false,
1506+
name: Identifier::from_name(transform_span(span.tokens), "current_date"),
1507+
args: vec![],
1508+
params: vec![],
1509+
order_by: vec![],
1510+
window: None,
1511+
lambda: None,
1512+
},
1513+
}
1514+
});
1515+
1516+
let current_time = map(consumed(rule! { CURRENT_TIME }), |(span, _)| {
1517+
ExprElement::FunctionCall {
1518+
func: FunctionCall {
1519+
distinct: false,
1520+
name: Identifier::from_name(transform_span(span.tokens), "current_time"),
1521+
args: vec![],
1522+
params: vec![],
1523+
order_by: vec![],
1524+
window: None,
1525+
lambda: None,
1526+
},
1527+
}
1528+
});
1529+
15021530
let current_timestamp = map(consumed(rule! { CURRENT_TIMESTAMP }), |(span, _)| {
15031531
ExprElement::FunctionCall {
15041532
func: FunctionCall {
@@ -1573,6 +1601,8 @@ pub fn expr_element(i: Input) -> IResult<WithSpan<ExprElement>> {
15731601
| #dot_access : "<dot_access>"
15741602
| #map_access : "[<key>] | .<key> | :<key>"
15751603
| #literal : "<literal>"
1604+
| #current_date: "CURRENT_DATE"
1605+
| #current_time: "CURRENT_TIME"
15761606
| #current_timestamp: "CURRENT_TIMESTAMP"
15771607
| #array : "`[<expr>, ...]`"
15781608
| #map_expr : "`{ <literal> : <expr>, ... }`"

src/query/ast/src/parser/token.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,10 +497,14 @@ pub enum TokenKind {
497497
CROSS,
498498
#[token("CSV", ignore(ascii_case))]
499499
CSV,
500-
#[token("CURRENT", ignore(ascii_case))]
501-
CURRENT,
500+
#[token("CURRENT_DATE", ignore(ascii_case))]
501+
CURRENT_DATE,
502+
#[token("CURRENT_TIME", ignore(ascii_case))]
503+
CURRENT_TIME,
502504
#[token("CURRENT_TIMESTAMP", ignore(ascii_case))]
503505
CURRENT_TIMESTAMP,
506+
#[token("CURRENT", ignore(ascii_case))]
507+
CURRENT,
504508
#[token("DATABASE", ignore(ascii_case))]
505509
DATABASE,
506510
#[token("DATABASES", ignore(ascii_case))]

src/query/functions/src/scalars/timestamp/src/datetime.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use databend_common_expression::vectorize_with_builder_1_arg;
6868
use databend_common_expression::vectorize_with_builder_2_arg;
6969
use databend_common_expression::vectorize_with_builder_4_arg;
7070
use databend_common_expression::EvalContext;
71+
use databend_common_expression::FunctionContext;
7172
use databend_common_expression::FunctionDomain;
7273
use databend_common_expression::FunctionProperty;
7374
use databend_common_expression::FunctionRegistry;
@@ -2101,13 +2102,50 @@ fn register_between_functions(registry: &mut FunctionRegistry) {
21012102
]);
21022103
}
21032104

2105+
fn normalize_time_precision(raw: i64) -> Result<u8, String> {
2106+
if (0..=9).contains(&raw) {
2107+
Ok(raw as u8)
2108+
} else {
2109+
Err(format!(
2110+
"Invalid fractional seconds precision `{raw}` for `current_time` (expect 0-9)"
2111+
))
2112+
}
2113+
}
2114+
2115+
fn current_time_string(func_ctx: &FunctionContext, precision: Option<u8>) -> String {
2116+
let datetime = func_ctx.now.with_time_zone(func_ctx.tz.clone()).datetime();
2117+
let nanos = datetime.subsec_nanosecond() as u32;
2118+
let mut value = format!(
2119+
"{:02}:{:02}:{:02}",
2120+
datetime.hour(),
2121+
datetime.minute(),
2122+
datetime.second()
2123+
);
2124+
2125+
let precision = precision.unwrap_or(9).min(9);
2126+
if precision > 0 {
2127+
let divisor = 10_u32.pow(9 - precision as u32);
2128+
let truncated = nanos / divisor;
2129+
let frac = format!("{:0width$}", truncated, width = precision as usize);
2130+
value.push('.');
2131+
value.push_str(&frac);
2132+
}
2133+
2134+
value
2135+
}
2136+
21042137
fn register_real_time_functions(registry: &mut FunctionRegistry) {
21052138
registry.register_aliases("now", &["current_timestamp"]);
2139+
registry.register_aliases("today", &["current_date"]);
21062140

21072141
registry.properties.insert(
21082142
"now".to_string(),
21092143
FunctionProperty::default().non_deterministic(),
21102144
);
2145+
registry.properties.insert(
2146+
"current_time".to_string(),
2147+
FunctionProperty::default().non_deterministic(),
2148+
);
21112149
registry.properties.insert(
21122150
"today".to_string(),
21132151
FunctionProperty::default().non_deterministic(),
@@ -2153,6 +2191,28 @@ fn register_real_time_functions(registry: &mut FunctionRegistry) {
21532191
|ctx| Value::Scalar(ctx.func_ctx.now.timestamp().as_microsecond()),
21542192
);
21552193

2194+
registry.register_0_arg_core::<StringType, _, _>(
2195+
"current_time",
2196+
|_| FunctionDomain::Full,
2197+
|ctx| Value::Scalar(current_time_string(ctx.func_ctx, None)),
2198+
);
2199+
2200+
registry.register_passthrough_nullable_1_arg::<Int64Type, StringType, _, _>(
2201+
"current_time",
2202+
|_, _| FunctionDomain::MayThrow,
2203+
vectorize_with_builder_1_arg::<Int64Type, StringType>(|precision, output, ctx| {
2204+
match normalize_time_precision(precision) {
2205+
Ok(valid_precision) => {
2206+
output.put_and_commit(current_time_string(ctx.func_ctx, Some(valid_precision)));
2207+
}
2208+
Err(err) => {
2209+
ctx.set_error(output.len(), err);
2210+
output.commit_row();
2211+
}
2212+
}
2213+
}),
2214+
);
2215+
21562216
registry.register_0_arg_core::<DateType, _, _>(
21572217
"today",
21582218
|_| FunctionDomain::Full,

src/query/functions/tests/it/scalars/datetime.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,18 @@
1313
// limitations under the License.
1414

1515
use std::io::Write;
16+
use std::str::FromStr;
1617

1718
use databend_common_expression::types::*;
1819
use databend_common_expression::FromData;
20+
use databend_common_expression::FunctionContext;
1921
use goldenfile::Mint;
22+
use jiff::tz::TimeZone;
23+
use jiff::Timestamp;
2024

2125
use super::run_ast;
26+
use super::run_ast_with_context;
27+
use super::TestContext;
2228

2329
#[test]
2430
fn test_datetime() {
@@ -36,6 +42,7 @@ fn test_datetime() {
3642
test_to_number(file);
3743
test_rounder_functions(file);
3844
test_date_date_diff(file);
45+
test_current_time(file);
3946
}
4047

4148
fn test_to_timestamp(file: &mut impl Write) {
@@ -742,3 +749,24 @@ fn test_date_date_diff(file: &mut impl Write) {
742749
&[],
743750
);
744751
}
752+
753+
fn test_current_time(file: &mut impl Write) {
754+
let tz = TimeZone::UTC;
755+
let now = Timestamp::from_str("2024-02-03T04:05:06.789123Z")
756+
.unwrap()
757+
.to_zoned(tz.clone());
758+
let func_ctx = FunctionContext {
759+
tz: tz.clone(),
760+
now,
761+
..FunctionContext::default()
762+
};
763+
let ctx = TestContext {
764+
func_ctx,
765+
..TestContext::default()
766+
};
767+
768+
run_ast_with_context(file, "typeof(current_time())", ctx.clone());
769+
run_ast_with_context(file, "current_time()", ctx.clone());
770+
run_ast_with_context(file, "current_time(3)", ctx.clone());
771+
run_ast_with_context(file, "current_time(10)", ctx);
772+
}

src/query/functions/tests/it/scalars/testdata/datetime.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,3 +3835,41 @@ output domain : {-31622400..=-31622400}
38353835
output : -31622400
38363836

38373837

3838+
ast : typeof(current_time())
3839+
raw expr : typeof(current_time())
3840+
checked expr : typeof<T0=String><T0>(current_time<>())
3841+
optimized expr : "VARCHAR"
3842+
func ctx : (modified)
3843+
output type : String
3844+
output domain : {"VARCHAR"..="VARCHAR"}
3845+
output : 'VARCHAR'
3846+
3847+
3848+
ast : current_time()
3849+
raw expr : current_time()
3850+
checked expr : current_time<>()
3851+
optimized expr : "04:05:06.789123000"
3852+
func ctx : (modified)
3853+
output type : String
3854+
output domain : {"04:05:06.789123000"..="04:05:06.789123000"}
3855+
output : '04:05:06.789123000'
3856+
3857+
3858+
ast : current_time(3)
3859+
raw expr : current_time(3)
3860+
checked expr : current_time<Int64>(CAST<UInt8>(3_u8 AS Int64))
3861+
optimized expr : "04:05:06.789"
3862+
func ctx : (modified)
3863+
output type : String
3864+
output domain : {"04:05:06.789"..="04:05:06.789"}
3865+
output : '04:05:06.789'
3866+
3867+
3868+
error:
3869+
--> SQL:1:1
3870+
|
3871+
1 | current_time(10)
3872+
| ^^^^^^^^^^^^^^^^ 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))`
3873+
3874+
3875+

src/query/functions/tests/it/scalars/testdata/function_list.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ceiling -> ceil
1919
char_length -> length
2020
character_length -> length
2121
chr -> char
22+
current_date -> today
2223
current_timestamp -> now
2324
date -> to_date
2425
date_format -> to_string
@@ -1360,6 +1361,9 @@ Functions overloads:
13601361
1 cot(Float64 NULL) :: Float64 NULL
13611362
0 crc32(String) :: UInt32
13621363
1 crc32(String NULL) :: UInt32 NULL
1364+
0 current_time() :: String
1365+
1 current_time(Int64) :: String
1366+
2 current_time(Int64 NULL) :: String NULL
13631367
0 date_add_months(Date, Int64) :: Date
13641368
1 date_add_months(Date NULL, Int64 NULL) :: Date NULL
13651369
2 date_add_months(Timestamp, Int64) :: Timestamp

tests/sqllogictests/suites/query/functions/02_0012_function_datetimes.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,21 @@ select typeof(now() - now())
222222
----
223223
BIGINT
224224

225+
query TTT
226+
select typeof(CURRENT_DATE()), typeof(CURRENT_TIME()), typeof(CURRENT_TIMESTAMP())
227+
----
228+
DATE VARCHAR TIMESTAMP
229+
230+
query B
231+
select regexp_like(CURRENT_TIME(), '^[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?$')
232+
----
233+
1
234+
235+
query B
236+
select regexp_like(CURRENT_TIME(3), '^[0-9]{2}:[0-9]{2}:[0-9]{2}\.\d{3}$')
237+
----
238+
1
239+
225240
query B
226241
select typeof(to_unix_timestamp('2023-04-06 04:06:23.231808'))
227242
----

0 commit comments

Comments
 (0)