1use super::{
18 planner::{ExecutionStep, IndexSelection, ParallelizationInfo, QueryPlan, StepType},
19 ComparisonOperator, Condition,
20};
21use crate::{
22 schema::SchemaManager, storage::StorageEngine, Config, Error, Result, RowKey, TableId, Value,
23};
24use crossbeam::channel;
25use std::cmp::Ordering;
26use std::collections::HashMap;
27use std::sync::Arc;
28use std::time::Instant;
29
30pub use super::result::{QueryResult, QueryRow};
32
33const DEFAULT_PARALLEL_WORKERS: usize = 4;
35
36static DEFAULT_PARALLELIZATION: ParallelizationInfo = ParallelizationInfo {
39 can_parallelize: true,
40 suggested_threads: DEFAULT_PARALLEL_WORKERS,
41 partition_key: None,
42};
43
44#[derive(Debug, Clone)]
46pub struct QueryExecutor {
47 storage: Arc<StorageEngine>,
49 _schema: Arc<SchemaManager>,
51 _config: Config,
53}
54
55impl QueryExecutor {
56 pub fn new(storage: Arc<StorageEngine>, schema: Arc<SchemaManager>, config: &Config) -> Self {
58 Self {
59 storage,
60 _schema: schema,
61 _config: config.clone(),
62 }
63 }
64
65 pub async fn execute(&self, plan: &QueryPlan) -> Result<QueryResult> {
67 let start_time = Instant::now();
68
69 let has_insert_step = plan
71 .steps
72 .iter()
73 .any(|step| matches!(step.step_type, StepType::Insert));
74 let is_create_table =
75 plan.steps.is_empty() && plan.table.is_some() && plan.estimated_rows == 0;
76
77 #[cfg(debug_assertions)]
78 eprintln!(
79 "DEBUG: Plan steps: {:?}, has_insert_step: {}, is_create_table: {}",
80 plan.steps.iter().map(|s| &s.step_type).collect::<Vec<_>>(),
81 has_insert_step,
82 is_create_table
83 );
84
85 let result = match plan.plan_type {
86 super::planner::PlanType::PointLookup => self.execute_point_lookup(plan).await,
87 super::planner::PlanType::IndexScan => self.execute_index_scan(plan).await,
88 super::planner::PlanType::RangeScan => self.execute_range_scan(plan).await,
89 super::planner::PlanType::TableScan if has_insert_step => {
90 #[cfg(feature = "experimental")]
91 {
92 self.execute_insert_operation(plan).await
93 }
94 #[cfg(not(feature = "experimental"))]
95 {
96 Err(Error::UnsupportedFormat(
97 "INSERT operations require the 'experimental' feature. \
98 Add 'experimental' to your Cargo.toml features."
99 .to_string(),
100 ))
101 }
102 }
103 super::planner::PlanType::TableScan if is_create_table => {
104 self.execute_create_table_operation(plan).await
105 }
106 super::planner::PlanType::TableScan => self.execute_table_scan(plan).await,
107 super::planner::PlanType::Join => self.execute_join(plan).await,
108 super::planner::PlanType::Aggregation => self.execute_aggregation(plan).await,
109 super::planner::PlanType::Subquery => self.execute_subquery(plan).await,
110 };
111
112 let mut query_result = result?;
113 let elapsed_ms = start_time.elapsed().as_millis() as u64;
114
115 #[cfg(debug_assertions)]
116 eprintln!(
117 "DEBUG: Final result before metadata update - rows_affected: {}",
118 query_result.rows_affected
119 );
120
121 query_result.execution_time_ms = elapsed_ms;
122 query_result.metadata.plan_info = Some(super::result::PlanInfo {
123 plan_type: format!("{:?}", plan.plan_type),
124 estimated_cost: plan.estimated_cost,
125 actual_cost: elapsed_ms as f64,
126 indexes_used: Self::indexes_used_for(plan),
128 steps: plan
129 .steps
130 .iter()
131 .map(|s| format!("{:?}", s.step_type))
132 .collect(),
133 parallelization: plan
134 .steps
135 .iter()
136 .find(|s| s.parallelization.can_parallelize)
137 .map(|s| super::result::ParallelizationInfo {
138 threads_used: s.parallelization.suggested_threads,
139 effective: true,
140 partitions: Vec::new(),
141 }),
142 });
143 Ok(query_result)
144 }
145
146 fn indexes_used_for(plan: &QueryPlan) -> Vec<String> {
177 use super::planner::{IndexType, PlanType, StepType};
178
179 let scan = || vec!["scan".to_string()];
181
182 let has_insert_step = plan
187 .steps
188 .iter()
189 .any(|step| matches!(step.step_type, StepType::Insert));
190 let is_create_table =
191 plan.steps.is_empty() && plan.table.is_some() && plan.estimated_rows == 0;
192 if matches!(plan.plan_type, PlanType::TableScan) && (has_insert_step || is_create_table) {
193 return Vec::new();
194 }
195
196 match plan.plan_type {
197 PlanType::PointLookup => match plan.selected_indexes.first() {
199 Some(idx) => vec![idx.index_name.clone()],
200 None => vec!["PRIMARY".to_string()],
203 },
204 PlanType::IndexScan => match plan.selected_indexes.first() {
207 Some(idx) => match idx.index_type {
208 IndexType::Primary | IndexType::BloomFilter => {
209 vec![idx.index_name.clone()]
210 }
211 IndexType::Secondary | IndexType::Composite => scan(),
212 },
213 None => scan(),
214 },
215 PlanType::TableScan | PlanType::RangeScan => scan(),
217 PlanType::Join | PlanType::Aggregation | PlanType::Subquery => Vec::new(),
220 }
221 }
222
223 fn require_table<'a>(&self, plan: &'a QueryPlan) -> Result<&'a TableId> {
225 plan.table
226 .as_ref()
227 .ok_or_else(|| Error::query_execution("Missing table in plan"))
228 }
229
230 fn find_condition<'a>(steps: &'a [ExecutionStep], column: &str) -> Option<&'a Condition> {
232 steps
233 .iter()
234 .flat_map(|s| s.conditions.iter())
235 .find(|c| c.column == column)
236 }
237
238 fn scan_pairs_to_rows(&self, pairs: Vec<(RowKey, Value)>) -> Result<Vec<QueryRow>> {
240 let mut rows = Vec::with_capacity(pairs.len());
241 for (row_key, row_data) in pairs {
242 rows.push(self.storage_data_to_query_row(row_data, &row_key)?);
243 }
244 Ok(rows)
245 }
246
247 async fn full_scan_rows(&self, table: &TableId) -> Result<Vec<QueryRow>> {
249 let scan_results = self.storage.scan(table, None, None, None, None).await?;
250 self.scan_pairs_to_rows(scan_results)
251 }
252
253 async fn point_lookup_rows(
255 &self,
256 table: &TableId,
257 condition: &Condition,
258 ) -> Result<Vec<QueryRow>> {
259 let row_key = self.condition_to_row_key(condition)?;
260 match self.storage.get(table, &row_key).await? {
261 Some(row_data) => Ok(vec![self.storage_data_to_query_row(row_data, &row_key)?]),
262 None => Ok(Vec::new()),
263 }
264 }
265
266 fn make_result(rows: Vec<QueryRow>) -> QueryResult {
268 QueryResult::with_rows(rows)
269 }
270
271 async fn execute_point_lookup(&self, plan: &QueryPlan) -> Result<QueryResult> {
275 let table = self.require_table(plan)?;
276
277 let lookup_condition = plan
279 .steps
280 .iter()
281 .find_map(|step| step.conditions.first())
282 .ok_or_else(|| Error::query_execution("No lookup condition found"))?;
283
284 let row_key = self.condition_to_row_key(lookup_condition)?;
285
286 #[cfg(debug_assertions)]
287 eprintln!(
288 "DEBUG: SELECT point lookup using row key: {:?}",
289 std::str::from_utf8(row_key.as_bytes()).unwrap_or("<invalid-utf8>")
290 );
291
292 let mut rows = Vec::new();
293 if let Some(row_data) = self.storage.get(table, &row_key).await? {
294 rows.push(self.storage_data_to_query_row(row_data, &row_key)?);
295 }
296
297 Ok(Self::make_result(rows))
298 }
299
300 async fn execute_index_scan(&self, plan: &QueryPlan) -> Result<QueryResult> {
302 let table = self.require_table(plan)?;
303
304 let index_selection = plan
305 .selected_indexes
306 .first()
307 .ok_or_else(|| Error::query_execution("No index selected"))?;
308
309 let mut rows = match index_selection.index_type {
310 super::planner::IndexType::Secondary => {
311 self.execute_secondary_index_scan(table, index_selection, &plan.steps)
312 .await?
313 }
314 super::planner::IndexType::BloomFilter => {
315 self.execute_bloom_filter_scan(table, index_selection, &plan.steps)
316 .await?
317 }
318 super::planner::IndexType::Primary => {
319 self.execute_primary_index_scan(table, index_selection, &plan.steps)
320 .await?
321 }
322 super::planner::IndexType::Composite => {
323 self.execute_composite_index_scan(table, index_selection, &plan.steps)
324 .await?
325 }
326 };
327
328 rows = self.apply_execution_steps(rows, &plan.steps).await?;
329 Ok(Self::make_result(rows))
330 }
331
332 async fn execute_range_scan(&self, plan: &QueryPlan) -> Result<QueryResult> {
334 let table = self.require_table(plan)?;
335
336 let mut rows = self.full_scan_rows(table).await?;
339 rows = self.apply_execution_steps(rows, &plan.steps).await?;
340 Ok(Self::make_result(rows))
341 }
342
343 async fn execute_table_scan(&self, plan: &QueryPlan) -> Result<QueryResult> {
345 let table = self.require_table(plan)?;
346
347 #[cfg(debug_assertions)]
348 log::debug!("executor: Scanning for table: {:?}", table.name());
349
350 let can_parallelize = plan
351 .steps
352 .iter()
353 .any(|step| step.parallelization.can_parallelize);
354
355 let mut rows = if can_parallelize {
356 self.execute_parallel_table_scan(table, &plan.steps).await?
357 } else {
358 self.full_scan_rows(table).await?
359 };
360
361 rows = self.apply_execution_steps(rows, &plan.steps).await?;
362 Ok(Self::make_result(rows))
363 }
364
365 async fn execute_join(&self, _plan: &QueryPlan) -> Result<QueryResult> {
367 Ok(QueryResult::new())
368 }
369
370 async fn execute_aggregation(&self, _plan: &QueryPlan) -> Result<QueryResult> {
372 Ok(QueryResult::new())
373 }
374
375 async fn execute_subquery(&self, _plan: &QueryPlan) -> Result<QueryResult> {
377 Ok(QueryResult::new())
378 }
379
380 async fn execute_secondary_index_scan(
385 &self,
386 table: &TableId,
387 index_selection: &IndexSelection,
388 steps: &[ExecutionStep],
389 ) -> Result<Vec<QueryRow>> {
390 Self::find_condition(steps, &index_selection.columns[0])
392 .ok_or_else(|| Error::query_execution("No condition found for index"))?;
393 self.full_scan_rows(table).await
394 }
395
396 async fn execute_bloom_filter_scan(
398 &self,
399 table: &TableId,
400 index_selection: &IndexSelection,
401 steps: &[ExecutionStep],
402 ) -> Result<Vec<QueryRow>> {
403 let condition = Self::find_condition(steps, &index_selection.columns[0])
404 .ok_or_else(|| Error::query_execution("No condition found for bloom filter"))?;
405 self.point_lookup_rows(table, condition).await
406 }
407
408 async fn execute_primary_index_scan(
410 &self,
411 table: &TableId,
412 index_selection: &IndexSelection,
413 steps: &[ExecutionStep],
414 ) -> Result<Vec<QueryRow>> {
415 let condition = Self::find_condition(steps, &index_selection.columns[0])
416 .ok_or_else(|| Error::query_execution("No condition found for primary key"))?;
417 self.point_lookup_rows(table, condition).await
418 }
419
420 async fn execute_composite_index_scan(
423 &self,
424 table: &TableId,
425 _index_selection: &IndexSelection,
426 _steps: &[ExecutionStep],
427 ) -> Result<Vec<QueryRow>> {
428 self.full_scan_rows(table).await
429 }
430
431 async fn execute_parallel_table_scan(
440 &self,
441 table: &TableId,
442 steps: &[ExecutionStep],
443 ) -> Result<Vec<QueryRow>> {
444 let parallelization = steps
445 .iter()
446 .find(|step| step.parallelization.can_parallelize)
447 .map(|step| &step.parallelization)
448 .unwrap_or(&DEFAULT_PARALLELIZATION);
449
450 let thread_count = parallelization.suggested_threads;
451 let (tx, rx) = channel::unbounded();
452
453 let mut handles = Vec::with_capacity(thread_count);
454 for worker_id in 0..thread_count {
455 let storage = self.storage.clone();
456 let table = table.clone();
457 let tx = tx.clone();
458
459 handles.push(tokio::spawn(async move {
460 match storage.scan(&table, None, None, None, None).await {
461 Ok(results) => {
462 for pair in results {
463 if tx.send(pair).is_err() {
465 break;
466 }
467 }
468 }
469 Err(e) => log::error!("Worker {} error: {:?}", worker_id, e),
470 }
471 }));
472 }
473
474 drop(tx);
476
477 let mut rows = Vec::new();
478 while let Ok((row_key, row_data)) = rx.recv() {
479 rows.push(self.storage_data_to_query_row(row_data, &row_key)?);
480 }
481
482 for handle in handles {
483 let _ = handle.await;
484 }
485
486 Ok(rows)
487 }
488
489 async fn apply_execution_steps(
497 &self,
498 mut rows: Vec<QueryRow>,
499 steps: &[ExecutionStep],
500 ) -> Result<Vec<QueryRow>> {
501 for step in steps {
502 match step.step_type {
503 StepType::Filter => rows = self.apply_filter_step(rows, step)?,
504 StepType::Sort => rows = self.apply_sort_step(rows, step),
505 StepType::Project => rows = self.apply_project_step(rows, step),
506 StepType::Limit
508 | StepType::Aggregate
509 | StepType::Join
510 | StepType::Scan
511 | StepType::Insert => {}
512 }
513 }
514 Ok(rows)
515 }
516
517 fn apply_filter_step(
519 &self,
520 rows: Vec<QueryRow>,
521 step: &ExecutionStep,
522 ) -> Result<Vec<QueryRow>> {
523 let mut filtered_rows = Vec::with_capacity(rows.len());
524 for row in rows {
525 let mut matches = true;
526 for condition in &step.conditions {
527 if !self.evaluate_condition(&row, condition)? {
528 matches = false;
529 break;
530 }
531 }
532 if matches {
533 filtered_rows.push(row);
534 }
535 }
536 Ok(filtered_rows)
537 }
538
539 fn apply_sort_step(&self, mut rows: Vec<QueryRow>, step: &ExecutionStep) -> Vec<QueryRow> {
541 let Some(sort_column) = step.columns.first() else {
542 return rows;
543 };
544
545 rows.sort_by(|a, b| {
546 let a_val = a.values.get(sort_column).unwrap_or(&Value::Null);
547 let b_val = b.values.get(sort_column).unwrap_or(&Value::Null);
548 self.compare_values(a_val, b_val).unwrap_or(Ordering::Equal)
549 });
550 rows
551 }
552
553 fn apply_project_step(&self, rows: Vec<QueryRow>, step: &ExecutionStep) -> Vec<QueryRow> {
555 rows.into_iter()
556 .map(|row| {
557 let mut projected_values = HashMap::with_capacity(step.columns.len());
558 for column in &step.columns {
559 if let Some(value) = row.values.get(column) {
560 projected_values.insert(column.clone(), value.clone());
561 }
562 }
563 QueryRow::with_values(row.key, projected_values)
564 })
565 .collect()
566 }
567
568 fn evaluate_condition(&self, row: &QueryRow, condition: &Condition) -> Result<bool> {
572 let row_value = row.values.get(&condition.column).unwrap_or(&Value::Null);
573
574 match condition.operator {
575 ComparisonOperator::Equal => Ok(row_value == &condition.value),
576 ComparisonOperator::NotEqual => Ok(row_value != &condition.value),
577 ComparisonOperator::LessThan => Ok(matches!(
578 self.compare_values(row_value, &condition.value)?,
579 Ordering::Less
580 )),
581 ComparisonOperator::LessThanOrEqual => Ok(matches!(
582 self.compare_values(row_value, &condition.value)?,
583 Ordering::Less | Ordering::Equal
584 )),
585 ComparisonOperator::GreaterThan => Ok(matches!(
586 self.compare_values(row_value, &condition.value)?,
587 Ordering::Greater
588 )),
589 ComparisonOperator::GreaterThanOrEqual => Ok(matches!(
590 self.compare_values(row_value, &condition.value)?,
591 Ordering::Greater | Ordering::Equal
592 )),
593 ComparisonOperator::In => Ok(row_value == &condition.value),
595 ComparisonOperator::NotIn => Ok(row_value != &condition.value),
596 ComparisonOperator::Like => match (row_value, &condition.value) {
597 (Value::Text(row_text), Value::Text(pattern)) => Ok(row_text.contains(pattern)),
598 _ => Ok(false),
599 },
600 ComparisonOperator::NotLike => match (row_value, &condition.value) {
601 (Value::Text(row_text), Value::Text(pattern)) => Ok(!row_text.contains(pattern)),
602 _ => Ok(true),
603 },
604 }
605 }
606
607 fn compare_values(&self, a: &Value, b: &Value) -> Result<Ordering> {
609 match (a, b) {
610 (Value::Integer(a), Value::Integer(b)) => Ok(a.cmp(b)),
611 (Value::Float(a), Value::Float(b)) => Ok(a.partial_cmp(b).unwrap_or(Ordering::Equal)),
612 (Value::Text(a), Value::Text(b)) => Ok(a.cmp(b)),
613 (Value::Boolean(a), Value::Boolean(b)) => Ok(a.cmp(b)),
614 (Value::Uuid(a), Value::Uuid(b)) => Ok(a.cmp(b)),
617 (Value::Null, Value::Null) => Ok(Ordering::Equal),
618 (Value::Null, _) => Ok(Ordering::Less),
619 (_, Value::Null) => Ok(Ordering::Greater),
620 _ => Err(Error::query_execution(
621 "Cannot compare values of different types",
622 )),
623 }
624 }
625
626 fn value_to_row_key(&self, value: &Value) -> Result<RowKey> {
638 match value {
639 Value::Integer(i) => Ok(RowKey::new(i.to_be_bytes().to_vec())),
640 Value::Text(s) => Ok(RowKey::new(s.as_bytes().to_vec())),
641 Value::Float(f) => Ok(RowKey::new(f.to_be_bytes().to_vec())),
642 Value::Boolean(b) => Ok(RowKey::new(vec![u8::from(*b)])),
643 Value::Null => Ok(RowKey::new(vec![0])),
644 Value::Uuid(bytes) => Ok(RowKey::new(bytes.to_vec())),
647 Value::BigInt(i) => Ok(RowKey::new(i.to_be_bytes().to_vec())),
648 Value::Tuple(components) => {
652 let mut result = Vec::new();
653 for component in components {
654 let raw = self.value_to_raw_pk_bytes(component)?;
655 let len = raw.len();
656 if len > u16::MAX as usize {
657 return Err(Error::query_execution(
658 "Composite partition key component too large",
659 ));
660 }
661 result.extend_from_slice(&(len as u16).to_be_bytes());
662 result.extend_from_slice(&raw);
663 result.push(0x00);
664 }
665 Ok(RowKey::new(result))
666 }
667 _ => Err(Error::query_execution("Cannot convert value to row key")),
668 }
669 }
670
671 fn value_to_raw_pk_bytes(&self, value: &Value) -> Result<Vec<u8>> {
675 match value {
676 Value::Integer(i) => Ok(i.to_be_bytes().to_vec()),
677 Value::Text(s) => Ok(s.as_bytes().to_vec()),
678 Value::Float(f) => Ok(f.to_be_bytes().to_vec()),
679 Value::Boolean(b) => Ok(vec![u8::from(*b)]),
680 Value::Null => Ok(Vec::new()),
681 Value::Uuid(bytes) => Ok(bytes.to_vec()),
682 Value::BigInt(i) => Ok(i.to_be_bytes().to_vec()),
683 _ => Err(Error::query_execution(
684 "Cannot serialize value as partition key component",
685 )),
686 }
687 }
688
689 fn condition_to_row_key(&self, condition: &Condition) -> Result<RowKey> {
691 if condition.column == "id" {
693 if let Value::Integer(id) = &condition.value {
694 return Ok(RowKey::new(format!("user_key_{}", id).into_bytes()));
695 }
696 }
697 self.value_to_row_key(&condition.value)
698 }
699
700 fn storage_data_to_query_row(&self, data: Value, key: &RowKey) -> Result<QueryRow> {
702 let mut values = HashMap::new();
703
704 match data {
706 Value::Map(map) => {
707 for (map_key, map_value) in map {
708 if let Value::Text(column_name) = map_key {
709 values.insert(column_name, map_value);
710 }
711 }
712 }
713 other => {
714 values.insert("data".to_string(), other);
715 }
716 }
717
718 if values.is_empty() {
720 values.insert("id".to_string(), Value::Text(format!("{:?}", key)));
721 }
722
723 Ok(QueryRow::with_values(key.clone(), values))
724 }
725
726 #[cfg(feature = "experimental")]
730 async fn execute_insert_operation(&self, plan: &QueryPlan) -> Result<QueryResult> {
731 let table_id = self
732 .require_table(plan)
733 .map_err(|_| Error::query_execution("No table specified in INSERT plan"))?;
734
735 let mut inserted_count: u64 = 0;
736
737 for step in &plan.steps {
738 if !matches!(step.step_type, StepType::Insert) {
739 continue;
740 }
741
742 #[cfg(debug_assertions)]
743 eprintln!("DEBUG: INSERT step conditions: {:?}", step.conditions);
744
745 let mut key_value = format!("test_key_{}", inserted_count);
748 for condition in &step.conditions {
749 if condition.column == "id" {
750 if let Value::Integer(id) = &condition.value {
751 key_value = format!("user_key_{}", id);
752 break;
753 }
754 }
755 }
756
757 #[cfg(debug_assertions)]
758 eprintln!("DEBUG: Using row key: {}", key_value);
759
760 let row_key = RowKey::new(key_value.into_bytes());
761
762 let mut value_map: HashMap<String, Value> = step
765 .conditions
766 .iter()
767 .map(|c| (c.column.clone(), c.value.clone()))
768 .collect();
769
770 if value_map.is_empty() {
771 value_map.insert("id".to_string(), Value::Integer(inserted_count as i32 + 1));
772 value_map.insert(
773 "name".to_string(),
774 Value::Text(format!("TestUser{}", inserted_count + 1)),
775 );
776 }
777
778 let row_value = map_to_value(value_map);
779
780 self.storage.put(table_id, row_key, row_value).await?;
781 inserted_count += 1;
782
783 #[cfg(debug_assertions)]
784 eprintln!(
785 "DEBUG: execute_insert_operation - stored row {} in table {}",
786 inserted_count, table_id
787 );
788 }
789
790 if inserted_count == 0 {
793 let row_key = RowKey::new(b"default_test_key".to_vec());
794 let mut value_map = HashMap::new();
795 value_map.insert("id".to_string(), Value::Integer(1));
796 value_map.insert("name".to_string(), Value::Text("DefaultUser".to_string()));
797
798 self.storage
799 .put(table_id, row_key, map_to_value(value_map))
800 .await?;
801 inserted_count = 1;
802 }
803
804 #[cfg(debug_assertions)]
805 eprintln!(
806 "DEBUG: execute_insert_operation called, returning rows_affected: {}",
807 inserted_count
808 );
809
810 Ok(QueryResult {
811 rows: vec![],
812 rows_affected: inserted_count,
813 execution_time_ms: 0,
814 metadata: super::result::QueryMetadata::default(),
815 })
816 }
817
818 async fn execute_create_table_operation(&self, _plan: &QueryPlan) -> Result<QueryResult> {
820 Ok(QueryResult {
821 rows: vec![],
822 rows_affected: 0,
823 execution_time_ms: 0,
824 metadata: super::result::QueryMetadata::default(),
825 })
826 }
827}
828
829#[cfg(feature = "experimental")]
831fn map_to_value(map: HashMap<String, Value>) -> Value {
832 Value::Map(map.into_iter().map(|(k, v)| (Value::Text(k), v)).collect())
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838 use crate::Config;
839 use std::sync::Arc;
840 use tempfile::TempDir;
841
842 async fn make_executor() -> (TempDir, QueryExecutor, Config) {
844 let temp_dir = TempDir::new().unwrap();
845 let config = Config::default();
846 let platform = Arc::new(crate::platform::Platform::new(&config).await.unwrap());
847 let storage = Arc::new(
848 crate::storage::StorageEngine::open(
849 temp_dir.path(),
850 &config,
851 platform,
852 #[cfg(feature = "state_machine")]
853 None,
854 )
855 .await
856 .unwrap(),
857 );
858 let schema = Arc::new(
859 crate::schema::SchemaManager::new(temp_dir.path())
860 .await
861 .unwrap(),
862 );
863 let executor = QueryExecutor::new(storage, schema, &config);
864 (temp_dir, executor, config)
865 }
866
867 #[tokio::test]
868 async fn test_query_executor_creation() {
869 let (_tmp, executor, config) = make_executor().await;
870 assert_eq!(
871 executor._config.query.query_parallelism,
872 config.query.query_parallelism
873 );
874 }
875
876 #[tokio::test]
877 async fn test_value_comparison() {
878 let (_tmp, executor, _) = make_executor().await;
879
880 let result = executor
881 .compare_values(&Value::Integer(10), &Value::Integer(20))
882 .unwrap();
883 assert_eq!(result, Ordering::Less);
884
885 let result = executor
886 .compare_values(
887 &Value::Text("apple".to_string()),
888 &Value::Text("banana".to_string()),
889 )
890 .unwrap();
891 assert_eq!(result, Ordering::Less);
892 }
893
894 #[tokio::test]
895 async fn test_condition_evaluation() {
896 let (_tmp, executor, _) = make_executor().await;
897
898 let mut row_values = HashMap::new();
899 row_values.insert("id".to_string(), Value::Integer(1));
900 row_values.insert("name".to_string(), Value::Text("test".to_string()));
901 let row = QueryRow::with_values(RowKey::new(vec![1]), row_values);
902
903 let condition = Condition {
904 column: "id".to_string(),
905 operator: ComparisonOperator::Equal,
906 value: Value::Integer(1),
907 };
908 assert!(executor.evaluate_condition(&row, &condition).unwrap());
909
910 let condition = Condition {
911 column: "name".to_string(),
912 operator: ComparisonOperator::Like,
913 value: Value::Text("test".to_string()),
914 };
915 assert!(executor.evaluate_condition(&row, &condition).unwrap());
916 }
917
918 use super::super::planner::{IndexSelection, IndexType};
921
922 fn plan_with(
924 plan_type: super::super::planner::PlanType,
925 selected_indexes: Vec<IndexSelection>,
926 ) -> QueryPlan {
927 QueryPlan {
928 plan_type,
929 table: None,
930 estimated_cost: 0.0,
931 estimated_rows: 0,
932 selected_indexes,
933 steps: Vec::new(),
934 hints: super::super::planner::QueryHints::default(),
935 }
936 }
937
938 fn primary_index() -> IndexSelection {
939 IndexSelection {
940 index_name: "PRIMARY".to_string(),
941 columns: vec!["id".to_string()],
942 selectivity: 0.1,
943 index_type: IndexType::Primary,
944 }
945 }
946
947 #[test]
950 fn test_indexes_used_point_lookup_reports_partition_index() {
951 let plan = plan_with(
952 super::super::planner::PlanType::PointLookup,
953 vec![primary_index()],
954 );
955 assert_eq!(QueryExecutor::indexes_used_for(&plan), vec!["PRIMARY"]);
956 }
957
958 #[test]
961 fn test_indexes_used_table_scan_reports_scan_marker() {
962 let plan = plan_with(super::super::planner::PlanType::TableScan, Vec::new());
963 assert_eq!(QueryExecutor::indexes_used_for(&plan), vec!["scan"]);
964 }
965
966 #[test]
971 fn test_indexes_used_insert_and_ddl_table_scan_report_no_scan() {
972 use super::super::planner::{ParallelizationInfo, StepType};
973
974 let insert_step = ExecutionStep {
976 step_type: StepType::Insert,
977 columns: Vec::new(),
978 conditions: Vec::new(),
979 cost: 0.0,
980 parallelization: ParallelizationInfo {
981 can_parallelize: false,
982 suggested_threads: 1,
983 partition_key: None,
984 },
985 };
986 let mut insert_plan = plan_with(super::super::planner::PlanType::TableScan, Vec::new());
987 insert_plan.steps = vec![insert_step];
988 assert!(
989 QueryExecutor::indexes_used_for(&insert_plan).is_empty(),
990 "INSERT must not report a scan access path"
991 );
992
993 let mut ddl_plan = plan_with(super::super::planner::PlanType::TableScan, Vec::new());
995 ddl_plan.table = Some(TableId::new("t"));
996 ddl_plan.estimated_rows = 0;
997 assert!(
998 QueryExecutor::indexes_used_for(&ddl_plan).is_empty(),
999 "CREATE TABLE must not report a scan access path"
1000 );
1001 }
1002
1003 #[test]
1005 fn test_indexes_used_range_scan_reports_scan_marker() {
1006 let plan = plan_with(super::super::planner::PlanType::RangeScan, Vec::new());
1007 assert_eq!(QueryExecutor::indexes_used_for(&plan), vec!["scan"]);
1008 }
1009
1010 #[test]
1013 fn test_indexes_used_index_scan_primary_reports_index() {
1014 let plan = plan_with(
1015 super::super::planner::PlanType::IndexScan,
1016 vec![primary_index()],
1017 );
1018 assert_eq!(QueryExecutor::indexes_used_for(&plan), vec!["PRIMARY"]);
1019 }
1020
1021 #[test]
1025 fn test_indexes_used_index_scan_secondary_reports_scan() {
1026 let secondary = IndexSelection {
1027 index_name: "idx_name".to_string(),
1028 columns: vec!["name".to_string()],
1029 selectivity: 0.1,
1030 index_type: IndexType::Secondary,
1031 };
1032 let plan = plan_with(super::super::planner::PlanType::IndexScan, vec![secondary]);
1033 assert_eq!(QueryExecutor::indexes_used_for(&plan), vec!["scan"]);
1034 }
1035
1036 #[tokio::test]
1037 async fn test_condition_to_row_key_mapping() {
1038 let (_tmp, executor, _) = make_executor().await;
1039
1040 let id_condition = Condition {
1041 column: "id".to_string(),
1042 operator: ComparisonOperator::Equal,
1043 value: Value::Integer(42),
1044 };
1045 let key = executor
1046 .condition_to_row_key(&id_condition)
1047 .expect("id condition key");
1048 assert_eq!(std::str::from_utf8(key.as_bytes()).unwrap(), "user_key_42");
1049
1050 let name_condition = Condition {
1051 column: "username".to_string(),
1052 operator: ComparisonOperator::Equal,
1053 value: Value::Text("carol".to_string()),
1054 };
1055 let key = executor
1056 .condition_to_row_key(&name_condition)
1057 .expect("fallback key");
1058 assert_eq!(key.as_bytes(), b"carol");
1059 }
1060}