Skip to main content

veclite_sql/
lib.rs

1pub mod executor;
2pub mod parser;
3pub mod planner;
4
5pub use executor::{Executor, QueryResult};
6pub use parser::parse_sql;
7pub use planner::{LogicalPlan, PlanBuilder};
8
9pub fn execute_query(db: &mut veclite_core::VecLite, query: &str) -> Result<QueryResult, String> {
10    let plan = PlanBuilder::build_from_sql(query)?;
11    Executor::execute(db, plan)
12}
13
14#[cfg(test)]
15mod tests {
16    use super::*;
17
18    #[test]
19    fn test_parse_insert() {
20        let sql = "INSERT INTO memory VALUES ('doc_1', '[0.1, 0.2, 0.3]', '{\"tag\":\"note\"}')";
21        let ast = parse_sql(sql).unwrap();
22        let plan = PlanBuilder::build(ast[0].clone()).unwrap();
23
24        assert_eq!(
25            plan,
26            LogicalPlan::Insert {
27                table: "memory".to_string(),
28                id: "doc_1".to_string(),
29                vector: vec![0.1, 0.2, 0.3],
30                metadata: Some("{\"tag\":\"note\"}".to_string()),
31            }
32        );
33    }
34
35    #[test]
36    fn test_parse_search() {
37        let sql = "SELECT * FROM memory ORDER BY vector <=> '[0.1, 0.2, 0.3]' LIMIT 5";
38        let ast = parse_sql(sql).unwrap();
39        let plan = PlanBuilder::build(ast[0].clone()).unwrap();
40
41        assert_eq!(
42            plan,
43            LogicalPlan::Search {
44                table: "memory".to_string(),
45                query_vector: vec![0.1, 0.2, 0.3],
46                limit: 5,
47                filter: None,
48            }
49        );
50    }
51
52    #[test]
53    fn test_parse_search_array() {
54        let sql = "SELECT * FROM memory ORDER BY vector <=> ARRAY[0.1, 0.2, 0.3] LIMIT 5";
55        let ast = parse_sql(sql).unwrap();
56        let plan = PlanBuilder::build(ast[0].clone()).unwrap();
57
58        assert_eq!(
59            plan,
60            LogicalPlan::Search {
61                table: "memory".to_string(),
62                query_vector: vec![0.1, 0.2, 0.3],
63                limit: 5,
64                filter: None,
65            }
66        );
67    }
68}
69
70#[cfg(test)]
71mod integration_tests {
72    use super::*;
73    use tempfile::NamedTempFile;
74
75    #[test]
76    fn test_execute_query_integration() {
77        let temp_file = NamedTempFile::new().unwrap();
78        let path = temp_file.path();
79
80        let mut db = veclite_core::VecLite::open(path).unwrap();
81
82        // 1. Create table (should be ignored, returning empty Rows)
83        let create_res = execute_query(
84            &mut db,
85            "CREATE TABLE memory (id VARCHAR, vector VECTOR(3))",
86        )
87        .unwrap();
88        assert_eq!(create_res, QueryResult::Rows(vec![]));
89
90        // 2. Insert vectors
91        let insert_res1 = execute_query(
92            &mut db,
93            "INSERT INTO memory VALUES ('doc_1', '[0.1, 0.2, 0.3]')",
94        )
95        .unwrap();
96        assert_eq!(
97            insert_res1,
98            QueryResult::Inserted {
99                id: "doc_1".to_string()
100            }
101        );
102
103        let insert_res2 = execute_query(
104            &mut db,
105            "INSERT INTO memory VALUES ('doc_2', '[0.9, 0.8, 0.7]', '{\"tag\":\"important\"}')",
106        )
107        .unwrap();
108        assert_eq!(
109            insert_res2,
110            QueryResult::Inserted {
111                id: "doc_2".to_string()
112            }
113        );
114
115        // 3. Search
116        let search_res = execute_query(
117            &mut db,
118            "SELECT * FROM memory ORDER BY vector <=> '[0.1, 0.25, 0.3]' LIMIT 1",
119        )
120        .unwrap();
121
122        if let QueryResult::Rows(rows) = search_res {
123            assert_eq!(rows.len(), 1);
124            assert_eq!(rows[0].0, "doc_1"); // doc_1 is closer to [0.1, 0.25, 0.3]
125        } else {
126            panic!("Expected Rows result");
127        }
128    }
129}