sql_cli/data/
query_executor.rs

1use anyhow::Result;
2
3use crate::api_client::QueryResponse;
4use crate::csv_datasource::CsvApiClient;
5use crate::data::datatable::DataTable;
6
7/// Trait for executing SQL queries against data sources
8pub trait QueryExecutor {
9    /// Execute a SQL query and return results
10    fn execute(&self, query: &str) -> Result<QueryResponse>;
11
12    /// Check if this executor can handle the given query
13    fn can_handle(&self, query: &str) -> bool;
14
15    /// Get row count without executing a query
16    fn row_count(&self) -> usize;
17
18    /// Get column count without executing a query
19    fn column_count(&self) -> usize;
20}
21
22/// Direct DataTable query executor (for simple SELECT * queries)
23pub struct DataTableExecutor {
24    datatable: std::sync::Arc<DataTable>,
25    table_name: String,
26}
27
28impl DataTableExecutor {
29    pub fn new(datatable: std::sync::Arc<DataTable>, table_name: String) -> Self {
30        Self {
31            datatable,
32            table_name,
33        }
34    }
35}
36
37impl QueryExecutor for DataTableExecutor {
38    fn execute(&self, query: &str) -> Result<QueryResponse> {
39        // For now, only handle SELECT * FROM table
40        let upper_query = query.trim().to_uppercase();
41        if !self.can_handle(query) {
42            return Err(anyhow::anyhow!(
43                "DataTableExecutor can only handle simple SELECT * queries"
44            ));
45        }
46
47        // Return a response that references the DataTable directly
48        // In the future, this will return a DataView
49        Ok(QueryResponse {
50            data: vec![], // Empty for now - TUI will use DataTable directly
51            count: self.datatable.row_count(),
52            query: crate::api_client::QueryInfo {
53                select: vec!["*".to_string()],
54                where_clause: None,
55                order_by: None,
56            },
57            source: Some("datatable".to_string()),
58            table: Some(self.table_name.clone()),
59            cached: Some(false),
60        })
61    }
62
63    fn can_handle(&self, query: &str) -> bool {
64        let upper_query = query.trim().to_uppercase();
65        upper_query.starts_with("SELECT *")
66            && !upper_query.contains(" WHERE ")
67            && !upper_query.contains(" ORDER BY ")
68            && !upper_query.contains(" LIMIT ")
69            && !upper_query.contains(" GROUP BY ")
70    }
71
72    fn row_count(&self) -> usize {
73        self.datatable.row_count()
74    }
75
76    fn column_count(&self) -> usize {
77        self.datatable.column_count()
78    }
79}
80
81/// CSV API Client query executor (for complex queries with WHERE, ORDER BY, etc.)
82pub struct CsvClientExecutor {
83    csv_client: CsvApiClient,
84    table_name: String,
85}
86
87impl CsvClientExecutor {
88    pub fn new(csv_client: CsvApiClient, table_name: String) -> Self {
89        Self {
90            csv_client,
91            table_name,
92        }
93    }
94
95    pub fn from_csv(path: &str, table_name: &str, case_insensitive: bool) -> Result<Self> {
96        let mut csv_client = CsvApiClient::new();
97        csv_client.set_case_insensitive(case_insensitive);
98        csv_client.load_csv(path, table_name)?;
99        Ok(Self {
100            csv_client,
101            table_name: table_name.to_string(),
102        })
103    }
104
105    pub fn from_json(path: &str, table_name: &str, case_insensitive: bool) -> Result<Self> {
106        let mut csv_client = CsvApiClient::new();
107        csv_client.set_case_insensitive(case_insensitive);
108        csv_client.load_json(path, table_name)?;
109        Ok(Self {
110            csv_client,
111            table_name: table_name.to_string(),
112        })
113    }
114}
115
116impl QueryExecutor for CsvClientExecutor {
117    fn execute(&self, query: &str) -> Result<QueryResponse> {
118        let result = self.csv_client.query_csv(query)?;
119        Ok(QueryResponse {
120            data: result.data,
121            count: result.count,
122            query: crate::api_client::QueryInfo {
123                select: result.query.select,
124                where_clause: result.query.where_clause,
125                order_by: result.query.order_by,
126            },
127            source: Some("csv_client".to_string()),
128            table: Some(self.table_name.clone()),
129            cached: Some(false),
130        })
131    }
132
133    fn can_handle(&self, _query: &str) -> bool {
134        // CSV client can handle all queries
135        true
136    }
137
138    fn row_count(&self) -> usize {
139        // This is approximate - CSV client doesn't expose exact count
140        0
141    }
142
143    fn column_count(&self) -> usize {
144        // This is approximate - CSV client doesn't expose exact count
145        0
146    }
147}
148
149/// Composite query executor that tries multiple executors in order
150pub struct CompositeQueryExecutor {
151    executors: Vec<Box<dyn QueryExecutor>>,
152}
153
154impl CompositeQueryExecutor {
155    pub fn new() -> Self {
156        Self {
157            executors: Vec::new(),
158        }
159    }
160
161    pub fn add_executor(&mut self, executor: Box<dyn QueryExecutor>) {
162        self.executors.push(executor);
163    }
164}
165
166impl QueryExecutor for CompositeQueryExecutor {
167    fn execute(&self, query: &str) -> Result<QueryResponse> {
168        // Try each executor in order
169        for executor in &self.executors {
170            if executor.can_handle(query) {
171                return executor.execute(query);
172            }
173        }
174        Err(anyhow::anyhow!("No executor can handle query: {}", query))
175    }
176
177    fn can_handle(&self, query: &str) -> bool {
178        self.executors.iter().any(|e| e.can_handle(query))
179    }
180
181    fn row_count(&self) -> usize {
182        // Return the row count from the first executor
183        self.executors.first().map(|e| e.row_count()).unwrap_or(0)
184    }
185
186    fn column_count(&self) -> usize {
187        // Return the column count from the first executor
188        self.executors
189            .first()
190            .map(|e| e.column_count())
191            .unwrap_or(0)
192    }
193}