quill_sql/tests/
sql_test.rs1use crate::database::Database;
2use crate::error::QuillSQLError;
3use crate::session::SessionContext;
4use crate::storage::tuple::Tuple;
5use regex::Regex;
6use sqllogictest::{DBOutput, DefaultColumnType};
7use std::path::{Path, PathBuf};
8
9pub struct QuillSQLDB {
10 db: Database,
11 session: SessionContext,
12}
13
14impl Default for QuillSQLDB {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl QuillSQLDB {
21 pub fn new() -> Self {
22 let db = Database::new_temp().unwrap();
23 let session = SessionContext::new(db.default_isolation());
24 Self { db, session }
25 }
26}
27
28fn tuples_to_sqllogictest_string(tuples: Vec<Tuple>) -> Vec<Vec<String>> {
29 let mut output = vec![];
30 for tuple in tuples.iter() {
31 let mut row = vec![];
32 for value in tuple.data.iter() {
33 row.push(format!("{value}"));
34 }
35 output.push(row);
36 }
37 output
38}
39
40impl sqllogictest::DB for QuillSQLDB {
41 type Error = QuillSQLError;
42 type ColumnType = DefaultColumnType;
43
44 fn run(&mut self, sql: &str) -> Result<DBOutput<Self::ColumnType>, Self::Error> {
45 let is_query_sql = {
46 let lower_sql = sql.trim_start().to_ascii_lowercase();
47 lower_sql.starts_with("select") || lower_sql.starts_with("explain")
48 };
49 let tuples = self.db.run_with_session(&mut self.session, sql)?;
50 if tuples.is_empty() {
51 if is_query_sql {
52 return Ok(DBOutput::Rows {
53 types: vec![],
54 rows: vec![],
55 });
56 } else {
57 return Ok(DBOutput::StatementComplete(0));
58 }
59 }
60 let types = vec![DefaultColumnType::Any; tuples[0].schema.column_count()];
61 let rows = tuples_to_sqllogictest_string(tuples);
62 Ok(DBOutput::Rows { types, rows })
63 }
64}
65
66#[test]
67fn sqllogictest() {
68 let test_files = read_dir_recursive("src/tests/sql_example/");
69 println!("test_files: {:?}", test_files);
70
71 for file in test_files {
72 let db = QuillSQLDB::new();
73 let mut tester = sqllogictest::Runner::new(db);
74 println!(
75 "======== start to run file {} ========",
76 file.to_str().unwrap()
77 );
78 tester.run_file(file).unwrap();
79 }
80}
81
82fn read_dir_recursive<P: AsRef<Path>>(path: P) -> Vec<PathBuf> {
83 let mut dst = vec![];
84 read_dir_recursive_impl(&mut dst, path.as_ref());
85 dst
86}
87
88fn read_dir_recursive_impl(dst: &mut Vec<PathBuf>, path: &Path) {
89 let push_file = |dst: &mut Vec<PathBuf>, path: PathBuf| {
90 if Regex::new(r"/_.*\.slt")
92 .unwrap()
93 .is_match(path.to_str().unwrap())
94 {
95 println!("skip file: {:?}", path);
96 } else {
97 dst.push(path);
98 }
99 };
100
101 if path.is_dir() {
102 let entries = std::fs::read_dir(path).unwrap();
103 for entry in entries {
104 let path = entry.unwrap().path();
105
106 if path.is_dir() {
107 read_dir_recursive_impl(dst, &path);
108 } else {
109 push_file(dst, path);
110 }
111 }
112 } else {
113 push_file(dst, path.to_path_buf());
114 }
115}