Skip to content

Commit 00ea62a

Browse files
authored
chore(sqlparser): add do-apply-parser-test (#8486)
Signed-off-by: TennyZhuang <[email protected]>
1 parent a77f6cc commit 00ea62a

File tree

19 files changed

+300
-234
lines changed

19 files changed

+300
-234
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extend = [
99
{ path = "src/risedevtool/redis.toml" },
1010
{ path = "src/risedevtool/connector.toml" },
1111
{ path = "src/risedevtool/risedev-components.toml" },
12+
{ path = "src/sqlparser/test_runner/sqlparser_test.toml"},
1213
{ path = "src/frontend/planner_test/planner_test.toml" },
1314
{ path = "src/tests/compaction_test/Makefile.toml" },
1415
{ path = "src/storage/backup/integration_tests/Makefile.toml" },

src/cmd_all/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ ignored = ["workspace-hack", "workspace-config", "task_stats_alloc"]
2020
[dependencies]
2121
anyhow = "1"
2222
clap = { version = "4", features = ["derive"] }
23-
console = "0.15.2"
23+
console = "0.15"
2424
risingwave_common = { path = "../common" }
2525
risingwave_compactor = { path = "../storage/compactor" }
2626
risingwave_compute = { path = "../compute" }

src/sqlparser/test_runner/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,27 @@ normal = ["workspace-hack"]
1111

1212
[dependencies]
1313
anyhow = "1"
14+
console = "0.15"
15+
futures = { version = "0.3", default-features = false, features = ["alloc"] }
1416
risingwave_sqlparser = { path = "../" }
1517
serde = { version = "1", features = ["derive"] }
18+
serde_with = "2"
1619
serde_yaml = "0.9"
20+
tokio = { version = "0.2", package = "madsim-tokio", features = [
21+
"rt",
22+
"rt-multi-thread",
23+
"sync",
24+
"macros",
25+
"time",
26+
"signal",
27+
"fs",
28+
] }
1729
walkdir = "2"
1830

31+
[[bin]]
32+
name = "parser-test-apply"
33+
path = "src/bin/apply.rs"
34+
1935
[target.'cfg(not(madsim))'.dependencies]
2036
workspace-hack = { path = "../../workspace-hack" }
2137

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
[tasks.update-parser-test]
2+
description = "Update parser test data"
3+
private = true
4+
script = '''
5+
#!/usr/bin/env bash
6+
set -e
7+
cargo run --bin parser-test-apply
8+
'''
9+
10+
[tasks.apply-parser-test]
11+
description = "Generate parser test data"
12+
dependencies = [
13+
"update-parser-test"
14+
]
15+
script = '''
16+
#!/usr/bin/env bash
17+
set -e
18+
cd src/sqlparser/tests/testdata/
19+
20+
for f in *.apply.yaml
21+
do
22+
diff "$f" "$(basename "$f" .apply.yaml).yaml" || true
23+
done
24+
25+
echo "If you want to apply the parser test data, run: $(tput setaf 2)./risedev do-apply-parser-test$(tput sgr 0)"
26+
'''
27+
category = "RiseDev - Test"
28+
29+
[tasks.do-apply-parser-test]
30+
description = "Apply parser test data"
31+
dependencies = [
32+
"update-parser-test"
33+
]
34+
script = '''
35+
#!/usr/bin/env bash
36+
set -e
37+
cd src/sqlparser/tests/testdata/
38+
39+
for f in *.apply.yaml
40+
do
41+
SOURCE="$(basename $f .apply.yaml).yaml"
42+
if [ -f "$SOURCE" ]; then
43+
cat <<EOF > temp.apply.yaml
44+
# This file is automatically generated. See \`src/sqlparser/test_runner/src/bin/apply.rs\` for more information.
45+
EOF
46+
cat "$f" >> temp.apply.yaml
47+
mv temp.apply.yaml "$SOURCE"
48+
fi
49+
done
50+
51+
rm *.apply.yaml
52+
53+
echo "$(tput setaf 2)Diff applied!$(tput sgr 0)"
54+
'''
55+
category = "RiseDev - Test"
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright 2023 RisingWave Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use std::path::Path;
16+
use std::sync::{Arc, Mutex};
17+
18+
use anyhow::Result;
19+
use console::style;
20+
use futures::future::try_join_all;
21+
use risingwave_sqlparser::ast::Statement;
22+
use risingwave_sqlparser::parser::Parser;
23+
use risingwave_sqlparser_test_runner::TestCase;
24+
use walkdir::WalkDir;
25+
26+
#[tokio::main]
27+
async fn main() -> Result<()> {
28+
let manifest_dir = env!("CARGO_MANIFEST_DIR");
29+
let dir = Path::new(manifest_dir)
30+
.parent()
31+
.unwrap()
32+
.join("tests")
33+
.join("testdata");
34+
println!("Using test cases from {:?}", dir);
35+
36+
let mut futs = vec![];
37+
38+
let log_lock = Arc::new(Mutex::new(()));
39+
40+
for entry in WalkDir::new(dir) {
41+
let entry = entry.unwrap();
42+
let path = entry.path();
43+
44+
if !path.is_file() {
45+
continue;
46+
}
47+
48+
if path.is_file()
49+
&& path.extension().map_or(false, |p| {
50+
p.eq_ignore_ascii_case("yml") || p.eq_ignore_ascii_case("yaml")
51+
})
52+
&& !path
53+
.file_name()
54+
.unwrap()
55+
.to_string_lossy()
56+
.ends_with(".apply.yaml")
57+
{
58+
let target = path.with_extension("apply.yaml");
59+
60+
let path = path.to_path_buf();
61+
let log_lock = Arc::clone(&log_lock);
62+
futs.push(async move {
63+
let file_content = tokio::fs::read_to_string(&path).await?;
64+
65+
let cases: Vec<TestCase> = serde_yaml::from_str(&file_content)?;
66+
67+
let mut new_cases = Vec::with_capacity(cases.len());
68+
69+
for case in cases {
70+
let input = &case.input;
71+
let ast = Parser::parse_sql(input);
72+
let actual_case = match ast {
73+
Ok(ast) => {
74+
let [ast]: [Statement; 1] = ast
75+
.try_into()
76+
.expect("Only one statement is supported now.");
77+
78+
let actual_formatted_sql =
79+
case.formatted_sql.as_ref().map(|_| format!("{}", ast));
80+
let actual_formatted_ast =
81+
case.formatted_ast.as_ref().map(|_| format!("{:?}", ast));
82+
83+
TestCase {
84+
input: input.clone(),
85+
formatted_sql: actual_formatted_sql,
86+
formatted_ast: actual_formatted_ast,
87+
error_msg: None,
88+
}
89+
}
90+
Err(err) => {
91+
let actual_error_msg = format!("{}", err);
92+
TestCase {
93+
input: input.clone(),
94+
formatted_sql: None,
95+
formatted_ast: None,
96+
error_msg: Some(actual_error_msg),
97+
}
98+
}
99+
};
100+
101+
if actual_case != case {
102+
let _guard = log_lock.lock();
103+
println!("{}\n{}\n", style(&case).red(), style(&actual_case).green())
104+
}
105+
106+
new_cases.push(actual_case);
107+
}
108+
109+
let output_content = serde_yaml::to_string(&new_cases)?;
110+
111+
tokio::fs::write(target, output_content).await?;
112+
113+
Ok::<_, anyhow::Error>(())
114+
});
115+
}
116+
}
117+
118+
let _res = try_join_all(futs).await?;
119+
120+
Ok(())
121+
}

src/sqlparser/test_runner/src/lib.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,27 @@
1414

1515
// Data-driven tests.
1616

17+
use std::fmt::Display;
18+
1719
use anyhow::{anyhow, Result};
1820
use risingwave_sqlparser::parser::Parser;
19-
use serde::Deserialize;
21+
use serde::{Deserialize, Serialize};
2022

2123
/// `TestCase` will be deserialized from yaml.
22-
#[derive(PartialEq, Eq, Debug, Deserialize)]
23-
struct TestCase {
24-
input: String,
25-
formatted_sql: Option<String>,
26-
error_msg: Option<String>,
27-
formatted_ast: Option<String>,
24+
#[serde_with::skip_serializing_none]
25+
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
26+
#[serde(deny_unknown_fields)]
27+
pub struct TestCase {
28+
pub input: String,
29+
pub formatted_sql: Option<String>,
30+
pub error_msg: Option<String>,
31+
pub formatted_ast: Option<String>,
32+
}
33+
34+
impl Display for TestCase {
35+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36+
f.write_str(&serde_yaml::to_string(self).unwrap())
37+
}
2838
}
2939

3040
fn run_test_case(c: TestCase) -> Result<()> {
@@ -97,7 +107,14 @@ pub fn run_all_test_files() {
97107
use walkdir::WalkDir;
98108
for entry in WalkDir::new("../tests/testdata/") {
99109
let entry = entry.unwrap();
100-
if !entry.path().is_file() {
110+
if !(entry.path().is_file()) {
111+
continue;
112+
}
113+
if !(entry
114+
.path()
115+
.extension()
116+
.map_or(false, |p| p.eq_ignore_ascii_case("yaml")))
117+
{
101118
continue;
102119
}
103120
let file_content = std::fs::read_to_string(entry.path()).unwrap();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.apply.yaml
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1+
# This file is automatically generated. See `src/sqlparser/test_runner/src/bin/apply.rs` for more information.
12
- input: ALTER USER user WITH SUPERUSER CREATEDB PASSWORD 'password'
23
formatted_sql: ALTER USER user WITH SUPERUSER CREATEDB PASSWORD 'password'
3-
44
- input: ALTER USER user RENAME TO another
55
formatted_sql: ALTER USER user RENAME TO another
6-
76
- input: ALTER SYSTEM SET a = 'abc'
87
formatted_sql: ALTER SYSTEM SET a = 'abc'
9-
108
- input: ALTER SYSTEM SET a = DEFAULT
119
formatted_sql: ALTER SYSTEM SET a = DEFAULT
Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
1+
# This file is automatically generated. See `src/sqlparser/test_runner/src/bin/apply.rs` for more information.
12
- input: CREATE TABLE t(a int[]);
23
formatted_sql: CREATE TABLE t (a INT[])
3-
44
- input: CREATE TABLE t(a int[][]);
55
formatted_sql: CREATE TABLE t (a INT[][])
6-
76
- input: CREATE TABLE t(a int[][][]);
87
formatted_sql: CREATE TABLE t (a INT[][][])
9-
108
- input: CREATE TABLE t(a int[);
11-
error_msg: |
12-
sql parser error: Expected ], found: )
13-
9+
error_msg: 'sql parser error: Expected ], found: )'
1410
- input: CREATE TABLE t(a int[[]);
15-
error_msg: |
16-
sql parser error: Expected ], found: [
17-
11+
error_msg: 'sql parser error: Expected ], found: ['
1812
- input: CREATE TABLE t(a int]);
19-
error_msg: |
20-
sql parser error: Expected ',' or ')' after column definition, found: ]
21-
13+
error_msg: 'sql parser error: Expected '','' or '')'' after column definition, found: ]'
2214
- input: SELECT foo[0] FROM foos
2315
formatted_sql: SELECT foo[0] FROM foos
24-
2516
- input: SELECT foo[0][0] FROM foos
2617
formatted_sql: SELECT foo[0][0] FROM foos
27-
2818
- input: SELECT (CAST(ARRAY[ARRAY[2, 3]] AS INT[][]))[1][2]
2919
formatted_sql: SELECT (CAST(ARRAY[ARRAY[2, 3]] AS INT[][]))[1][2]
30-
3120
- input: SELECT ARRAY[]
3221
formatted_sql: SELECT ARRAY[]

0 commit comments

Comments
 (0)