1#![warn(missing_docs)]
33#![warn(clippy::all)]
34#![allow(clippy::module_name_repetitions)]
35
36pub mod cache;
37pub mod error;
38pub mod executor;
39pub mod explain;
40pub mod index;
41pub mod optimizer;
42#[cfg(feature = "parallel")]
45pub mod parallel;
46pub mod parser;
47
48pub use cache::{CacheConfig, QueryCache};
49pub use error::{QueryError, Result};
50pub use executor::Executor;
51pub use explain::ExplainPlan;
52pub use optimizer::{OptimizedQuery, Optimizer, OptimizerConfig};
53pub use parser::{Statement, parse_sql};
54
55pub struct QueryEngine {
57 _parser_marker: std::marker::PhantomData<()>,
59 optimizer: Optimizer,
61 executor: Executor,
63 cache: QueryCache,
65}
66
67impl QueryEngine {
68 pub fn new() -> Self {
70 Self {
71 _parser_marker: std::marker::PhantomData,
72 optimizer: Optimizer::new(),
73 executor: Executor::new(),
74 cache: QueryCache::new(CacheConfig::default()),
75 }
76 }
77
78 pub fn with_config(optimizer_config: OptimizerConfig, cache_config: CacheConfig) -> Self {
80 Self {
81 _parser_marker: std::marker::PhantomData,
82 optimizer: Optimizer::with_config(optimizer_config),
83 executor: Executor::new(),
84 cache: QueryCache::new(cache_config),
85 }
86 }
87
88 pub fn optimizer(&self) -> &Optimizer {
90 &self.optimizer
91 }
92
93 pub fn executor(&mut self) -> &mut Executor {
95 &mut self.executor
96 }
97
98 pub fn cache(&self) -> &QueryCache {
100 &self.cache
101 }
102
103 pub async fn execute_sql(&mut self, sql: &str) -> Result<Vec<executor::scan::RecordBatch>> {
105 let statement = parse_sql(sql)?;
107
108 if let Some(cached) = self.cache.get(&statement) {
110 return Ok(cached);
111 }
112
113 let optimized = self.optimizer.optimize(statement.clone())?;
115
116 let results = self.executor.execute(&optimized.statement).await?;
118
119 self.cache.put(&statement, results.clone());
121
122 Ok(results)
123 }
124
125 pub fn explain_sql(&self, sql: &str) -> Result<ExplainPlan> {
127 let statement = parse_sql(sql)?;
128 let optimized = self.optimizer.optimize(statement)?;
129 Ok(ExplainPlan::from_optimized(&optimized))
130 }
131
132 pub fn register_data_source(
134 &mut self,
135 name: String,
136 source: std::sync::Arc<dyn executor::scan::DataSource>,
137 ) {
138 self.executor.register_data_source(name, source);
139 }
140
141 pub fn clear_cache(&self) {
143 self.cache.clear();
144 }
145
146 pub fn cache_statistics(&self) -> cache::CacheStatistics {
148 self.cache.statistics()
149 }
150}
151
152impl Default for QueryEngine {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_query_engine_creation() {
164 let engine = QueryEngine::new();
165 assert!(engine.cache_statistics().hits == 0);
166 }
167
168 #[test]
169 fn test_parse_simple_query() -> Result<()> {
170 let sql = "SELECT id, name FROM users";
171 let statement = parse_sql(sql)?;
172
173 match statement {
174 Statement::Select(select) => {
175 assert_eq!(select.projection.len(), 2);
176 assert!(select.from.is_some());
177 }
178 }
179
180 Ok(())
181 }
182
183 #[test]
184 fn test_optimizer() -> Result<()> {
185 let sql = "SELECT * FROM users WHERE 1 + 1 = 2";
186 let statement = parse_sql(sql)?;
187
188 let optimizer = Optimizer::new();
189 let optimized = optimizer.optimize(statement)?;
190
191 assert!(optimized.original_cost.total() >= 0.0);
192 assert!(optimized.optimized_cost.total() >= 0.0);
193
194 Ok(())
195 }
196}