From 73cb224b9cda521118b9d1e61fa35cf03681ce74 Mon Sep 17 00:00:00 2001 From: Arseniy Volynets <vol0ncar@yandex.ru> Date: Wed, 24 Apr 2024 10:56:19 +0000 Subject: [PATCH] feat: support CURRENT_DATE special func --- Cargo.lock | 12 ++++++ doc/sql/query.ebnf | 1 + .../test_app/test/integration/api_test.lua | 38 +++++++++++++++++++ sbroad-core/Cargo.toml | 1 + sbroad-core/src/frontend/sql.rs | 12 ++++++ sbroad-core/src/frontend/sql/ir/tests.rs | 26 +++++++++++++ sbroad-core/src/frontend/sql/query.pest | 3 +- 7 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 44532d2a19..82a7a74499 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1168,6 +1168,7 @@ dependencies = [ "serde_yaml", "smol_str", "tarantool", + "time", "uuid 1.4.0", ] @@ -1440,8 +1441,10 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ + "itoa", "serde", "time-core", + "time-macros", ] [[package]] @@ -1450,6 +1453,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 21d7e4fae0..a0329c7831 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -33,6 +33,7 @@ expression ::= (table '.')? column | ('(' (expression(',' expression)*) ')') | 'NOT' expression | '(' expression ')' + | 'CURRENT_DATE' | 'TO_DATE' '(' expression',' format ')' | 'TO_CHAR' '(' expression',' format ')' | 'TRIM' '(' ((('LEADING' | 'TRAILING' | 'BOTH')? expression) | ('LEADING' | 'TRAILING' | 'BOTH')) 'FROM' expression ')' diff --git a/sbroad-cartridge/test_app/test/integration/api_test.lua b/sbroad-cartridge/test_app/test/integration/api_test.lua index 4a1caaac23..581bf0b37f 100644 --- a/sbroad-cartridge/test_app/test/integration/api_test.lua +++ b/sbroad-cartridge/test_app/test/integration/api_test.lua @@ -631,3 +631,41 @@ g.test_to_char = function () t.assert_str_contains(tostring(error), "bad argument #1 to 'format' (number expected, got string)") end +g.test_current_date = function () + local api = cluster:server("api-1").net_box + + local r, err = api:call("sbroad.execute", { [[ + select currEnt_dAte from "datetime_t" + ]]}) + + local current_date = datetime.now() + current_date:set({hour=0, min=0, sec=0, nsec=0, tzoffset=0}) + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "COL_1", type = "datetime"}, + }, + rows = { + {current_date}, + {current_date}, + }, + }) + + r, err = api:call("sbroad.execute", { [[ + select to_char(COL_1, '%Y') from (select to_date(COLUMN_2, '%Y.%m.%d') from ( + values ('2077.1.1'), ('2000.10.10') + )) + where COL_1 > CURRENT_DATE + ]]}) + t.assert_equals(err, nil) + t.assert_equals(r, { + metadata = { + {name = "COL_1", type = "string"}, + }, + rows = { + {'2077'}, + }, + }) +end + + diff --git a/sbroad-core/Cargo.toml b/sbroad-core/Cargo.toml index 1715b77873..e699d3ec86 100644 --- a/sbroad-core/Cargo.toml +++ b/sbroad-core/Cargo.toml @@ -28,6 +28,7 @@ serde_yaml = "0.8" tarantool = { git = "https://git.picodata.io/picodata/picodata/tarantool-module.git" } uuid = { version = "1.1", features = ["v4", "fast-rng", "macro-diagnostics"] } smol_str = { version = "0.2.1", features = ["serde"] } +time = { version = ">=0.3.0, <0.3.18", features = ["formatting"] } [dev-dependencies] pretty_assertions = "1.3" diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 563fdc1a98..b41b16190d 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -13,7 +13,9 @@ use std::{ collections::{HashMap, HashSet}, str::FromStr, }; +use tarantool::datetime::Datetime; use tarantool::index::{IndexType, RtreeIndexDistanceType}; +use time::{OffsetDateTime, Time}; use crate::errors::{Action, Entity, SbroadError}; use crate::executor::engine::{helpers::normalize_name_for_space_api, Metadata}; @@ -1179,6 +1181,9 @@ where /// As `ColumnPositionMap` is used for parsing references and as it may be shared for the same /// relational node we cache it so that we don't have to recreate it every time. column_positions_cache: HashMap<usize, ColumnPositionMap>, + /// Time at the start of the plan building stage without timezone. + /// It is used to replace CURRENT_DATE to actual value. + current_time: OffsetDateTime, } impl<'worker, M> ExpressionsWorker<'worker, M> @@ -1194,6 +1199,7 @@ where met_tnt_param: false, met_pg_param: false, column_positions_cache: HashMap::with_capacity(COLUMN_POSITIONS_CACHE_CAPACITY), + current_time: OffsetDateTime::now_utc(), } } @@ -1852,6 +1858,12 @@ where }?; ParseExpression::Cast { cast_type, child: Box::new(child_parse_expr) } } + Rule::CurrentDate => { + let date = worker.current_time.replace_time(Time::MIDNIGHT); + let val = Value::Datetime(Datetime::from_inner(date)); + let plan_id = plan.add_const(val); + ParseExpression::PlanId { plan_id } + } Rule::CountAsterisk => { let plan_id = plan.nodes.push(Node::Expression(Expression::CountAsterisk)); ParseExpression::PlanId { plan_id } diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs index 08229263d1..da4c7b664f 100644 --- a/sbroad-core/src/frontend/sql/ir/tests.rs +++ b/sbroad-core/src/frontend/sql/ir/tests.rs @@ -7,6 +7,7 @@ use crate::ir::transformation::helpers::sql_to_optimized_ir; use crate::ir::tree::traversal::PostOrder; use crate::ir::value::Value; use pretty_assertions::assert_eq; +use time::{format_description, OffsetDateTime, Time}; fn sql_to_optimized_ir_add_motions_err(query: &str) -> SbroadError { let metadata = &RouterConfigurationMock::new(); @@ -3395,6 +3396,31 @@ vtable_max_rows = 5000 ) } +#[test] +fn front_sql_current_date() { + let input = r#" + SELECT current_date FROM (values ('2010/10/10')) + where to_date('2010/10/10', '%Y/%d/%m') < current_Date"#; + + let today = OffsetDateTime::now_utc().replace_time(Time::MIDNIGHT); + let format = format_description::parse("[year]-[month]-[day]").unwrap(); + let plan = sql_to_optimized_ir(input, vec![]); + let expected_explain = format!( + r#"projection ({today} 0:00:00.0 +00:00:00::datetime -> "COL_1") + selection ROW("TO_DATE"(('2010/10/10'::string, '%Y/%d/%m'::string))::datetime) < ROW({today} 0:00:00.0 +00:00:00::datetime) + scan + values + value row (data=ROW('2010/10/10'::string)) +execution options: +sql_vdbe_max_steps = 45000 +vtable_max_rows = 5000 +"#, + today = today.format(&format).unwrap() + ); + + assert_eq!(expected_explain, plan.as_explain().unwrap()); +} + #[test] fn front_sql_check_non_null_columns_specified() { let input = r#"insert into "test_space" ("sys_op") values (1)"#; diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 5b1ffb35da..524af41093 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -233,7 +233,7 @@ Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* } ExprAtomValue = _{ UnaryNot* ~ AtomicExpr ~ IsNullPostfix? } UnaryNot = @{ NotFlag } IsNullPostfix = { ^"is" ~ NotFlag? ~ ^"null" } - AtomicExpr = _{ Literal | Parameter | Cast | Trim | IdentifierWithOptionalContinuation | ExpressionInParentheses | UnaryOperator | SubQuery | Row } + AtomicExpr = _{ Literal | Parameter | Cast | Trim | CurrentDate | IdentifierWithOptionalContinuation | ExpressionInParentheses | UnaryOperator | SubQuery | Row } Literal = { True | False | Null | Double | Decimal | Unsigned | Integer | SingleQuotedString } True = { ^"true" } False = { ^"false" } @@ -252,6 +252,7 @@ Expr = { ExprAtomValue ~ (ExprInfixOp ~ ExprAtomValue)* } FunctionArgs = { Distinct? ~ (Expr ~ ("," ~ Expr)*)? } CountAsterisk = { "*" } ExpressionInParentheses = { "(" ~ Expr ~ ")" } + CurrentDate = { ^"current_date" } Trim = { ^"trim" ~ "(" ~ (((TrimKind? ~ TrimPattern) | TrimKind) ~ ^"from")? ~ TrimTarget -- GitLab