1pub mod chunk_helpers;
10pub mod graph_source;
11pub mod join_helpers;
12pub mod mapper;
13pub mod plan_serializer;
14pub mod projection_helper;
15#[cfg(test)]
16mod tests;
17#[cfg(test)]
18mod tests_only;
19pub mod union_helpers;
20
21pub use chunk_helpers::*;
22pub use graph_source::*;
23pub use join_helpers::*;
24pub use mapper::*;
25pub use plan_serializer::*;
26pub use projection_helper::*;
27pub use union_helpers::*;
28
29use crate::physical_operator::*;
30use akar_common::error::ProcessorError;
31use akar_common::types::{PhysicalTypeID, Value};
32use akar_common::vector::{DataChunk, ValueVector};
33use akar_function::registry::{FunctionRegistry, TableFunction};
34use akar_planner::logical_operator::LogicalOperator;
35use akar_storage::table::TableCatalog;
36use akar_storage::wal::{WALRecord, WalSink};
37use akar_transaction::UndoRecord;
38use std::collections::HashMap;
39use std::sync::{Arc, Mutex};
40
41pub type SequenceFn = Arc<dyn Fn(&str, bool) -> Result<Value, ProcessorError> + Send + Sync>;
42pub type SubqueryFn = Arc<dyn Fn(&akar_parser::ast::Query) -> Result<Vec<DataChunk>, ProcessorError> + Send + Sync>;
43
44#[derive(Debug, Clone)]
48pub enum SchemaDdlOp {
49 CreateSequence {
50 name: String,
51 if_not_exists: bool,
52 start_value: i64,
53 increment: i64,
54 min_value: i64,
55 max_value: i64,
56 cycle: bool,
57 },
58 DropSequence {
59 name: String,
60 if_exists: bool,
61 },
62 ExportDatabase {
63 file_path: String,
64 file_type: String,
65 schema_only: bool,
66 },
67 ImportDatabase {
68 file_path: String,
69 query: String,
70 index_query: String,
71 },
72}
73pub type SchemaDdlFn = Arc<dyn Fn(SchemaDdlOp) -> Result<String, ProcessorError> + Send + Sync>;
74
75pub trait StandaloneCallHandler: Send + Sync {
76 fn execute_call(
77 &self,
78 name: &str,
79 args: &[akar_parser::ast::Expression],
80 ) -> Result<Vec<akar_common::vector::DataChunk>, ProcessorError>;
81}
82
83pub trait StandaloneCallFn: Send + Sync {
84 fn execute(
85 &self,
86 args: &[akar_parser::ast::Expression],
87 ) -> Result<Vec<Vec<akar_common::types::Value>>, ProcessorError>;
88 fn aliases(&self) -> Vec<&'static str>;
89}
90
91#[derive(Default)]
92pub struct StandaloneCallRegistry {
93 handlers: std::collections::HashMap<String, std::sync::Arc<dyn StandaloneCallFn>>,
94}
95
96impl StandaloneCallRegistry {
97 pub fn new() -> Self {
98 Self::default()
99 }
100
101 pub fn register(&mut self, handler: std::sync::Arc<dyn StandaloneCallFn>) {
102 for alias in handler.aliases() {
103 self.handlers.insert(alias.to_lowercase(), handler.clone());
104 }
105 }
106
107 pub fn get(&self, name: &str) -> Option<std::sync::Arc<dyn StandaloneCallFn>> {
108 self.handlers.get(&name.to_lowercase()).cloned()
109 }
110}
111
112pub struct QueryProcessor {
114 function_registry: Option<Arc<Mutex<FunctionRegistry>>>,
115 table_catalog: Option<Arc<TableCatalog>>,
116 vfs: Option<Arc<akar_common::file_system::VirtualFileSystemRegistry>>,
117 standalone_call_handler: Option<Arc<dyn StandaloneCallHandler>>,
118 sequence_fn: Option<SequenceFn>,
121 subquery_fn: Option<SubqueryFn>,
123 schema_ddl_fn: Option<SchemaDdlFn>,
125 snapshot_ts: Option<u64>,
127 commit_history: HashMap<u64, u64>,
129 written_rows: Mutex<Vec<(u64, u64)>>,
133 txn_id: Option<u64>,
136 undo_records: Arc<Mutex<Vec<UndoRecord>>>,
140 wal_records: WalSink,
145}
146
147impl QueryProcessor {
148 pub fn new() -> Self {
149 Self {
150 function_registry: None,
151 table_catalog: None,
152 vfs: None,
153 standalone_call_handler: None,
154 sequence_fn: None,
155 subquery_fn: None,
156 schema_ddl_fn: None,
157 snapshot_ts: None,
158 commit_history: HashMap::new(),
159 written_rows: Mutex::new(Vec::new()),
160 txn_id: None,
161 undo_records: Arc::new(Mutex::new(Vec::new())),
162 wal_records: Arc::new(Mutex::new(Vec::new())),
163 }
164 }
165
166 pub fn with_registry(registry: Arc<Mutex<FunctionRegistry>>) -> Self {
168 Self {
169 function_registry: Some(registry),
170 table_catalog: None,
171 vfs: None,
172 standalone_call_handler: None,
173 sequence_fn: None,
174 subquery_fn: None,
175 schema_ddl_fn: None,
176 snapshot_ts: None,
177 commit_history: HashMap::new(),
178 written_rows: Mutex::new(Vec::new()),
179 txn_id: None,
180 undo_records: Arc::new(Mutex::new(Vec::new())),
181 wal_records: Arc::new(Mutex::new(Vec::new())),
182 }
183 }
184
185 pub fn with_catalog(
187 registry: Arc<Mutex<FunctionRegistry>>,
188 table_catalog: Arc<TableCatalog>,
189 vfs: Arc<akar_common::file_system::VirtualFileSystemRegistry>,
190 ) -> Self {
191 Self {
192 function_registry: Some(registry),
193 table_catalog: Some(table_catalog),
194 vfs: Some(vfs),
195 standalone_call_handler: None,
196 sequence_fn: None,
197 subquery_fn: None,
198 schema_ddl_fn: None,
199 snapshot_ts: None,
200 commit_history: HashMap::new(),
201 written_rows: Mutex::new(Vec::new()),
202 txn_id: None,
203 undo_records: Arc::new(Mutex::new(Vec::new())),
204 wal_records: Arc::new(Mutex::new(Vec::new())),
205 }
206 }
207
208 pub fn with_standalone_call_handler(mut self, handler: Arc<dyn StandaloneCallHandler>) -> Self {
211 self.standalone_call_handler = Some(handler);
212 self
213 }
214
215 pub fn with_sequence_fn(mut self, f: SequenceFn) -> Self {
216 self.sequence_fn = Some(f);
217 self
218 }
219
220 pub fn with_subquery_fn(mut self, f: SubqueryFn) -> Self {
222 self.subquery_fn = Some(f);
223 self
224 }
225
226 pub fn with_schema_ddl_fn(mut self, f: SchemaDdlFn) -> Self {
228 self.schema_ddl_fn = Some(f);
229 self
230 }
231
232 pub fn with_snapshot(mut self, snapshot_ts: Option<u64>, commit_history: HashMap<u64, u64>) -> Self {
234 self.snapshot_ts = snapshot_ts;
235 self.commit_history = commit_history;
236 self
237 }
238
239 pub fn with_txn_id(mut self, txn_id: Option<u64>) -> Self {
245 self.txn_id = txn_id;
246 self
247 }
248
249 pub fn record_insert_undo(&self, table_id: u64, row_id: u64) {
251 if let Ok(mut u) = self.undo_records.lock() {
252 u.push(UndoRecord::insert(table_id, row_id));
253 }
254 }
255
256 pub fn record_update_undo(&self, table_id: u64, row_id: u64, column: u32, old_data: Vec<u8>) {
258 if let Ok(mut u) = self.undo_records.lock() {
259 u.push(UndoRecord::update(table_id, row_id, column, old_data));
260 }
261 }
262
263 pub fn record_delete_undo(&self, table_id: u64, row_id: u64, old_data: Vec<u8>) {
265 if let Ok(mut u) = self.undo_records.lock() {
266 u.push(UndoRecord::delete(table_id, row_id, old_data));
267 }
268 }
269
270 pub fn take_undo_records(&self) -> Vec<UndoRecord> {
272 self.undo_records
273 .lock()
274 .map(|mut u| std::mem::take(&mut *u))
275 .unwrap_or_default()
276 }
277
278 pub fn undo_sink(&self) -> Arc<Mutex<Vec<UndoRecord>>> {
281 Arc::clone(&self.undo_records)
282 }
283
284 pub fn wal_sink(&self) -> WalSink {
287 Arc::clone(&self.wal_records)
288 }
289
290 pub fn take_wal_records(&self) -> Vec<WALRecord> {
293 self.wal_records
294 .lock()
295 .map(|mut w| std::mem::take(&mut *w))
296 .unwrap_or_default()
297 }
298
299 pub fn execute(&self, operators: &[LogicalOperator]) -> Result<Vec<DataChunk>, ProcessorError> {
301 self.execute_internal(operators)
302 }
303
304 pub fn execute_internal(&self, operators: &[LogicalOperator]) -> Result<Vec<DataChunk>, ProcessorError> {
305 if operators.is_empty() {
306 return Ok(vec![DataChunk {
307 fields: vec![],
308 field_types: vec![],
309 size: 0,
310 field_names: vec![],
311 sel_vector: None,
312 }]);
313 }
314
315 let mut intermediate_result: Option<Vec<DataChunk>> = None;
316
317 for (i, op) in operators.iter().enumerate() {
318 let current = intermediate_result.take().unwrap_or_else(|| {
319 let mut dummy = DataChunk::new(vec![], vec![]);
320 dummy.size = 1;
321 vec![dummy]
322 });
323 let next_op = operators.get(i + 1);
324
325 let mut ctx = mapper::ExecutionContext {
326 processor: self,
327 function_registry: self.function_registry.clone(),
328 table_catalog: self.table_catalog.clone(),
329 vfs: self.vfs.clone(),
330 standalone_call_handler: self.standalone_call_handler.clone(),
331 sequence_fn: self.sequence_fn.clone(),
332 subquery_fn: self.subquery_fn.clone(),
333 schema_ddl_fn: self.schema_ddl_fn.clone(),
334 snapshot_ts: self.snapshot_ts,
335 commit_history: self.commit_history.clone(),
336 written_rows: Vec::new(),
337 txn_id: self.txn_id,
338 };
339
340 let result = mapper::PlanMapper::map_and_execute(op, next_op, current, &mut ctx)?;
341
342 if !ctx.written_rows.is_empty() {
344 if let Ok(mut writes) = self.written_rows.lock() {
345 writes.append(&mut ctx.written_rows);
346 }
347 }
348
349 if let LogicalOperator::ScanRel(_) = op {
350 match &mut intermediate_result {
352 Some(existing) => existing.extend(result),
353 None => intermediate_result = Some(result),
354 }
355 } else {
356 intermediate_result = Some(result);
357 }
358 }
359
360 Ok(intermediate_result.unwrap_or_default())
361 }
362
363 pub fn take_written_rows(&self) -> Vec<(u64, u64)> {
366 self.written_rows
367 .lock()
368 .map(|mut w| std::mem::take(&mut *w))
369 .unwrap_or_default()
370 }
371
372 fn execute_table_function(
375 &self,
376 tf: &akar_planner::logical_operator::LogicalTableFunctionCall,
377 ) -> Result<Vec<DataChunk>, ProcessorError> {
378 let func_name = &tf.function_name;
379 let args: Vec<Value> = Vec::new(); if let Some(ref registry) = self.function_registry {
383 let reg = registry.lock().map_err(|e| format!("Lock poisoned: {e}"))?;
384 if let Some(tbl_fn) = reg.get_table(func_name) {
385 match tbl_fn {
386 TableFunction::CustomTable { execute, .. } => {
387 let mut chunk = DataChunk::new(Vec::new(), Vec::new());
388 (execute)(&args, &mut chunk)?;
389 Ok(vec![chunk])
390 }
391 TableFunction::CustomTableWithGraph { execute, .. } => {
392 let mut chunk = DataChunk::new(Vec::new(), Vec::new());
393 let graph = CatalogGraphSource::new(self.table_catalog.as_ref());
394 (execute)(&args, Some(&graph), &mut chunk)?;
395 Ok(vec![chunk])
396 }
397 TableFunction::ScanCsv { .. }
398 | TableFunction::ScanParquet { .. }
399 | TableFunction::ScanJson { .. }
400 | TableFunction::ListTables
401 | TableFunction::ShowColumns { .. }
402 | TableFunction::CurrentSetting { .. } => Err(format!(
403 "Table function '{}' cannot be executed dynamically (no callback)",
404 func_name
405 )
406 .into()),
407 TableFunction::Custom { name } if name == "vector_similarity_scan" => {
408 drop(reg);
412 self.execute_vector_similarity_scan(tf)
413 }
414 TableFunction::Custom { name } => {
415 Err(format!("Custom table function '{}' has no registered handler", name).into())
416 }
417 }
418 } else {
419 Err(format!("Table function '{}' not found", func_name).into())
420 }
421 } else {
422 Err(format!(
423 "Cannot execute table function '{}': no function registry available",
424 func_name
425 )
426 .into())
427 }
428 }
429
430 fn execute_vector_similarity_scan(
435 &self,
436 tf: &akar_planner::logical_operator::LogicalTableFunctionCall,
437 ) -> Result<Vec<DataChunk>, ProcessorError> {
438 if tf.args.len() < 4 {
440 return Err(
441 "vector_similarity_scan requires 4 arguments: table_name, column_name, query_vector, top_k".into(),
442 );
443 }
444
445 fn eval_expr_to_value(expr: &akar_parser::ast::Expression) -> Option<Value> {
448 match expr {
449 akar_parser::ast::Expression::Constant(c) => match c {
450 akar_parser::ast::Constant::String(s) => Some(Value::String(s.clone())),
451 akar_parser::ast::Constant::Integer(i) => Some(Value::Int64(*i)),
452 akar_parser::ast::Constant::Float(f) => Some(Value::Double(*f)),
453 akar_parser::ast::Constant::Bool(b) => Some(Value::Bool(*b)),
454 akar_parser::ast::Constant::Null => Some(Value::Null),
455 },
456 akar_parser::ast::Expression::List(items) => {
457 let vals: Vec<Value> = items.iter().filter_map(eval_expr_to_value).collect();
458 Some(Value::List(vals))
459 }
460 _ => None, }
462 }
463
464 let table_name = match eval_expr_to_value(&tf.args[0]) {
465 Some(Value::String(s)) => s,
466 _ => return Err("First argument to vector_similarity_scan must be a table name string".into()),
467 };
468
469 let _column_name = match eval_expr_to_value(&tf.args[1]) {
470 Some(Value::String(s)) => s,
471 _ => return Err("Second argument to vector_similarity_scan must be a column name string".into()),
472 };
473
474 let query_vector = match eval_expr_to_value(&tf.args[2]) {
475 Some(Value::List(items)) => {
476 let mut vec = Vec::with_capacity(items.len());
477 for item in &items {
478 match item {
479 Value::Double(d) => vec.push(*d),
480 Value::Int64(i) => vec.push(*i as f64),
481 Value::Int32(i) => vec.push(*i as f64),
482 Value::Float(f) => vec.push(*f as f64),
483 _ => return Err("query_vector must be a list of numbers".into()),
484 }
485 }
486 vec
487 }
488 _ => return Err("Third argument to vector_similarity_scan must be a list of numbers".into()),
489 };
490
491 let top_k = match eval_expr_to_value(&tf.args[3]) {
492 Some(Value::Int64(k)) if k > 0 => k as u64,
493 _ => return Err("Fourth argument to vector_similarity_scan must be a positive integer".into()),
494 };
495
496 let tc = self
498 .table_catalog
499 .clone()
500 .ok_or_else(|| "No table catalog available for vector_similarity_scan".to_string())?;
501
502 let index_name = {
504 let mut found = None;
505 for entry in tc.all_vector_indexes() {
506 if entry.table_name == table_name {
507 found = Some(entry.name.clone());
508 break;
509 }
510 }
511 found.ok_or_else(|| format!("No vector index found on table '{}'", table_name))?
512 };
513
514 let scan = PhysicalVectorSimilarityScan {
516 index_name,
517 index_id: 0,
518 query_vector,
519 top_k,
520 table_name,
521 table_catalog: Some(tc),
522 };
523 scan.execute(vec![])
524 }
525
526 pub fn evaluate_expression(
528 _expr: &akar_parser::ast::Expression,
529 _chunk: &DataChunk,
530 ) -> Result<ValueVector, ProcessorError> {
531 let size = _chunk.size;
533 let mut v = ValueVector::new(PhysicalTypeID::Int64, size);
534 for i in 0..size {
535 v.set_i64(i, 0);
536 }
537 v.resize(size);
538 Ok(v)
539 }
540}
541
542impl Default for QueryProcessor {
543 fn default() -> Self {
544 Self::new()
545 }
546}