Skip to main content

cqlite_core/query/
mod.rs

1//! Query execution engine for CQLite
2//!
3//! This module provides the core query execution engine that bridges between
4//! CQL parsing and the storage engine. It includes:
5//!
6//! - CQL query parsing and validation
7//! - Query planning and optimization
8//! - Query execution with storage integration
9//! - Prepared statement support
10//! - Result set management
11//! - REVOLUTIONARY SELECT parser for direct SSTable access
12
13// CQL (Cassandra Query Language) Reference:
14// https://cassandra.apache.org/doc/latest/cassandra/developing/cql/cql_singlefile.html
15//
16// This implements CQL v3.4.3+ for Apache Cassandra 5.0+
17// CQL is NOT SQL - it's a query language specifically designed for Cassandra's distributed architecture.
18
19pub mod engine;
20pub mod executor;
21pub mod m2_select_validator;
22pub mod parser;
23pub mod planner;
24pub mod prepared;
25pub mod result;
26pub mod writetime_ttl_validator;
27
28// Advanced SELECT query components
29#[cfg(feature = "state_machine")]
30pub mod select_ast;
31#[cfg(feature = "state_machine")]
32pub mod select_executor;
33#[cfg(feature = "state_machine")]
34pub mod select_integration_tests;
35#[cfg(feature = "state_machine")]
36pub mod select_optimizer;
37#[cfg(feature = "state_machine")]
38pub mod select_parser;
39
40pub use engine::{
41    AnalyzeResult, CacheStats, ExplainResult, QueryCacheEntry, QueryEngine as AdvancedQueryEngine,
42    SchemaStatus,
43};
44pub use executor::{
45    QueryExecutor, QueryResult as ExecutorQueryResult, QueryRow as ExecutorQueryRow,
46};
47pub use m2_select_validator::{M2SelectValidator, SelectValidationResult, UnsupportedFeature};
48pub use parser::QueryParser;
49pub use planner::{ExecutionStep, IndexSelection, PlanType, QueryHints, QueryPlan, QueryPlanner};
50pub use prepared::{
51    ExecutionHints, ParameterMetadata, PreparedQuery, PreparedQueryBuilder, PreparedQueryStats,
52};
53pub use result::{
54    cql_type_to_data_type, ColumnInfo, PerformanceMetrics, QueryMetadata, QueryResult,
55    QueryResultIterator, QueryRow, RowMetadata, StreamingConfig,
56};
57pub use writetime_ttl_validator::{
58    descriptors_from_table_schema, validate_all_writetime_ttl_calls, validate_writetime_ttl_call,
59    ColumnDescriptor as WriteTimeTtlColumnDescriptor, WriteTimeTtlKind,
60};
61
62// Re-export advanced SELECT components.
63// Only the symbols with known callers are re-exported here. `select_ast` is
64// intentionally not glob-re-exported: a `pub use select_ast::*` would shadow the
65// inline `ComparisonOperator`, `OrderByClause`, and `SortDirection` types below.
66// Other `select_ast` items remain reachable via `cqlite_core::query::select_ast::*`.
67#[cfg(feature = "state_machine")]
68pub use select_ast::SelectStatement;
69#[cfg(feature = "state_machine")]
70pub use select_executor::{
71    build_row_from_scan, evaluate_leaf, evaluate_predicates, LeafOutcome, SelectExecutor,
72};
73#[cfg(feature = "state_machine")]
74pub use select_optimizer::{
75    OptimizedQueryPlan, SSTableFilterOp, SSTablePredicate, SelectOptimizer,
76};
77#[cfg(feature = "state_machine")]
78pub use select_parser::{parse_select, SelectParser};
79
80use std::collections::HashMap;
81use std::sync::Arc;
82
83use crate::{
84    memory::MemoryManager, schema::SchemaManager, storage::StorageEngine, Config, Result, TableId,
85    Value,
86};
87
88/// Query execution statistics
89#[derive(Debug, Clone, Default)]
90pub struct QueryStats {
91    /// Total queries executed
92    pub total_queries: u64,
93    /// Queries that resulted in errors
94    pub error_queries: u64,
95    /// Average execution time in microseconds
96    pub avg_execution_time_us: u64,
97    /// Cache hit ratio
98    pub cache_hit_ratio: f64,
99    /// Total rows affected by write operations
100    pub rows_affected: u64,
101}
102
103/// Legacy query engine wrapper for backward compatibility.
104///
105/// Delegates to [`AdvancedQueryEngine`] (re-exported from [`engine`]); kept as a
106/// stable public surface used by [`crate::Database`] and the CLI's REPL.
107#[derive(Debug)]
108pub struct QueryEngine {
109    advanced_engine: AdvancedQueryEngine,
110}
111
112impl QueryEngine {
113    /// Create a new query engine.
114    pub fn new(
115        storage: Arc<StorageEngine>,
116        schema: Arc<SchemaManager>,
117        memory: Arc<MemoryManager>,
118        config: &Config,
119    ) -> Result<Self> {
120        Ok(Self {
121            advanced_engine: AdvancedQueryEngine::new(storage, schema, memory, config)?,
122        })
123    }
124
125    /// Execute a CQL query
126    pub async fn execute(&self, cql: &str) -> Result<QueryResult> {
127        self.advanced_engine.execute(cql).await
128    }
129
130    /// Prepare a query for repeated execution
131    pub async fn prepare(&self, cql: &str) -> Result<Arc<PreparedQuery>> {
132        self.advanced_engine.prepare(cql).await
133    }
134
135    /// Get query statistics
136    pub fn stats(&self) -> QueryStats {
137        self.advanced_engine.stats()
138    }
139
140    /// Clear prepared statement cache
141    pub fn clear_cache(&self) {
142        self.advanced_engine.clear_prepared_cache();
143    }
144
145    /// Explain a query
146    pub async fn explain(&self, cql: &str) -> Result<ExplainResult> {
147        self.advanced_engine.explain(cql).await
148    }
149
150    /// Analyze query performance
151    pub async fn analyze(&self, cql: &str) -> Result<AnalyzeResult> {
152        self.advanced_engine.analyze(cql).await
153    }
154
155    /// Execute a CQL query with streaming results (Issue #280)
156    ///
157    /// Returns a `QueryResultIterator` that yields rows incrementally via a bounded
158    /// channel, enabling memory-efficient processing of large result sets.
159    #[cfg(feature = "state_machine")]
160    pub async fn execute_streaming(
161        &self,
162        cql: &str,
163        config: StreamingConfig,
164    ) -> Result<QueryResultIterator> {
165        self.advanced_engine.execute_streaming(cql, config).await
166    }
167
168    /// Get cache statistics
169    pub fn cache_stats(&self) -> CacheStats {
170        self.advanced_engine.cache_stats()
171    }
172
173    /// Check if schema is available for a table
174    pub async fn has_schema_for_table(&self, table: &str) -> bool {
175        self.advanced_engine.has_schema_for_table(table).await
176    }
177
178    /// Get detailed schema status for debugging
179    pub async fn schema_status(&self, table: &str) -> SchemaStatus {
180        self.advanced_engine.schema_status(table).await
181    }
182}
183
184/// CQL query types
185#[derive(Debug, Clone, PartialEq)]
186pub enum QueryType {
187    /// SELECT statement
188    Select,
189    /// INSERT statement
190    Insert,
191    /// UPDATE statement
192    Update,
193    /// DELETE statement
194    Delete,
195    /// CREATE TABLE statement
196    CreateTable,
197    /// DROP TABLE statement
198    DropTable,
199    /// CREATE INDEX statement
200    CreateIndex,
201    /// DROP INDEX statement
202    DropIndex,
203    /// DESCRIBE statement
204    Describe,
205    /// USE statement (for keyspace)
206    Use,
207}
208
209/// Parsed CQL query representation
210#[derive(Debug, Clone)]
211pub struct ParsedQuery {
212    /// Query type
213    pub query_type: QueryType,
214    /// Target table (if applicable)
215    pub table: Option<TableId>,
216    /// Column selections (for SELECT)
217    pub columns: Vec<String>,
218    /// WHERE clause conditions
219    pub where_clause: Option<WhereClause>,
220    /// VALUES for INSERT
221    pub values: Vec<Value>,
222    /// SET clause for UPDATE
223    pub set_clause: HashMap<String, Value>,
224    /// ORDER BY clause
225    pub order_by: Vec<OrderByClause>,
226    /// LIMIT clause
227    pub limit: Option<usize>,
228    /// Original CQL text
229    pub cql: String,
230}
231
232/// WHERE clause representation
233#[derive(Debug, Clone)]
234pub struct WhereClause {
235    /// Conditions in the WHERE clause
236    pub conditions: Vec<Condition>,
237}
238
239/// Individual condition in WHERE clause
240#[derive(Debug, Clone)]
241pub struct Condition {
242    /// Column name
243    pub column: String,
244    /// Comparison operator
245    pub operator: ComparisonOperator,
246    /// Value to compare against
247    pub value: Value,
248}
249
250/// Comparison operators
251#[derive(Debug, Clone, PartialEq)]
252pub enum ComparisonOperator {
253    /// Equal (=)
254    Equal,
255    /// Not equal (<>)
256    NotEqual,
257    /// Less than (<)
258    LessThan,
259    /// Less than or equal (<=)
260    LessThanOrEqual,
261    /// Greater than (>)
262    GreaterThan,
263    /// Greater than or equal (>=)
264    GreaterThanOrEqual,
265    /// IN operator
266    In,
267    /// NOT IN operator
268    NotIn,
269    /// LIKE operator
270    Like,
271    /// NOT LIKE operator
272    NotLike,
273}
274
275/// ORDER BY clause
276#[derive(Debug, Clone)]
277pub struct OrderByClause {
278    /// Column name
279    pub column: String,
280    /// Sort direction
281    pub direction: SortDirection,
282}
283
284/// Sort direction
285#[derive(Debug, Clone, PartialEq)]
286pub enum SortDirection {
287    /// Ascending
288    Asc,
289    /// Descending
290    Desc,
291}
292
293#[cfg(all(test, feature = "state_machine"))]
294mod tests {
295    use super::*;
296    use crate::platform::Platform;
297    use tempfile::TempDir;
298
299    #[tokio::test]
300    async fn test_query_engine_creation() {
301        let temp_dir = TempDir::new().unwrap();
302        let config = Config::default();
303        let platform = Arc::new(Platform::new(&config).await.unwrap());
304
305        let storage = Arc::new(
306            StorageEngine::open(
307                temp_dir.path(),
308                &config,
309                platform,
310                #[cfg(feature = "state_machine")]
311                None,
312            )
313            .await
314            .unwrap(),
315        );
316        let schema = Arc::new(SchemaManager::new(temp_dir.path()).await.unwrap());
317        let memory = Arc::new(MemoryManager::new(&config).unwrap());
318
319        let query_engine = QueryEngine::new(storage, schema, memory, &config).unwrap();
320
321        assert_eq!(query_engine.stats().total_queries, 0);
322    }
323}