Skip to content

Commit 2b33ceb

Browse files
Merge pull request #18 from kemingy/v0.54.x
fix cargo fmt Merge branch 'v0.54.x' into v0.54.x Merge pull request #20 from kemingy/calc_overflow fix lint Merge branch 'v0.54.x' into v0.54.x Merge pull request #19 from kemingy/lint add test for statement convertion fix: overflow in the calculation expr fix the test in another pr fix overflow fix: lint and test fix: add recursive protection to Convert
1 parent e96fe54 commit 2b33ceb

File tree

3 files changed

+262
-2
lines changed

3 files changed

+262
-2
lines changed

src/ast/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ where
136136
Self: Into<T>,
137137
{
138138
#[inline(always)]
139+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
139140
fn convert(value: Self) -> T {
140141
Self::into(value)
141142
}

src/parser/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,8 +1346,7 @@ impl<'a> Parser<'a> {
13461346
})?;
13471347
if rewrite_count == 0 {
13481348
return Err(ParserError::ParserError(format!(
1349-
"Can't use the RANGE keyword in Expr {} without function",
1350-
expr
1349+
"Can't use the RANGE keyword in Expr {expr} without function"
13511350
)));
13521351
}
13531352
Ok(expr)
@@ -17905,6 +17904,7 @@ impl Word {
1790517904
/// * `Ok(Some(replacement_expr))`: A replacement `Expr` is provided, use replacement `Expr`.
1790617905
/// * `Ok(None)`: A replacement `Expr` is not provided, use old `Expr`.
1790717906
/// * `Err(err)`: Any error returned.
17907+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
1790817908
fn rewrite_calculation_expr<F>(
1790917909
expr: &Expr,
1791017910
rewrite_func_expr: bool,

tests/sqlparser_common.rs

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17193,3 +17193,262 @@ fn parse_adjacent_string_literal_concatenation() {
1719317193
let sql = "SELECT * FROM t WHERE col = 'Hello' \n ' ' \t 'World!'";
1719417194
dialects.one_statement_parses_to(sql, r"SELECT * FROM t WHERE col = 'Hello World!'");
1719517195
}
17196+
17197+
fn assert_sql(input: &str, expected: &str) {
17198+
let dialects = TestedDialects::new(vec![Box::new(GenericDialect {})]);
17199+
dialects.verified_query_with_canonical(input, expected);
17200+
}
17201+
17202+
fn assert_sql_err(input: &str, expected: &str) {
17203+
let res = parse_sql_statements(input);
17204+
let res_str = format!("{:?}", res.unwrap_err());
17205+
assert!(
17206+
res_str.contains(expected),
17207+
"`{res_str}` doesn't contains `{expected}`"
17208+
);
17209+
}
17210+
17211+
#[test]
17212+
fn parse_range_select() {
17213+
// rewrite format `range_fn(func, range, fill, byc, [byv], align, to)`
17214+
// regular without by
17215+
assert_sql("SELECT rate(metrics) RANGE '5m', sum(metrics) RANGE '10m' FILL MAX, sum(metrics) RANGE '10m' FROM t ALIGN '1h' FILL NULL;",
17216+
"SELECT range_fn(rate(metrics), '5m', 'NULL', '0', '1h', ''), range_fn(sum(metrics), '10m', 'MAX', '0', '1h', ''), range_fn(sum(metrics), '10m', 'NULL', '0', '1h', '') FROM t");
17217+
17218+
// regular with by
17219+
assert_sql("SELECT rate(metrics) RANGE '5m', sum(metrics) RANGE '10m' FILL MAX, sum(metrics) RANGE '10m' FROM t ALIGN '1h' by ((a+1)/2, b) FILL NULL;",
17220+
"SELECT range_fn(rate(metrics), '5m', 'NULL', '2', (a + 1) / 2, b, '1h', ''), range_fn(sum(metrics), '10m', 'MAX', '2', (a + 1) / 2, b, '1h', ''), range_fn(sum(metrics), '10m', 'NULL', '2', (a + 1) / 2, b, '1h', '') FROM t GROUP BY a, b");
17221+
17222+
// explicit empty by
17223+
assert_sql("SELECT rate(metrics) RANGE '5m', sum(metrics) RANGE '10m' FILL MAX, sum(metrics) RANGE '10m' FROM t ALIGN '1h' by () FILL NULL;",
17224+
"SELECT range_fn(rate(metrics), '5m', 'NULL', '1', 1, '1h', ''), range_fn(sum(metrics), '10m', 'MAX', '1', 1, '1h', ''), range_fn(sum(metrics), '10m', 'NULL', '1', 1, '1h', '') FROM t");
17225+
17226+
// expression1
17227+
assert_sql(
17228+
"SELECT avg(a/2 + 1) RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17229+
"SELECT range_fn(avg(a / 2 + 1), '5m', 'NULL', '0', '1h', '') FROM t",
17230+
);
17231+
17232+
// expression2
17233+
assert_sql(
17234+
"SELECT avg(a) RANGE '5m' FILL NULL + 1 FROM t ALIGN '1h' FILL NULL;",
17235+
"SELECT range_fn(avg(a), '5m', 'NULL', '0', '1h', '') + 1 FROM t",
17236+
);
17237+
17238+
// expression3
17239+
assert_sql(
17240+
"SELECT ((avg(a) + sum(b))/2) RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17241+
"SELECT ((range_fn(avg(a), '5m', 'NULL', '0', '1h', '') + range_fn(sum(b), '5m', 'NULL', '0', '1h', '')) / 2) FROM t",
17242+
);
17243+
17244+
// expression4
17245+
assert_sql(
17246+
"SELECT covariance(a, b) RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17247+
"SELECT range_fn(covariance(a, b), '5m', 'NULL', '0', '1h', '') FROM t",
17248+
);
17249+
17250+
// expression5
17251+
assert_sql(
17252+
"SELECT covariance(cos(a), sin(b)) RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17253+
"SELECT range_fn(covariance(cos(a), sin(b)), '5m', 'NULL', '0', '1h', '') FROM t",
17254+
);
17255+
17256+
// expression6
17257+
assert_sql(
17258+
"SELECT ((covariance(a+1, b/2) + sum(b))/2) RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17259+
"SELECT ((range_fn(covariance(a + 1, b / 2), '5m', 'NULL', '0', '1h', '') + range_fn(sum(b), '5m', 'NULL', '0', '1h', '')) / 2) FROM t",
17260+
);
17261+
17262+
// FILL... ALIGN...
17263+
assert_sql(
17264+
"SELECT sum(metrics) RANGE '10m' FROM t FILL NULL ALIGN '1h';",
17265+
"SELECT range_fn(sum(metrics), '10m', 'NULL', '0', '1h', '') FROM t",
17266+
);
17267+
17268+
// FILL ... FILL ...
17269+
assert_sql_err(
17270+
"SELECT sum(metrics) RANGE '10m' FILL MAX FROM t FILL NULL FILL NULL;",
17271+
"Duplicate FILL keyword detected in SELECT clause.",
17272+
);
17273+
17274+
// ALIGN ... ALIGN ...
17275+
assert_sql_err(
17276+
"SELECT sum(metrics) RANGE '10m' FILL MAX FROM t ALIGN '1h' ALIGN '1h';",
17277+
"Duplicate ALIGN keyword detected in SELECT clause.",
17278+
);
17279+
17280+
// FILL without RANGE
17281+
assert_sql_err(
17282+
"SELECT sum(metrics) FILL MAX FROM t FILL NULL ALIGN '1h';",
17283+
"Detect FILL keyword in SELECT Expr, but no RANGE given or RANGE after FILL",
17284+
);
17285+
17286+
// RANGE after FILL
17287+
assert_sql_err(
17288+
"SELECT sum(metrics) FILL MAX RANGE '10m' FROM t FILL NULL ALIGN '1h';",
17289+
"Detect FILL keyword in SELECT Expr, but no RANGE given or RANGE after FILL",
17290+
);
17291+
17292+
// INVALID Duration String
17293+
assert_sql_err(
17294+
"SELECT sum(metrics) RANGE '10m' FILL MAX FROM t FILL NULL ALIGN '1ff';",
17295+
"not a valid duration string: 1ff",
17296+
);
17297+
assert_sql_err(
17298+
"SELECT sum(metrics) RANGE '1regr' FILL MAX FROM t FILL NULL ALIGN '1h';",
17299+
"not a valid duration string: 1regr",
17300+
);
17301+
17302+
// omit RANGE
17303+
assert_sql_err(
17304+
"SELECT sum(metrics) FROM t ALIGN '1h' FILL NULL;",
17305+
"Illegal Range select, no RANGE keyword found in any SelectItem",
17306+
);
17307+
17308+
// omit ALIGN
17309+
assert_sql_err(
17310+
"SELECT sum(metrics) RANGE '10m' FILL MAX FROM t FILL NULL;",
17311+
"ALIGN argument cannot be omitted in the range select query",
17312+
);
17313+
17314+
assert_sql_err(
17315+
"SELECT sum(metrics) RANGE '10m', * FROM t FILL NULL ALIGN '1h';",
17316+
"Wildcard `*` is not allowed in range select query",
17317+
);
17318+
}
17319+
17320+
#[test]
17321+
fn parse_range_in_expr() {
17322+
// use range in expr
17323+
assert_sql(
17324+
"SELECT rate(a) RANGE '6m' + 1 FROM t ALIGN '1h' FILL NULL;",
17325+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', '') + 1 FROM t",
17326+
);
17327+
17328+
assert_sql(
17329+
"SELECT sin(rate(a) RANGE '6m' + 1) FROM t ALIGN '1h' FILL NULL;",
17330+
"SELECT sin(range_fn(rate(a), '6m', 'NULL', '0', '1h', '') + 1) FROM t",
17331+
);
17332+
17333+
assert_sql(
17334+
"SELECT sin(first_value(a ORDER BY b ASC NULLS LAST) RANGE '6m' + 1) FROM t ALIGN '1h' by (tag0, tag1) FILL NULL;",
17335+
"SELECT sin(range_fn(first_value(a ORDER BY b ASC NULLS LAST), '6m', 'NULL', '2', tag0, tag1, '1h', '') + 1) FROM t GROUP BY tag0, tag1",
17336+
);
17337+
17338+
assert_sql(
17339+
"SELECT sin(count(distinct a) RANGE '6m' + 1) FROM t ALIGN '1h' by (tag0, tag1) FILL NULL;",
17340+
"SELECT sin(range_fn(count(DISTINCT a), '6m', 'NULL', '2', tag0, tag1, '1h', '') + 1) FROM t GROUP BY tag0, tag1",
17341+
);
17342+
17343+
assert_sql(
17344+
"SELECT sin(rank() OVER (PARTITION BY a ORDER BY b DESC) RANGE '6m' + 1) FROM t ALIGN '1h' by (tag0, tag1) FILL NULL;",
17345+
"SELECT sin(range_fn(rank() OVER (PARTITION BY a ORDER BY b DESC), '6m', 'NULL', '2', tag0, tag1, '1h', '') + 1) FROM t GROUP BY tag0, tag1",
17346+
);
17347+
17348+
assert_sql(
17349+
"SELECT sin(cos(round(sin(avg(a + b) RANGE '5m' + 1)))) FROM test ALIGN '1h' by (tag_0,tag_1);",
17350+
"SELECT sin(cos(round(sin(range_fn(avg(a + b), '5m', '', '2', tag_0, tag_1, '1h', '') + 1)))) FROM test GROUP BY tag_0, tag_1",
17351+
);
17352+
17353+
assert_sql("SELECT rate(a) RANGE '6m' + rate(a) RANGE '5m' FROM t ALIGN '1h' FILL NULL;",
17354+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', '') + range_fn(rate(a), '5m', 'NULL', '0', '1h', '') FROM t");
17355+
17356+
assert_sql("SELECT (rate(a) RANGE '6m' + rate(a) RANGE '5m')/b + b * rate(a) RANGE '5m' FROM t ALIGN '1h' FILL NULL;",
17357+
"SELECT (range_fn(rate(a), '6m', 'NULL', '0', '1h', '') + range_fn(rate(a), '5m', 'NULL', '0', '1h', '')) / b + b * range_fn(rate(a), '5m', 'NULL', '0', '1h', '') FROM t GROUP BY b");
17358+
17359+
assert_sql("SELECT round(max(a+1) Range '5m' FILL NULL), sin((max(a) + 1) Range '5m' FILL NULL) from t ALIGN '1h' by (b) FILL NULL;",
17360+
"SELECT round(range_fn(max(a + 1), '5m', 'NULL', '1', b, '1h', '')), sin((range_fn(max(a), '5m', 'NULL', '1', b, '1h', '') + 1)) FROM t GROUP BY b");
17361+
17362+
assert_sql(
17363+
"SELECT floor(ceil((min(a * 2) + max(a *2)) RANGE '20s' + 1.0)) FROM t ALIGN '1h';",
17364+
"SELECT FLOOR(CEIL((range_fn(min(a * 2), '20s', '', '0', '1h', '') + range_fn(max(a * 2), '20s', '', '0', '1h', '')) + 1.0)) FROM t",
17365+
);
17366+
17367+
assert_sql(
17368+
"SELECT gcd(CAST(max(a + 1) Range '5m' FILL NULL AS INT64), CAST(b AS INT64)) + round(max(c+1) Range '6m' FILL NULL + 1) + max(d+3) Range '10m' FILL NULL * CAST(e AS FLOAT64) + 1 FROM test ALIGN '1h' by (f, g);",
17369+
"SELECT gcd(CAST(range_fn(max(a + 1), '5m', 'NULL', '2', f, g, '1h', '') AS INT64), CAST(b AS INT64)) + round(range_fn(max(c + 1), '6m', 'NULL', '2', f, g, '1h', '') + 1) + range_fn(max(d + 3), '10m', 'NULL', '2', f, g, '1h', '') * CAST(e AS FLOAT64) + 1 FROM test GROUP BY b, e, f, g",
17370+
);
17371+
17372+
// Legal syntax but illegal semantic, nested range semantics are problematic, leave semantic problem to greptimedb
17373+
assert_sql(
17374+
"SELECT rate(max(a) RANGE '6m') RANGE '6m' + 1 FROM t ALIGN '1h' FILL NULL;",
17375+
"SELECT range_fn(rate(range_fn(max(a), '6m', '')), '6m', 'NULL', '0', '1h', '') + 1 FROM t",
17376+
);
17377+
17378+
assert_sql_err(
17379+
"SELECT rate(a) RANGE '6m' RANGE '6m' + 1 FROM t ALIGN '1h' FILL NULL;",
17380+
"Expected: end of statement, found: RANGE",
17381+
);
17382+
17383+
assert_sql_err(
17384+
"SELECT rate(a) + 1 RANGE '5m' FROM t ALIGN '1h' FILL NULL;",
17385+
"Can't use the RANGE keyword in Expr 1 without function",
17386+
);
17387+
17388+
assert_sql_err(
17389+
"SELECT 1 RANGE '5m' FILL NULL FROM t ALIGN '1h' FILL NULL;",
17390+
"Can't use the RANGE keyword in Expr 1 without function",
17391+
);
17392+
}
17393+
17394+
#[test]
17395+
fn parse_range_interval() {
17396+
assert_sql(
17397+
"SELECT rate(a) RANGE (INTERVAL '1 year 2 hours 3 minutes') FROM t ALIGN (INTERVAL '1 year 2 hours 3 minutes') FILL NULL;",
17398+
"SELECT range_fn(rate(a), INTERVAL '1 year 2 hours 3 minutes', 'NULL', '0', INTERVAL '1 year 2 hours 3 minutes', '') FROM t",
17399+
);
17400+
assert_sql(
17401+
"SELECT rate(a) RANGE (INTERVAL '1' YEAR) FROM t ALIGN (INTERVAL '1' YEAR) FILL NULL;",
17402+
"SELECT range_fn(rate(a), INTERVAL '1' YEAR, 'NULL', '0', INTERVAL '1' YEAR, '') FROM t",
17403+
);
17404+
assert_sql(
17405+
"SELECT sin(count(distinct a) RANGE (INTERVAL '1 year 2 hours 3 minutes') + 1) FROM t ALIGN (INTERVAL '1 year 2 hours 3 minutes') FILL NULL;",
17406+
"SELECT sin(range_fn(count(DISTINCT a), INTERVAL '1 year 2 hours 3 minutes', 'NULL', '0', INTERVAL '1 year 2 hours 3 minutes', '') + 1) FROM t",
17407+
);
17408+
assert_sql(
17409+
"SELECT rate(a) RANGE (INTERVAL '1' YEAR) FROM t ALIGN (INTERVAL '1' YEAR) TO '1970-01-01T00:00:00+08:00' BY (b, c) FILL NULL;",
17410+
"SELECT range_fn(rate(a), INTERVAL '1' YEAR, 'NULL', '2', b, c, INTERVAL '1' YEAR, '1970-01-01T00:00:00+08:00') FROM t GROUP BY b, c",
17411+
);
17412+
assert_sql_err(
17413+
"SELECT rate(a) RANGE INTERVAL '1 year 2 hours 3 minutes' FROM t ALIGN '1h' FILL NULL;",
17414+
"Expected: end of statement, found: RANGE",
17415+
);
17416+
}
17417+
17418+
#[test]
17419+
fn parse_range_to() {
17420+
assert_sql(
17421+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO NOW FILL NULL;",
17422+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', 'NOW') FROM t",
17423+
);
17424+
assert_sql(
17425+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO CALENDAR FILL NULL;",
17426+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', 'CALENDAR') FROM t",
17427+
);
17428+
assert_sql(
17429+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO '2021-07-01 00:00:00' FILL NULL;",
17430+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', '2021-07-01 00:00:00') FROM t",
17431+
);
17432+
}
17433+
17434+
#[test]
17435+
fn parse_range_range_align_to_calculate() {
17436+
assert_sql(
17437+
"SELECT ts, min(val) RANGE (INTERVAL '1' day + INTERVAL '1 year 2 hours 3 minutes') FROM host ALIGN (INTERVAL '1' day + INTERVAL '1 year 2 hours 3 minutes') TO (now() - INTERVAL '1' day) by (1);",
17438+
"SELECT ts, range_fn(min(val), INTERVAL '1' DAY + INTERVAL '1 year 2 hours 3 minutes', '', '1', 1, INTERVAL '1' DAY + INTERVAL '1 year 2 hours 3 minutes', now() - INTERVAL '1' DAY) FROM host GROUP BY ts",
17439+
);
17440+
assert_sql(
17441+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO (( (now()) - ((INTERVAL '1' day)) )) FILL NULL;",
17442+
"SELECT range_fn(rate(a), '6m', 'NULL', '0', '1h', ((now()) - ((INTERVAL '1' DAY)))) FROM t",
17443+
);
17444+
// missing the last comma
17445+
assert_sql_err(
17446+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO (( (now()) - ((INTERVAL '1' day)) ) FILL NULL;",
17447+
"Detect FILL keyword in SELECT Expr, but no RANGE given or RANGE after FILL",
17448+
);
17449+
// addition last comma
17450+
assert_sql_err(
17451+
"SELECT rate(a) RANGE '6m' FROM t ALIGN '1h' TO (( (now()) - ((INTERVAL '1' day)) ) ) ) FILL NULL;",
17452+
"Expected: end of statement, found: )",
17453+
);
17454+
}

0 commit comments

Comments
 (0)