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