1pub mod cursor;
31pub mod cursor_store;
32pub mod cypher;
33pub mod distributed;
34
35use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
36use std::sync::Arc;
37use std::time::Duration;
38use std::{collections::HashMap, net::SocketAddr};
39
40pub use cursor::{CursorError, CursorId, CursorResultType, CursorState};
41pub use cursor_store::{CursorStore, CursorStoreConfig};
42pub use distributed::{
43 DistributedQueryConfig, MergeStrategy, QueryPlan, QueryPlanner, ResultMerger, ShardId,
44 ShardResult,
45};
46use graph_engine::{
47 CentralityConfig, CommunityConfig, Constraint, ConstraintTarget as GConstraintTarget,
48 ConstraintType as GConstraintType, Direction, EdgeInput, GraphEngine, GraphError, NodeInput,
49 PageRankConfig, PropertyValue,
50};
51use neumann_parser::{
52 self as parser, error::ParseErrorKind, AggregateFunction, BinaryOp, BlobOp, BlobOptions,
53 BlobStmt, BlobsOp, BlobsStmt, CacheOp, CacheStmt, ChainOp, ChainStmt, CheckpointStmt,
54 CheckpointsStmt, ClusterOp, ClusterStmt, ConstraintTarget, ConstraintType, DeleteStmt,
55 DescribeStmt, DescribeTarget, Direction as ParsedDirection,
56 DistanceMetric as ParsedDistanceMetric, EdgeOp, EdgeStmt, EmbedOp, EmbedStmt, EntityOp,
57 EntityStmt, Expr, ExprKind, FindPattern, FindStmt, GraphAggregateOp, GraphAggregateStmt,
58 GraphAlgorithmOp, GraphAlgorithmStmt, GraphBatchOp, GraphBatchStmt, GraphConstraintOp,
59 GraphConstraintStmt, GraphIndexOp, GraphIndexStmt, GraphPatternOp, GraphPatternStmt,
60 InsertSource, InsertStmt, JoinCondition, JoinKind, Literal, NeighborsStmt, NodeOp, NodeStmt,
61 NullsOrder, PathStmt, Property, RollbackStmt, SelectStmt, SimilarQuery, SimilarStmt,
62 SortDirection, SpatialOp, SpatialStmt, Statement, StatementKind, TableRefKind, UpdateStmt,
63 VaultOp, VaultStmt,
64};
65use relational_engine::{
66 ColumnarScanOptions, Condition, RelationalEngine, RelationalError, Row, Value,
67};
68use serde::{Deserialize, Serialize};
69use std::path::{Path, PathBuf};
70use tensor_blob::{BlobConfig, BlobError, BlobStore};
71use tensor_cache::{Cache, CacheConfig, CacheError, CacheLayer};
72use tensor_chain::{
73 ChainError, ClusterNodeConfig, ClusterOrchestrator, ClusterPeerConfig, OrchestratorConfig,
74 QueryExecutor, TensorChain,
75};
76use tensor_checkpoint::{
77 CheckpointConfig, CheckpointError, CheckpointManager, ConfirmationHandler, DestructiveOp,
78 FileCheckpointStore,
79};
80use tensor_store::{ConsistentHashConfig, ConsistentHashPartitioner, TensorStore};
81use tensor_unified::{
82 FindPattern as UnifiedFindPattern, UnifiedEngine, UnifiedError, UnifiedItem,
83 UnifiedResult as TensorUnifiedResult,
84};
85use tensor_vault::{Vault, VaultConfig, VaultError};
86use tokio::runtime::Runtime;
87use tracing::instrument;
88use vector_engine::{DistanceMetric as VectorDistanceMetric, HNSWIndex, VectorEngine, VectorError};
89
90pub use vector_engine::{FilterCondition, FilterStrategy, FilterValue, FilteredSearchConfig};
92
93#[derive(Debug, Clone)]
95enum AggregateFunc {
96 Count(Option<String>), Sum(String),
98 Avg(String),
99 Min(String),
100 Max(String),
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105enum ProtectedOpResult {
106 Proceed,
108 Cancelled,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114pub enum RouterError {
115 ParseError(String),
117 UnknownCommand(String),
119 RelationalError(String),
121 GraphError(String),
123 VectorError(String),
125 VaultError(String),
127 CacheError(String),
129 BlobError(String),
131 CheckpointError(String),
133 ChainError(String),
135 InvalidArgument(String),
137 MissingArgument(String),
139 TypeMismatch(String),
141 AuthenticationRequired,
143 NotFound(String),
145 CursorError(String),
147}
148
149impl std::fmt::Display for RouterError {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 match self {
152 Self::ParseError(msg) => write!(f, "Parse error: {msg}"),
153 Self::UnknownCommand(cmd) => write!(f, "Unknown command: {cmd}"),
154 Self::RelationalError(msg) => write!(f, "Relational error: {msg}"),
155 Self::GraphError(msg) => write!(f, "Graph error: {msg}"),
156 Self::VectorError(msg) => write!(f, "Vector error: {msg}"),
157 Self::VaultError(msg) => write!(f, "Vault error: {msg}"),
158 Self::CacheError(msg) => write!(f, "Cache error: {msg}"),
159 Self::BlobError(msg) => write!(f, "Blob error: {msg}"),
160 Self::CheckpointError(msg) => write!(f, "Checkpoint error: {msg}"),
161 Self::ChainError(msg) => write!(f, "Chain error: {msg}"),
162 Self::InvalidArgument(msg) => write!(f, "Invalid argument: {msg}"),
163 Self::TypeMismatch(msg) => write!(f, "Type mismatch: {msg}"),
164 Self::MissingArgument(msg) => write!(f, "Missing argument: {msg}"),
165 Self::AuthenticationRequired => {
166 write!(
167 f,
168 "Authentication required: call SET IDENTITY before vault operations"
169 )
170 },
171 Self::NotFound(msg) => write!(f, "Not found: {msg}"),
172 Self::CursorError(msg) => write!(f, "Cursor error: {msg}"),
173 }
174 }
175}
176
177impl std::error::Error for RouterError {}
178
179impl From<CursorError> for RouterError {
180 fn from(e: CursorError) -> Self {
181 Self::CursorError(e.to_string())
182 }
183}
184
185impl From<RelationalError> for RouterError {
186 fn from(e: RelationalError) -> Self {
187 Self::RelationalError(e.to_string())
188 }
189}
190
191impl From<GraphError> for RouterError {
192 fn from(e: GraphError) -> Self {
193 Self::GraphError(e.to_string())
194 }
195}
196
197impl From<VectorError> for RouterError {
198 fn from(e: VectorError) -> Self {
199 Self::VectorError(e.to_string())
200 }
201}
202
203impl From<VaultError> for RouterError {
204 fn from(e: VaultError) -> Self {
205 Self::VaultError(e.to_string())
206 }
207}
208
209impl From<CacheError> for RouterError {
210 fn from(e: CacheError) -> Self {
211 Self::CacheError(e.to_string())
212 }
213}
214
215impl From<BlobError> for RouterError {
216 fn from(e: BlobError) -> Self {
217 Self::BlobError(e.to_string())
218 }
219}
220
221impl From<CheckpointError> for RouterError {
222 fn from(e: CheckpointError) -> Self {
223 Self::CheckpointError(e.to_string())
224 }
225}
226
227impl From<ChainError> for RouterError {
228 fn from(e: ChainError) -> Self {
229 Self::ChainError(e.to_string())
230 }
231}
232
233impl From<UnifiedError> for RouterError {
234 fn from(e: UnifiedError) -> Self {
235 match e {
236 UnifiedError::RelationalError(msg) => Self::RelationalError(msg),
237 UnifiedError::GraphError(msg) => Self::GraphError(msg),
238 UnifiedError::VectorError(msg) => Self::VectorError(msg),
239 UnifiedError::NotFound(msg) => Self::VectorError(format!("Not found: {msg}")),
240 UnifiedError::InvalidOperation(msg) => Self::InvalidArgument(msg),
241 UnifiedError::BatchOperationFailed { index, key, cause } => Self::VectorError(format!(
242 "Batch operation failed at index {index} (key: {key}): {cause}"
243 )),
244 UnifiedError::SpatialError(msg) => {
245 Self::InvalidArgument(format!("Spatial error: {msg}"))
246 },
247 }
248 }
249}
250
251pub type Result<T> = std::result::Result<T, RouterError>;
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub enum QueryResult {
256 Empty,
258 Value(String),
260 Count(usize),
262 Ids(Vec<u64>),
264 Rows(Vec<Row>),
266 Nodes(Vec<NodeResult>),
268 Edges(Vec<EdgeResult>),
270 Path(Vec<u64>),
272 Similar(Vec<SimilarResult>),
274 Unified(UnifiedResult),
276 TableList(Vec<String>),
278 Blob(Vec<u8>),
280 ArtifactInfo(ArtifactInfoResult),
282 ArtifactList(Vec<String>),
284 BlobStats(BlobStatsResult),
286 CheckpointList(Vec<CheckpointInfo>),
288 Chain(ChainResult),
290 PageRank(PageRankResult),
292 Centrality(CentralityResult),
294 Communities(CommunityResult),
296 Constraints(Vec<ConstraintInfo>),
298 GraphIndexes(Vec<String>),
300 Aggregate(AggregateResultValue),
302 BatchResult(BatchOperationResult),
304 PatternMatch(PatternMatchResultValue),
306 Spatial(Vec<SpatialResult>),
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct PagedQueryResult {
313 pub result: QueryResult,
315 pub next_cursor: Option<String>,
317 pub prev_cursor: Option<String>,
319 pub total_count: Option<usize>,
321 pub has_more: bool,
323 pub page_size: usize,
325}
326
327#[derive(Debug, Clone, Default)]
329pub struct PaginationOptions {
330 pub cursor: Option<String>,
332 pub page_size: Option<usize>,
334 pub count_total: bool,
336 pub cursor_ttl: Option<Duration>,
338}
339
340impl PaginationOptions {
341 #[must_use]
343 pub fn new() -> Self {
344 Self::default()
345 }
346
347 #[must_use]
349 pub fn with_cursor(mut self, cursor: String) -> Self {
350 self.cursor = Some(cursor);
351 self
352 }
353
354 #[must_use]
356 pub const fn with_page_size(mut self, size: usize) -> Self {
357 self.page_size = Some(size);
358 self
359 }
360
361 #[must_use]
363 pub const fn with_count_total(mut self, count: bool) -> Self {
364 self.count_total = count;
365 self
366 }
367
368 #[must_use]
370 pub const fn with_cursor_ttl(mut self, ttl: Duration) -> Self {
371 self.cursor_ttl = Some(ttl);
372 self
373 }
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct NodeResult {
379 pub id: u64,
380 pub label: String,
381 pub properties: HashMap<String, String>,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct EdgeResult {
387 pub id: u64,
388 pub from: u64,
389 pub to: u64,
390 pub label: String,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct SimilarResult {
396 pub key: String,
398 pub score: f32,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct SpatialResult {
405 pub key: String,
407 pub distance: f32,
409 pub x: f32,
411 pub y: f32,
413 pub width: f32,
415 pub height: f32,
417}
418
419#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct UnifiedResult {
422 pub description: String,
423 pub items: Vec<UnifiedItem>,
424}
425
426impl From<TensorUnifiedResult> for UnifiedResult {
427 fn from(r: TensorUnifiedResult) -> Self {
428 Self {
429 description: r.description,
430 items: r.items,
431 }
432 }
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct ArtifactInfoResult {
438 pub id: String,
439 pub filename: String,
440 pub content_type: String,
441 pub size: usize,
442 pub checksum: String,
443 pub chunk_count: usize,
444 pub created: u64,
445 pub modified: u64,
446 pub created_by: String,
447 pub tags: Vec<String>,
448 pub linked_to: Vec<String>,
449 pub custom: HashMap<String, String>,
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct BlobStatsResult {
455 pub artifact_count: usize,
456 pub chunk_count: usize,
457 pub total_bytes: usize,
458 pub unique_bytes: usize,
459 pub dedup_ratio: f64,
460 pub orphaned_chunks: usize,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct CheckpointInfo {
466 pub id: String,
467 pub name: String,
468 pub created_at: u64,
469 pub is_auto: bool,
470}
471
472#[derive(Debug, Clone, Serialize, Deserialize)]
474pub enum ChainResult {
475 TransactionBegun { tx_id: String },
477 Committed { block_hash: String, height: u64 },
479 RolledBack { to_height: u64 },
481 History(Vec<ChainHistoryEntry>),
483 Similar(Vec<ChainSimilarResult>),
485 Drift(ChainDriftResult),
487 Height(u64),
489 Tip { hash: String, height: u64 },
491 Block(ChainBlockInfo),
493 Codebook(ChainCodebookInfo),
495 Verified { ok: bool, errors: Vec<String> },
497 TransitionAnalysis(ChainTransitionAnalysis),
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct ChainHistoryEntry {
504 pub height: u64,
505 pub transaction_type: String,
506 pub data: Option<Vec<u8>>,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct ChainSimilarResult {
512 pub block_hash: String,
513 pub height: u64,
514 pub similarity: f32,
515}
516
517#[derive(Debug, Clone, Serialize, Deserialize)]
519pub struct ChainDriftResult {
520 pub from_height: u64,
521 pub to_height: u64,
522 pub total_drift: f32,
523 pub avg_drift_per_block: f32,
524 pub max_drift: f32,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct ChainBlockInfo {
530 pub height: u64,
531 pub hash: String,
532 pub prev_hash: String,
533 pub timestamp: u64,
534 pub transaction_count: usize,
535 pub proposer: String,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct ChainCodebookInfo {
541 pub scope: String,
542 pub entry_count: usize,
543 pub dimension: usize,
544 pub domain: Option<String>,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct ChainTransitionAnalysis {
550 pub total_transitions: usize,
551 pub valid_transitions: usize,
552 pub invalid_transitions: usize,
553 pub avg_validity_score: f32,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct PageRankItem {
559 pub node_id: u64,
560 pub score: f64,
561}
562
563#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct PageRankResult {
566 pub items: Vec<PageRankItem>,
567 pub iterations: usize,
568 pub convergence: f64,
569 pub converged: bool,
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
574pub enum CentralityType {
575 Betweenness,
576 Closeness,
577 Eigenvector,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct CentralityItem {
583 pub node_id: u64,
584 pub score: f64,
585}
586
587#[derive(Debug, Clone, Serialize, Deserialize)]
589pub struct CentralityResult {
590 pub items: Vec<CentralityItem>,
591 pub centrality_type: CentralityType,
592 pub iterations: Option<usize>,
593 pub converged: Option<bool>,
594 pub sample_count: Option<usize>,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct CommunityItem {
600 pub node_id: u64,
601 pub community_id: u64,
602}
603
604#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct CommunityResult {
607 pub items: Vec<CommunityItem>,
608 pub members: HashMap<u64, Vec<u64>>,
609 pub community_count: usize,
610 pub modularity: Option<f64>,
611 pub passes: Option<usize>,
612 pub iterations: Option<usize>,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct ConstraintInfo {
618 pub name: String,
619 pub target: String,
620 pub property: String,
621 pub constraint_type: String,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
626pub enum AggregateResultValue {
627 Count(u64),
628 Sum(f64),
629 Avg(f64),
630 Min(f64),
631 Max(f64),
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct BatchOperationResult {
637 pub operation: String,
638 pub affected_count: usize,
639 pub created_ids: Option<Vec<u64>>,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct PatternMatchResultValue {
645 pub matches: Vec<PatternMatchBinding>,
646 pub stats: PatternMatchStatsValue,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct PatternMatchBinding {
652 pub bindings: HashMap<String, BindingValue>,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
657pub enum BindingValue {
658 Node {
659 id: u64,
660 label: String,
661 },
662 Edge {
663 id: u64,
664 edge_type: String,
665 from: u64,
666 to: u64,
667 },
668 Path {
669 nodes: Vec<u64>,
670 edges: Vec<u64>,
671 length: usize,
672 },
673}
674
675#[derive(Debug, Clone, Serialize, Deserialize)]
677pub struct PatternMatchStatsValue {
678 pub matches_found: usize,
679 pub nodes_evaluated: usize,
680 pub edges_evaluated: usize,
681 pub truncated: bool,
682}
683
684impl QueryResult {
685 #[must_use]
687 pub fn to_json(&self) -> String {
688 serde_json::to_string(self).unwrap_or_else(|_| "{}".to_string())
689 }
690
691 #[must_use]
693 pub fn to_pretty_json(&self) -> String {
694 serde_json::to_string_pretty(self).unwrap_or_else(|_| "{}".to_string())
695 }
696
697 #[must_use]
699 pub const fn is_empty(&self) -> bool {
700 matches!(self, Self::Empty)
701 }
702
703 #[must_use]
705 pub const fn as_count(&self) -> Option<usize> {
706 if let Self::Count(n) = self {
707 Some(*n)
708 } else {
709 None
710 }
711 }
712
713 #[must_use]
715 pub fn as_value(&self) -> Option<&str> {
716 if let Self::Value(v) = self {
717 Some(v)
718 } else {
719 None
720 }
721 }
722
723 #[must_use]
725 pub fn as_rows(&self) -> Option<&[Row]> {
726 if let Self::Rows(rows) = self {
727 Some(rows)
728 } else {
729 None
730 }
731 }
732}
733
734pub struct QueryRouter {
736 relational: Arc<RelationalEngine>,
737 graph: Arc<GraphEngine>,
738 vector: Arc<VectorEngine>,
739 unified: Option<UnifiedEngine>,
741 vault: Option<Arc<Vault>>,
743 cache: Option<Arc<Cache>>,
745 blob: Option<Arc<tokio::sync::Mutex<BlobStore>>>,
747 blob_runtime: Option<Arc<Runtime>>,
749 current_identity: Option<String>,
751 hnsw_index: Option<(HNSWIndex, Vec<String>)>,
753 vector_generation: AtomicU64,
758 hnsw_generation: AtomicU64,
760 checkpoint_dir: Option<PathBuf>,
762 checkpoint: Option<Arc<CheckpointManager>>,
764 chain: Option<Arc<TensorChain>>,
766 cluster: Option<Arc<ClusterOrchestrator>>,
768 cluster_runtime: Option<Arc<Runtime>>,
770 distributed_planner: Option<Arc<QueryPlanner>>,
772 distributed_config: DistributedQueryConfig,
774 local_shard_id: ShardId,
776 cursor_store: Arc<CursorStore>,
778 spatial: Arc<parking_lot::RwLock<tensor_spatial::SpatialIndex<String>>>,
780}
781
782impl QueryRouter {
783 #[must_use]
785 pub fn new() -> Self {
786 Self::with_shared_store(TensorStore::new())
787 }
788
789 pub fn with_engines(
793 relational: Arc<RelationalEngine>,
794 graph: Arc<GraphEngine>,
795 vector: Arc<VectorEngine>,
796 ) -> Self {
797 let store = vector.store().clone();
798 let unified = UnifiedEngine::with_engines(
799 store,
800 Arc::clone(&relational),
801 Arc::clone(&graph),
802 Arc::clone(&vector),
803 );
804 Self {
805 relational,
806 graph,
807 vector,
808 unified: Some(unified),
809 vault: None,
810 cache: None,
811 blob: None,
812 blob_runtime: None,
813 current_identity: None,
814 hnsw_index: None,
815 vector_generation: AtomicU64::new(0),
816 hnsw_generation: AtomicU64::new(0),
817 checkpoint_dir: None,
818 checkpoint: None,
819 chain: None,
820 cluster: None,
821 cluster_runtime: None,
822 distributed_planner: None,
823 distributed_config: DistributedQueryConfig::default(),
824 local_shard_id: 0,
825 cursor_store: Arc::new(CursorStore::new()),
826 spatial: Arc::new(parking_lot::RwLock::new(tensor_spatial::SpatialIndex::new())),
827 }
828 }
829
830 #[must_use]
835 pub fn with_shared_store(store: TensorStore) -> Self {
836 let relational = Arc::new(RelationalEngine::with_store(store.clone()));
837 let graph = Arc::new(GraphEngine::with_store(store.clone()));
838 let vector = Arc::new(VectorEngine::with_store(store.clone()));
839 let unified = UnifiedEngine::with_engines(
840 store,
841 Arc::clone(&relational),
842 Arc::clone(&graph),
843 Arc::clone(&vector),
844 );
845 Self {
846 relational,
847 graph,
848 vector,
849 unified: Some(unified),
850 vault: None,
851 cache: None,
852 blob: None,
853 blob_runtime: None,
854 current_identity: None,
855 hnsw_index: None,
856 vector_generation: AtomicU64::new(0),
857 hnsw_generation: AtomicU64::new(0),
858 checkpoint_dir: None,
859 checkpoint: None,
860 chain: None,
861 cluster: None,
862 cluster_runtime: None,
863 distributed_planner: None,
864 distributed_config: DistributedQueryConfig::default(),
865 local_shard_id: 0,
866 cursor_store: Arc::new(CursorStore::new()),
867 spatial: Arc::new(parking_lot::RwLock::new(tensor_spatial::SpatialIndex::new())),
868 }
869 }
870
871 pub fn relational(&self) -> &RelationalEngine {
873 &self.relational
874 }
875
876 pub fn graph(&self) -> &GraphEngine {
878 &self.graph
879 }
880
881 pub fn vector(&self) -> &VectorEngine {
883 &self.vector
884 }
885
886 pub const fn spatial(&self) -> &Arc<parking_lot::RwLock<tensor_spatial::SpatialIndex<String>>> {
888 &self.spatial
889 }
890
891 pub const fn unified(&self) -> Option<&UnifiedEngine> {
893 self.unified.as_ref()
894 }
895
896 fn require_unified(&self) -> Result<&UnifiedEngine> {
898 self.unified.as_ref().ok_or_else(|| {
899 RouterError::InvalidArgument("Unified engine not initialized".to_string())
900 })
901 }
902
903 fn create_runtime() -> Result<Runtime> {
905 Runtime::new()
906 .map_err(|e| RouterError::InvalidArgument(format!("Failed to create runtime: {e}")))
907 }
908
909 pub fn vault(&self) -> Option<&Vault> {
911 self.vault.as_deref()
912 }
913
914 pub fn init_vault(&mut self, master_key: &[u8]) -> Result<()> {
920 let vault = Vault::new(
921 master_key,
922 Arc::clone(&self.graph),
923 self.vector.store().clone(),
924 VaultConfig::default(),
925 )?;
926 self.vault = Some(Arc::new(vault));
927 Ok(())
928 }
929
930 pub fn cache(&self) -> Option<&Cache> {
932 self.cache.as_deref()
933 }
934
935 pub fn init_cache(&mut self) {
937 self.cache = Some(Arc::new(Cache::new()));
938 }
939
940 pub fn init_cache_default(&mut self) -> Result<()> {
946 self.cache = Some(Arc::new(Cache::new()));
947 Ok(())
948 }
949
950 pub fn init_cache_with_config(&mut self, config: CacheConfig) -> Result<()> {
956 let cache =
957 Cache::with_config(config).map_err(|e| RouterError::CacheError(e.to_string()))?;
958 self.cache = Some(Arc::new(cache));
959 Ok(())
960 }
961
962 pub const fn blob(&self) -> Option<&Arc<tokio::sync::Mutex<BlobStore>>> {
964 self.blob.as_ref()
965 }
966
967 pub fn ensure_vault(&mut self) -> Result<&Vault> {
979 if self.vault.is_none() {
980 if let Ok(key) = std::env::var("NEUMANN_VAULT_KEY") {
981 self.init_vault(key.as_bytes())?;
982 } else {
983 return Err(RouterError::VaultError(
984 "Vault not initialized. Set NEUMANN_VAULT_KEY env var or call init_vault()"
985 .to_string(),
986 ));
987 }
988 }
989 Ok(self.vault.as_deref().unwrap())
990 }
991
992 pub fn ensure_cache(&mut self) -> &Cache {
998 if self.cache.is_none() {
999 self.init_cache();
1000 }
1001 self.cache.as_deref().unwrap()
1002 }
1003
1004 pub fn ensure_blob(&mut self) -> Result<&Arc<tokio::sync::Mutex<BlobStore>>> {
1014 if self.blob.is_none() {
1015 self.init_blob()?;
1016 }
1017 Ok(self.blob.as_ref().unwrap())
1018 }
1019
1020 pub fn init_blob(&mut self) -> Result<()> {
1026 self.init_blob_with_config(BlobConfig::default())
1027 }
1028
1029 pub fn init_blob_with_config(&mut self, config: BlobConfig) -> Result<()> {
1035 let runtime = Runtime::new()
1037 .map_err(|e| RouterError::BlobError(format!("Failed to create runtime: {e}")))?;
1038
1039 let store = self.vector.store().clone();
1041 let blob_store = runtime
1042 .block_on(async { BlobStore::new(store, config).await })
1043 .map_err(|e| RouterError::BlobError(e.to_string()))?;
1044
1045 self.blob = Some(Arc::new(tokio::sync::Mutex::new(blob_store)));
1046 self.blob_runtime = Some(Arc::new(runtime));
1047 Ok(())
1048 }
1049
1050 pub fn start_blob(&mut self) -> Result<()> {
1056 let blob = self
1057 .blob
1058 .as_ref()
1059 .ok_or_else(|| RouterError::BlobError("Blob store not initialized".to_string()))?;
1060 let runtime = self
1061 .blob_runtime
1062 .as_ref()
1063 .ok_or_else(|| RouterError::BlobError("Blob runtime not initialized".to_string()))?;
1064
1065 runtime.block_on(async {
1066 let mut blob_guard = blob.lock().await;
1067 blob_guard.start().await
1068 })?;
1069 Ok(())
1070 }
1071
1072 pub fn shutdown_blob(&mut self) -> Result<()> {
1078 if let (Some(blob), Some(runtime)) = (self.blob.as_ref(), self.blob_runtime.as_ref()) {
1079 runtime.block_on(async {
1080 let mut blob_guard = blob.lock().await;
1081 blob_guard.shutdown().await
1082 })?;
1083 }
1084 Ok(())
1085 }
1086
1087 pub fn init_cluster(
1101 &mut self,
1102 node_id: &str,
1103 bind_addr: SocketAddr,
1104 peers: &[(String, SocketAddr)],
1105 ) -> Result<()> {
1106 self.init_cluster_with_executor(node_id, bind_addr, peers, None)
1107 }
1108
1109 pub fn init_cluster_with_wal(
1121 &mut self,
1122 node_id: &str,
1123 bind_addr: SocketAddr,
1124 peers: &[(String, SocketAddr)],
1125 wal_dir: &std::path::Path,
1126 ) -> Result<()> {
1127 if self.cluster.is_some() {
1128 return Err(RouterError::InvalidArgument(
1129 "Cluster already initialized".to_string(),
1130 ));
1131 }
1132
1133 let runtime = Runtime::new()
1134 .map_err(|e| RouterError::ChainError(format!("Failed to create runtime: {e}")))?;
1135
1136 let local_config = ClusterNodeConfig::new(node_id, bind_addr);
1137 let peer_configs: Vec<ClusterPeerConfig> = peers
1138 .iter()
1139 .map(|(id, addr)| ClusterPeerConfig::new(id.clone(), *addr))
1140 .collect();
1141
1142 let config =
1143 OrchestratorConfig::new(local_config, peer_configs).with_wal_dir(wal_dir.to_path_buf());
1144
1145 let orchestrator = runtime
1146 .block_on(ClusterOrchestrator::start(config))
1147 .map_err(|e| RouterError::ChainError(e.to_string()))?;
1148
1149 let all_nodes: Vec<String> = std::iter::once(node_id.to_string())
1150 .chain(peers.iter().map(|(id, _)| id.clone()))
1151 .collect();
1152
1153 let hash_config = ConsistentHashConfig::new(node_id);
1154 let partitioner = ConsistentHashPartitioner::with_nodes(hash_config, all_nodes.clone());
1155
1156 let local_shard = all_nodes.iter().position(|n| n == node_id).unwrap_or(0);
1157
1158 let planner = QueryPlanner::new(Arc::new(partitioner), local_shard);
1159
1160 self.cluster = Some(Arc::new(orchestrator));
1161 self.cluster_runtime = Some(Arc::new(runtime));
1162 self.distributed_planner = Some(Arc::new(planner));
1163 self.local_shard_id = local_shard;
1164 Ok(())
1165 }
1166
1167 pub fn init_cluster_with_executor(
1179 &mut self,
1180 node_id: &str,
1181 bind_addr: SocketAddr,
1182 peers: &[(String, SocketAddr)],
1183 executor: Option<Arc<dyn tensor_chain::QueryExecutor>>,
1184 ) -> Result<()> {
1185 if self.cluster.is_some() {
1186 return Err(RouterError::InvalidArgument(
1187 "Cluster already initialized".to_string(),
1188 ));
1189 }
1190
1191 let runtime = Runtime::new()
1193 .map_err(|e| RouterError::ChainError(format!("Failed to create runtime: {e}")))?;
1194
1195 let local_config = ClusterNodeConfig::new(node_id, bind_addr);
1197 let peer_configs: Vec<ClusterPeerConfig> = peers
1198 .iter()
1199 .map(|(id, addr)| ClusterPeerConfig::new(id.clone(), *addr))
1200 .collect();
1201
1202 let config = OrchestratorConfig::new(local_config, peer_configs);
1203
1204 let orchestrator = runtime
1206 .block_on(ClusterOrchestrator::start(config))
1207 .map_err(|e| RouterError::ChainError(e.to_string()))?;
1208
1209 if let Some(exec) = executor {
1211 orchestrator.register_query_executor(exec);
1212 }
1213
1214 let all_nodes: Vec<String> = std::iter::once(node_id.to_string())
1216 .chain(peers.iter().map(|(id, _)| id.clone()))
1217 .collect();
1218
1219 let hash_config = ConsistentHashConfig::new(node_id);
1220 let partitioner = ConsistentHashPartitioner::with_nodes(hash_config, all_nodes.clone());
1221
1222 let local_shard = all_nodes.iter().position(|n| n == node_id).unwrap_or(0);
1224
1225 let planner = QueryPlanner::new(Arc::new(partitioner), local_shard);
1227
1228 self.cluster = Some(Arc::new(orchestrator));
1229 self.cluster_runtime = Some(Arc::new(runtime));
1230 self.distributed_planner = Some(Arc::new(planner));
1231 self.local_shard_id = local_shard;
1232 Ok(())
1233 }
1234
1235 pub fn shutdown_cluster(&mut self) -> Result<()> {
1241 if let (Some(cluster), Some(runtime)) =
1242 (self.cluster.as_ref(), self.cluster_runtime.as_ref())
1243 {
1244 runtime
1245 .block_on(cluster.shutdown())
1246 .map_err(|e| RouterError::ChainError(e.to_string()))?;
1247 }
1248 self.cluster = None;
1249 self.cluster_runtime = None;
1250 Ok(())
1251 }
1252
1253 pub const fn is_cluster_active(&self) -> bool {
1255 self.cluster.is_some()
1256 }
1257
1258 pub const fn cluster(&self) -> Option<&Arc<ClusterOrchestrator>> {
1260 self.cluster.as_ref()
1261 }
1262
1263 pub const fn checkpoint(&self) -> Option<&Arc<CheckpointManager>> {
1265 self.checkpoint.as_ref()
1266 }
1267
1268 pub fn set_checkpoint_dir(&mut self, dir: PathBuf) {
1270 self.checkpoint_dir = Some(dir);
1271 }
1272
1273 pub fn checkpoint_dir(&self) -> Option<&Path> {
1275 self.checkpoint_dir.as_deref()
1276 }
1277
1278 pub fn init_checkpoint(&mut self) -> Result<()> {
1286 self.init_checkpoint_with_config(CheckpointConfig::default())
1287 }
1288
1289 pub fn init_checkpoint_with_config(&mut self, config: CheckpointConfig) -> Result<()> {
1297 let dir = self.checkpoint_dir.as_ref().ok_or_else(|| {
1298 RouterError::CheckpointError(
1299 "Checkpoint directory must be set before initializing checkpoint manager"
1300 .to_string(),
1301 )
1302 })?;
1303
1304 let store = Arc::new(
1305 FileCheckpointStore::new(dir)
1306 .map_err(|e| RouterError::CheckpointError(e.to_string()))?,
1307 );
1308
1309 let manager = CheckpointManager::new(store, config);
1310 self.checkpoint = Some(Arc::new(manager));
1311 Ok(())
1312 }
1313
1314 pub fn ensure_checkpoint(&mut self) -> Result<&Arc<CheckpointManager>> {
1324 if self.checkpoint.is_none() {
1325 self.init_checkpoint()?;
1326 }
1327 Ok(self.checkpoint.as_ref().unwrap())
1328 }
1329
1330 pub const fn has_checkpoint(&self) -> bool {
1332 self.checkpoint.is_some()
1333 }
1334
1335 pub const fn has_hnsw_index(&self) -> bool {
1337 self.hnsw_index.is_some()
1338 }
1339
1340 fn bump_vector_generation(&self) {
1343 self.vector_generation.fetch_add(1, AtomicOrdering::SeqCst);
1344 }
1345
1346 fn hnsw_is_fresh(&self) -> bool {
1348 self.hnsw_generation.load(AtomicOrdering::SeqCst)
1349 == self.vector_generation.load(AtomicOrdering::SeqCst)
1350 }
1351
1352 pub fn tls_cert_path(&self) -> Option<std::path::PathBuf> {
1354 self.cluster.as_ref().and_then(|c| c.tls_cert_path())
1355 }
1356
1357 pub fn set_confirmation_handler(&self, handler: Arc<dyn ConfirmationHandler>) -> Result<()> {
1366 let checkpoint = self.checkpoint.as_ref().ok_or_else(|| {
1367 RouterError::CheckpointError("Checkpoint manager not initialized".to_string())
1368 })?;
1369
1370 checkpoint.set_confirmation_handler(handler);
1371 Ok(())
1372 }
1373
1374 pub fn init_chain(&mut self, node_id: &str) -> Result<()> {
1380 let store = self.vector.store().clone();
1381 let chain = TensorChain::new(store, node_id);
1382 chain.initialize()?;
1383 self.chain = Some(Arc::new(chain));
1384 Ok(())
1385 }
1386
1387 pub const fn chain(&self) -> Option<&Arc<TensorChain>> {
1389 self.chain.as_ref()
1390 }
1391
1392 pub fn ensure_chain(&mut self) -> Result<&Arc<TensorChain>> {
1402 if self.chain.is_none() {
1403 self.init_chain("default_node")?;
1404 }
1405 Ok(self.chain.as_ref().unwrap())
1406 }
1407
1408 pub fn set_identity(&mut self, identity: &str) {
1411 self.current_identity = Some(identity.to_string());
1412 }
1413
1414 pub fn current_identity(&self) -> Option<&str> {
1417 self.current_identity.as_deref()
1418 }
1419
1420 fn require_identity(&self) -> Result<&str> {
1423 self.current_identity
1424 .as_deref()
1425 .ok_or(RouterError::AuthenticationRequired)
1426 }
1427
1428 pub const fn is_authenticated(&self) -> bool {
1430 self.current_identity.is_some()
1431 }
1432
1433 pub fn build_vector_index(&mut self) -> Result<()> {
1443 let (index, keys) = self.vector.build_hnsw_index_default()?;
1444 self.hnsw_index = Some((index, keys));
1445 self.hnsw_generation.store(
1446 self.vector_generation.load(AtomicOrdering::SeqCst),
1447 AtomicOrdering::SeqCst,
1448 );
1449 Ok(())
1450 }
1451
1452 pub fn find_similar_connected(
1467 &self,
1468 query_key: &str,
1469 connected_to: &str,
1470 top_k: usize,
1471 ) -> Result<Vec<UnifiedItem>> {
1472 let unified = self.require_unified()?;
1473 let runtime = Self::create_runtime()?;
1474
1475 let hnsw_results = if let Some((ref index, ref keys)) =
1477 self.hnsw_index.as_ref().filter(|_| self.hnsw_is_fresh())
1478 {
1479 let query_embedding = self
1480 .vector
1481 .get_entity_embedding(query_key)
1482 .map_err(|e| RouterError::VectorError(e.to_string()))?;
1483 Some(
1484 self.vector
1485 .search_with_hnsw(index, keys, &query_embedding, top_k.saturating_mul(8))
1486 .map_err(|e| RouterError::VectorError(e.to_string()))?,
1487 )
1488 } else {
1489 None
1490 };
1491
1492 runtime
1493 .block_on(unified.find_similar_connected_with_hnsw(
1494 query_key,
1495 connected_to,
1496 top_k,
1497 hnsw_results,
1498 ))
1499 .map_err(Into::into)
1500 }
1501
1502 pub fn find_neighbors_by_similarity(
1510 &self,
1511 entity_key: &str,
1512 query: &[f32],
1513 top_k: usize,
1514 ) -> Result<Vec<UnifiedItem>> {
1515 let unified = self.require_unified()?;
1516 let runtime = Self::create_runtime()?;
1517
1518 runtime
1519 .block_on(unified.find_neighbors_by_similarity(entity_key, query, top_k))
1520 .map_err(Into::into)
1521 }
1522
1523 pub fn create_unified_entity(
1531 &self,
1532 key: &str,
1533 fields: HashMap<String, String>,
1534 embedding: Option<Vec<f32>>,
1535 ) -> Result<()> {
1536 let has_embedding = embedding.is_some();
1537 let unified = self.require_unified()?;
1538 let runtime = Self::create_runtime()?;
1539
1540 runtime
1541 .block_on(unified.create_entity(key, fields, embedding))
1542 .map_err(|e| RouterError::VectorError(e.to_string()))?;
1543
1544 if has_embedding {
1545 self.bump_vector_generation();
1546 }
1547 Ok(())
1548 }
1549
1550 pub fn connect_entities(
1558 &self,
1559 from_key: &str,
1560 to_key: &str,
1561 edge_type: &str,
1562 ) -> Result<String> {
1563 let unified = self.require_unified()?;
1564 let runtime = Self::create_runtime()?;
1565
1566 runtime
1567 .block_on(unified.connect_entities(from_key, to_key, edge_type))
1568 .map(|edge_id| format!("edge:{edge_type}:{edge_id}"))
1569 .map_err(|e| RouterError::GraphError(e.to_string()))
1570 }
1571
1572 #[instrument(skip(self))]
1583 #[allow(clippy::too_many_lines)]
1584 pub fn execute(&self, command: &str) -> Result<QueryResult> {
1585 let trimmed = command.trim();
1586 if trimmed.is_empty() {
1587 return Err(RouterError::ParseError("Empty command".to_string()));
1588 }
1589
1590 if let Some(result) = self.try_execute_distributed(trimmed) {
1592 return result;
1593 }
1594
1595 let stmt = parser::parse(trimmed).map_err(|parse_err| {
1597 let upper = trimmed.to_ascii_uppercase();
1598 let first_word = upper.split_whitespace().next().unwrap_or("");
1599 if matches!(&parse_err.kind, ParseErrorKind::UnknownCommand(_)) {
1600 RouterError::UnknownCommand(first_word.to_string())
1601 } else {
1602 RouterError::ParseError(parse_err.format_with_source(trimmed))
1603 }
1604 })?;
1605
1606 if Self::is_cacheable_statement(&stmt) {
1608 if let Some(cached) = self.try_cache_get(trimmed) {
1609 return Ok(cached);
1610 }
1611 }
1612 let result = self.execute_statement(&stmt)?;
1613 if Self::is_cacheable_statement(&stmt) {
1614 self.try_cache_put(trimmed, &result);
1615 }
1616 if Self::is_write_statement(&stmt) {
1617 self.invalidate_cache_on_write();
1618 }
1619 Ok(result)
1620 }
1621
1622 #[instrument(skip(self))]
1632 #[allow(clippy::needless_pass_by_value)] pub fn execute_paginated(
1634 &self,
1635 command: &str,
1636 options: PaginationOptions,
1637 ) -> Result<PagedQueryResult> {
1638 let page_size = options.page_size.unwrap_or(CursorState::DEFAULT_PAGE_SIZE);
1639 #[allow(clippy::cast_possible_truncation)] let ttl_secs = options
1641 .cursor_ttl
1642 .map_or(CursorState::DEFAULT_TTL_SECS, |d| d.as_secs() as u32)
1643 .min(CursorState::MAX_TTL_SECS);
1644
1645 let cursor_state = if let Some(ref token) = options.cursor {
1647 let state = CursorState::decode(token)?;
1648
1649 if state.query != command {
1651 return Err(RouterError::CursorError(
1652 "Cursor query does not match request".to_string(),
1653 ));
1654 }
1655
1656 Some(state)
1657 } else {
1658 None
1659 };
1660
1661 let full_result = self.execute(command)?;
1663
1664 let (result_type, total_count, paged_result) = self.apply_pagination(
1666 &full_result,
1667 cursor_state.as_ref(),
1668 page_size,
1669 options.count_total,
1670 )?;
1671
1672 let offset = cursor_state.as_ref().map_or(0, |s| s.offset);
1673
1674 let has_more = total_count.is_some_and(|total| offset + page_size < total);
1676
1677 let next_cursor = if has_more {
1678 let cursor_id = uuid::Uuid::new_v4().to_string();
1679 let mut next_state = CursorState::new(
1680 cursor_id,
1681 command.to_string(),
1682 result_type,
1683 page_size,
1684 total_count,
1685 ttl_secs,
1686 );
1687 next_state.offset = offset + page_size;
1688
1689 self.cursor_store.insert(next_state.clone())?;
1691
1692 Some(next_state.encode()?)
1693 } else {
1694 None
1695 };
1696
1697 let prev_cursor = if offset > 0 {
1698 let cursor_id = uuid::Uuid::new_v4().to_string();
1699 let mut prev_state = CursorState::new(
1700 cursor_id,
1701 command.to_string(),
1702 result_type,
1703 page_size,
1704 total_count,
1705 ttl_secs,
1706 );
1707 prev_state.offset = offset.saturating_sub(page_size);
1708
1709 self.cursor_store.insert(prev_state.clone())?;
1710
1711 Some(prev_state.encode()?)
1712 } else {
1713 None
1714 };
1715
1716 Ok(PagedQueryResult {
1717 result: paged_result,
1718 next_cursor,
1719 prev_cursor,
1720 total_count,
1721 has_more,
1722 page_size,
1723 })
1724 }
1725
1726 #[allow(clippy::unused_self)] fn apply_pagination(
1729 &self,
1730 result: &QueryResult,
1731 cursor_state: Option<&CursorState>,
1732 page_size: usize,
1733 count_total: bool,
1734 ) -> Result<(CursorResultType, Option<usize>, QueryResult)> {
1735 let offset = cursor_state.map_or(0, |s| s.offset);
1736
1737 match result {
1738 QueryResult::Rows(rows) => {
1739 let total = if count_total { Some(rows.len()) } else { None };
1740 let paged: Vec<_> = rows.iter().skip(offset).take(page_size).cloned().collect();
1741 Ok((CursorResultType::Rows, total, QueryResult::Rows(paged)))
1742 },
1743 QueryResult::Nodes(nodes) => {
1744 let total = if count_total { Some(nodes.len()) } else { None };
1745 let paged: Vec<_> = nodes.iter().skip(offset).take(page_size).cloned().collect();
1746 Ok((CursorResultType::Nodes, total, QueryResult::Nodes(paged)))
1747 },
1748 QueryResult::Edges(edges) => {
1749 let total = if count_total { Some(edges.len()) } else { None };
1750 let paged: Vec<_> = edges.iter().skip(offset).take(page_size).cloned().collect();
1751 Ok((CursorResultType::Edges, total, QueryResult::Edges(paged)))
1752 },
1753 QueryResult::Similar(items) => {
1754 let total = if count_total { Some(items.len()) } else { None };
1755 let paged: Vec<_> = items.iter().skip(offset).take(page_size).cloned().collect();
1756 Ok((
1757 CursorResultType::Similar,
1758 total,
1759 QueryResult::Similar(paged),
1760 ))
1761 },
1762 QueryResult::Unified(unified) => {
1763 let total = if count_total {
1764 Some(unified.items.len())
1765 } else {
1766 None
1767 };
1768 let paged_items: Vec<_> = unified
1769 .items
1770 .iter()
1771 .skip(offset)
1772 .take(page_size)
1773 .cloned()
1774 .collect();
1775 Ok((
1776 CursorResultType::Unified,
1777 total,
1778 QueryResult::Unified(UnifiedResult {
1779 description: unified.description.clone(),
1780 items: paged_items,
1781 }),
1782 ))
1783 },
1784 QueryResult::PatternMatch(pattern) => {
1785 let total = if count_total {
1786 Some(pattern.matches.len())
1787 } else {
1788 None
1789 };
1790 let paged_matches: Vec<_> = pattern
1791 .matches
1792 .iter()
1793 .skip(offset)
1794 .take(page_size)
1795 .cloned()
1796 .collect();
1797 Ok((
1798 CursorResultType::PatternMatch,
1799 total,
1800 QueryResult::PatternMatch(PatternMatchResultValue {
1801 matches: paged_matches,
1802 stats: pattern.stats.clone(),
1803 }),
1804 ))
1805 },
1806 _ => Err(RouterError::InvalidArgument(
1808 "Result type does not support pagination".to_string(),
1809 )),
1810 }
1811 }
1812
1813 pub fn close_cursor(&self, cursor_token: &str) -> Result<bool> {
1821 let state = CursorState::decode(cursor_token)?;
1822 Ok(self.cursor_store.remove(&state.id))
1823 }
1824
1825 #[must_use]
1827 pub const fn cursor_store(&self) -> &Arc<CursorStore> {
1828 &self.cursor_store
1829 }
1830
1831 fn try_execute_distributed(&self, command: &str) -> Option<Result<QueryResult>> {
1834 let planner = self.distributed_planner.as_ref()?;
1835 let cluster = self.cluster.as_ref()?;
1836 let runtime = self.cluster_runtime.as_ref()?;
1837
1838 let plan = planner.plan(command);
1839
1840 match plan {
1841 QueryPlan::Local { .. } => None, QueryPlan::Remote { shard, query } => {
1843 Some(self.execute_on_shard(runtime, cluster, shard, &query))
1845 },
1846 QueryPlan::ScatterGather {
1847 shards,
1848 query,
1849 merge,
1850 } => {
1851 Some(self.execute_scatter_gather(runtime, cluster, &shards, &query, &merge))
1853 },
1854 }
1855 }
1856
1857 fn execute_on_shard(
1859 &self,
1860 runtime: &Runtime,
1861 cluster: &ClusterOrchestrator,
1862 shard: ShardId,
1863 query: &str,
1864 ) -> Result<QueryResult> {
1865 let nodes = cluster.membership().view().nodes;
1866 let node_id = nodes
1867 .get(shard)
1868 .map(|n| n.node_id.clone())
1869 .ok_or_else(|| RouterError::InvalidArgument(format!("Shard {shard} not found")))?;
1870
1871 let request = tensor_chain::QueryRequest {
1873 query_id: rand::random(),
1874 query: query.to_string(),
1875 shard_id: shard,
1876 embedding: None,
1877 timeout_ms: self.distributed_config.shard_timeout_ms,
1878 };
1879
1880 let response = runtime.block_on(async {
1881 cluster
1882 .send_query(&node_id, request)
1883 .await
1884 .map_err(|e| RouterError::ChainError(e.to_string()))
1885 })?;
1886
1887 if response.success {
1888 bitcode::deserialize(&response.result)
1889 .map_err(|e| RouterError::ChainError(format!("Deserialization error: {e}")))
1890 } else {
1891 Err(RouterError::ChainError(
1892 response
1893 .error
1894 .unwrap_or_else(|| "Unknown error".to_string()),
1895 ))
1896 }
1897 }
1898
1899 fn execute_scatter_gather(
1901 &self,
1902 runtime: &Runtime,
1903 cluster: &ClusterOrchestrator,
1904 shards: &[ShardId],
1905 query: &str,
1906 merge_strategy: &MergeStrategy,
1907 ) -> Result<QueryResult> {
1908 let nodes = cluster.membership().view().nodes;
1909
1910 let results: Vec<ShardResult> = runtime.block_on(async {
1912 let mut results = Vec::with_capacity(shards.len());
1913
1914 for &shard in shards {
1915 let start = std::time::Instant::now();
1916
1917 if shard == self.local_shard_id {
1919 match self.execute_parsed_local(query) {
1921 Ok(result) => {
1922 #[allow(clippy::cast_possible_truncation)]
1923 let elapsed = start.elapsed().as_micros() as u64;
1925 results.push(ShardResult::success(shard, result, elapsed));
1926 },
1927 Err(e) => {
1928 if self.distributed_config.fail_fast {
1929 return vec![ShardResult::error(shard, e.to_string())];
1930 }
1931 results.push(ShardResult::error(shard, e.to_string()));
1932 },
1933 }
1934 continue;
1935 }
1936
1937 let Some(n) = nodes.get(shard) else {
1939 results.push(ShardResult::error(
1940 shard,
1941 format!("Shard {shard} not found"),
1942 ));
1943 continue;
1944 };
1945 let node_id = n.node_id.clone();
1946
1947 let request = tensor_chain::QueryRequest {
1949 query_id: rand::random(),
1950 query: query.to_string(),
1951 shard_id: shard,
1952 embedding: None,
1953 timeout_ms: self.distributed_config.shard_timeout_ms,
1954 };
1955
1956 match cluster.send_query(&node_id, request).await {
1957 Ok(response) => {
1958 if response.success {
1959 match bitcode::deserialize(&response.result) {
1960 Ok(result) => {
1961 results.push(ShardResult::success(
1962 shard,
1963 result,
1964 response.execution_time_us,
1965 ));
1966 },
1967 Err(e) => {
1968 results.push(ShardResult::error(
1969 shard,
1970 format!("Deserialization error: {e}"),
1971 ));
1972 },
1973 }
1974 } else {
1975 results.push(ShardResult::error(
1976 shard,
1977 response
1978 .error
1979 .unwrap_or_else(|| "Unknown error".to_string()),
1980 ));
1981 }
1982 },
1983 Err(e) => {
1984 results.push(ShardResult::error(shard, e.to_string()));
1985 },
1986 }
1987 }
1988
1989 results
1990 });
1991
1992 ResultMerger::merge(results, merge_strategy)
1994 }
1995
1996 fn execute_parsed_local(&self, command: &str) -> Result<QueryResult> {
1998 let stmt = parser::parse(command)
1999 .map_err(|e| RouterError::ParseError(e.format_with_source(command)))?;
2000 self.execute_statement(&stmt)
2001 }
2002
2003 pub fn execute_parsed(&self, command: &str) -> Result<QueryResult> {
2014 if let Some(result) = self.try_execute_distributed(command) {
2016 return result;
2017 }
2018
2019 let stmt = parser::parse(command)
2020 .map_err(|e| RouterError::ParseError(e.format_with_source(command)))?;
2021
2022 if Self::is_cacheable_statement(&stmt) {
2024 if let Some(cached) = self.try_cache_get(command) {
2025 return Ok(cached);
2026 }
2027 }
2028
2029 let result = self.execute_statement(&stmt)?;
2031
2032 if Self::is_cacheable_statement(&stmt) {
2034 self.try_cache_put(command, &result);
2035 }
2036
2037 if Self::is_write_statement(&stmt) {
2039 self.invalidate_cache_on_write();
2040 }
2041
2042 Ok(result)
2043 }
2044
2045 #[instrument(skip(self, stmt))]
2051 #[allow(clippy::too_many_lines)] pub fn execute_statement(&self, stmt: &Statement) -> Result<QueryResult> {
2053 match &stmt.kind {
2054 StatementKind::Select(select) => self.exec_select(select),
2056 StatementKind::Insert(insert) => self.exec_insert(insert),
2057 StatementKind::Update(update) => self.exec_update(update),
2058 StatementKind::Delete(delete) => self.exec_delete(delete),
2059 StatementKind::CreateTable(create) => self.exec_create_table(create),
2060 StatementKind::DropTable(drop) => {
2061 let table = &drop.table.name;
2062 let (row_count, sample_data) = self.collect_table_sample(table, 5);
2063 let op = DestructiveOp::DropTable {
2064 table: table.clone(),
2065 row_count,
2066 };
2067 match self.protect_destructive_op(&format!("DROP TABLE {table}"), op, sample_data) {
2068 ProtectedOpResult::Proceed => {},
2069 ProtectedOpResult::Cancelled => {
2070 return Err(RouterError::CheckpointError(
2071 "Operation cancelled by user".to_string(),
2072 ));
2073 },
2074 }
2075 self.relational.drop_table(table)?;
2076 Ok(QueryResult::Empty)
2077 },
2078 StatementKind::CreateIndex(create) => {
2079 if let Some(col) = create.columns.first() {
2081 self.relational
2082 .create_index(&create.table.name, &col.name)?;
2083 }
2084 Ok(QueryResult::Empty)
2085 },
2086 StatementKind::DropIndex(drop) => {
2087 if let (Some(table), Some(column)) = (&drop.table, &drop.column) {
2088 if drop.if_exists && !self.relational.has_index(&table.name, &column.name) {
2090 return Ok(QueryResult::Empty);
2091 }
2092 let op = DestructiveOp::DropIndex {
2093 table: table.name.clone(),
2094 column: column.name.clone(),
2095 };
2096 match self.protect_destructive_op(
2097 &format!("DROP INDEX ON {}({})", table.name, column.name),
2098 op,
2099 vec![format!("index on {}.{}", table.name, column.name)],
2100 ) {
2101 ProtectedOpResult::Proceed => {},
2102 ProtectedOpResult::Cancelled => {
2103 return Err(RouterError::CheckpointError(
2104 "Operation cancelled by user".to_string(),
2105 ));
2106 },
2107 }
2108 self.relational.drop_index(&table.name, &column.name)?;
2109 Ok(QueryResult::Empty)
2110 } else if let Some(name) = &drop.name {
2111 Err(RouterError::ParseError(format!(
2114 "Named index '{}' not supported. Use: DROP INDEX ON table(column)",
2115 name.name
2116 )))
2117 } else {
2118 Err(RouterError::ParseError(
2119 "Invalid DROP INDEX syntax".to_string(),
2120 ))
2121 }
2122 },
2123 StatementKind::ShowTables => {
2124 let tables = self.relational.list_tables();
2125 Ok(QueryResult::TableList(tables))
2126 },
2127 StatementKind::ShowEmbeddings { limit } => {
2128 let limit_val = limit
2129 .as_ref()
2130 .map(|e| self.expr_to_usize(e))
2131 .transpose()?
2132 .unwrap_or(100);
2133 let keys = self.vector.list_keys();
2134 let limited: Vec<String> = keys.into_iter().take(limit_val).collect();
2135 Ok(QueryResult::Value(format!("Embeddings: {limited:?}")))
2136 },
2137 StatementKind::ShowVectorIndex => match &self.hnsw_index {
2138 Some((_, keys)) => {
2139 let count = keys.len();
2140 Ok(QueryResult::Value(format!(
2141 "HNSW index: {count} vectors indexed"
2142 )))
2143 },
2144 None => Ok(QueryResult::Value("No HNSW index built".to_string())),
2145 },
2146 StatementKind::CountEmbeddings => {
2147 let count = self.vector.list_keys().len();
2148 Ok(QueryResult::Count(count))
2149 },
2150 StatementKind::Describe(desc) => self.exec_describe(desc),
2151
2152 StatementKind::Node(node) => self.exec_node(node),
2154 StatementKind::Edge(edge) => self.exec_edge(edge),
2155 StatementKind::Neighbors(neighbors) => self.exec_neighbors(neighbors),
2156 StatementKind::Path(path) => self.exec_path(path),
2157
2158 StatementKind::Embed(embed) => self.exec_embed(embed),
2160 StatementKind::Similar(similar) => self.exec_similar(similar),
2161
2162 StatementKind::Spatial(spatial) => self.exec_spatial(spatial),
2164
2165 StatementKind::Find(find) => self.exec_find(find),
2167 StatementKind::Entity(entity) => self.exec_entity(entity),
2168
2169 StatementKind::Vault(vault) => self.exec_vault(vault),
2171
2172 StatementKind::Cache(cache) => self.exec_cache(cache),
2174
2175 StatementKind::Blob(blob) => self.exec_blob(blob),
2177 StatementKind::Blobs(blobs) => self.exec_blobs(blobs),
2178
2179 StatementKind::Checkpoint(cp) => self.exec_checkpoint(cp),
2181 StatementKind::Rollback(rb) => self.exec_rollback(rb),
2182 StatementKind::Checkpoints(cps) => self.exec_checkpoints(cps),
2183
2184 StatementKind::Chain(chain) => self.exec_chain(chain),
2186
2187 StatementKind::Cluster(cluster) => self.exec_cluster(cluster),
2189
2190 StatementKind::GraphAlgorithm(algo) => self.exec_graph_algorithm(algo),
2192 StatementKind::GraphConstraint(constraint) => self.exec_graph_constraint(constraint),
2193 StatementKind::GraphIndex(idx) => self.exec_graph_index(idx),
2194 StatementKind::GraphAggregate(agg) => self.exec_graph_aggregate(agg),
2195 StatementKind::GraphPattern(pattern) => self.exec_graph_pattern(pattern),
2196 StatementKind::GraphBatch(batch) => self.exec_graph_batch(batch),
2197
2198 StatementKind::CypherMatch(stmt) => cypher::exec_cypher_match(&self.graph, stmt),
2200 StatementKind::CypherCreate(stmt) => cypher::exec_cypher_create(&self.graph, stmt),
2201 StatementKind::CypherDelete(stmt) => cypher::exec_cypher_delete(&self.graph, stmt),
2202 StatementKind::CypherMerge(stmt) => cypher::exec_cypher_merge(&self.graph, stmt),
2203
2204 StatementKind::Empty => Ok(QueryResult::Empty),
2206 }
2207 }
2208
2209 fn exec_describe(&self, desc: &DescribeStmt) -> Result<QueryResult> {
2212 match &desc.target {
2213 DescribeTarget::Table(name) => {
2214 use std::fmt::Write;
2215 let schema = self.relational.get_schema(&name.name)?;
2216 let mut info = format!("Table: {}\n", name.name);
2217 info.push_str("Columns:\n");
2218 for col in &schema.columns {
2219 let _ = writeln!(
2220 info,
2221 " {} {:?}{}",
2222 col.name,
2223 col.column_type,
2224 if col.nullable { "" } else { " NOT NULL" }
2225 );
2226 }
2227 Ok(QueryResult::Value(info))
2228 },
2229 DescribeTarget::Node(label) => {
2230 let total_nodes = self.graph.node_count();
2232 Ok(QueryResult::Value(format!(
2233 "Node label '{}': Use NODE LIST {} to see nodes. Total nodes in graph: {}",
2234 label.name, label.name, total_nodes
2235 )))
2236 },
2237 DescribeTarget::Edge(edge_type) => {
2238 let total_edges = self.graph.edge_count();
2240 Ok(QueryResult::Value(format!(
2241 "Edge type '{}': Use EDGE LIST {} to see edges. Total edges in graph: {}",
2242 edge_type.name, edge_type.name, total_edges
2243 )))
2244 },
2245 }
2246 }
2247
2248 #[allow(clippy::too_many_lines)] fn exec_cache(&self, stmt: &CacheStmt) -> Result<QueryResult> {
2252 let _identity = self.require_identity()?;
2253
2254 let cache = self
2255 .cache
2256 .as_ref()
2257 .ok_or_else(|| RouterError::CacheError("Cache not initialized".to_string()))?;
2258
2259 match &stmt.operation {
2260 CacheOp::Init => {
2261 Ok(QueryResult::Value("Cache initialized".to_string()))
2263 },
2264 CacheOp::Stats => {
2265 let stats = cache.stats();
2266 let (tokens_in, tokens_out) = stats.tokens_saved();
2267 let output = format!(
2268 "Cache Statistics:\n\
2269 Exact: {} hits, {} misses\n\
2270 Semantic: {} hits, {} misses\n\
2271 Embedding: {} hits, {} misses\n\
2272 Tokens saved: {} in, {} out\n\
2273 Evictions: {}",
2274 stats.hits(CacheLayer::Exact),
2275 stats.misses(CacheLayer::Exact),
2276 stats.hits(CacheLayer::Semantic),
2277 stats.misses(CacheLayer::Semantic),
2278 stats.hits(CacheLayer::Embedding),
2279 stats.misses(CacheLayer::Embedding),
2280 tokens_in,
2281 tokens_out,
2282 stats.evictions(),
2283 );
2284 Ok(QueryResult::Value(output))
2285 },
2286 CacheOp::Clear => {
2287 let entry_count = cache.stats().total_entries();
2289
2290 let op = DestructiveOp::CacheClear { entry_count };
2292
2293 match self.protect_destructive_op(
2294 "CACHE CLEAR",
2295 op,
2296 vec![format!("{} cached entries", entry_count)],
2297 ) {
2298 ProtectedOpResult::Proceed => {},
2299 ProtectedOpResult::Cancelled => {
2300 return Err(RouterError::CheckpointError(
2301 "Operation cancelled by user".to_string(),
2302 ));
2303 },
2304 }
2305
2306 cache.clear();
2307 Ok(QueryResult::Value("Cache cleared".to_string()))
2308 },
2309 CacheOp::Evict { count } => {
2310 let count_val = match count {
2311 Some(expr) => self.expr_to_usize(expr)?,
2312 None => 100, };
2314 let evicted = cache.evict(count_val);
2315 Ok(QueryResult::Value(format!("Evicted {evicted} entries")))
2316 },
2317 CacheOp::Get { key } => {
2318 let key_str = self.expr_to_string(key)?;
2319 Ok(QueryResult::Value(
2320 cache
2321 .get_simple(&key_str)
2322 .unwrap_or_else(|| "(not found)".to_string()),
2323 ))
2324 },
2325 CacheOp::Put { key, value } => {
2326 let key_str = self.expr_to_string(key)?;
2327 let value_str = self.expr_to_string(value)?;
2328 cache
2329 .put_simple(&key_str, &value_str)
2330 .map_err(|e| RouterError::CacheError(e.to_string()))?;
2331 Ok(QueryResult::Value("OK".to_string()))
2332 },
2333 CacheOp::SemanticGet { query, threshold } => {
2334 let query_str = self.expr_to_string(query)?;
2335 let embedding = self.vector.get_embedding(&query_str).ok();
2337 let _threshold = threshold
2338 .as_ref()
2339 .map(|e| self.expr_to_f32(e))
2340 .transpose()?;
2341
2342 match cache.get(&query_str, embedding.as_deref()) {
2343 Some(hit) => {
2344 let similarity_str = hit
2345 .similarity
2346 .map(|s| format!(", similarity: {s:.4}"))
2347 .unwrap_or_default();
2348 Ok(QueryResult::Value(format!(
2349 "response: {}, layer: {:?}{}",
2350 hit.response, hit.layer, similarity_str
2351 )))
2352 },
2353 None => Ok(QueryResult::Value("(not found)".to_string())),
2354 }
2355 },
2356 CacheOp::SemanticPut {
2357 query,
2358 response,
2359 embedding,
2360 } => {
2361 let query_str = self.expr_to_string(query)?;
2362 let response_str = self.expr_to_string(response)?;
2363 let emb: Vec<f32> = embedding
2364 .iter()
2365 .map(|e| self.expr_to_f32(e))
2366 .collect::<Result<_>>()?;
2367
2368 cache
2369 .put(&query_str, &emb, &response_str, "manual", None)
2370 .map_err(|e| RouterError::CacheError(e.to_string()))?;
2371 Ok(QueryResult::Value("OK".to_string()))
2372 },
2373 }
2374 }
2375
2376 const fn is_cacheable_statement(stmt: &Statement) -> bool {
2379 matches!(
2380 &stmt.kind,
2381 StatementKind::Select(_)
2382 | StatementKind::Similar(_)
2383 | StatementKind::Neighbors(_)
2384 | StatementKind::Path(_)
2385 )
2386 }
2387
2388 #[allow(clippy::match_same_arms)] const fn is_write_statement(stmt: &Statement) -> bool {
2390 match &stmt.kind {
2391 StatementKind::Insert(_)
2393 | StatementKind::Update(_)
2394 | StatementKind::Delete(_)
2395 | StatementKind::CreateTable(_)
2396 | StatementKind::DropTable(_)
2397 | StatementKind::CreateIndex(_)
2398 | StatementKind::DropIndex(_) => true,
2399
2400 StatementKind::GraphBatch(_)
2402 | StatementKind::GraphConstraint(_)
2403 | StatementKind::GraphIndex(_)
2404 | StatementKind::CypherCreate(_)
2405 | StatementKind::CypherDelete(_)
2406 | StatementKind::CypherMerge(_)
2407 | StatementKind::Rollback(_) => true,
2408
2409 StatementKind::Node(n) => {
2411 matches!(&n.operation, NodeOp::Create { .. } | NodeOp::Delete { .. })
2412 },
2413 StatementKind::Edge(e) => {
2414 matches!(&e.operation, EdgeOp::Create { .. } | EdgeOp::Delete { .. })
2415 },
2416
2417 StatementKind::Embed(e) => matches!(
2419 &e.operation,
2420 EmbedOp::Store { .. } | EmbedOp::Delete { .. } | EmbedOp::Batch { .. }
2421 ),
2422
2423 StatementKind::Spatial(s) => {
2425 matches!(&s.op, SpatialOp::Insert { .. } | SpatialOp::Delete { .. })
2426 },
2427
2428 StatementKind::Entity(e) => matches!(
2430 &e.operation,
2431 EntityOp::Create { .. }
2432 | EntityOp::Update { .. }
2433 | EntityOp::Delete { .. }
2434 | EntityOp::Connect { .. }
2435 | EntityOp::Batch { .. }
2436 ),
2437
2438 StatementKind::Vault(v) => matches!(
2440 &v.operation,
2441 VaultOp::Set { .. } | VaultOp::Delete { .. } | VaultOp::Rotate { .. }
2442 ),
2443
2444 StatementKind::Blob(b) => matches!(
2446 &b.operation,
2447 BlobOp::Put { .. }
2448 | BlobOp::Delete { .. }
2449 | BlobOp::Link { .. }
2450 | BlobOp::Unlink { .. }
2451 | BlobOp::Tag { .. }
2452 | BlobOp::Untag { .. }
2453 | BlobOp::MetaSet { .. }
2454 ),
2455
2456 _ => false,
2459 }
2460 }
2461
2462 fn cache_key_for_query(command: &str) -> String {
2463 format!("query:{}", command.trim().to_lowercase())
2464 }
2465
2466 fn try_cache_get(&self, command: &str) -> Option<QueryResult> {
2467 let cache = self.cache.as_ref()?;
2468 let key = Self::cache_key_for_query(command);
2469 let json = cache.get_simple(&key)?;
2470 serde_json::from_str(&json).ok()
2471 }
2472
2473 fn try_cache_put(&self, command: &str, result: &QueryResult) {
2474 if let Some(cache) = self.cache.as_ref() {
2475 let key = Self::cache_key_for_query(command);
2476 if let Ok(json) = serde_json::to_string(result) {
2477 let _ = cache.put_simple(&key, &json);
2479 }
2480 }
2481 }
2482
2483 fn invalidate_cache_on_write(&self) {
2484 if let Some(cache) = self.cache.as_ref() {
2485 cache.clear();
2488 }
2489 }
2490
2491 fn exec_vault(&self, stmt: &VaultStmt) -> Result<QueryResult> {
2494 let vault = self
2495 .vault
2496 .as_ref()
2497 .ok_or_else(|| RouterError::VaultError("Vault not initialized".to_string()))?;
2498
2499 let identity = self.require_identity()?;
2501
2502 match &stmt.operation {
2503 VaultOp::Set { key, value } => {
2504 let key_str = self.eval_string_expr(key)?;
2505 let value_str = self.eval_string_expr(value)?;
2506 vault.set(identity, &key_str, &value_str)?;
2507 Ok(QueryResult::Empty)
2508 },
2509 VaultOp::Get { key } => {
2510 let key_str = self.eval_string_expr(key)?;
2511 let value = vault.get(identity, &key_str)?;
2512 Ok(QueryResult::Value(value))
2513 },
2514 VaultOp::Delete { key } => {
2515 let key_str = self.eval_string_expr(key)?;
2516
2517 let op = DestructiveOp::VaultDelete {
2519 key: key_str.clone(),
2520 };
2521
2522 match self.protect_destructive_op(
2523 &format!("VAULT DELETE '{key_str}'"),
2524 op,
2525 vec![format!("secret key: {}", key_str)],
2526 ) {
2527 ProtectedOpResult::Proceed => {},
2528 ProtectedOpResult::Cancelled => {
2529 return Err(RouterError::CheckpointError(
2530 "Operation cancelled by user".to_string(),
2531 ));
2532 },
2533 }
2534
2535 vault.delete(identity, &key_str)?;
2536 Ok(QueryResult::Empty)
2537 },
2538 VaultOp::List { pattern } => {
2539 let pat = pattern
2540 .as_ref()
2541 .map(|p| self.eval_string_expr(p))
2542 .transpose()?
2543 .unwrap_or_else(|| "*".to_string());
2544 let keys = vault.list(identity, &pat)?;
2545 Ok(QueryResult::Value(keys.join("\n")))
2546 },
2547 VaultOp::Rotate { key, new_value } => {
2548 let key_str = self.eval_string_expr(key)?;
2549 let new_value_str = self.eval_string_expr(new_value)?;
2550 vault.rotate(identity, &key_str, &new_value_str)?;
2551 Ok(QueryResult::Empty)
2552 },
2553 VaultOp::Grant { entity, key } => {
2554 let entity_str = self.eval_string_expr(entity)?;
2555 let key_str = self.eval_string_expr(key)?;
2556 vault.grant(identity, &entity_str, &key_str)?;
2557 Ok(QueryResult::Empty)
2558 },
2559 VaultOp::Revoke { entity, key } => {
2560 let entity_str = self.eval_string_expr(entity)?;
2561 let key_str = self.eval_string_expr(key)?;
2562 vault.revoke(identity, &entity_str, &key_str)?;
2563 Ok(QueryResult::Empty)
2564 },
2565 }
2566 }
2567
2568 #[allow(clippy::unused_self)] fn eval_string_expr(&self, expr: &Expr) -> Result<String> {
2570 match &expr.kind {
2571 ExprKind::Literal(Literal::String(s)) => Ok(s.clone()),
2572 ExprKind::Ident(ident) => Ok(ident.name.clone()),
2573 _ => Err(RouterError::InvalidArgument(
2574 "Expected string literal or identifier".to_string(),
2575 )),
2576 }
2577 }
2578
2579 #[allow(clippy::too_many_lines)] fn exec_blob(&self, stmt: &BlobStmt) -> Result<QueryResult> {
2583 let _identity = self.require_identity()?;
2584
2585 if matches!(stmt.operation, BlobOp::Init) {
2587 if self.blob.is_some() {
2588 return Ok(QueryResult::Value(
2589 "Blob store already initialized".to_string(),
2590 ));
2591 }
2592 return Err(RouterError::BlobError(
2593 "Use router.init_blob() to initialize blob storage".to_string(),
2594 ));
2595 }
2596
2597 let blob = self
2598 .blob
2599 .as_ref()
2600 .ok_or_else(|| RouterError::BlobError("Blob store not initialized".to_string()))?;
2601 let runtime = self
2602 .blob_runtime
2603 .as_ref()
2604 .ok_or_else(|| RouterError::BlobError("Blob runtime not initialized".to_string()))?;
2605
2606 match &stmt.operation {
2607 BlobOp::Init => unreachable!(), BlobOp::Put {
2609 filename,
2610 data,
2611 from_path,
2612 options,
2613 } => {
2614 let filename_str = self.eval_string_expr(filename)?;
2615 let put_options = self.blob_options_to_put_options(options)?;
2616
2617 let blob_data = if let Some(data_expr) = data {
2619 self.expr_to_bytes(data_expr)?
2620 } else if let Some(path_expr) = from_path {
2621 let path = self.eval_string_expr(path_expr)?;
2622 std::fs::read(&path)
2623 .map_err(|e| RouterError::BlobError(format!("Failed to read file: {e}")))?
2624 } else {
2625 return Err(RouterError::MissingArgument(
2626 "PUT requires either DATA or FROM path".to_string(),
2627 ));
2628 };
2629
2630 let artifact_id = runtime.block_on(async {
2631 let blob_guard = blob.lock().await;
2632 blob_guard.put(&filename_str, &blob_data, put_options).await
2633 })?;
2634 Ok(QueryResult::Value(artifact_id))
2635 },
2636 BlobOp::Get {
2637 artifact_id,
2638 to_path,
2639 } => {
2640 let id = self.eval_string_expr(artifact_id)?;
2641 let data = runtime.block_on(async {
2642 let blob_guard = blob.lock().await;
2643 blob_guard.get(&id).await
2644 })?;
2645
2646 if let Some(path_expr) = to_path {
2647 let path = self.eval_string_expr(path_expr)?;
2648 std::fs::write(&path, &data).map_err(|e| {
2649 RouterError::BlobError(format!("Failed to write file: {e}"))
2650 })?;
2651 Ok(QueryResult::Value(format!(
2652 "Written {} bytes to {path}",
2653 data.len()
2654 )))
2655 } else {
2656 Ok(QueryResult::Blob(data))
2657 }
2658 },
2659 BlobOp::Delete { artifact_id } => {
2660 let id = self.eval_string_expr(artifact_id)?;
2661
2662 let size = runtime
2664 .block_on(async {
2665 let blob_guard = blob.lock().await;
2666 blob_guard.metadata(&id).await
2667 })
2668 .map(|m| m.size)
2669 .unwrap_or(0);
2670
2671 let op = DestructiveOp::BlobDelete {
2673 artifact_id: id.clone(),
2674 size,
2675 };
2676
2677 match self.protect_destructive_op(
2678 &format!("BLOB DELETE '{id}'"),
2679 op,
2680 vec![format!("artifact: {}, size: {} bytes", id, size)],
2681 ) {
2682 ProtectedOpResult::Proceed => {},
2683 ProtectedOpResult::Cancelled => {
2684 return Err(RouterError::CheckpointError(
2685 "Operation cancelled by user".to_string(),
2686 ));
2687 },
2688 }
2689
2690 runtime.block_on(async {
2691 let blob_guard = blob.lock().await;
2692 blob_guard.delete(&id).await
2693 })?;
2694 Ok(QueryResult::Empty)
2695 },
2696 BlobOp::Info { artifact_id } => {
2697 let id = self.eval_string_expr(artifact_id)?;
2698 let meta = runtime.block_on(async {
2699 let blob_guard = blob.lock().await;
2700 blob_guard.metadata(&id).await
2701 })?;
2702
2703 Ok(QueryResult::ArtifactInfo(ArtifactInfoResult {
2704 id: meta.id,
2705 filename: meta.filename,
2706 content_type: meta.content_type,
2707 size: meta.size,
2708 checksum: meta.checksum,
2709 chunk_count: meta.chunk_count,
2710 created: meta.created,
2711 modified: meta.modified,
2712 created_by: meta.created_by,
2713 tags: meta.tags,
2714 linked_to: meta.linked_to,
2715 custom: meta.custom,
2716 }))
2717 },
2718 BlobOp::Link {
2719 artifact_id,
2720 entity,
2721 } => {
2722 let id = self.eval_string_expr(artifact_id)?;
2723 let entity_str = self.eval_string_expr(entity)?;
2724 runtime.block_on(async {
2725 let blob_guard = blob.lock().await;
2726 blob_guard.link(&id, &entity_str).await
2727 })?;
2728 Ok(QueryResult::Empty)
2729 },
2730 BlobOp::Unlink {
2731 artifact_id,
2732 entity,
2733 } => {
2734 let id = self.eval_string_expr(artifact_id)?;
2735 let entity_str = self.eval_string_expr(entity)?;
2736 runtime.block_on(async {
2737 let blob_guard = blob.lock().await;
2738 blob_guard.unlink(&id, &entity_str).await
2739 })?;
2740 Ok(QueryResult::Empty)
2741 },
2742 BlobOp::Links { artifact_id } => {
2743 let id = self.eval_string_expr(artifact_id)?;
2744 let links = runtime.block_on(async {
2745 let blob_guard = blob.lock().await;
2746 blob_guard.links(&id).await
2747 })?;
2748 Ok(QueryResult::ArtifactList(links))
2749 },
2750 BlobOp::Tag { artifact_id, tag } => {
2751 let id = self.eval_string_expr(artifact_id)?;
2752 let tag_str = self.eval_string_expr(tag)?;
2753 runtime.block_on(async {
2754 let blob_guard = blob.lock().await;
2755 blob_guard.tag(&id, &tag_str).await
2756 })?;
2757 Ok(QueryResult::Empty)
2758 },
2759 BlobOp::Untag { artifact_id, tag } => {
2760 let id = self.eval_string_expr(artifact_id)?;
2761 let tag_str = self.eval_string_expr(tag)?;
2762 runtime.block_on(async {
2763 let blob_guard = blob.lock().await;
2764 blob_guard.untag(&id, &tag_str).await
2765 })?;
2766 Ok(QueryResult::Empty)
2767 },
2768 BlobOp::Verify { artifact_id } => {
2769 let id = self.eval_string_expr(artifact_id)?;
2770 let valid = runtime.block_on(async {
2771 let blob_guard = blob.lock().await;
2772 blob_guard.verify(&id)
2773 })?;
2774 Ok(QueryResult::Value(if valid {
2775 "OK".to_string()
2776 } else {
2777 "INVALID".to_string()
2778 }))
2779 },
2780 BlobOp::Gc { full } => {
2781 let stats = runtime.block_on(async {
2782 let blob_guard = blob.lock().await;
2783 if *full {
2784 blob_guard.full_gc().await
2785 } else {
2786 blob_guard.gc().await
2787 }
2788 })?;
2789 Ok(QueryResult::Value(format!(
2790 "Deleted {} chunks, freed {} bytes",
2791 stats.deleted, stats.freed_bytes
2792 )))
2793 },
2794 BlobOp::Repair => {
2795 let stats = runtime.block_on(async {
2796 let blob_guard = blob.lock().await;
2797 blob_guard.repair()
2798 })?;
2799 Ok(QueryResult::Value(format!(
2800 "Fixed {} refs, deleted {} orphans",
2801 stats.refs_fixed, stats.orphans_deleted
2802 )))
2803 },
2804 BlobOp::Stats => {
2805 let stats = runtime.block_on(async {
2806 let blob_guard = blob.lock().await;
2807 blob_guard.stats().await
2808 })?;
2809 Ok(QueryResult::BlobStats(BlobStatsResult {
2810 artifact_count: stats.artifact_count,
2811 chunk_count: stats.chunk_count,
2812 total_bytes: stats.total_bytes,
2813 unique_bytes: stats.unique_bytes,
2814 dedup_ratio: stats.dedup_ratio,
2815 orphaned_chunks: stats.orphaned_chunks,
2816 }))
2817 },
2818 BlobOp::MetaSet {
2819 artifact_id,
2820 key,
2821 value,
2822 } => {
2823 let id = self.eval_string_expr(artifact_id)?;
2824 let key_str = self.eval_string_expr(key)?;
2825 let value_str = self.eval_string_expr(value)?;
2826 runtime.block_on(async {
2827 let blob_guard = blob.lock().await;
2828 blob_guard.set_meta(&id, &key_str, &value_str).await
2829 })?;
2830 Ok(QueryResult::Empty)
2831 },
2832 BlobOp::MetaGet { artifact_id, key } => {
2833 let id = self.eval_string_expr(artifact_id)?;
2834 let key_str = self.eval_string_expr(key)?;
2835 let value = runtime.block_on(async {
2836 let blob_guard = blob.lock().await;
2837 blob_guard.get_meta(&id, &key_str).await
2838 })?;
2839 Ok(QueryResult::Value(
2840 value.unwrap_or_else(|| "(not found)".to_string()),
2841 ))
2842 },
2843 }
2844 }
2845
2846 fn exec_blobs(&self, stmt: &BlobsStmt) -> Result<QueryResult> {
2847 let _identity = self.require_identity()?;
2848
2849 let blob = self
2850 .blob
2851 .as_ref()
2852 .ok_or_else(|| RouterError::BlobError("Blob store not initialized".to_string()))?;
2853 let runtime = self
2854 .blob_runtime
2855 .as_ref()
2856 .ok_or_else(|| RouterError::BlobError("Blob runtime not initialized".to_string()))?;
2857
2858 match &stmt.operation {
2859 BlobsOp::List { pattern } => {
2860 let prefix = pattern
2861 .as_ref()
2862 .map(|p| self.eval_string_expr(p))
2863 .transpose()?;
2864 let ids = runtime.block_on(async {
2865 let blob_guard = blob.lock().await;
2866 blob_guard.list(prefix.as_deref()).await
2867 })?;
2868 Ok(QueryResult::ArtifactList(ids))
2869 },
2870 BlobsOp::For { entity } => {
2871 let entity_str = self.eval_string_expr(entity)?;
2872 let ids = runtime.block_on(async {
2873 let blob_guard = blob.lock().await;
2874 blob_guard.artifacts_for(&entity_str).await
2875 })?;
2876 Ok(QueryResult::ArtifactList(ids))
2877 },
2878 BlobsOp::ByTag { tag } => {
2879 let tag_str = self.eval_string_expr(tag)?;
2880 let ids = runtime.block_on(async {
2881 let blob_guard = blob.lock().await;
2882 blob_guard.by_tag(&tag_str).await
2883 })?;
2884 Ok(QueryResult::ArtifactList(ids))
2885 },
2886 BlobsOp::ByType { content_type } => {
2887 let ct = self.eval_string_expr(content_type)?;
2888 let ids = runtime.block_on(async {
2889 let blob_guard = blob.lock().await;
2890 blob_guard.by_content_type(&ct).await
2891 })?;
2892 Ok(QueryResult::ArtifactList(ids))
2893 },
2894 BlobsOp::Similar { artifact_id, limit } => {
2895 let id = self.eval_string_expr(artifact_id)?;
2896 let k = limit
2897 .as_ref()
2898 .map(|e| self.expr_to_usize(e))
2899 .transpose()?
2900 .unwrap_or(10);
2901 let similar = runtime.block_on(async {
2902 let blob_guard = blob.lock().await;
2903 blob_guard.similar(&id, k).await
2904 })?;
2905 Ok(QueryResult::Similar(
2906 similar
2907 .into_iter()
2908 .map(|s| SimilarResult {
2909 key: s.id,
2910 score: s.similarity,
2911 })
2912 .collect(),
2913 ))
2914 },
2915 }
2916 }
2917
2918 fn blob_options_to_put_options(
2919 &self,
2920 options: &BlobOptions,
2921 ) -> Result<tensor_blob::PutOptions> {
2922 let mut put_options = tensor_blob::PutOptions::new();
2923
2924 if let Some(ct) = &options.content_type {
2925 put_options = put_options.with_content_type(&self.eval_string_expr(ct)?);
2926 }
2927
2928 if let Some(cb) = &options.created_by {
2929 put_options = put_options.with_created_by(&self.eval_string_expr(cb)?);
2930 }
2931
2932 for link_expr in &options.link {
2933 let link = self.eval_string_expr(link_expr)?;
2934 put_options = put_options.with_link(&link);
2935 }
2936
2937 for tag_expr in &options.tag {
2938 let tag = self.eval_string_expr(tag_expr)?;
2939 put_options = put_options.with_tag(&tag);
2940 }
2941
2942 Ok(put_options)
2943 }
2944
2945 #[allow(clippy::unused_self)] fn expr_to_bytes(&self, expr: &Expr) -> Result<Vec<u8>> {
2947 match &expr.kind {
2948 ExprKind::Literal(Literal::String(s)) => Ok(s.as_bytes().to_vec()),
2949 _ => Err(RouterError::InvalidArgument(
2951 "Expected string literal for blob data".to_string(),
2952 )),
2953 }
2954 }
2955
2956 fn exec_checkpoint(&self, stmt: &CheckpointStmt) -> Result<QueryResult> {
2959 let checkpoint = self.checkpoint.as_ref().ok_or_else(|| {
2960 RouterError::CheckpointError("Checkpoint manager not initialized".to_string())
2961 })?;
2962
2963 let name = stmt
2964 .name
2965 .as_ref()
2966 .map(|e| self.eval_string_expr(e))
2967 .transpose()?;
2968
2969 let store = self.vector.store();
2970 let checkpoint_id = checkpoint.create(name.as_deref(), store)?;
2971
2972 Ok(QueryResult::Value(format!(
2973 "Checkpoint created: {checkpoint_id}"
2974 )))
2975 }
2976
2977 fn exec_rollback(&self, stmt: &RollbackStmt) -> Result<QueryResult> {
2978 let checkpoint = self.checkpoint.as_ref().ok_or_else(|| {
2979 RouterError::CheckpointError("Checkpoint manager not initialized".to_string())
2980 })?;
2981
2982 let target = self.eval_string_expr(&stmt.target)?;
2983
2984 let store = self.vector.store();
2985 checkpoint.rollback(&target, store)?;
2986
2987 Ok(QueryResult::Value(format!(
2988 "Rolled back to checkpoint: {target}"
2989 )))
2990 }
2991
2992 fn exec_checkpoints(&self, stmt: &CheckpointsStmt) -> Result<QueryResult> {
2993 let checkpoint = self.checkpoint.as_ref().ok_or_else(|| {
2994 RouterError::CheckpointError("Checkpoint manager not initialized".to_string())
2995 })?;
2996
2997 let limit = stmt
2998 .limit
2999 .as_ref()
3000 .map(|e| self.expr_to_usize(e))
3001 .transpose()?;
3002
3003 let limit_opt = limit.or(Some(10));
3005
3006 let checkpoints = checkpoint.list(limit_opt)?;
3007
3008 let info_list: Vec<CheckpointInfo> = checkpoints
3009 .into_iter()
3010 .map(|cp| CheckpointInfo {
3011 id: cp.id,
3012 name: cp.name,
3013 created_at: cp.created_at,
3014 is_auto: cp.trigger.is_some(),
3015 })
3016 .collect();
3017
3018 Ok(QueryResult::CheckpointList(info_list))
3019 }
3020
3021 #[allow(clippy::too_many_lines)] fn exec_chain(&self, stmt: &ChainStmt) -> Result<QueryResult> {
3025 let _identity = self.require_identity()?;
3026
3027 let chain = self
3028 .chain
3029 .as_ref()
3030 .ok_or_else(|| RouterError::ChainError("Chain not initialized".to_string()))?;
3031
3032 match &stmt.operation {
3033 ChainOp::Begin => {
3034 let workspace = chain.begin()?;
3035 Ok(QueryResult::Chain(ChainResult::TransactionBegun {
3036 tx_id: workspace.id().to_string(),
3037 }))
3038 },
3039 ChainOp::Commit => {
3040 Ok(QueryResult::Chain(ChainResult::Committed {
3043 block_hash: "pending".to_string(),
3044 height: chain.height(),
3045 }))
3046 },
3047 ChainOp::Rollback { height } => {
3048 let h = self.expr_to_u64(height)?;
3049 Ok(QueryResult::Chain(ChainResult::RolledBack { to_height: h }))
3051 },
3052 ChainOp::History { key } => {
3053 let key_str = self.eval_string_expr(key)?;
3054 let history = chain.history(&key_str)?;
3055 let entries: Vec<ChainHistoryEntry> = history
3056 .into_iter()
3057 .map(|(height, tx)| ChainHistoryEntry {
3058 height,
3059 transaction_type: format!("{tx:?}"),
3060 data: None,
3061 })
3062 .collect();
3063 Ok(QueryResult::Chain(ChainResult::History(entries)))
3064 },
3065 ChainOp::Similar { embedding, limit } => {
3066 let _embedding: Vec<f32> = embedding
3067 .iter()
3068 .map(|e| self.expr_to_f32(e))
3069 .collect::<Result<Vec<_>>>()?;
3070 let _limit = limit.as_ref().map(|e| self.expr_to_usize(e)).transpose()?;
3071 Ok(QueryResult::Chain(ChainResult::Similar(vec![])))
3073 },
3074 ChainOp::Drift {
3075 from_height,
3076 to_height,
3077 } => {
3078 let from_h = self.expr_to_u64(from_height)?;
3079 let to_h = self.expr_to_u64(to_height)?;
3080 Ok(QueryResult::Chain(ChainResult::Drift(ChainDriftResult {
3082 from_height: from_h,
3083 to_height: to_h,
3084 total_drift: 0.0,
3085 avg_drift_per_block: 0.0,
3086 max_drift: 0.0,
3087 })))
3088 },
3089 ChainOp::Height => {
3090 let height = chain.height();
3091 Ok(QueryResult::Chain(ChainResult::Height(height)))
3092 },
3093 ChainOp::Tip => {
3094 let hash = chain.tip_hash();
3095 let height = chain.height();
3096 Ok(QueryResult::Chain(ChainResult::Tip {
3097 hash: hex::encode(hash),
3098 height,
3099 }))
3100 },
3101 ChainOp::Block { height } => {
3102 let h = self.expr_to_u64(height)?;
3103 if let Some(block) = chain.get_block(h)? {
3104 Ok(QueryResult::Chain(ChainResult::Block(ChainBlockInfo {
3105 height: h,
3106 hash: hex::encode(block.hash()),
3107 prev_hash: hex::encode(block.header.prev_hash),
3108 timestamp: block.header.timestamp,
3109 transaction_count: block.transactions.len(),
3110 proposer: block.header.proposer,
3111 })))
3112 } else {
3113 Err(RouterError::ChainError(format!("Block {h} not found")))
3114 }
3115 },
3116 ChainOp::Verify => match chain.verify() {
3117 Ok(()) => Ok(QueryResult::Chain(ChainResult::Verified {
3118 ok: true,
3119 errors: vec![],
3120 })),
3121 Err(e) => Ok(QueryResult::Chain(ChainResult::Verified {
3122 ok: false,
3123 errors: vec![e.to_string()],
3124 })),
3125 },
3126 ChainOp::ShowCodebookGlobal => {
3127 Ok(QueryResult::Chain(ChainResult::Codebook(
3129 ChainCodebookInfo {
3130 scope: "global".to_string(),
3131 entry_count: 0,
3132 dimension: 0,
3133 domain: None,
3134 },
3135 )))
3136 },
3137 ChainOp::ShowCodebookLocal { domain } => {
3138 let domain_str = self.eval_string_expr(domain)?;
3139 Ok(QueryResult::Chain(ChainResult::Codebook(
3140 ChainCodebookInfo {
3141 scope: "local".to_string(),
3142 entry_count: 0,
3143 dimension: 0,
3144 domain: Some(domain_str),
3145 },
3146 )))
3147 },
3148 ChainOp::AnalyzeTransitions => Ok(QueryResult::Chain(ChainResult::TransitionAnalysis(
3149 ChainTransitionAnalysis {
3150 total_transitions: 0,
3151 valid_transitions: 0,
3152 invalid_transitions: 0,
3153 avg_validity_score: 0.0,
3154 },
3155 ))),
3156 }
3157 }
3158
3159 #[allow(clippy::too_many_lines)] fn exec_cluster(&self, stmt: &ClusterStmt) -> Result<QueryResult> {
3163 match &stmt.operation {
3164 ClusterOp::Connect { addresses } => {
3165 let addr_str = self.eval_string_expr(addresses)?;
3167 Err(RouterError::InvalidArgument(format!(
3168 "CLUSTER CONNECT '{addr_str}' requires shell support. Use the shell command or call router.init_cluster() from code."
3169 )))
3170 },
3171 ClusterOp::Disconnect => {
3172 if self.cluster.is_none() {
3174 return Err(RouterError::InvalidArgument(
3175 "Not connected to cluster".to_string(),
3176 ));
3177 }
3178 Err(RouterError::InvalidArgument(
3179 "CLUSTER DISCONNECT requires shell support. Use the shell command or call router.shutdown_cluster() from code.".to_string(),
3180 ))
3181 },
3182 ClusterOp::Status => {
3183 self.cluster.as_ref().map_or_else(
3184 || {
3185 Ok(QueryResult::Value(
3186 "Cluster: single-node mode (not connected)".to_string(),
3187 ))
3188 },
3189 |cluster| {
3190 let node_id = cluster.node_id();
3191 let is_leader = cluster.is_leader();
3192 let leader = cluster
3193 .current_leader()
3194 .unwrap_or_else(|| "(unknown)".to_string());
3195 let height = cluster.chain_height();
3196 let commit_idx = cluster.commit_index();
3197
3198 let status = format!(
3199 "Cluster Status:\n Node ID: {}\n Role: {}\n Leader: {}\n Chain Height: {}\n Commit Index: {}",
3200 node_id,
3201 if is_leader { "Leader" } else { "Follower" },
3202 leader,
3203 height,
3204 commit_idx
3205 );
3206 Ok(QueryResult::Value(status))
3207 },
3208 )
3209 },
3210 ClusterOp::Nodes => {
3211 self.cluster.as_ref().map_or_else(
3212 || {
3213 Ok(QueryResult::Value(
3214 "No cluster nodes (single-node mode)".to_string(),
3215 ))
3216 },
3217 |cluster| {
3218 let membership = cluster.membership();
3219 let view = membership.view();
3220 let local_id = cluster.node_id();
3221
3222 let mut nodes = vec![format!(" {} (self)", local_id)];
3223 for node in &view.nodes {
3224 if &node.node_id != local_id {
3225 let status = if view.healthy_nodes.contains(&node.node_id) {
3226 "healthy"
3227 } else if view.failed_nodes.contains(&node.node_id) {
3228 "failed"
3229 } else {
3230 "unknown"
3231 };
3232 nodes.push(format!(" {} - {}", node.node_id, status));
3233 }
3234 }
3235
3236 Ok(QueryResult::Value(format!(
3237 "Cluster Nodes ({}):\n{}",
3238 nodes.len(),
3239 nodes.join("\n")
3240 )))
3241 },
3242 )
3243 },
3244 ClusterOp::Leader => {
3245 self.cluster.as_ref().map_or_else(
3246 || {
3247 Ok(QueryResult::Value(
3248 "No leader (single-node mode)".to_string(),
3249 ))
3250 },
3251 |cluster| {
3252 let leader = cluster.current_leader();
3253 let is_self = cluster.is_leader();
3254
3255 leader.map_or_else(
3256 || {
3257 Ok(QueryResult::Value(
3258 "No leader elected (election in progress)".to_string(),
3259 ))
3260 },
3261 |leader_id| {
3262 if is_self {
3263 Ok(QueryResult::Value(format!(
3264 "Leader: {leader_id} (this node)"
3265 )))
3266 } else {
3267 Ok(QueryResult::Value(format!("Leader: {leader_id}")))
3268 }
3269 },
3270 )
3271 },
3272 )
3273 },
3274 }
3275 }
3276
3277 #[allow(clippy::too_many_lines)] fn exec_graph_algorithm(&self, stmt: &GraphAlgorithmStmt) -> Result<QueryResult> {
3281 match &stmt.operation {
3282 GraphAlgorithmOp::PageRank {
3283 damping,
3284 tolerance,
3285 max_iterations,
3286 direction,
3287 edge_type,
3288 } => {
3289 let config = PageRankConfig {
3290 damping: damping
3291 .as_ref()
3292 .map(|e| self.expr_to_f64(e))
3293 .transpose()?
3294 .unwrap_or(0.85),
3295 tolerance: tolerance
3296 .as_ref()
3297 .map(|e| self.expr_to_f64(e))
3298 .transpose()?
3299 .unwrap_or(1e-6),
3300 max_iterations: max_iterations
3301 .as_ref()
3302 .map(|e| self.expr_to_usize(e))
3303 .transpose()?
3304 .unwrap_or(100),
3305 direction: direction
3306 .as_ref()
3307 .map_or(Direction::Outgoing, |d| self.convert_parsed_direction(d)),
3308 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3309 };
3310 let result = self.graph.pagerank(Some(config))?;
3311 let items: Vec<PageRankItem> = result
3312 .scores
3313 .into_iter()
3314 .map(|(node_id, score)| PageRankItem { node_id, score })
3315 .collect();
3316 Ok(QueryResult::PageRank(PageRankResult {
3317 items,
3318 iterations: result.iterations,
3319 convergence: result.convergence,
3320 converged: result.converged,
3321 }))
3322 },
3323 GraphAlgorithmOp::BetweennessCentrality {
3324 sampling_ratio,
3325 direction,
3326 edge_type,
3327 } => {
3328 let config = CentralityConfig {
3329 direction: direction
3330 .as_ref()
3331 .map_or(Direction::Both, |d| self.convert_parsed_direction(d)),
3332 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3333 sampling_ratio: sampling_ratio
3334 .as_ref()
3335 .map(|e| self.expr_to_f64(e))
3336 .transpose()?
3337 .unwrap_or(1.0),
3338 max_iterations: 100,
3339 tolerance: 1e-6,
3340 };
3341 let result = self.graph.betweenness_centrality(Some(config))?;
3342 let items: Vec<CentralityItem> = result
3343 .scores
3344 .into_iter()
3345 .map(|(node_id, score)| CentralityItem { node_id, score })
3346 .collect();
3347 Ok(QueryResult::Centrality(CentralityResult {
3348 items,
3349 centrality_type: CentralityType::Betweenness,
3350 iterations: result.iterations,
3351 converged: result.converged,
3352 sample_count: result.sample_count,
3353 }))
3354 },
3355 GraphAlgorithmOp::ClosenessCentrality {
3356 direction,
3357 edge_type,
3358 } => {
3359 let config = CentralityConfig {
3360 direction: direction
3361 .as_ref()
3362 .map_or(Direction::Both, |d| self.convert_parsed_direction(d)),
3363 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3364 sampling_ratio: 1.0,
3365 max_iterations: 100,
3366 tolerance: 1e-6,
3367 };
3368 let result = self.graph.closeness_centrality(Some(config))?;
3369 let items: Vec<CentralityItem> = result
3370 .scores
3371 .into_iter()
3372 .map(|(node_id, score)| CentralityItem { node_id, score })
3373 .collect();
3374 Ok(QueryResult::Centrality(CentralityResult {
3375 items,
3376 centrality_type: CentralityType::Closeness,
3377 iterations: result.iterations,
3378 converged: result.converged,
3379 sample_count: result.sample_count,
3380 }))
3381 },
3382 GraphAlgorithmOp::EigenvectorCentrality {
3383 max_iterations,
3384 tolerance,
3385 direction,
3386 edge_type,
3387 } => {
3388 let config = CentralityConfig {
3389 direction: direction
3390 .as_ref()
3391 .map_or(Direction::Both, |d| self.convert_parsed_direction(d)),
3392 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3393 sampling_ratio: 1.0,
3394 max_iterations: max_iterations
3395 .as_ref()
3396 .map(|e| self.expr_to_usize(e))
3397 .transpose()?
3398 .unwrap_or(100),
3399 tolerance: tolerance
3400 .as_ref()
3401 .map(|e| self.expr_to_f64(e))
3402 .transpose()?
3403 .unwrap_or(1e-6),
3404 };
3405 let result = self.graph.eigenvector_centrality(Some(config))?;
3406 let items: Vec<CentralityItem> = result
3407 .scores
3408 .into_iter()
3409 .map(|(node_id, score)| CentralityItem { node_id, score })
3410 .collect();
3411 Ok(QueryResult::Centrality(CentralityResult {
3412 items,
3413 centrality_type: CentralityType::Eigenvector,
3414 iterations: result.iterations,
3415 converged: result.converged,
3416 sample_count: result.sample_count,
3417 }))
3418 },
3419 GraphAlgorithmOp::LouvainCommunities {
3420 resolution,
3421 max_passes,
3422 direction,
3423 edge_type,
3424 } => {
3425 let config = CommunityConfig {
3426 direction: direction
3427 .as_ref()
3428 .map_or(Direction::Both, |d| self.convert_parsed_direction(d)),
3429 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3430 resolution: resolution
3431 .as_ref()
3432 .map(|e| self.expr_to_f64(e))
3433 .transpose()?
3434 .unwrap_or(1.0),
3435 max_passes: max_passes
3436 .as_ref()
3437 .map(|e| self.expr_to_usize(e))
3438 .transpose()?
3439 .unwrap_or(10),
3440 max_iterations: 100,
3441 seed: None,
3442 };
3443 let result = self.graph.louvain_communities(Some(config))?;
3444 let items: Vec<CommunityItem> = result
3445 .communities
3446 .iter()
3447 .map(|(&node_id, &community_id)| CommunityItem {
3448 node_id,
3449 community_id,
3450 })
3451 .collect();
3452 Ok(QueryResult::Communities(CommunityResult {
3453 items,
3454 members: result.members,
3455 community_count: result.community_count,
3456 modularity: result.modularity,
3457 passes: result.passes,
3458 iterations: result.iterations,
3459 }))
3460 },
3461 GraphAlgorithmOp::LabelPropagation {
3462 max_iterations,
3463 direction,
3464 edge_type,
3465 } => {
3466 let config = CommunityConfig {
3467 direction: direction
3468 .as_ref()
3469 .map_or(Direction::Both, |d| self.convert_parsed_direction(d)),
3470 edge_type: edge_type.as_ref().map(|e| e.name.clone()),
3471 resolution: 1.0,
3472 max_passes: 10,
3473 max_iterations: max_iterations
3474 .as_ref()
3475 .map(|e| self.expr_to_usize(e))
3476 .transpose()?
3477 .unwrap_or(100),
3478 seed: None,
3479 };
3480 let result = self.graph.label_propagation(Some(config))?;
3481 let items: Vec<CommunityItem> = result
3482 .communities
3483 .iter()
3484 .map(|(&node_id, &community_id)| CommunityItem {
3485 node_id,
3486 community_id,
3487 })
3488 .collect();
3489 Ok(QueryResult::Communities(CommunityResult {
3490 items,
3491 members: result.members,
3492 community_count: result.community_count,
3493 modularity: result.modularity,
3494 passes: result.passes,
3495 iterations: result.iterations,
3496 }))
3497 },
3498 }
3499 }
3500
3501 fn exec_graph_constraint(&self, stmt: &GraphConstraintStmt) -> Result<QueryResult> {
3502 match &stmt.operation {
3503 GraphConstraintOp::Create {
3504 name,
3505 target,
3506 property,
3507 constraint_type,
3508 } => {
3509 let g_target = match target {
3510 ConstraintTarget::Node { label } => {
3511 label.as_ref().map_or(GConstraintTarget::AllNodes, |l| {
3512 GConstraintTarget::NodeLabel(l.name.clone())
3513 })
3514 },
3515 ConstraintTarget::Edge { edge_type } => {
3516 edge_type.as_ref().map_or(GConstraintTarget::AllEdges, |t| {
3517 GConstraintTarget::EdgeType(t.name.clone())
3518 })
3519 },
3520 };
3521 let g_type = match constraint_type {
3522 ConstraintType::Unique => GConstraintType::Unique,
3523 ConstraintType::Exists => GConstraintType::Exists,
3524 ConstraintType::Type(t) => {
3525 use graph_engine::PropertyValueType;
3526 let type_name = t.to_uppercase();
3527 match type_name.as_str() {
3528 "INT" | "INTEGER" => {
3529 GConstraintType::PropertyType(PropertyValueType::Int)
3530 },
3531 "FLOAT" | "DOUBLE" => {
3532 GConstraintType::PropertyType(PropertyValueType::Float)
3533 },
3534 "BOOL" | "BOOLEAN" => {
3535 GConstraintType::PropertyType(PropertyValueType::Bool)
3536 },
3537 _ => GConstraintType::PropertyType(PropertyValueType::String),
3539 }
3540 },
3541 };
3542 let constraint = Constraint {
3543 name: name.name.clone(),
3544 target: g_target,
3545 property: property.name.clone(),
3546 constraint_type: g_type,
3547 };
3548 self.graph.create_constraint(constraint)?;
3549 Ok(QueryResult::Empty)
3550 },
3551 GraphConstraintOp::Drop { name } => {
3552 self.graph.drop_constraint(&name.name)?;
3553 Ok(QueryResult::Empty)
3554 },
3555 GraphConstraintOp::List => {
3556 let constraints = self.graph.list_constraints();
3557 let results: Vec<ConstraintInfo> = constraints
3558 .into_iter()
3559 .map(|c| ConstraintInfo {
3560 name: c.name,
3561 target: match c.target {
3562 GConstraintTarget::NodeLabel(l) => format!("Node({l})"),
3563 GConstraintTarget::EdgeType(t) => format!("Edge({t})"),
3564 GConstraintTarget::AllNodes => "AllNodes".to_string(),
3565 GConstraintTarget::AllEdges => "AllEdges".to_string(),
3566 },
3567 property: c.property,
3568 constraint_type: match c.constraint_type {
3569 GConstraintType::Unique => "UNIQUE".to_string(),
3570 GConstraintType::Exists => "EXISTS".to_string(),
3571 GConstraintType::PropertyType(t) => format!("TYPE({t:?})"),
3572 },
3573 })
3574 .collect();
3575 Ok(QueryResult::Constraints(results))
3576 },
3577 GraphConstraintOp::Get { name } => match self.graph.get_constraint(&name.name) {
3578 Some(c) => {
3579 let info = ConstraintInfo {
3580 name: c.name,
3581 target: match c.target {
3582 GConstraintTarget::NodeLabel(l) => format!("Node({l})"),
3583 GConstraintTarget::EdgeType(t) => format!("Edge({t})"),
3584 GConstraintTarget::AllNodes => "AllNodes".to_string(),
3585 GConstraintTarget::AllEdges => "AllEdges".to_string(),
3586 },
3587 property: c.property,
3588 constraint_type: match c.constraint_type {
3589 GConstraintType::Unique => "UNIQUE".to_string(),
3590 GConstraintType::Exists => "EXISTS".to_string(),
3591 GConstraintType::PropertyType(t) => format!("TYPE({t:?})"),
3592 },
3593 };
3594 Ok(QueryResult::Constraints(vec![info]))
3595 },
3596 None => Ok(QueryResult::Constraints(vec![])),
3597 },
3598 }
3599 }
3600
3601 fn exec_graph_index(&self, stmt: &GraphIndexStmt) -> Result<QueryResult> {
3602 match &stmt.operation {
3603 GraphIndexOp::CreateNodeProperty { property } => {
3604 self.graph.create_node_property_index(&property.name)?;
3605 Ok(QueryResult::Empty)
3606 },
3607 GraphIndexOp::CreateEdgeProperty { property } => {
3608 self.graph.create_edge_property_index(&property.name)?;
3609 Ok(QueryResult::Empty)
3610 },
3611 GraphIndexOp::CreateLabel => {
3612 self.graph.create_label_index()?;
3613 Ok(QueryResult::Empty)
3614 },
3615 GraphIndexOp::CreateEdgeType => {
3616 self.graph.create_edge_type_index()?;
3617 Ok(QueryResult::Empty)
3618 },
3619 GraphIndexOp::DropNode { property } => {
3620 self.graph.drop_node_index(&property.name)?;
3621 Ok(QueryResult::Empty)
3622 },
3623 GraphIndexOp::DropEdge { property } => {
3624 self.graph.drop_edge_index(&property.name)?;
3625 Ok(QueryResult::Empty)
3626 },
3627 GraphIndexOp::ShowNodeIndexes => {
3628 let indexes = self.graph.get_indexed_node_properties();
3629 Ok(QueryResult::GraphIndexes(indexes))
3630 },
3631 GraphIndexOp::ShowEdgeIndexes => {
3632 let indexes = self.graph.get_indexed_edge_properties();
3633 Ok(QueryResult::GraphIndexes(indexes))
3634 },
3635 }
3636 }
3637
3638 fn exec_graph_aggregate(&self, stmt: &GraphAggregateStmt) -> Result<QueryResult> {
3639 match &stmt.operation {
3640 GraphAggregateOp::CountNodes { label } => {
3641 let count = match label {
3642 Some(l) => self.graph.count_nodes_by_label(&l.name)?,
3643 None => self.graph.count_nodes(),
3644 };
3645 Ok(QueryResult::Aggregate(AggregateResultValue::Count(count)))
3646 },
3647 GraphAggregateOp::CountEdges { edge_type } => {
3648 let count = match edge_type {
3649 Some(t) => self.graph.count_edges_by_type(&t.name)?,
3650 None => self.graph.count_edges(),
3651 };
3652 Ok(QueryResult::Aggregate(AggregateResultValue::Count(count)))
3653 },
3654 GraphAggregateOp::AggregateNodeProperty {
3655 function,
3656 property,
3657 label,
3658 ..
3659 } => {
3660 let agg = match label {
3662 Some(l) => self
3663 .graph
3664 .aggregate_node_property_by_label(&l.name, &property.name)?,
3665 None => self.graph.aggregate_node_property(&property.name),
3666 };
3667 let result = match function {
3668 AggregateFunction::Sum => AggregateResultValue::Sum(agg.sum.unwrap_or(0.0)),
3669 AggregateFunction::Avg => AggregateResultValue::Avg(agg.avg.unwrap_or(0.0)),
3670 AggregateFunction::Min => AggregateResultValue::Min(
3671 self.property_value_to_f64(agg.min).unwrap_or(0.0),
3672 ),
3673 AggregateFunction::Max => AggregateResultValue::Max(
3674 self.property_value_to_f64(agg.max).unwrap_or(0.0),
3675 ),
3676 AggregateFunction::Count => AggregateResultValue::Count(agg.count),
3677 };
3678 Ok(QueryResult::Aggregate(result))
3679 },
3680 GraphAggregateOp::AggregateEdgeProperty {
3681 function,
3682 property,
3683 edge_type,
3684 ..
3685 } => {
3686 let agg = match edge_type {
3688 Some(t) => self
3689 .graph
3690 .aggregate_edge_property_by_type(&t.name, &property.name)?,
3691 None => self.graph.aggregate_edge_property(&property.name),
3692 };
3693 let result = match function {
3694 AggregateFunction::Sum => AggregateResultValue::Sum(agg.sum.unwrap_or(0.0)),
3695 AggregateFunction::Avg => AggregateResultValue::Avg(agg.avg.unwrap_or(0.0)),
3696 AggregateFunction::Min => AggregateResultValue::Min(
3697 self.property_value_to_f64(agg.min).unwrap_or(0.0),
3698 ),
3699 AggregateFunction::Max => AggregateResultValue::Max(
3700 self.property_value_to_f64(agg.max).unwrap_or(0.0),
3701 ),
3702 AggregateFunction::Count => AggregateResultValue::Count(agg.count),
3703 };
3704 Ok(QueryResult::Aggregate(result))
3705 },
3706 }
3707 }
3708
3709 fn exec_graph_pattern(&self, stmt: &GraphPatternStmt) -> Result<QueryResult> {
3710 match &stmt.operation {
3711 GraphPatternOp::Match { pattern, limit } => {
3712 let gp = self.pattern_spec_to_graph_pattern(pattern, limit.as_ref())?;
3713 let result = self.graph.match_pattern(&gp)?;
3714 Ok(QueryResult::PatternMatch(
3715 self.convert_pattern_match_result(&result),
3716 ))
3717 },
3718 GraphPatternOp::Count { pattern } => {
3719 let gp = self.pattern_spec_to_graph_pattern(pattern, None)?;
3720 let count = self.graph.count_pattern_matches(&gp)?;
3721 Ok(QueryResult::Aggregate(AggregateResultValue::Count(count)))
3722 },
3723 GraphPatternOp::Exists { pattern } => {
3724 let gp = self.pattern_spec_to_graph_pattern(pattern, None)?;
3725 let exists = self.graph.pattern_exists(&gp)?;
3726 Ok(QueryResult::Value(exists.to_string()))
3727 },
3728 }
3729 }
3730
3731 fn pattern_spec_to_graph_pattern(
3732 &self,
3733 pattern: &parser::PatternSpec,
3734 limit: Option<&Expr>,
3735 ) -> Result<graph_engine::Pattern> {
3736 use graph_engine::{EdgePattern, NodePattern, PathPattern, Pattern};
3737
3738 if pattern.nodes.is_empty() {
3739 return Err(RouterError::InvalidArgument(
3740 "Pattern must have at least one node".to_string(),
3741 ));
3742 }
3743
3744 let build_node_pattern = |spec: &parser::NodePatternSpec| -> NodePattern {
3746 let mut np = NodePattern::new();
3747 if let Some(alias) = &spec.alias {
3748 np = np.variable(&alias.name);
3749 }
3750 if let Some(label) = &spec.label {
3751 np = np.label(&label.name);
3752 }
3753 np
3754 };
3755
3756 if pattern.edges.is_empty() {
3758 let start = build_node_pattern(&pattern.nodes[0]);
3759 let path = PathPattern::new(start, EdgePattern::new(), NodePattern::new());
3761 let mut gp = Pattern::new(path);
3762 if let Some(lim) = limit {
3763 gp = gp.limit(self.expr_to_usize(lim)?);
3764 }
3765 return Ok(gp);
3766 }
3767
3768 let first_edge = &pattern.edges[0];
3770 let start_node = build_node_pattern(&pattern.nodes[first_edge.from_node]);
3771
3772 let edge =
3773 EdgePattern::new().direction(self.convert_parsed_direction(&first_edge.direction));
3774 let edge = if let Some(alias) = &first_edge.alias {
3775 edge.variable(&alias.name)
3776 } else {
3777 edge
3778 };
3779 let edge = if let Some(et) = &first_edge.edge_type {
3780 edge.edge_type(&et.name)
3781 } else {
3782 edge
3783 };
3784
3785 let end_node = build_node_pattern(&pattern.nodes[first_edge.to_node]);
3786 let mut path = PathPattern::new(start_node, edge, end_node);
3787
3788 for edge_spec in pattern.edges.iter().skip(1) {
3790 let edge =
3791 EdgePattern::new().direction(self.convert_parsed_direction(&edge_spec.direction));
3792 let edge = if let Some(alias) = &edge_spec.alias {
3793 edge.variable(&alias.name)
3794 } else {
3795 edge
3796 };
3797 let edge = if let Some(et) = &edge_spec.edge_type {
3798 edge.edge_type(&et.name)
3799 } else {
3800 edge
3801 };
3802
3803 let target_node = build_node_pattern(&pattern.nodes[edge_spec.to_node]);
3804 path = path.extend(edge, target_node);
3805 }
3806
3807 let mut gp = Pattern::new(path);
3808 if let Some(lim) = limit {
3809 gp = gp.limit(self.expr_to_usize(lim)?);
3810 }
3811
3812 Ok(gp)
3813 }
3814
3815 #[allow(clippy::unused_self)] fn convert_pattern_match_result(
3817 &self,
3818 result: &graph_engine::PatternMatchResult,
3819 ) -> PatternMatchResultValue {
3820 use graph_engine::Binding;
3821
3822 let matches = result
3823 .matches
3824 .iter()
3825 .map(|m| {
3826 let bindings = m
3827 .bindings
3828 .iter()
3829 .map(|(k, v)| {
3830 let binding = match v {
3831 Binding::Node(n) => BindingValue::Node {
3832 id: n.id,
3833 label: n.labels.join(", "),
3834 },
3835 Binding::Edge(e) => BindingValue::Edge {
3836 id: e.id,
3837 edge_type: e.edge_type.clone(),
3838 from: e.from,
3839 to: e.to,
3840 },
3841 Binding::Path(p) => BindingValue::Path {
3842 nodes: p.nodes.clone(),
3843 edges: p.edges.clone(),
3844 length: p.nodes.len().saturating_sub(1),
3845 },
3846 };
3847 (k.clone(), binding)
3848 })
3849 .collect();
3850 PatternMatchBinding { bindings }
3851 })
3852 .collect();
3853
3854 PatternMatchResultValue {
3855 matches,
3856 stats: PatternMatchStatsValue {
3857 matches_found: result.stats.matches_found,
3858 nodes_evaluated: result.stats.nodes_evaluated,
3859 edges_evaluated: result.stats.edges_evaluated,
3860 truncated: result.stats.truncated,
3861 },
3862 }
3863 }
3864
3865 #[allow(clippy::too_many_lines)] fn exec_graph_batch(&self, stmt: &GraphBatchStmt) -> Result<QueryResult> {
3867 match &stmt.operation {
3868 GraphBatchOp::CreateNodes { nodes } => {
3869 let inputs: Vec<NodeInput> = nodes
3870 .iter()
3871 .map(|n| {
3872 let props = n
3873 .properties
3874 .iter()
3875 .map(|p| {
3876 let pv = self
3877 .expr_to_property_value(&p.value)
3878 .unwrap_or(PropertyValue::Null);
3879 (p.key.name.clone(), pv)
3880 })
3881 .collect();
3882 NodeInput {
3883 labels: n.labels.iter().map(|l| l.name.clone()).collect(),
3884 properties: props,
3885 }
3886 })
3887 .collect();
3888 let result = self.graph.batch_create_nodes(inputs)?;
3889 Ok(QueryResult::BatchResult(BatchOperationResult {
3890 operation: "CREATE_NODES".to_string(),
3891 affected_count: result.count,
3892 created_ids: Some(result.created_ids),
3893 }))
3894 },
3895 GraphBatchOp::CreateEdges { edges } => {
3896 let inputs: Vec<EdgeInput> = edges
3897 .iter()
3898 .map(|e| {
3899 let from_id = self.expr_to_u64(&e.from_id).unwrap_or(0);
3900 let to_id = self.expr_to_u64(&e.to_id).unwrap_or(0);
3901 let props = e
3902 .properties
3903 .iter()
3904 .map(|p| {
3905 let pv = self
3906 .expr_to_property_value(&p.value)
3907 .unwrap_or(PropertyValue::Null);
3908 (p.key.name.clone(), pv)
3909 })
3910 .collect();
3911 EdgeInput {
3912 from: from_id,
3913 to: to_id,
3914 edge_type: e.edge_type.name.clone(),
3915 properties: props,
3916 directed: true,
3917 }
3918 })
3919 .collect();
3920 let result = self.graph.batch_create_edges(inputs)?;
3921 Ok(QueryResult::BatchResult(BatchOperationResult {
3922 operation: "CREATE_EDGES".to_string(),
3923 affected_count: result.count,
3924 created_ids: Some(result.created_ids),
3925 }))
3926 },
3927 GraphBatchOp::DeleteNodes { ids } => {
3928 let node_ids: Vec<u64> = ids
3929 .iter()
3930 .filter_map(|e| self.expr_to_u64(e).ok())
3931 .collect();
3932
3933 if !node_ids.is_empty() {
3934 let sample_data: Vec<String> =
3936 node_ids.iter().map(|id| format!("node {id}")).collect();
3937 let op = DestructiveOp::NodeDelete {
3938 node_id: node_ids[0],
3939 edge_count: node_ids.len().saturating_sub(1),
3940 };
3941
3942 match self.protect_destructive_op(
3943 &format!("BATCH DELETE NODES ({})", node_ids.len()),
3944 op,
3945 sample_data,
3946 ) {
3947 ProtectedOpResult::Proceed => {},
3948 ProtectedOpResult::Cancelled => {
3949 return Err(RouterError::CheckpointError(
3950 "Operation cancelled by user".to_string(),
3951 ));
3952 },
3953 }
3954 }
3955
3956 let result = self.graph.batch_delete_nodes(node_ids)?;
3957 Ok(QueryResult::BatchResult(BatchOperationResult {
3958 operation: "DELETE_NODES".to_string(),
3959 affected_count: result.count,
3960 created_ids: None,
3961 }))
3962 },
3963 GraphBatchOp::DeleteEdges { ids } => {
3964 let edge_ids: Vec<u64> = ids
3965 .iter()
3966 .filter_map(|e| self.expr_to_u64(e).ok())
3967 .collect();
3968
3969 if !edge_ids.is_empty() {
3970 let sample_data: Vec<String> =
3972 edge_ids.iter().map(|id| format!("edge {id}")).collect();
3973 let op = DestructiveOp::EdgeDelete {
3974 edge_id: edge_ids[0],
3975 };
3976
3977 match self.protect_destructive_op(
3978 &format!("BATCH DELETE EDGES ({})", edge_ids.len()),
3979 op,
3980 sample_data,
3981 ) {
3982 ProtectedOpResult::Proceed => {},
3983 ProtectedOpResult::Cancelled => {
3984 return Err(RouterError::CheckpointError(
3985 "Operation cancelled by user".to_string(),
3986 ));
3987 },
3988 }
3989 }
3990
3991 let result = self.graph.batch_delete_edges(edge_ids)?;
3992 Ok(QueryResult::BatchResult(BatchOperationResult {
3993 operation: "DELETE_EDGES".to_string(),
3994 affected_count: result.count,
3995 created_ids: None,
3996 }))
3997 },
3998 GraphBatchOp::UpdateNodes { updates } => {
3999 #[allow(clippy::type_complexity)]
4000 let update_inputs: Vec<(
4001 u64,
4002 Option<Vec<String>>,
4003 HashMap<String, PropertyValue>,
4004 )> = updates
4005 .iter()
4006 .filter_map(|u| {
4007 let id = self.expr_to_u64(&u.id).ok()?;
4008 let props: HashMap<String, PropertyValue> = u
4009 .properties
4010 .iter()
4011 .map(|p| {
4012 let pv = self
4013 .expr_to_property_value(&p.value)
4014 .unwrap_or(PropertyValue::Null);
4015 (p.key.name.clone(), pv)
4016 })
4017 .collect();
4018 Some((id, None, props))
4019 })
4020 .collect();
4021 let count = self.graph.batch_update_nodes(update_inputs)?;
4022 Ok(QueryResult::BatchResult(BatchOperationResult {
4023 operation: "UPDATE_NODES".to_string(),
4024 affected_count: count,
4025 created_ids: None,
4026 }))
4027 },
4028 }
4029 }
4030
4031 #[allow(clippy::unused_self)] #[allow(clippy::trivially_copy_pass_by_ref)] const fn convert_parsed_direction(&self, dir: &ParsedDirection) -> Direction {
4034 match dir {
4035 ParsedDirection::Outgoing => Direction::Outgoing,
4036 ParsedDirection::Incoming => Direction::Incoming,
4037 ParsedDirection::Both => Direction::Both,
4038 }
4039 }
4040
4041 #[allow(clippy::unused_self)] fn expr_to_property_value(&self, expr: &Expr) -> Result<PropertyValue> {
4043 match &expr.kind {
4044 ExprKind::Literal(lit) => match lit {
4045 Literal::Null => Ok(PropertyValue::Null),
4046 Literal::Boolean(b) => Ok(PropertyValue::Bool(*b)),
4047 Literal::Integer(i) => Ok(PropertyValue::Int(*i)),
4048 Literal::Float(f) => Ok(PropertyValue::Float(*f)),
4049 Literal::String(s) => Ok(PropertyValue::String(s.clone())),
4050 },
4051 ExprKind::Ident(ident) => Ok(PropertyValue::String(ident.name.clone())),
4052 _ => Err(RouterError::InvalidArgument(
4053 "Cannot convert expression to property value".to_string(),
4054 )),
4055 }
4056 }
4057
4058 #[allow(clippy::unused_self)] #[allow(clippy::cast_precision_loss)] #[allow(clippy::needless_pass_by_value)] fn property_value_to_f64(&self, value: Option<PropertyValue>) -> Option<f64> {
4062 match value {
4063 Some(PropertyValue::Int(i)) => Some(i as f64),
4064 Some(PropertyValue::Float(f)) => Some(f),
4065 _ => None,
4066 }
4067 }
4068
4069 fn exec_select(&self, select: &SelectStmt) -> Result<QueryResult> {
4072 let from = select
4073 .from
4074 .as_ref()
4075 .ok_or_else(|| RouterError::MissingArgument("FROM clause".to_string()))?;
4076
4077 let table_name = match &from.table.kind {
4078 TableRefKind::Table(ident) => &ident.name,
4079 TableRefKind::Subquery(_) => {
4080 return Err(RouterError::ParseError(
4081 "Subqueries not yet supported".to_string(),
4082 ))
4083 },
4084 };
4085
4086 if !from.joins.is_empty() {
4088 return self.exec_select_with_joins(select, table_name, from);
4089 }
4090
4091 let condition = if let Some(ref where_expr) = select.where_clause {
4092 self.expr_to_condition(where_expr)?
4093 } else {
4094 Condition::True
4095 };
4096
4097 if let Some(agg_result) = self.try_exec_aggregates(select, table_name, &condition)? {
4099 return Ok(agg_result);
4100 }
4101
4102 let projection = self.extract_projection(&select.columns)?;
4104
4105 let options = ColumnarScanOptions {
4106 projection,
4107 prefer_columnar: true,
4108 };
4109
4110 let mut rows = self
4111 .relational
4112 .select_columnar(table_name, condition, options)?;
4113
4114 if !select.order_by.is_empty() {
4116 self.sort_rows(&mut rows, &select.order_by);
4117 }
4118
4119 if let Some(ref offset_expr) = select.offset {
4121 if let ExprKind::Literal(neumann_parser::Literal::Integer(n)) = &offset_expr.kind {
4122 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
4123 let offset = *n as usize; if offset < rows.len() {
4125 rows = rows.into_iter().skip(offset).collect();
4126 } else {
4127 rows.clear();
4128 }
4129 }
4130 }
4131
4132 if let Some(ref limit_expr) = select.limit {
4134 if let ExprKind::Literal(neumann_parser::Literal::Integer(n)) = &limit_expr.kind {
4135 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
4136 let limit = *n as usize; rows.truncate(limit);
4138 }
4139 }
4140
4141 Ok(QueryResult::Rows(rows))
4142 }
4143
4144 #[allow(clippy::too_many_lines)] fn exec_select_with_joins(
4146 &self,
4147 select: &SelectStmt,
4148 left_table: &str,
4149 from: &neumann_parser::FromClause,
4150 ) -> Result<QueryResult> {
4151 if from.joins.len() > 1 {
4154 return Err(RouterError::ParseError(
4155 "Multiple JOINs not yet supported; use single JOIN".to_string(),
4156 ));
4157 }
4158
4159 let join = &from.joins[0];
4160 let right_table = match &join.table.kind {
4161 TableRefKind::Table(ident) => &ident.name,
4162 TableRefKind::Subquery(_) => {
4163 return Err(RouterError::ParseError(
4164 "Subquery JOINs not yet supported".to_string(),
4165 ))
4166 },
4167 };
4168
4169 let left_alias: &str = match &from.table.alias {
4171 Some(a) => &a.name,
4172 None => left_table,
4173 };
4174 let right_alias: &str = join.table.alias.as_ref().map_or(right_table, |a| &a.name);
4175
4176 let mut rows: Vec<Row> = match join.kind {
4178 JoinKind::Inner => {
4179 let (on_a, on_b) =
4180 self.get_join_columns(join.condition.as_ref(), left_table, right_table)?;
4181 let pairs = self
4182 .relational
4183 .join(left_table, right_table, &on_a, &on_b)?;
4184 pairs
4185 .into_iter()
4186 .map(|(a, b)| self.merge_rows(Some(&a), Some(&b), left_alias, right_alias))
4187 .collect()
4188 },
4189 JoinKind::Left => {
4190 let (on_a, on_b) =
4191 self.get_join_columns(join.condition.as_ref(), left_table, right_table)?;
4192 let pairs = self
4193 .relational
4194 .left_join(left_table, right_table, &on_a, &on_b)?;
4195 pairs
4196 .into_iter()
4197 .map(|(a, b)| self.merge_rows(Some(&a), b.as_ref(), left_alias, right_alias))
4198 .collect()
4199 },
4200 JoinKind::Right => {
4201 let (on_a, on_b) =
4202 self.get_join_columns(join.condition.as_ref(), left_table, right_table)?;
4203 let pairs = self
4204 .relational
4205 .right_join(left_table, right_table, &on_a, &on_b)?;
4206 pairs
4207 .into_iter()
4208 .map(|(a, b)| self.merge_rows(a.as_ref(), Some(&b), left_alias, right_alias))
4209 .collect()
4210 },
4211 JoinKind::Full => {
4212 let (on_a, on_b) =
4213 self.get_join_columns(join.condition.as_ref(), left_table, right_table)?;
4214 let pairs = self
4215 .relational
4216 .full_join(left_table, right_table, &on_a, &on_b)?;
4217 pairs
4218 .into_iter()
4219 .map(|(a, b)| self.merge_rows(a.as_ref(), b.as_ref(), left_alias, right_alias))
4220 .collect()
4221 },
4222 JoinKind::Cross => {
4223 let pairs = self.relational.cross_join(left_table, right_table)?;
4224 pairs
4225 .into_iter()
4226 .map(|(a, b)| self.merge_rows(Some(&a), Some(&b), left_alias, right_alias))
4227 .collect()
4228 },
4229 JoinKind::Natural => {
4230 let pairs = self.relational.natural_join(left_table, right_table)?;
4231 pairs
4232 .into_iter()
4233 .map(|(a, b)| self.merge_rows(Some(&a), Some(&b), left_alias, right_alias))
4234 .collect()
4235 },
4236 };
4237
4238 if let Some(ref where_expr) = select.where_clause {
4240 rows.retain(|row| self.evaluate_join_condition(where_expr, row));
4241 }
4242
4243 if !select.order_by.is_empty() {
4245 self.sort_rows(&mut rows, &select.order_by);
4246 }
4247
4248 if let Some(ref offset_expr) = select.offset {
4250 if let ExprKind::Literal(neumann_parser::Literal::Integer(n)) = &offset_expr.kind {
4251 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
4252 let offset = *n as usize; if offset < rows.len() {
4254 rows = rows.into_iter().skip(offset).collect();
4255 } else {
4256 rows.clear();
4257 }
4258 }
4259 }
4260
4261 if let Some(ref limit_expr) = select.limit {
4263 if let ExprKind::Literal(neumann_parser::Literal::Integer(n)) = &limit_expr.kind {
4264 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
4265 let limit = *n as usize; rows.truncate(limit);
4267 }
4268 }
4269
4270 Ok(QueryResult::Rows(rows))
4271 }
4272
4273 fn get_join_columns(
4274 &self,
4275 condition: Option<&JoinCondition>,
4276 _left_table: &str,
4277 _right_table: &str,
4278 ) -> Result<(String, String)> {
4279 condition.map_or_else(
4280 || {
4281 Err(RouterError::ParseError(
4282 "JOIN requires ON or USING clause (except CROSS/NATURAL)".to_string(),
4283 ))
4284 },
4285 |cond| self.extract_join_columns(cond),
4286 )
4287 }
4288
4289 fn evaluate_join_condition(&self, expr: &Expr, row: &Row) -> bool {
4290 match &expr.kind {
4291 ExprKind::Binary(left, op, right) => {
4292 let left_val = self.get_row_value(left, row);
4293 let right_val = self.get_row_value(right, row);
4294 match (left_val, right_val) {
4295 (Some(l), Some(r)) => match op {
4296 BinaryOp::Eq => l == r,
4297 BinaryOp::Ne => l != r,
4298 BinaryOp::Lt => {
4299 self.compare_values(&l, &r) == Some(std::cmp::Ordering::Less)
4300 },
4301 BinaryOp::Le => matches!(
4302 self.compare_values(&l, &r),
4303 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
4304 ),
4305 BinaryOp::Gt => {
4306 self.compare_values(&l, &r) == Some(std::cmp::Ordering::Greater)
4307 },
4308 BinaryOp::Ge => matches!(
4309 self.compare_values(&l, &r),
4310 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
4311 ),
4312 BinaryOp::And => l.is_truthy() && r.is_truthy(),
4313 BinaryOp::Or => l.is_truthy() || r.is_truthy(),
4314 _ => false,
4315 },
4316 _ => false,
4317 }
4318 },
4319 ExprKind::Ident(_) | ExprKind::Qualified(_, _) => {
4320 self.get_row_value(expr, row).is_some_and(|v| v.is_truthy())
4321 },
4322 _ => true,
4323 }
4324 }
4325
4326 #[allow(clippy::unused_self)] #[allow(clippy::cast_precision_loss)] fn compare_values(&self, a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
4329 match (a, b) {
4330 (Value::Int(x), Value::Int(y)) => Some(x.cmp(y)),
4331 (Value::Float(x), Value::Float(y)) => x.partial_cmp(y),
4332 (Value::Int(x), Value::Float(y)) => (*x as f64).partial_cmp(y),
4334 (Value::Float(x), Value::Int(y)) => x.partial_cmp(&(*y as f64)),
4335 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
4336 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
4337 _ => None,
4338 }
4339 }
4340
4341 fn sort_rows(&self, rows: &mut [Row], order_by: &[neumann_parser::OrderByItem]) {
4342 rows.sort_by(|a, b| {
4343 for item in order_by {
4344 let val_a = self.get_sort_value(&item.expr, a);
4345 let val_b = self.get_sort_value(&item.expr, b);
4346
4347 let cmp =
4348 self.compare_values_with_nulls(val_a.as_ref(), val_b.as_ref(), item.nulls);
4349 let cmp = match item.direction {
4350 SortDirection::Asc => cmp,
4351 SortDirection::Desc => cmp.reverse(),
4352 };
4353
4354 if cmp != std::cmp::Ordering::Equal {
4355 return cmp;
4356 }
4357 }
4358 std::cmp::Ordering::Equal
4359 });
4360 }
4361
4362 #[allow(clippy::unused_self)] fn get_sort_value(&self, expr: &Expr, row: &Row) -> Option<Value> {
4364 match &expr.kind {
4365 ExprKind::Ident(ident) => {
4366 row.values
4368 .iter()
4369 .find(|(col, _)| col == &ident.name)
4370 .or_else(|| {
4371 row.values
4372 .iter()
4373 .find(|(col, _)| col.ends_with(&format!(".{}", ident.name)))
4374 })
4375 .map(|(_, v)| v.clone())
4376 },
4377 ExprKind::Qualified(table_expr, col) => {
4378 if let ExprKind::Ident(table) = &table_expr.kind {
4379 let full_name = format!("{}.{}", table.name, col.name);
4380 row.values
4381 .iter()
4382 .find(|(c, _)| c == &full_name)
4383 .map(|(_, v)| v.clone())
4384 } else {
4385 None
4386 }
4387 },
4388 _ => None,
4389 }
4390 }
4391
4392 fn compare_values_with_nulls(
4393 &self,
4394 a: Option<&Value>,
4395 b: Option<&Value>,
4396 nulls_order: Option<NullsOrder>,
4397 ) -> std::cmp::Ordering {
4398 use std::cmp::Ordering;
4399
4400 match (a, b) {
4401 (None, None) | (Some(Value::Null), Some(Value::Null)) => Ordering::Equal,
4402 (None | Some(Value::Null), _) => match nulls_order.unwrap_or(NullsOrder::Last) {
4403 NullsOrder::First => Ordering::Less,
4404 NullsOrder::Last => Ordering::Greater,
4405 },
4406 (_, None | Some(Value::Null)) => match nulls_order.unwrap_or(NullsOrder::Last) {
4407 NullsOrder::First => Ordering::Greater,
4408 NullsOrder::Last => Ordering::Less,
4409 },
4410 (Some(va), Some(vb)) => self.compare_values(va, vb).unwrap_or(Ordering::Equal),
4411 }
4412 }
4413
4414 fn try_exec_aggregates(
4417 &self,
4418 select: &SelectStmt,
4419 table_name: &str,
4420 condition: &Condition,
4421 ) -> Result<Option<QueryResult>> {
4422 let mut aggregates: Vec<(String, AggregateFunc)> = Vec::new();
4424 let mut non_agg_columns: Vec<(String, Expr)> = Vec::new();
4425
4426 for item in &select.columns {
4427 if let Some(agg) = self.parse_aggregate(&item.expr) {
4428 let alias = item.alias.as_ref().map_or_else(
4429 || self.aggregate_default_name(&item.expr),
4430 |a| a.name.clone(),
4431 );
4432 aggregates.push((alias, agg));
4433 } else {
4434 let alias = item.alias.as_ref().map_or_else(
4435 || {
4436 self.expr_to_column_name(&item.expr)
4437 .unwrap_or_else(|_| "?".to_string())
4438 },
4439 |a| a.name.clone(),
4440 );
4441 non_agg_columns.push((alias, item.expr.clone()));
4442 }
4443 }
4444
4445 if !select.group_by.is_empty() {
4447 return self.exec_grouped_aggregates(
4448 select,
4449 table_name,
4450 condition,
4451 &aggregates,
4452 &non_agg_columns,
4453 );
4454 }
4455
4456 if aggregates.is_empty() {
4457 return Ok(None);
4458 }
4459
4460 let mut values: Vec<(String, Value)> = Vec::new();
4462
4463 for (alias, agg) in aggregates {
4464 let val = match agg {
4465 AggregateFunc::Count(col) => {
4466 let count = if let Some(ref column_name) = col {
4467 self.relational
4468 .count_column(table_name, column_name, condition.clone())?
4469 } else {
4470 self.relational.count(table_name, condition.clone())?
4471 };
4472 #[allow(clippy::cast_possible_wrap)]
4473 Value::Int(count as i64)
4474 },
4475 AggregateFunc::Sum(col) => {
4476 let sum = self.relational.sum(table_name, &col, condition.clone())?;
4477 Value::Float(sum)
4478 },
4479 AggregateFunc::Avg(col) => self
4480 .relational
4481 .avg(table_name, &col, condition.clone())?
4482 .map_or(Value::Null, Value::Float),
4483 AggregateFunc::Min(col) => self
4484 .relational
4485 .min(table_name, &col, condition.clone())?
4486 .unwrap_or(Value::Null),
4487 AggregateFunc::Max(col) => self
4488 .relational
4489 .max(table_name, &col, condition.clone())?
4490 .unwrap_or(Value::Null),
4491 };
4492 values.push((alias, val));
4493 }
4494
4495 let row = Row { id: 0, values };
4497 Ok(Some(QueryResult::Rows(vec![row])))
4498 }
4499
4500 fn exec_grouped_aggregates(
4501 &self,
4502 select: &SelectStmt,
4503 table_name: &str,
4504 condition: &Condition,
4505 aggregates: &[(String, AggregateFunc)],
4506 non_agg_columns: &[(String, Expr)],
4507 ) -> Result<Option<QueryResult>> {
4508 use std::collections::HashMap;
4509
4510 let rows = self.relational.select_columnar(
4512 table_name,
4513 condition.clone(),
4514 ColumnarScanOptions {
4515 projection: None,
4516 prefer_columnar: true,
4517 },
4518 )?;
4519
4520 let group_key_names: Vec<String> = select
4522 .group_by
4523 .iter()
4524 .filter_map(|expr| self.expr_to_column_name(expr).ok())
4525 .collect();
4526
4527 let mut groups: HashMap<String, (Vec<Value>, Vec<&Row>)> = HashMap::new();
4529 for row in &rows {
4530 let group_key: Vec<Value> = group_key_names
4531 .iter()
4532 .map(|col| {
4533 row.values
4534 .iter()
4535 .find(|(c, _)| c == col)
4536 .map_or(Value::Null, |(_, v)| v.clone())
4537 })
4538 .collect();
4539 let key_str = self.values_to_group_key(&group_key);
4540 groups
4541 .entry(key_str)
4542 .or_insert_with(|| (group_key, Vec::new()))
4543 .1
4544 .push(row);
4545 }
4546
4547 let mut result_rows: Vec<Row> = Vec::new();
4549
4550 for (_, (_group_key, group_rows)) in groups {
4551 let mut values: Vec<(String, Value)> = Vec::new();
4552
4553 for (alias, expr) in non_agg_columns {
4555 let val = group_rows.first().map_or(Value::Null, |first_row| {
4556 self.get_row_value(expr, first_row).unwrap_or(Value::Null)
4557 });
4558 values.push((alias.clone(), val));
4559 }
4560
4561 for (alias, agg) in aggregates {
4563 let val = self.compute_aggregate_for_group(agg, &group_rows);
4564 values.push((alias.clone(), val));
4565 }
4566
4567 let row = Row { id: 0, values };
4568
4569 if let Some(ref having_expr) = select.having {
4571 if !self.evaluate_join_condition(having_expr, &row) {
4572 continue;
4573 }
4574 }
4575
4576 result_rows.push(row);
4577 }
4578
4579 Ok(Some(QueryResult::Rows(result_rows)))
4580 }
4581
4582 #[allow(clippy::too_many_lines)] #[allow(clippy::unused_self)] fn compute_aggregate_for_group(&self, agg: &AggregateFunc, rows: &[&Row]) -> Value {
4585 match agg {
4586 AggregateFunc::Count(col) => {
4587 let count = if col.is_none() {
4588 rows.len() as u64
4589 } else {
4590 rows.iter()
4591 .filter(|r| {
4592 r.values.iter().any(|(c, v)| {
4593 c == col.as_ref().unwrap() && !matches!(v, Value::Null)
4594 })
4595 })
4596 .count() as u64
4597 };
4598 #[allow(clippy::cast_possible_wrap)]
4599 Value::Int(count as i64)
4600 },
4601 AggregateFunc::Sum(col) => {
4602 let mut sum = 0.0;
4603 for row in rows {
4604 if let Some((_, val)) = row.values.iter().find(|(c, _)| c == col) {
4605 match val {
4606 #[allow(clippy::cast_precision_loss)]
4607 Value::Int(i) => sum += *i as f64,
4608 Value::Float(f) => sum += *f,
4609 _ => {},
4610 }
4611 }
4612 }
4613 Value::Float(sum)
4614 },
4615 AggregateFunc::Avg(col) => {
4616 let mut sum = 0.0;
4617 let mut count = 0;
4618 for row in rows {
4619 if let Some((_, val)) = row.values.iter().find(|(c, _)| c == col) {
4620 match val {
4621 Value::Int(i) => {
4622 #[allow(clippy::cast_precision_loss)]
4623 {
4624 sum += *i as f64;
4625 }
4626 count += 1;
4627 },
4628 Value::Float(f) => {
4629 sum += *f;
4630 count += 1;
4631 },
4632 _ => {},
4633 }
4634 }
4635 }
4636 if count == 0 {
4637 Value::Null
4638 } else {
4639 Value::Float(sum / f64::from(count))
4640 }
4641 },
4642 AggregateFunc::Min(col) => {
4643 let mut min_val: Option<Value> = None;
4644 for row in rows {
4645 if let Some((_, val)) = row.values.iter().find(|(c, _)| c == col) {
4646 if matches!(val, Value::Null) {
4647 continue;
4648 }
4649 min_val = Some(min_val.as_ref().map_or_else(
4650 || val.clone(),
4651 |current| match (current, val) {
4652 (Value::Int(a), Value::Int(b)) if b < a => val.clone(),
4653 (Value::Float(a), Value::Float(b)) if b < a => val.clone(),
4654 (Value::String(a), Value::String(b)) if b < a => val.clone(),
4655 _ => current.clone(),
4656 },
4657 ));
4658 }
4659 }
4660 min_val.unwrap_or(Value::Null)
4661 },
4662 AggregateFunc::Max(col) => {
4663 let mut max_val: Option<Value> = None;
4664 for row in rows {
4665 if let Some((_, val)) = row.values.iter().find(|(c, _)| c == col) {
4666 if matches!(val, Value::Null) {
4667 continue;
4668 }
4669 max_val = Some(max_val.as_ref().map_or_else(
4670 || val.clone(),
4671 |current| match (current, val) {
4672 (Value::Int(a), Value::Int(b)) if b > a => val.clone(),
4673 (Value::Float(a), Value::Float(b)) if b > a => val.clone(),
4674 (Value::String(a), Value::String(b)) if b > a => val.clone(),
4675 _ => current.clone(),
4676 },
4677 ));
4678 }
4679 }
4680 max_val.unwrap_or(Value::Null)
4681 },
4682 }
4683 }
4684
4685 #[allow(clippy::unused_self)] fn values_to_group_key(&self, values: &[Value]) -> String {
4687 values
4688 .iter()
4689 .map(|v| match v {
4690 Value::Null => "NULL".to_string(),
4691 Value::Int(i) => format!("I:{i}"),
4692 Value::Float(f) => format!("F:{f}"),
4693 Value::String(s) => format!("S:{s}"),
4694 Value::Bool(b) => format!("B:{b}"),
4695 Value::Bytes(b) => format!("BY:{}", hex::encode(b)),
4696 Value::Json(j) => format!("J:{j}"),
4697 _ => "UNKNOWN".to_string(),
4698 })
4699 .collect::<Vec<_>>()
4700 .join("|")
4701 }
4702
4703 fn parse_aggregate(&self, expr: &Expr) -> Option<AggregateFunc> {
4704 if let ExprKind::Call(call) = &expr.kind {
4705 let name = call.name.name.to_uppercase();
4706 match name.as_str() {
4707 "COUNT" => {
4708 if call.args.is_empty() || matches!(&call.args[0].kind, ExprKind::Wildcard) {
4709 Some(AggregateFunc::Count(None))
4710 } else {
4711 let col = self.expr_to_column_name(&call.args[0]).ok()?;
4712 Some(AggregateFunc::Count(Some(col)))
4713 }
4714 },
4715 "SUM" => {
4716 if call.args.is_empty() {
4717 return None;
4718 }
4719 let col = self.expr_to_column_name(&call.args[0]).ok()?;
4720 Some(AggregateFunc::Sum(col))
4721 },
4722 "AVG" => {
4723 if call.args.is_empty() {
4724 return None;
4725 }
4726 let col = self.expr_to_column_name(&call.args[0]).ok()?;
4727 Some(AggregateFunc::Avg(col))
4728 },
4729 "MIN" => {
4730 if call.args.is_empty() {
4731 return None;
4732 }
4733 let col = self.expr_to_column_name(&call.args[0]).ok()?;
4734 Some(AggregateFunc::Min(col))
4735 },
4736 "MAX" => {
4737 if call.args.is_empty() {
4738 return None;
4739 }
4740 let col = self.expr_to_column_name(&call.args[0]).ok()?;
4741 Some(AggregateFunc::Max(col))
4742 },
4743 _ => None,
4744 }
4745 } else {
4746 None
4747 }
4748 }
4749
4750 fn aggregate_default_name(&self, expr: &Expr) -> String {
4751 if let ExprKind::Call(call) = &expr.kind {
4752 let name = call.name.name.to_uppercase();
4753 if call.args.is_empty() || matches!(&call.args[0].kind, ExprKind::Wildcard) {
4754 format!("{name}(*)")
4755 } else if let Ok(col) = self.expr_to_column_name(&call.args[0]) {
4756 format!("{name}({col})")
4757 } else {
4758 format!("{name}(?)")
4759 }
4760 } else {
4761 "?".to_string()
4762 }
4763 }
4764
4765 fn get_row_value(&self, expr: &Expr, row: &Row) -> Option<Value> {
4766 match &expr.kind {
4767 ExprKind::Literal(lit) => Some(match lit {
4768 Literal::Null => Value::Null,
4769 Literal::Boolean(b) => Value::Bool(*b),
4770 Literal::Integer(i) => Value::Int(*i),
4771 Literal::Float(f) => Value::Float(*f),
4772 Literal::String(s) => Value::String(s.clone()),
4773 }),
4774 ExprKind::Ident(ident) => {
4775 row.values
4777 .iter()
4778 .find(|(col, _)| {
4779 col == &ident.name || col.ends_with(&format!(".{}", ident.name))
4780 })
4781 .map(|(_, v)| v.clone())
4782 },
4783 ExprKind::Qualified(table_expr, col) => {
4784 if let ExprKind::Ident(table) = &table_expr.kind {
4785 let full_name = format!("{}.{}", table.name, col.name);
4786 row.values
4787 .iter()
4788 .find(|(c, _)| c == &full_name)
4789 .map(|(_, v)| v.clone())
4790 } else {
4791 None
4792 }
4793 },
4794 ExprKind::Call(_) => {
4795 let col_name = self.aggregate_default_name(expr);
4798 row.values
4799 .iter()
4800 .find(|(c, _)| c == &col_name)
4801 .map(|(_, v)| v.clone())
4802 },
4803 _ => None,
4804 }
4805 }
4806
4807 #[allow(clippy::unused_self)] #[allow(clippy::unnecessary_wraps)] fn extract_projection(
4810 &self,
4811 items: &[neumann_parser::SelectItem],
4812 ) -> Result<Option<Vec<String>>> {
4813 if items.len() == 1 && matches!(&items[0].expr.kind, ExprKind::Wildcard) {
4815 return Ok(None);
4816 }
4817
4818 for item in items {
4820 if matches!(
4821 &item.expr.kind,
4822 ExprKind::Wildcard | ExprKind::QualifiedWildcard(_)
4823 ) {
4824 return Ok(None);
4825 }
4826 }
4827
4828 let mut columns = Vec::with_capacity(items.len());
4829 for item in items {
4830 match &item.expr.kind {
4831 ExprKind::Ident(ident) => {
4832 columns.push(ident.name.clone());
4833 },
4834 ExprKind::Qualified(_, name) => {
4835 columns.push(name.name.clone());
4836 },
4837 _ => {
4838 return Ok(None);
4840 },
4841 }
4842 }
4843
4844 Ok(Some(columns))
4845 }
4846
4847 fn exec_insert(&self, insert: &InsertStmt) -> Result<QueryResult> {
4848 match &insert.source {
4849 InsertSource::Values(rows) => {
4850 let mut ids = Vec::new();
4851 for row_values in rows {
4852 let mut values = HashMap::new();
4853 if let Some(ref cols) = insert.columns {
4855 for (col, val) in cols.iter().zip(row_values.iter()) {
4857 values.insert(col.name.clone(), self.expr_to_value(val)?);
4858 }
4859 } else {
4860 let schema = self.relational.get_schema(&insert.table.name)?;
4862 for (col, val) in schema.columns.iter().zip(row_values.iter()) {
4863 values.insert(col.name.clone(), self.expr_to_value(val)?);
4864 }
4865 }
4866 let id = self.relational.insert(&insert.table.name, values)?;
4867 ids.push(id);
4868 }
4869 Ok(QueryResult::Ids(ids))
4870 },
4871 InsertSource::Query(select) => {
4872 let select_result = self.exec_select(select)?;
4874
4875 let QueryResult::Rows(rows) = select_result else {
4877 return Err(RouterError::ParseError(
4878 "INSERT ... SELECT query did not return rows".to_string(),
4879 ));
4880 };
4881
4882 if rows.is_empty() {
4883 return Ok(QueryResult::Ids(vec![]));
4884 }
4885
4886 let schema = self.relational.get_schema(&insert.table.name)?;
4888
4889 let columns: Vec<String> = if let Some(ref cols) = insert.columns {
4891 cols.iter().map(|c| c.name.clone()).collect()
4892 } else {
4893 schema.columns.iter().map(|c| c.name.clone()).collect()
4894 };
4895
4896 let mut ids = Vec::new();
4898 for row in rows {
4899 let mut values: HashMap<String, Value> = HashMap::new();
4900
4901 for col in &columns {
4902 if let Some(val) = row.get(col) {
4903 values.insert(col.clone(), val.clone());
4904 }
4905 }
4906
4907 let id = self.relational.insert(&insert.table.name, values)?;
4908 ids.push(id);
4909 }
4910
4911 Ok(QueryResult::Ids(ids))
4912 },
4913 }
4914 }
4915
4916 fn protect_destructive_op(
4920 &self,
4921 command: &str,
4922 op: DestructiveOp,
4923 sample_data: Vec<String>,
4924 ) -> ProtectedOpResult {
4925 let Some(checkpoint) = self.checkpoint.as_ref() else {
4927 return ProtectedOpResult::Proceed;
4928 };
4929
4930 if !checkpoint.auto_checkpoint_enabled() {
4932 return ProtectedOpResult::Proceed;
4933 }
4934
4935 let preview = checkpoint.generate_preview(&op, sample_data);
4937
4938 if !checkpoint.request_confirmation(&op, &preview) {
4940 return ProtectedOpResult::Cancelled;
4941 }
4942
4943 let store = self.vector.store();
4945 if let Err(e) = checkpoint.create_auto(command, op, preview, store) {
4946 eprintln!("Warning: Failed to create auto-checkpoint: {e}");
4948 }
4949
4950 ProtectedOpResult::Proceed
4951 }
4952
4953 fn collect_delete_sample(
4955 &self,
4956 table: &str,
4957 condition: &Condition,
4958 limit: usize,
4959 ) -> (usize, Vec<String>) {
4960 let Ok(rows) = self.relational.select(table, condition.clone()) else {
4961 return (0, vec![]);
4962 };
4963
4964 let count = rows.len();
4965 let sample: Vec<String> = rows
4966 .into_iter()
4967 .take(limit)
4968 .map(|row| {
4969 let pairs: Vec<String> = row
4970 .values
4971 .iter()
4972 .map(|(k, v)| format!("{k}={v:?}"))
4973 .collect();
4974 format!("_id={}, {}", row.id, pairs.join(", "))
4975 })
4976 .collect();
4977
4978 (count, sample)
4979 }
4980
4981 fn collect_table_sample(&self, table: &str, limit: usize) -> (usize, Vec<String>) {
4983 self.collect_delete_sample(table, &Condition::True, limit)
4984 }
4985
4986 fn collect_node_info(&self, node_id: u64) -> (usize, Vec<String>) {
4988 let edge_count = self
4990 .graph
4991 .neighbors(node_id, None, Direction::Both, None)
4992 .map(|nodes| nodes.len())
4993 .unwrap_or(0);
4994
4995 let sample = match self.graph.get_node(node_id) {
4997 Ok(node) => {
4998 let props: Vec<String> = node
4999 .properties
5000 .iter()
5001 .take(3)
5002 .map(|(k, v)| format!("{k}={v:?}"))
5003 .collect();
5004 vec![format!(
5005 "label='{}', {}",
5006 node.labels.join(":"),
5007 props.join(", ")
5008 )]
5009 },
5010 Err(_) => vec![],
5011 };
5012
5013 (edge_count, sample)
5014 }
5015
5016 fn collect_edge_info(&self, edge_id: u64) -> Vec<String> {
5017 match self.graph.get_edge(edge_id) {
5018 Ok(edge) => {
5019 let props: Vec<String> = edge
5020 .properties
5021 .iter()
5022 .take(3)
5023 .map(|(k, v)| format!("{k}={v:?}"))
5024 .collect();
5025 vec![format!(
5026 "type='{}', from={}, to={}, {}",
5027 edge.edge_type,
5028 edge.from,
5029 edge.to,
5030 props.join(", ")
5031 )]
5032 },
5033 Err(_) => vec![],
5034 }
5035 }
5036
5037 fn exec_update(&self, update: &UpdateStmt) -> Result<QueryResult> {
5040 let condition = if let Some(ref where_expr) = update.where_clause {
5041 self.expr_to_condition(where_expr)?
5042 } else {
5043 Condition::True
5044 };
5045
5046 let mut values = HashMap::new();
5047 for assign in &update.assignments {
5048 values.insert(
5049 assign.column.name.clone(),
5050 self.expr_to_value(&assign.value)?,
5051 );
5052 }
5053
5054 let count = self
5055 .relational
5056 .update(&update.table.name, condition, values)?;
5057 Ok(QueryResult::Count(count))
5058 }
5059
5060 fn exec_delete(&self, delete: &DeleteStmt) -> Result<QueryResult> {
5061 let table = &delete.table.name;
5062 let condition = if let Some(ref where_expr) = delete.where_clause {
5063 self.expr_to_condition(where_expr)?
5064 } else {
5065 Condition::True
5066 };
5067
5068 let (row_count, sample_data) = self.collect_delete_sample(table, &condition, 5);
5070
5071 if row_count > 0 {
5073 let op = DestructiveOp::Delete {
5074 table: table.clone(),
5075 row_count,
5076 };
5077
5078 let command = format!(
5079 "DELETE FROM {}{}",
5080 table,
5081 if delete.where_clause.is_some() {
5082 " WHERE ..."
5083 } else {
5084 ""
5085 }
5086 );
5087
5088 match self.protect_destructive_op(&command, op, sample_data) {
5089 ProtectedOpResult::Proceed => {},
5090 ProtectedOpResult::Cancelled => {
5091 return Err(RouterError::CheckpointError(
5092 "Operation cancelled by user".to_string(),
5093 ));
5094 },
5095 }
5096 }
5097
5098 let count = self.relational.delete_rows(table, condition)?;
5099 Ok(QueryResult::Count(count))
5100 }
5101
5102 fn exec_create_table(&self, create: &parser::CreateTableStmt) -> Result<QueryResult> {
5103 if create.if_not_exists && self.relational.table_exists(&create.table.name) {
5104 return Ok(QueryResult::Empty);
5105 }
5106
5107 let mut columns = Vec::new();
5108 for col in &create.columns {
5109 let col_type = self.data_type_to_column_type(&col.data_type)?;
5110 let mut column = relational_engine::Column::new(&col.name.name, col_type);
5111
5112 let is_nullable = !col
5114 .constraints
5115 .iter()
5116 .any(|c| matches!(c, parser::ColumnConstraint::NotNull));
5117 if is_nullable {
5118 column = column.nullable();
5119 }
5120 columns.push(column);
5121 }
5122
5123 let schema = relational_engine::Schema::new(columns);
5124 self.relational.create_table(&create.table.name, schema)?;
5125 Ok(QueryResult::Empty)
5126 }
5127
5128 fn exec_node(&self, node: &NodeStmt) -> Result<QueryResult> {
5129 match &node.operation {
5130 NodeOp::Create { label, properties } => {
5131 let props = self.properties_to_map(properties)?;
5132 let id = self.graph.create_node(&label.name, props)?;
5133 Ok(QueryResult::Ids(vec![id]))
5134 },
5135 NodeOp::Get { id } => {
5136 let node_id = self.expr_to_u64(id)?;
5137 let node = self.graph.get_node(node_id)?;
5138 let properties: HashMap<String, String> = node
5139 .properties
5140 .iter()
5141 .map(|(k, v)| (k.clone(), Self::property_to_string(v)))
5142 .collect();
5143 Ok(QueryResult::Nodes(vec![NodeResult {
5144 id: node.id,
5145 label: node.labels.join(":"),
5146 properties,
5147 }]))
5148 },
5149 NodeOp::Delete { id } => {
5150 let node_id = self.expr_to_u64(id)?;
5151
5152 let (edge_count, sample_data) = self.collect_node_info(node_id);
5154
5155 let op = DestructiveOp::NodeDelete {
5157 node_id,
5158 edge_count,
5159 };
5160
5161 match self.protect_destructive_op(
5162 &format!("NODE DELETE {node_id}"),
5163 op,
5164 sample_data,
5165 ) {
5166 ProtectedOpResult::Proceed => {},
5167 ProtectedOpResult::Cancelled => {
5168 return Err(RouterError::CheckpointError(
5169 "Operation cancelled by user".to_string(),
5170 ));
5171 },
5172 }
5173
5174 self.graph.delete_node(node_id)?;
5175 Ok(QueryResult::Count(1))
5176 },
5177 NodeOp::List {
5178 label,
5179 limit,
5180 offset,
5181 } => {
5182 let label_filter = label.as_ref().map(|l| l.name.as_str());
5184 let limit_val = limit
5185 .as_ref()
5186 .map(|e| self.expr_to_usize(e))
5187 .transpose()?
5188 .unwrap_or(1000);
5189 let offset_val = offset
5190 .as_ref()
5191 .map(|e| self.expr_to_usize(e))
5192 .transpose()?
5193 .unwrap_or(0);
5194 let unified_items =
5195 self.scan_find_nodes(label_filter, None, limit_val, offset_val)?;
5196
5197 let nodes: Vec<NodeResult> = unified_items
5199 .into_iter()
5200 .map(|item| {
5201 let id = item.id.parse::<u64>().unwrap_or(0);
5202 let label = item.data.get("label").cloned().unwrap_or_default();
5203 let properties: HashMap<String, String> = item
5204 .data
5205 .into_iter()
5206 .filter(|(k, _)| k != "label")
5207 .collect();
5208 NodeResult {
5209 id,
5210 label,
5211 properties,
5212 }
5213 })
5214 .collect();
5215
5216 Ok(QueryResult::Nodes(nodes))
5217 },
5218 }
5219 }
5220
5221 fn exec_edge(&self, edge: &EdgeStmt) -> Result<QueryResult> {
5222 match &edge.operation {
5223 EdgeOp::Create {
5224 from_id,
5225 to_id,
5226 edge_type,
5227 properties,
5228 } => {
5229 let from = self.expr_to_u64(from_id)?;
5230 let to = self.expr_to_u64(to_id)?;
5231 let props = self.properties_to_map(properties)?;
5232 let id = self
5233 .graph
5234 .create_edge(from, to, &edge_type.name, props, true)?;
5235 Ok(QueryResult::Ids(vec![id]))
5236 },
5237 EdgeOp::Get { id } => {
5238 let edge_id = self.expr_to_u64(id)?;
5239 let edge = self.graph.get_edge(edge_id)?;
5240 Ok(QueryResult::Edges(vec![EdgeResult {
5241 id: edge.id,
5242 from: edge.from,
5243 to: edge.to,
5244 label: edge.edge_type,
5245 }]))
5246 },
5247 EdgeOp::Delete { id } => {
5248 let edge_id = self.expr_to_u64(id)?;
5249 let sample_data = self.collect_edge_info(edge_id);
5250 let op = DestructiveOp::EdgeDelete { edge_id };
5251
5252 match self.protect_destructive_op(
5253 &format!("EDGE DELETE {edge_id}"),
5254 op,
5255 sample_data,
5256 ) {
5257 ProtectedOpResult::Proceed => {},
5258 ProtectedOpResult::Cancelled => {
5259 return Err(RouterError::CheckpointError(
5260 "Operation cancelled by user".to_string(),
5261 ));
5262 },
5263 }
5264
5265 self.graph.delete_edge(edge_id)?;
5266 Ok(QueryResult::Count(1))
5267 },
5268 EdgeOp::List {
5269 edge_type,
5270 limit,
5271 offset,
5272 } => {
5273 let type_filter = edge_type.as_ref().map(|t| t.name.as_str());
5275 let limit_val = limit
5276 .as_ref()
5277 .map(|e| self.expr_to_usize(e))
5278 .transpose()?
5279 .unwrap_or(1000);
5280 let offset_val = offset
5281 .as_ref()
5282 .map(|e| self.expr_to_usize(e))
5283 .transpose()?
5284 .unwrap_or(0);
5285 let unified_items =
5286 self.scan_find_edges(type_filter, None, limit_val, offset_val)?;
5287
5288 let edges: Vec<EdgeResult> = unified_items
5290 .into_iter()
5291 .map(|item| {
5292 let id = item.id.parse::<u64>().unwrap_or(0);
5293 let from = item
5294 .data
5295 .get("from")
5296 .and_then(|s| s.parse::<u64>().ok())
5297 .unwrap_or(0);
5298 let to = item
5299 .data
5300 .get("to")
5301 .and_then(|s| s.parse::<u64>().ok())
5302 .unwrap_or(0);
5303 let label = item.data.get("type").cloned().unwrap_or_default();
5304 EdgeResult {
5305 id,
5306 from,
5307 to,
5308 label,
5309 }
5310 })
5311 .collect();
5312
5313 Ok(QueryResult::Edges(edges))
5314 },
5315 }
5316 }
5317
5318 fn exec_neighbors(&self, neighbors: &NeighborsStmt) -> Result<QueryResult> {
5319 if let Some(ref similarity_vec) = neighbors.by_similarity {
5321 let entity_key = self.expr_to_string(&neighbors.node_id)?;
5323
5324 let query: Vec<f32> = similarity_vec
5325 .iter()
5326 .map(|e| self.expr_to_f32(e))
5327 .collect::<Result<_>>()?;
5328
5329 let top_k = neighbors
5330 .limit
5331 .as_ref()
5332 .map(|e| self.expr_to_usize(e))
5333 .transpose()?
5334 .unwrap_or(10);
5335
5336 let items = self.find_neighbors_by_similarity(&entity_key, &query, top_k)?;
5338
5339 let results: Vec<SimilarResult> = items
5340 .into_iter()
5341 .map(|item| SimilarResult {
5342 key: item.id,
5343 score: item.score.unwrap_or(0.0),
5344 })
5345 .collect();
5346
5347 return Ok(QueryResult::Similar(results));
5348 }
5349
5350 let node_id = self.expr_to_u64(&neighbors.node_id)?;
5352
5353 let direction = match neighbors.direction {
5354 ParsedDirection::Outgoing => Direction::Outgoing,
5355 ParsedDirection::Incoming => Direction::Incoming,
5356 ParsedDirection::Both => Direction::Both,
5357 };
5358
5359 let edge_type = neighbors.edge_type.as_ref().map(|e| e.name.as_str());
5360 let neighbor_nodes = self.graph.neighbors(node_id, edge_type, direction, None)?;
5361 let neighbor_ids: Vec<u64> = neighbor_nodes.iter().map(|n| n.id).collect();
5362
5363 Ok(QueryResult::Ids(neighbor_ids))
5364 }
5365
5366 fn exec_path(&self, path: &PathStmt) -> Result<QueryResult> {
5367 let from = self.expr_to_u64(&path.from_id)?;
5368 let to = self.expr_to_u64(&path.to_id)?;
5369
5370 match self.graph.find_path(from, to, None) {
5371 Ok(path) => Ok(QueryResult::Path(path.nodes)),
5372 Err(GraphError::PathNotFound) => Ok(QueryResult::Path(vec![])),
5373 Err(e) => Err(e.into()),
5374 }
5375 }
5376
5377 fn exec_embed(&self, embed: &EmbedStmt) -> Result<QueryResult> {
5378 let collection = embed.collection.as_deref();
5379
5380 match &embed.operation {
5381 EmbedOp::Store { key, vector } => {
5382 let key_str = self.expr_to_string(key)?;
5383 let vec: Vec<f32> = vector
5384 .iter()
5385 .map(|e| self.expr_to_f32(e))
5386 .collect::<Result<_>>()?;
5387
5388 if let Some(coll) = collection {
5389 self.vector.store_in_collection(coll, &key_str, vec)?;
5390 } else {
5391 self.vector.store_embedding(&key_str, vec)?;
5392 self.bump_vector_generation();
5393 }
5394 Ok(QueryResult::Empty)
5395 },
5396 EmbedOp::Get { key } => {
5397 let key_str = self.expr_to_string(key)?;
5398 let vec = if let Some(coll) = collection {
5399 self.vector.get_from_collection(coll, &key_str)?
5400 } else {
5401 self.vector.get_embedding(&key_str)?
5402 };
5403 Ok(QueryResult::Value(format!("{vec:?}")))
5404 },
5405 EmbedOp::Delete { key } => {
5406 let key_str = self.expr_to_string(key)?;
5407
5408 let op = DestructiveOp::EmbedDelete {
5410 key: key_str.clone(),
5411 };
5412
5413 match self.protect_destructive_op(
5414 &format!("EMBED DELETE '{key_str}'"),
5415 op,
5416 vec![format!("embedding key: {}", key_str)],
5417 ) {
5418 ProtectedOpResult::Proceed => {},
5419 ProtectedOpResult::Cancelled => {
5420 return Err(RouterError::CheckpointError(
5421 "Operation cancelled by user".to_string(),
5422 ));
5423 },
5424 }
5425
5426 if let Some(coll) = collection {
5427 self.vector.delete_from_collection(coll, &key_str)?;
5428 } else {
5429 self.vector.delete_embedding(&key_str)?;
5430 self.bump_vector_generation();
5431 }
5432 Ok(QueryResult::Count(1))
5433 },
5434 EmbedOp::BuildIndex => {
5435 if self.hnsw_index.is_some() {
5438 Ok(QueryResult::Value("HNSW index already built".to_string()))
5439 } else {
5440 Err(RouterError::VectorError(
5441 "Use router.build_vector_index() to build HNSW index".to_string(),
5442 ))
5443 }
5444 },
5445 EmbedOp::Batch { items } => {
5446 let mut count = 0;
5447 for (key_expr, vector_exprs) in items {
5448 let key_str = self.expr_to_string(key_expr)?;
5449 let vec: Vec<f32> = vector_exprs
5450 .iter()
5451 .map(|e| self.expr_to_f32(e))
5452 .collect::<Result<_>>()?;
5453
5454 if let Some(coll) = collection {
5455 self.vector.store_in_collection(coll, &key_str, vec)?;
5456 } else {
5457 self.vector.store_embedding(&key_str, vec)?;
5458 }
5459 count += 1;
5460 }
5461 if collection.is_none() && count > 0 {
5462 self.bump_vector_generation();
5463 }
5464 Ok(QueryResult::Count(count))
5465 },
5466 }
5467 }
5468
5469 #[allow(clippy::too_many_lines)] fn exec_similar(&self, similar: &SimilarStmt) -> Result<QueryResult> {
5471 let top_k = similar
5472 .limit
5473 .as_ref()
5474 .map(|e| self.expr_to_usize(e))
5475 .transpose()?
5476 .unwrap_or(10);
5477
5478 let collection = similar.collection.as_deref();
5479
5480 if let Some(ref connected_to_expr) = similar.connected_to {
5482 let query_key = match &similar.query {
5483 SimilarQuery::Key(key) => self.expr_to_string(key)?,
5484 SimilarQuery::Vector(_) => {
5485 return Err(RouterError::ParseError(
5486 "SIMILAR...CONNECTED TO requires a key, not a vector".to_string(),
5487 ));
5488 },
5489 };
5490 let connected_to = self.expr_to_string(connected_to_expr)?;
5491
5492 let items = self.find_similar_connected(&query_key, &connected_to, top_k)?;
5494
5495 let results: Vec<SimilarResult> = items
5496 .into_iter()
5497 .map(|item| SimilarResult {
5498 key: item.id,
5499 score: item.score.unwrap_or(0.0),
5500 })
5501 .collect();
5502
5503 return Ok(QueryResult::Similar(results));
5504 }
5505
5506 let query_vec = match &similar.query {
5508 SimilarQuery::Key(key) => {
5509 let key_str = self.expr_to_string(key)?;
5510 if let Some(coll) = collection {
5511 self.vector.get_from_collection(coll, &key_str)?
5512 } else {
5513 self.vector.get_embedding(&key_str)?
5514 }
5515 },
5516 SimilarQuery::Vector(exprs) => exprs
5517 .iter()
5518 .map(|e| self.expr_to_f32(e))
5519 .collect::<Result<_>>()?,
5520 };
5521
5522 let filter = if let Some(ref where_expr) = similar.where_clause {
5524 Some(self.expr_to_filter_condition(where_expr)?)
5525 } else {
5526 None
5527 };
5528
5529 let metric = match similar.metric {
5531 Some(ParsedDistanceMetric::Cosine) | None => VectorDistanceMetric::Cosine,
5532 Some(ParsedDistanceMetric::Euclidean) => VectorDistanceMetric::Euclidean,
5533 Some(ParsedDistanceMetric::DotProduct) => VectorDistanceMetric::DotProduct,
5534 };
5535
5536 let results = match (collection, &filter) {
5538 (Some(coll), Some(f)) => self
5540 .vector
5541 .search_filtered_in_collection(coll, &query_vec, top_k, f, None)?
5542 .into_iter()
5543 .map(|r| SimilarResult {
5544 key: r.key,
5545 score: r.score,
5546 })
5547 .collect(),
5548 (Some(coll), None) => self
5550 .vector
5551 .search_in_collection(coll, &query_vec, top_k)?
5552 .into_iter()
5553 .map(|r| SimilarResult {
5554 key: r.key,
5555 score: r.score,
5556 })
5557 .collect(),
5558 (None, Some(f)) => self
5560 .vector
5561 .search_similar_filtered(&query_vec, top_k, f, None)?
5562 .into_iter()
5563 .map(|r| SimilarResult {
5564 key: r.key,
5565 score: r.score,
5566 })
5567 .collect(),
5568 (None, None) => {
5570 if let Some((ref index, ref keys)) =
5571 self.hnsw_index.as_ref().filter(|_| self.hnsw_is_fresh())
5572 {
5573 if matches!(metric, VectorDistanceMetric::Cosine) {
5575 self.vector
5576 .search_with_hnsw(index, keys, &query_vec, top_k)?
5577 .into_iter()
5578 .map(|r| SimilarResult {
5579 key: r.key,
5580 score: r.score,
5581 })
5582 .collect()
5583 } else {
5584 self.vector
5585 .search_similar_with_metric(&query_vec, top_k, metric)?
5586 .into_iter()
5587 .map(|r| SimilarResult {
5588 key: r.key,
5589 score: r.score,
5590 })
5591 .collect()
5592 }
5593 } else {
5594 self.vector
5595 .search_similar_with_metric(&query_vec, top_k, metric)?
5596 .into_iter()
5597 .map(|r| SimilarResult {
5598 key: r.key,
5599 score: r.score,
5600 })
5601 .collect()
5602 }
5603 },
5604 };
5605
5606 Ok(QueryResult::Similar(results))
5607 }
5608
5609 fn exec_find(&self, find: &FindStmt) -> Result<QueryResult> {
5610 let unified = self.require_unified()?;
5611 let runtime = Self::create_runtime()?;
5612
5613 let limit = find
5614 .limit
5615 .as_ref()
5616 .map(|e| self.expr_to_usize(e))
5617 .transpose()?;
5618
5619 let has_similar = find.similar_to.is_some();
5620 let has_connected = find.connected_to.is_some();
5621
5622 if (has_similar || has_connected) && !matches!(find.pattern, FindPattern::Nodes { .. }) {
5624 return Err(RouterError::InvalidArgument(
5625 "SIMILAR TO and CONNECTED TO are only supported with FIND NODE".to_string(),
5626 ));
5627 }
5628
5629 if has_similar || has_connected {
5631 let FindPattern::Nodes { ref label } = find.pattern else {
5632 unreachable!()
5633 };
5634
5635 let condition = find
5636 .where_clause
5637 .as_ref()
5638 .map(|expr| self.expr_to_condition(expr))
5639 .transpose()?;
5640
5641 let similar_key = find
5642 .similar_to
5643 .as_ref()
5644 .map(|e| self.expr_to_string(e))
5645 .transpose()?;
5646
5647 let connected_key = find
5648 .connected_to
5649 .as_ref()
5650 .map(|e| self.expr_to_string(e))
5651 .transpose()?;
5652
5653 let effective_limit = limit.unwrap_or(100);
5654 let label_filter = label.as_ref().map(|l| l.name.as_str());
5655
5656 let items = runtime
5657 .block_on(unified.find_nodes_hybrid(
5658 label_filter,
5659 condition.as_ref(),
5660 similar_key.as_deref(),
5661 connected_key.as_deref(),
5662 effective_limit,
5663 ))
5664 .map_err(|e| RouterError::GraphError(e.to_string()))?;
5665
5666 let description = format!(
5667 "Found {} node{}",
5668 items.len(),
5669 if items.len() == 1 { "" } else { "s" }
5670 );
5671
5672 return Ok(QueryResult::Unified(UnifiedResult { description, items }));
5673 }
5674
5675 let pattern = self.convert_find_pattern(&find.pattern);
5676
5677 if let Some(ref where_expr) = find.where_clause {
5679 let condition = self.expr_to_condition(where_expr)?;
5680 let effective_limit = limit.unwrap_or(100);
5681
5682 let (items, entity_type) = match &find.pattern {
5683 FindPattern::Nodes { label } => {
5684 let label_filter = label.as_ref().map(|l| l.name.as_str());
5685 let items =
5686 self.scan_find_nodes(label_filter, Some(&condition), effective_limit, 0)?;
5687 (items, "node")
5688 },
5689 FindPattern::Edges { edge_type } => {
5690 let type_filter = edge_type.as_ref().map(|t| t.name.as_str());
5691 let items =
5692 self.scan_find_edges(type_filter, Some(&condition), effective_limit, 0)?;
5693 (items, "edge")
5694 },
5695 FindPattern::Rows { table } => {
5696 let items =
5697 self.scan_find_rows(&table.name, Some(&condition), effective_limit)?;
5698 (items, "row")
5699 },
5700 FindPattern::Path { .. } => (Vec::new(), "path"),
5701 };
5702
5703 let description = format!(
5704 "Found {} {}{}",
5705 items.len(),
5706 entity_type,
5707 if items.len() == 1 { "" } else { "s" }
5708 );
5709 return Ok(QueryResult::Unified(UnifiedResult { description, items }));
5710 }
5711
5712 let result = runtime
5714 .block_on(unified.find(&pattern, limit))
5715 .map_err(|e| RouterError::GraphError(e.to_string()))?;
5716
5717 Ok(QueryResult::Unified(UnifiedResult {
5718 description: result.description,
5719 items: result.items,
5720 }))
5721 }
5722
5723 #[allow(clippy::too_many_lines)] fn exec_entity(&self, entity: &EntityStmt) -> Result<QueryResult> {
5725 match &entity.operation {
5726 EntityOp::Create {
5727 key,
5728 properties,
5729 embedding,
5730 } => {
5731 let key_str = self.expr_to_string(key)?;
5732
5733 let fields: HashMap<String, String> = properties
5735 .iter()
5736 .filter_map(|p| {
5737 self.expr_to_string(&p.value)
5738 .ok()
5739 .map(|v| (p.key.name.clone(), v))
5740 })
5741 .collect();
5742
5743 let emb = if let Some(vec_exprs) = embedding {
5745 let embedding_vec: Result<Vec<f32>> =
5746 vec_exprs.iter().map(|e| self.expr_to_f32(e)).collect();
5747 Some(embedding_vec?)
5748 } else {
5749 None
5750 };
5751
5752 self.create_unified_entity(&key_str, fields, emb)?;
5755
5756 Ok(QueryResult::Value(format!("Entity '{key_str}' created")))
5757 },
5758 EntityOp::Get { key } => {
5759 let key_str = self.expr_to_string(key)?;
5760
5761 if let Some(ref unified) = self.unified {
5763 let runtime =
5764 Runtime::new().map_err(|e| RouterError::InvalidArgument(e.to_string()))?;
5765
5766 let item = runtime
5767 .block_on(unified.get_entity(&key_str))
5768 .map_err(|e| RouterError::NotFound(e.to_string()))?;
5769
5770 return Ok(QueryResult::Unified(UnifiedResult {
5771 description: format!("Entity: {key_str}"),
5772 items: vec![item],
5773 }));
5774 }
5775
5776 let mut data = HashMap::new();
5778 data.insert("key".to_string(), key_str.clone());
5779
5780 if let Ok(embedding) = self.vector.get_embedding(&key_str) {
5782 let item = UnifiedItem {
5783 id: key_str.clone(),
5784 source: "vector".to_string(),
5785 data,
5786 embedding: Some(embedding),
5787 score: None,
5788 };
5789 return Ok(QueryResult::Unified(UnifiedResult {
5790 description: format!("Entity: {key_str}"),
5791 items: vec![item],
5792 }));
5793 }
5794
5795 Err(RouterError::NotFound(format!(
5796 "Entity '{key_str}' not found"
5797 )))
5798 },
5799 EntityOp::Connect {
5800 from_key,
5801 to_key,
5802 edge_type,
5803 } => {
5804 let from_str = self.expr_to_string(from_key)?;
5805 let to_str = self.expr_to_string(to_key)?;
5806 let edge_type_str = &edge_type.name;
5807
5808 let edge_key = self.connect_entities(&from_str, &to_str, edge_type_str)?;
5810
5811 Ok(QueryResult::Value(format!(
5812 "Connected '{from_str}' -> '{to_str}' with edge '{edge_key}'"
5813 )))
5814 },
5815 EntityOp::Batch { entities } => {
5816 #[allow(clippy::type_complexity)]
5817 let items: Vec<(
5818 String,
5819 HashMap<String, String>,
5820 Option<Vec<f32>>,
5821 )> = entities
5822 .iter()
5823 .map(|e| {
5824 let key = self.expr_to_string(&e.key)?;
5825 let props: HashMap<String, String> = e
5826 .properties
5827 .iter()
5828 .filter_map(|p| {
5829 self.expr_to_string(&p.value)
5830 .ok()
5831 .map(|v| (p.key.name.clone(), v))
5832 })
5833 .collect();
5834 let emb = e
5835 .embedding
5836 .as_ref()
5837 .map(|v| v.iter().map(|ex| self.expr_to_f32(ex)).collect())
5838 .transpose()?;
5839 Ok((key, props, emb))
5840 })
5841 .collect::<Result<Vec<_>>>()?;
5842
5843 let has_embeddings = items.iter().any(|(_, _, emb)| emb.is_some());
5844 let unified = self.require_unified()?;
5845 let runtime = Self::create_runtime()?;
5846 let batch_result = runtime
5847 .block_on(unified.create_entities_batch(items))
5848 .map_err(|e| RouterError::VectorError(e.to_string()))?;
5849
5850 if has_embeddings {
5851 self.bump_vector_generation();
5852 }
5853
5854 Ok(QueryResult::BatchResult(BatchOperationResult {
5855 operation: "ENTITY CREATE".to_string(),
5856 affected_count: batch_result.count,
5857 created_ids: None, }))
5859 },
5860 EntityOp::Update {
5861 key,
5862 properties,
5863 embedding,
5864 } => {
5865 let key_str = self.expr_to_string(key)?;
5866
5867 let fields: HashMap<String, String> = properties
5869 .iter()
5870 .filter_map(|p| {
5871 self.expr_to_string(&p.value)
5872 .ok()
5873 .map(|v| (p.key.name.clone(), v))
5874 })
5875 .collect();
5876
5877 let emb = if let Some(vec_exprs) = embedding {
5879 let embedding_vec: Result<Vec<f32>> =
5880 vec_exprs.iter().map(|e| self.expr_to_f32(e)).collect();
5881 Some(embedding_vec?)
5882 } else {
5883 None
5884 };
5885
5886 let has_embedding = emb.is_some();
5887 let unified = self.require_unified()?;
5888 let runtime = Self::create_runtime()?;
5889 runtime
5890 .block_on(unified.update_entity(&key_str, fields, emb))
5891 .map_err(|e| RouterError::NotFound(e.to_string()))?;
5892
5893 if has_embedding {
5894 self.bump_vector_generation();
5895 }
5896
5897 Ok(QueryResult::Value(format!("Entity '{key_str}' updated")))
5898 },
5899 EntityOp::Delete { key } => {
5900 let key_str = self.expr_to_string(key)?;
5901
5902 let unified = self.require_unified()?;
5903 let runtime = Self::create_runtime()?;
5904 runtime
5905 .block_on(unified.delete_entity(&key_str))
5906 .map_err(|e| RouterError::NotFound(e.to_string()))?;
5907
5908 self.bump_vector_generation();
5909
5910 Ok(QueryResult::Value(format!("Entity '{key_str}' deleted")))
5911 },
5912 }
5913 }
5914
5915 fn scan_find_nodes(
5917 &self,
5918 label_filter: Option<&str>,
5919 condition: Option<&Condition>,
5920 limit: usize,
5921 offset: usize,
5922 ) -> Result<Vec<UnifiedItem>> {
5923 let unified = self.require_unified()?;
5924 let runtime = Self::create_runtime()?;
5925
5926 let items = runtime
5927 .block_on(unified.find_nodes(label_filter, condition))
5928 .map_err(|e| RouterError::GraphError(e.to_string()))?;
5929
5930 let items: Vec<UnifiedItem> = items.into_iter().skip(offset).take(limit).collect();
5931 Ok(items)
5932 }
5933
5934 fn scan_find_edges(
5936 &self,
5937 type_filter: Option<&str>,
5938 condition: Option<&Condition>,
5939 limit: usize,
5940 offset: usize,
5941 ) -> Result<Vec<UnifiedItem>> {
5942 let unified = self.require_unified()?;
5943 let runtime = Self::create_runtime()?;
5944
5945 let items = runtime
5946 .block_on(unified.find_edges(type_filter, condition))
5947 .map_err(|e| RouterError::GraphError(e.to_string()))?;
5948
5949 let items: Vec<UnifiedItem> = items.into_iter().skip(offset).take(limit).collect();
5950 Ok(items)
5951 }
5952
5953 fn scan_find_rows(
5955 &self,
5956 table: &str,
5957 condition: Option<&Condition>,
5958 limit: usize,
5959 ) -> Result<Vec<UnifiedItem>> {
5960 let unified = self.require_unified()?;
5961 let runtime = Self::create_runtime()?;
5962
5963 let mut items = runtime
5964 .block_on(unified.find_rows(table, condition))
5965 .map_err(|e| RouterError::RelationalError(e.to_string()))?;
5966
5967 items.truncate(limit);
5968 Ok(items)
5969 }
5970
5971 #[allow(clippy::unused_self)] fn convert_find_pattern(&self, ast_pattern: &FindPattern) -> UnifiedFindPattern {
5974 match ast_pattern {
5975 FindPattern::Nodes { label } => UnifiedFindPattern::Nodes {
5976 label: label.as_ref().map(|l| l.name.clone()),
5977 },
5978 FindPattern::Edges { edge_type } => UnifiedFindPattern::Edges {
5979 edge_type: edge_type.as_ref().map(|t| t.name.clone()),
5980 },
5981 FindPattern::Rows { table } => UnifiedFindPattern::Rows {
5982 table: table.name.clone(),
5983 },
5984 FindPattern::Path { from, edge, to } => UnifiedFindPattern::Path {
5985 from: from.as_ref().map(|f| f.name.clone()),
5986 edge: edge.as_ref().map(|e| e.name.clone()),
5987 to: to.as_ref().map(|t| t.name.clone()),
5988 },
5989 }
5990 }
5991
5992 fn expr_to_condition(&self, expr: &Expr) -> Result<Condition> {
5995 match &expr.kind {
5996 ExprKind::Binary(left, op, right) => match op {
5997 BinaryOp::And => {
5998 let l = self.expr_to_condition(left)?;
5999 let r = self.expr_to_condition(right)?;
6000 Ok(l.and(r))
6001 },
6002 BinaryOp::Or => {
6003 let l = self.expr_to_condition(left)?;
6004 let r = self.expr_to_condition(right)?;
6005 Ok(l.or(r))
6006 },
6007 BinaryOp::Eq => {
6008 let col = self.expr_to_column_name(left)?;
6009 let val = self.expr_to_value(right)?;
6010 Ok(Condition::Eq(col, val))
6011 },
6012 BinaryOp::Ne => {
6013 let col = self.expr_to_column_name(left)?;
6014 let val = self.expr_to_value(right)?;
6015 Ok(Condition::Ne(col, val))
6016 },
6017 BinaryOp::Lt => {
6018 let col = self.expr_to_column_name(left)?;
6019 let val = self.expr_to_value(right)?;
6020 Ok(Condition::Lt(col, val))
6021 },
6022 BinaryOp::Le => {
6023 let col = self.expr_to_column_name(left)?;
6024 let val = self.expr_to_value(right)?;
6025 Ok(Condition::Le(col, val))
6026 },
6027 BinaryOp::Gt => {
6028 let col = self.expr_to_column_name(left)?;
6029 let val = self.expr_to_value(right)?;
6030 Ok(Condition::Gt(col, val))
6031 },
6032 BinaryOp::Ge => {
6033 let col = self.expr_to_column_name(left)?;
6034 let val = self.expr_to_value(right)?;
6035 Ok(Condition::Ge(col, val))
6036 },
6037 _ => Err(RouterError::ParseError(format!(
6038 "Unsupported operator in condition: {op:?}"
6039 ))),
6040 },
6041 _ => Err(RouterError::ParseError(
6042 "Expected binary expression in condition".to_string(),
6043 )),
6044 }
6045 }
6046
6047 pub fn expr_to_filter_condition(&self, expr: &Expr) -> Result<FilterCondition> {
6056 match &expr.kind {
6057 ExprKind::Binary(left, op, right) => match op {
6058 BinaryOp::And => {
6059 let l = self.expr_to_filter_condition(left)?;
6060 let r = self.expr_to_filter_condition(right)?;
6061 Ok(l.and(r))
6062 },
6063 BinaryOp::Or => {
6064 let l = self.expr_to_filter_condition(left)?;
6065 let r = self.expr_to_filter_condition(right)?;
6066 Ok(l.or(r))
6067 },
6068 BinaryOp::Eq => {
6069 let col = self.expr_to_column_name(left)?;
6070 let val = self.expr_to_filter_value(right)?;
6071 Ok(FilterCondition::Eq(col, val))
6072 },
6073 BinaryOp::Ne => {
6074 let col = self.expr_to_column_name(left)?;
6075 let val = self.expr_to_filter_value(right)?;
6076 Ok(FilterCondition::Ne(col, val))
6077 },
6078 BinaryOp::Lt => {
6079 let col = self.expr_to_column_name(left)?;
6080 let val = self.expr_to_filter_value(right)?;
6081 Ok(FilterCondition::Lt(col, val))
6082 },
6083 BinaryOp::Le => {
6084 let col = self.expr_to_column_name(left)?;
6085 let val = self.expr_to_filter_value(right)?;
6086 Ok(FilterCondition::Le(col, val))
6087 },
6088 BinaryOp::Gt => {
6089 let col = self.expr_to_column_name(left)?;
6090 let val = self.expr_to_filter_value(right)?;
6091 Ok(FilterCondition::Gt(col, val))
6092 },
6093 BinaryOp::Ge => {
6094 let col = self.expr_to_column_name(left)?;
6095 let val = self.expr_to_filter_value(right)?;
6096 Ok(FilterCondition::Ge(col, val))
6097 },
6098 _ => Err(RouterError::ParseError(format!(
6099 "Unsupported operator in filter condition: {op:?}"
6100 ))),
6101 },
6102 _ => Err(RouterError::ParseError(
6103 "Expected binary expression in filter condition".to_string(),
6104 )),
6105 }
6106 }
6107
6108 pub fn expr_to_filter_value(&self, expr: &Expr) -> Result<FilterValue> {
6117 match &expr.kind {
6118 ExprKind::Literal(lit) => match lit {
6119 Literal::Null => Ok(FilterValue::String("null".to_string())),
6120 Literal::Boolean(b) => Ok(FilterValue::Bool(*b)),
6121 Literal::Integer(i) => Ok(FilterValue::Int(*i)),
6122 Literal::Float(f) => Ok(FilterValue::Float(*f)),
6123 Literal::String(s) => Ok(FilterValue::String(s.clone())),
6124 },
6125 ExprKind::Ident(ident) => Ok(FilterValue::String(ident.name.clone())),
6126 _ => Err(RouterError::ParseError(format!(
6127 "Cannot convert expression to filter value: {:?}",
6128 expr.kind
6129 ))),
6130 }
6131 }
6132
6133 #[allow(clippy::unused_self)] fn expr_to_value(&self, expr: &Expr) -> Result<Value> {
6135 match &expr.kind {
6136 ExprKind::Literal(lit) => match lit {
6137 Literal::Null => Ok(Value::Null),
6138 Literal::Boolean(b) => Ok(Value::Bool(*b)),
6139 Literal::Integer(i) => Ok(Value::Int(*i)),
6140 Literal::Float(f) => Ok(Value::Float(*f)),
6141 Literal::String(s) => Ok(Value::String(s.clone())),
6142 },
6143 ExprKind::Ident(ident) => Ok(Value::String(ident.name.clone())),
6144 _ => Err(RouterError::ParseError(format!(
6145 "Cannot convert expression to value: {:?}",
6146 expr.kind
6147 ))),
6148 }
6149 }
6150
6151 pub fn expr_to_column_name(&self, expr: &Expr) -> Result<String> {
6160 match &expr.kind {
6161 ExprKind::Ident(ident) => Ok(ident.name.clone()),
6162 ExprKind::Qualified(_, name) => Ok(name.name.clone()),
6163 _ => Err(RouterError::ParseError("Expected column name".to_string())),
6164 }
6165 }
6166
6167 #[allow(clippy::unused_self)] #[allow(clippy::cast_sign_loss)] fn expr_to_u64(&self, expr: &Expr) -> Result<u64> {
6170 match &expr.kind {
6171 ExprKind::Literal(Literal::Integer(i)) if *i >= 0 => Ok(*i as u64),
6172 _ => Err(RouterError::InvalidArgument(
6173 "Expected positive integer".to_string(),
6174 )),
6175 }
6176 }
6177
6178 #[allow(clippy::unused_self)] #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_precision_loss)] fn expr_to_f32(&self, expr: &Expr) -> Result<f32> {
6182 match &expr.kind {
6183 ExprKind::Literal(Literal::Float(f)) => Ok(*f as f32),
6184 ExprKind::Literal(Literal::Integer(i)) => Ok(*i as f32),
6185 ExprKind::Unary(parser::UnaryOp::Neg, inner) => match &inner.kind {
6186 ExprKind::Literal(Literal::Float(f)) => Ok(-(*f as f32)),
6187 ExprKind::Literal(Literal::Integer(i)) => Ok(-(*i as f32)),
6188 _ => Err(RouterError::InvalidArgument("Expected number".to_string())),
6189 },
6190 _ => Err(RouterError::InvalidArgument("Expected number".to_string())),
6191 }
6192 }
6193
6194 #[allow(clippy::unused_self)] #[allow(clippy::cast_precision_loss)] fn expr_to_f64(&self, expr: &Expr) -> Result<f64> {
6197 match &expr.kind {
6198 ExprKind::Literal(Literal::Float(f)) => Ok(*f),
6199 ExprKind::Literal(Literal::Integer(i)) => Ok(*i as f64),
6200 ExprKind::Unary(parser::UnaryOp::Neg, inner) => match &inner.kind {
6201 ExprKind::Literal(Literal::Float(f)) => Ok(-*f),
6202 ExprKind::Literal(Literal::Integer(i)) => Ok(-(*i as f64)),
6203 _ => Err(RouterError::InvalidArgument("Expected number".to_string())),
6204 },
6205 _ => Err(RouterError::InvalidArgument("Expected number".to_string())),
6206 }
6207 }
6208
6209 #[allow(clippy::unused_self)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation)] fn expr_to_usize(&self, expr: &Expr) -> Result<usize> {
6213 match &expr.kind {
6214 ExprKind::Literal(Literal::Integer(i)) if *i >= 0 => Ok(*i as usize),
6215 _ => Err(RouterError::InvalidArgument(
6216 "Expected positive integer".to_string(),
6217 )),
6218 }
6219 }
6220
6221 #[allow(clippy::unused_self)] fn expr_to_string(&self, expr: &Expr) -> Result<String> {
6223 match &expr.kind {
6224 ExprKind::Literal(Literal::String(s)) => Ok(s.clone()),
6225 ExprKind::Ident(ident) => Ok(ident.name.clone()),
6226 _ => Err(RouterError::InvalidArgument("Expected string".to_string())),
6227 }
6228 }
6229
6230 fn exec_spatial(&self, spatial_stmt: &SpatialStmt) -> Result<QueryResult> {
6232 match &spatial_stmt.op {
6233 SpatialOp::Insert {
6234 key,
6235 x,
6236 y,
6237 width,
6238 height,
6239 } => {
6240 let key_str = self.expr_to_string(key)?;
6241 let x_val = self.expr_to_f32(x)?;
6242 let y_val = self.expr_to_f32(y)?;
6243 let w_val = self.expr_to_f32(width)?;
6244 let h_val = self.expr_to_f32(height)?;
6245 let bounds = tensor_spatial::BoundingBox::new(x_val, y_val, w_val, h_val)
6246 .map_err(|e| RouterError::InvalidArgument(e.to_string()))?;
6247 let entry = tensor_spatial::SpatialEntry {
6248 bounds,
6249 data: key_str,
6250 };
6251 self.spatial.write().insert(entry);
6252 Ok(QueryResult::Empty)
6253 },
6254 SpatialOp::WithinRadius {
6255 x,
6256 y,
6257 radius,
6258 limit,
6259 } => {
6260 let cx = self.expr_to_f32(x)?;
6261 let cy = self.expr_to_f32(y)?;
6262 let r = self.expr_to_f32(radius)?;
6263 let max_results = limit.as_ref().map(|e| self.expr_to_usize(e)).transpose()?;
6264
6265 if !r.is_finite() || r < 0.0 {
6266 return Err(RouterError::InvalidArgument(
6267 "invalid radius: must be non-negative and finite".to_string(),
6268 ));
6269 }
6270
6271 let mut results: Vec<SpatialResult> = self
6272 .spatial
6273 .read()
6274 .query_within_radius_with_distances(cx, cy, r)
6275 .into_iter()
6276 .map(|(e, dist)| SpatialResult {
6277 key: e.data.clone(),
6278 distance: dist,
6279 x: e.bounds.x(),
6280 y: e.bounds.y(),
6281 width: e.bounds.width(),
6282 height: e.bounds.height(),
6283 })
6284 .collect();
6285
6286 if let Some(max) = max_results {
6287 results.truncate(max);
6288 }
6289
6290 Ok(QueryResult::Spatial(results))
6291 },
6292 SpatialOp::Delete {
6293 key,
6294 x,
6295 y,
6296 width,
6297 height,
6298 } => {
6299 let key_str = self.expr_to_string(key)?;
6300 let x_val = self.expr_to_f32(x)?;
6301 let y_val = self.expr_to_f32(y)?;
6302 let w_val = self.expr_to_f32(width)?;
6303 let h_val = self.expr_to_f32(height)?;
6304 let bounds = tensor_spatial::BoundingBox::new(x_val, y_val, w_val, h_val)
6305 .map_err(|e| RouterError::InvalidArgument(e.to_string()))?;
6306 self.spatial
6307 .write()
6308 .remove(bounds, |e| e.data == key_str && e.bounds == bounds)
6309 .map_err(|e| RouterError::NotFound(e.to_string()))?;
6310 Ok(QueryResult::Empty)
6311 },
6312 SpatialOp::Nearest { x, y, limit } => self.exec_spatial_nearest(x, y, limit.as_ref()),
6313 SpatialOp::Count => {
6314 let count = self.spatial.read().len();
6315 Ok(QueryResult::Count(count))
6316 },
6317 }
6318 }
6319
6320 fn exec_spatial_nearest(
6322 &self,
6323 x: &Expr,
6324 y: &Expr,
6325 limit: Option<&Expr>,
6326 ) -> Result<QueryResult> {
6327 let cx = self.expr_to_f32(x)?;
6328 let cy = self.expr_to_f32(y)?;
6329 let k = limit
6330 .map(|e| self.expr_to_usize(e))
6331 .transpose()?
6332 .unwrap_or(1);
6333 let results: Vec<SpatialResult> = self
6334 .spatial
6335 .read()
6336 .query_nearest_by_centroid(cx, cy, k)
6337 .into_iter()
6338 .map(|e| SpatialResult {
6339 key: e.data.clone(),
6340 distance: e.bounds.center_dist_sq(cx, cy).sqrt(),
6341 x: e.bounds.x(),
6342 y: e.bounds.y(),
6343 width: e.bounds.width(),
6344 height: e.bounds.height(),
6345 })
6346 .collect();
6347 Ok(QueryResult::Spatial(results))
6348 }
6349
6350 fn extract_join_columns(&self, condition: &JoinCondition) -> Result<(String, String)> {
6353 match condition {
6354 JoinCondition::On(expr) => match &expr.kind {
6355 ExprKind::Binary(left, BinaryOp::Eq, right) => {
6356 let left_col = self.extract_column_from_expr(left)?;
6357 let right_col = self.extract_column_from_expr(right)?;
6358 Ok((left_col, right_col))
6359 },
6360 _ => Err(RouterError::ParseError(
6361 "JOIN ON condition must be an equality comparison (a.col = b.col)".to_string(),
6362 )),
6363 },
6364 JoinCondition::Using(cols) => {
6365 if cols.len() == 1 {
6366 let col = cols[0].name.clone();
6367 Ok((col.clone(), col))
6368 } else {
6369 Err(RouterError::ParseError(
6370 "JOIN USING with multiple columns not yet supported".to_string(),
6371 ))
6372 }
6373 },
6374 }
6375 }
6376
6377 #[allow(clippy::unused_self)] fn extract_column_from_expr(&self, expr: &Expr) -> Result<String> {
6379 match &expr.kind {
6380 ExprKind::Ident(ident) => Ok(ident.name.clone()),
6381 ExprKind::Qualified(_, col) => Ok(col.name.clone()),
6382 _ => Err(RouterError::ParseError(
6383 "Expected column reference in JOIN condition".to_string(),
6384 )),
6385 }
6386 }
6387
6388 #[allow(clippy::unused_self)] #[allow(clippy::cast_possible_wrap)] fn merge_rows(
6391 &self,
6392 row_a: Option<&Row>,
6393 row_b: Option<&Row>,
6394 table_a: &str,
6395 table_b: &str,
6396 ) -> Row {
6397 let mut values = Vec::new();
6398
6399 if let Some(r) = row_a {
6401 values.push((format!("{table_a}._id"), Value::Int(r.id as i64)));
6402 for (col, val) in &r.values {
6403 values.push((format!("{table_a}.{col}"), val.clone()));
6404 }
6405 }
6406
6407 if let Some(r) = row_b {
6409 values.push((format!("{table_b}._id"), Value::Int(r.id as i64)));
6410 for (col, val) in &r.values {
6411 values.push((format!("{table_b}.{col}"), val.clone()));
6412 }
6413 }
6414
6415 Row {
6416 id: row_a
6417 .map(|r| r.id)
6418 .or_else(|| row_b.map(|r| r.id))
6419 .unwrap_or(0),
6420 values,
6421 }
6422 }
6423
6424 #[allow(clippy::unused_self)] fn properties_to_map(&self, properties: &[Property]) -> Result<HashMap<String, PropertyValue>> {
6426 let mut map = HashMap::new();
6427 for prop in properties {
6428 let value = match &prop.value.kind {
6429 ExprKind::Literal(Literal::Null) => PropertyValue::Null,
6430 ExprKind::Literal(Literal::Boolean(b)) => PropertyValue::Bool(*b),
6431 ExprKind::Literal(Literal::Integer(i)) => PropertyValue::Int(*i),
6432 ExprKind::Literal(Literal::Float(f)) => PropertyValue::Float(*f),
6433 ExprKind::Literal(Literal::String(s)) => PropertyValue::String(s.clone()),
6434 ExprKind::Ident(ident) => PropertyValue::String(ident.name.clone()),
6435 _ => {
6436 return Err(RouterError::InvalidArgument(format!(
6437 "Invalid property value: {:?}",
6438 prop.value.kind
6439 )))
6440 },
6441 };
6442 map.insert(prop.key.name.clone(), value);
6443 }
6444 Ok(map)
6445 }
6446
6447 #[allow(clippy::unused_self)] fn data_type_to_column_type(
6449 &self,
6450 dt: &parser::DataType,
6451 ) -> Result<relational_engine::ColumnType> {
6452 use parser::DataType;
6453 match dt {
6454 DataType::Int | DataType::Integer | DataType::Bigint | DataType::Smallint => {
6455 Ok(relational_engine::ColumnType::Int)
6456 },
6457 DataType::Float
6458 | DataType::Double
6459 | DataType::Real
6460 | DataType::Decimal(_, _)
6461 | DataType::Numeric(_, _) => Ok(relational_engine::ColumnType::Float),
6462 DataType::Varchar(_)
6463 | DataType::Char(_)
6464 | DataType::Text
6465 | DataType::Date
6466 | DataType::Time
6467 | DataType::Timestamp
6468 | DataType::Blob => Ok(relational_engine::ColumnType::String),
6469 DataType::Boolean => Ok(relational_engine::ColumnType::Bool),
6470 DataType::Custom(name) => match name.to_uppercase().as_str() {
6471 "STRING" => Ok(relational_engine::ColumnType::String),
6472 "BOOL" => Ok(relational_engine::ColumnType::Bool),
6473 _ => Err(RouterError::ParseError(format!(
6474 "Unsupported data type: {name}"
6475 ))),
6476 },
6477 }
6478 }
6479
6480 fn property_to_string(prop: &PropertyValue) -> String {
6481 match prop {
6482 PropertyValue::Null => "null".to_string(),
6483 PropertyValue::Int(i) => i.to_string(),
6484 PropertyValue::Float(f) => f.to_string(),
6485 PropertyValue::String(s) => s.clone(),
6486 PropertyValue::Bool(b) => b.to_string(),
6487 PropertyValue::DateTime(ts) => ts.to_string(),
6488 PropertyValue::List(items) => format!(
6489 "[{}]",
6490 items
6491 .iter()
6492 .map(Self::property_to_string)
6493 .collect::<Vec<_>>()
6494 .join(", ")
6495 ),
6496 PropertyValue::Map(map) => format!(
6497 "{{{}}}",
6498 map.iter()
6499 .map(|(k, v)| format!("{}: {}", k, Self::property_to_string(v)))
6500 .collect::<Vec<_>>()
6501 .join(", ")
6502 ),
6503 PropertyValue::Bytes(bytes) => format!("<{} bytes>", bytes.len()),
6504 PropertyValue::Point { lat, lon } => format!("POINT({lat}, {lon})"),
6505 }
6506 }
6507
6508 pub async fn execute_parsed_async(&self, command: &str) -> Result<QueryResult> {
6524 let stmt = parser::parse(command)
6525 .map_err(|e| RouterError::ParseError(e.format_with_source(command)))?;
6526
6527 if Self::is_cacheable_statement(&stmt) {
6529 if let Some(cached) = self.try_cache_get(command) {
6530 return Ok(cached);
6531 }
6532 }
6533
6534 let result = self.execute_statement_async(&stmt).await?;
6536
6537 if Self::is_cacheable_statement(&stmt) {
6539 self.try_cache_put(command, &result);
6540 }
6541
6542 if Self::is_write_statement(&stmt) {
6544 self.invalidate_cache_on_write();
6545 }
6546
6547 Ok(result)
6548 }
6549
6550 pub async fn execute_statement_async(&self, stmt: &Statement) -> Result<QueryResult> {
6559 match &stmt.kind {
6560 StatementKind::Blob(blob) => self.exec_blob_async(blob).await,
6562 StatementKind::Blobs(blobs) => self.exec_blobs_async(blobs).await,
6563
6564 StatementKind::Checkpoint(cp) => self.exec_checkpoint(cp),
6566 StatementKind::Rollback(rb) => self.exec_rollback(rb),
6567 StatementKind::Checkpoints(cps) => self.exec_checkpoints(cps),
6568
6569 _ => self.execute_statement(stmt),
6572 }
6573 }
6574
6575 #[allow(clippy::too_many_lines)] #[allow(clippy::significant_drop_tightening)] async fn exec_blob_async(&self, stmt: &BlobStmt) -> Result<QueryResult> {
6579 if matches!(stmt.operation, BlobOp::Init) {
6581 if self.blob.is_some() {
6582 return Ok(QueryResult::Value(
6583 "Blob store already initialized".to_string(),
6584 ));
6585 }
6586 return Err(RouterError::BlobError(
6587 "Use router.init_blob() to initialize blob storage".to_string(),
6588 ));
6589 }
6590
6591 let blob = self
6592 .blob
6593 .as_ref()
6594 .ok_or_else(|| RouterError::BlobError("Blob store not initialized".to_string()))?;
6595
6596 match &stmt.operation {
6597 BlobOp::Init => unreachable!(), BlobOp::Put {
6599 filename,
6600 data,
6601 from_path,
6602 options,
6603 } => {
6604 let filename_str = self.eval_string_expr(filename)?;
6605 let put_options = self.blob_options_to_put_options(options)?;
6606
6607 let blob_data = if let Some(data_expr) = data {
6609 self.expr_to_bytes(data_expr)?
6610 } else if let Some(path_expr) = from_path {
6611 let path = self.eval_string_expr(path_expr)?;
6612 tokio::fs::read(&path)
6613 .await
6614 .map_err(|e| RouterError::BlobError(format!("Failed to read file: {e}")))?
6615 } else {
6616 return Err(RouterError::MissingArgument(
6617 "PUT requires either DATA or FROM path".to_string(),
6618 ));
6619 };
6620
6621 let blob_guard = blob.lock().await;
6622 let artifact_id = blob_guard
6623 .put(&filename_str, &blob_data, put_options)
6624 .await?;
6625 Ok(QueryResult::Value(artifact_id))
6626 },
6627 BlobOp::Get {
6628 artifact_id,
6629 to_path,
6630 } => {
6631 let id = self.eval_string_expr(artifact_id)?;
6632 let blob_guard = blob.lock().await;
6633 let data = blob_guard.get(&id).await?;
6634
6635 if let Some(path_expr) = to_path {
6636 let path = self.eval_string_expr(path_expr)?;
6637 tokio::fs::write(&path, &data).await.map_err(|e| {
6638 RouterError::BlobError(format!("Failed to write file: {e}"))
6639 })?;
6640 Ok(QueryResult::Value(format!(
6641 "Written {} bytes to {path}",
6642 data.len()
6643 )))
6644 } else {
6645 Ok(QueryResult::Blob(data))
6646 }
6647 },
6648 BlobOp::Delete { artifact_id } => {
6649 let id = self.eval_string_expr(artifact_id)?;
6650 let blob_guard = blob.lock().await;
6651 blob_guard.delete(&id).await?;
6652 Ok(QueryResult::Empty)
6653 },
6654 BlobOp::Info { artifact_id } => {
6655 let id = self.eval_string_expr(artifact_id)?;
6656 let blob_guard = blob.lock().await;
6657 let meta = blob_guard.metadata(&id).await?;
6658
6659 Ok(QueryResult::ArtifactInfo(ArtifactInfoResult {
6660 id: meta.id,
6661 filename: meta.filename,
6662 content_type: meta.content_type,
6663 size: meta.size,
6664 checksum: meta.checksum,
6665 chunk_count: meta.chunk_count,
6666 created: meta.created,
6667 modified: meta.modified,
6668 created_by: meta.created_by,
6669 tags: meta.tags,
6670 linked_to: meta.linked_to,
6671 custom: meta.custom,
6672 }))
6673 },
6674 BlobOp::Link {
6675 artifact_id,
6676 entity,
6677 } => {
6678 let id = self.eval_string_expr(artifact_id)?;
6679 let entity_str = self.eval_string_expr(entity)?;
6680 let blob_guard = blob.lock().await;
6681 blob_guard.link(&id, &entity_str).await?;
6682 Ok(QueryResult::Empty)
6683 },
6684 BlobOp::Unlink {
6685 artifact_id,
6686 entity,
6687 } => {
6688 let id = self.eval_string_expr(artifact_id)?;
6689 let entity_str = self.eval_string_expr(entity)?;
6690 let blob_guard = blob.lock().await;
6691 blob_guard.unlink(&id, &entity_str).await?;
6692 Ok(QueryResult::Empty)
6693 },
6694 BlobOp::Links { artifact_id } => {
6695 let id = self.eval_string_expr(artifact_id)?;
6696 let blob_guard = blob.lock().await;
6697 let links = blob_guard.links(&id).await?;
6698 Ok(QueryResult::ArtifactList(links))
6699 },
6700 BlobOp::Tag { artifact_id, tag } => {
6701 let id = self.eval_string_expr(artifact_id)?;
6702 let tag_str = self.eval_string_expr(tag)?;
6703 let blob_guard = blob.lock().await;
6704 blob_guard.tag(&id, &tag_str).await?;
6705 Ok(QueryResult::Empty)
6706 },
6707 BlobOp::Untag { artifact_id, tag } => {
6708 let id = self.eval_string_expr(artifact_id)?;
6709 let tag_str = self.eval_string_expr(tag)?;
6710 let blob_guard = blob.lock().await;
6711 blob_guard.untag(&id, &tag_str).await?;
6712 Ok(QueryResult::Empty)
6713 },
6714 BlobOp::Verify { artifact_id } => {
6715 let id = self.eval_string_expr(artifact_id)?;
6716 let blob_guard = blob.lock().await;
6717 let valid = blob_guard.verify(&id)?;
6718 Ok(QueryResult::Value(if valid {
6719 "OK".to_string()
6720 } else {
6721 "INVALID".to_string()
6722 }))
6723 },
6724 BlobOp::Gc { full } => {
6725 let blob_guard = blob.lock().await;
6726 let stats = if *full {
6727 blob_guard.full_gc().await?
6728 } else {
6729 blob_guard.gc().await?
6730 };
6731 Ok(QueryResult::Value(format!(
6732 "Deleted {} chunks, freed {} bytes",
6733 stats.deleted, stats.freed_bytes
6734 )))
6735 },
6736 BlobOp::Repair => {
6737 let blob_guard = blob.lock().await;
6738 let stats = blob_guard.repair()?;
6739 Ok(QueryResult::Value(format!(
6740 "Fixed {} refs, deleted {} orphans",
6741 stats.refs_fixed, stats.orphans_deleted
6742 )))
6743 },
6744 BlobOp::Stats => {
6745 let blob_guard = blob.lock().await;
6746 let stats = blob_guard.stats().await?;
6747 Ok(QueryResult::BlobStats(BlobStatsResult {
6748 artifact_count: stats.artifact_count,
6749 chunk_count: stats.chunk_count,
6750 total_bytes: stats.total_bytes,
6751 unique_bytes: stats.unique_bytes,
6752 dedup_ratio: stats.dedup_ratio,
6753 orphaned_chunks: stats.orphaned_chunks,
6754 }))
6755 },
6756 BlobOp::MetaSet {
6757 artifact_id,
6758 key,
6759 value,
6760 } => {
6761 let id = self.eval_string_expr(artifact_id)?;
6762 let key_str = self.eval_string_expr(key)?;
6763 let value_str = self.eval_string_expr(value)?;
6764 let blob_guard = blob.lock().await;
6765 blob_guard.set_meta(&id, &key_str, &value_str).await?;
6766 Ok(QueryResult::Empty)
6767 },
6768 BlobOp::MetaGet { artifact_id, key } => {
6769 let id = self.eval_string_expr(artifact_id)?;
6770 let key_str = self.eval_string_expr(key)?;
6771 let blob_guard = blob.lock().await;
6772 let value = blob_guard.get_meta(&id, &key_str).await?;
6773 Ok(QueryResult::Value(
6774 value.unwrap_or_else(|| "(not found)".to_string()),
6775 ))
6776 },
6777 }
6778 }
6779
6780 #[allow(clippy::significant_drop_tightening)] async fn exec_blobs_async(&self, stmt: &BlobsStmt) -> Result<QueryResult> {
6783 let blob = self
6784 .blob
6785 .as_ref()
6786 .ok_or_else(|| RouterError::BlobError("Blob store not initialized".to_string()))?;
6787
6788 let blob_guard = blob.lock().await;
6789 match &stmt.operation {
6790 BlobsOp::List { pattern } => {
6791 let prefix = pattern
6792 .as_ref()
6793 .map(|p| self.eval_string_expr(p))
6794 .transpose()?;
6795 let ids = blob_guard.list(prefix.as_deref()).await?;
6796 Ok(QueryResult::ArtifactList(ids))
6797 },
6798 BlobsOp::For { entity } => {
6799 let entity_str = self.eval_string_expr(entity)?;
6800 let ids = blob_guard.artifacts_for(&entity_str).await?;
6801 Ok(QueryResult::ArtifactList(ids))
6802 },
6803 BlobsOp::ByTag { tag } => {
6804 let tag_str = self.eval_string_expr(tag)?;
6805 let ids = blob_guard.by_tag(&tag_str).await?;
6806 Ok(QueryResult::ArtifactList(ids))
6807 },
6808 BlobsOp::ByType { content_type } => {
6809 let ct = self.eval_string_expr(content_type)?;
6810 let ids = blob_guard.by_content_type(&ct).await?;
6811 Ok(QueryResult::ArtifactList(ids))
6812 },
6813 BlobsOp::Similar { artifact_id, limit } => {
6814 let id = self.eval_string_expr(artifact_id)?;
6815 let k = limit
6816 .as_ref()
6817 .map(|e| self.expr_to_usize(e))
6818 .transpose()?
6819 .unwrap_or(10);
6820 let similar = blob_guard.similar(&id, k).await?;
6821 Ok(QueryResult::Similar(
6822 similar
6823 .into_iter()
6824 .map(|s| SimilarResult {
6825 key: s.id,
6826 score: s.similarity,
6827 })
6828 .collect(),
6829 ))
6830 },
6831 }
6832 }
6833
6834 pub async fn embed_batch_parallel(&self, items: Vec<(String, Vec<f32>)>) -> Result<usize> {
6845 let unified = self.require_unified()?;
6846
6847 let count = unified
6848 .embed_batch(items)
6849 .await
6850 .map(|result| result.count)
6851 .map_err(|e| RouterError::VectorError(e.to_string()))?;
6852
6853 if count > 0 {
6854 self.bump_vector_generation();
6855 }
6856 Ok(count)
6857 }
6858
6859 pub async fn find_similar_connected_async(
6867 &self,
6868 query_key: &str,
6869 connected_to: &str,
6870 top_k: usize,
6871 ) -> Result<Vec<UnifiedItem>> {
6872 let unified = self.require_unified()?;
6873
6874 let hnsw_results = if let Some((ref index, ref keys)) =
6876 self.hnsw_index.as_ref().filter(|_| self.hnsw_is_fresh())
6877 {
6878 let query_embedding = self
6879 .vector
6880 .get_entity_embedding(query_key)
6881 .map_err(|e| RouterError::VectorError(e.to_string()))?;
6882 Some(
6883 self.vector
6884 .search_with_hnsw(index, keys, &query_embedding, top_k.saturating_mul(8))
6885 .map_err(|e| RouterError::VectorError(e.to_string()))?,
6886 )
6887 } else {
6888 None
6889 };
6890
6891 unified
6892 .find_similar_connected_with_hnsw(query_key, connected_to, top_k, hnsw_results)
6893 .await
6894 .map_err(Into::into)
6895 }
6896
6897 pub async fn find_neighbors_by_similarity_async(
6905 &self,
6906 entity_key: &str,
6907 query: &[f32],
6908 top_k: usize,
6909 ) -> Result<Vec<UnifiedItem>> {
6910 let unified = self.require_unified()?;
6911
6912 unified
6913 .find_neighbors_by_similarity(entity_key, query, top_k)
6914 .await
6915 .map_err(Into::into)
6916 }
6917
6918 pub fn runtime(&self) -> Option<&Runtime> {
6922 self.blob_runtime.as_deref()
6923 }
6924
6925 pub fn block_on<F: std::future::Future>(&self, future: F) -> Result<F::Output> {
6934 let runtime = self.blob_runtime.as_ref().ok_or_else(|| {
6935 RouterError::BlobError("Runtime not initialized. Call init_blob() first.".to_string())
6936 })?;
6937 Ok(runtime.block_on(future))
6938 }
6939}
6940
6941impl Default for QueryRouter {
6942 fn default() -> Self {
6943 Self::new()
6944 }
6945}
6946
6947impl QueryRouter {
6948 pub fn execute_for_cluster(&self, query: &str) -> std::result::Result<Vec<u8>, String> {
6957 let result = self.execute_parsed(query).map_err(|e| e.to_string())?;
6958 bitcode::serialize(&result).map_err(|e| format!("Serialization error: {e}"))
6959 }
6960}
6961
6962impl QueryExecutor for QueryRouter {
6967 fn execute(&self, query: &str) -> std::result::Result<Vec<u8>, String> {
6968 self.execute_for_cluster(query)
6969 }
6970}
6971
6972#[cfg(test)]
6973mod tests {
6974 use super::*;
6975 use tensor_checkpoint::OperationPreview;
6976
6977 fn add_test_edge(graph: &GraphEngine, from_key: &str, to_key: &str, edge_type: &str) {
6979 let get_or_create = |key: &str| -> u64 {
6980 if let Ok(nodes) =
6981 graph.find_nodes_by_property("entity_key", &PropertyValue::String(key.to_string()))
6982 {
6983 if let Some(node) = nodes.first() {
6984 return node.id;
6985 }
6986 }
6987 let mut props = HashMap::new();
6988 props.insert(
6989 "entity_key".to_string(),
6990 PropertyValue::String(key.to_string()),
6991 );
6992 graph.create_node("TestEntity", props).unwrap_or(0)
6993 };
6994
6995 let from_node = get_or_create(from_key);
6996 let to_node = get_or_create(to_key);
6997 graph
6998 .create_edge(from_node, to_node, edge_type, HashMap::new(), true)
6999 .ok();
7000 }
7001
7002 fn unwrap_qr_artifactinfo(result: QueryResult) -> ArtifactInfoResult {
7005 match result {
7006 QueryResult::ArtifactInfo(v) => v,
7007 _ => panic!("expected ArtifactInfo"),
7008 }
7009 }
7010
7011 fn unwrap_qr_artifactlist(result: QueryResult) -> Vec<String> {
7012 match result {
7013 QueryResult::ArtifactList(v) => v,
7014 _ => panic!("expected ArtifactList"),
7015 }
7016 }
7017
7018 fn unwrap_qr_blob(result: QueryResult) -> Vec<u8> {
7019 match result {
7020 QueryResult::Blob(v) => v,
7021 _ => panic!("expected Blob"),
7022 }
7023 }
7024
7025 fn unwrap_qr_blobstats(result: QueryResult) -> BlobStatsResult {
7026 match result {
7027 QueryResult::BlobStats(v) => v,
7028 _ => panic!("expected BlobStats"),
7029 }
7030 }
7031
7032 fn unwrap_qr_checkpointlist(result: QueryResult) -> Vec<CheckpointInfo> {
7033 match result {
7034 QueryResult::CheckpointList(v) => v,
7035 _ => panic!("expected CheckpointList"),
7036 }
7037 }
7038
7039 fn unwrap_qr_constraints(result: QueryResult) -> Vec<ConstraintInfo> {
7040 match result {
7041 QueryResult::Constraints(v) => v,
7042 _ => panic!("expected Constraints"),
7043 }
7044 }
7045
7046 fn unwrap_qr_edges(result: QueryResult) -> Vec<EdgeResult> {
7047 match result {
7048 QueryResult::Edges(v) => v,
7049 _ => panic!("expected Edges"),
7050 }
7051 }
7052
7053 fn unwrap_qr_nodes(result: QueryResult) -> Vec<NodeResult> {
7054 match result {
7055 QueryResult::Nodes(v) => v,
7056 _ => panic!("expected Nodes"),
7057 }
7058 }
7059
7060 fn unwrap_qr_rows(result: QueryResult) -> Vec<Row> {
7061 match result {
7062 QueryResult::Rows(v) => v,
7063 _ => panic!("expected Rows"),
7064 }
7065 }
7066
7067 fn unwrap_qr_similar(result: QueryResult) -> Vec<SimilarResult> {
7068 match result {
7069 QueryResult::Similar(v) => v,
7070 _ => panic!("expected Similar"),
7071 }
7072 }
7073
7074 fn unwrap_qr_unified(result: QueryResult) -> UnifiedResult {
7075 match result {
7076 QueryResult::Unified(v) => v,
7077 _ => panic!("expected Unified"),
7078 }
7079 }
7080
7081 fn unwrap_qr_value(result: QueryResult) -> String {
7082 match result {
7083 QueryResult::Value(v) => v,
7084 _ => panic!("expected Value"),
7085 }
7086 }
7087
7088 fn get_neighbors_out(graph: &GraphEngine, entity_key: &str) -> Vec<String> {
7090 let node_id = graph
7091 .find_nodes_by_property("entity_key", &PropertyValue::String(entity_key.to_string()))
7092 .ok()
7093 .and_then(|nodes| nodes.first().map(|n| n.id));
7094
7095 let Some(id) = node_id else {
7096 return Vec::new();
7097 };
7098
7099 let mut neighbors = Vec::new();
7100 if let Ok(edges) = graph.edges_of(id, Direction::Outgoing) {
7101 for edge in edges {
7102 let target_id = if edge.from == id { edge.to } else { edge.from };
7103 if let Ok(target_node) = graph.get_node(target_id) {
7104 if let Some(PropertyValue::String(key)) =
7105 target_node.properties.get("entity_key")
7106 {
7107 neighbors.push(key.clone());
7108 }
7109 }
7110 }
7111 }
7112 neighbors
7113 }
7114
7115 fn entity_has_edges(graph: &GraphEngine, entity_key: &str) -> bool {
7117 let node_id = graph
7118 .find_nodes_by_property("entity_key", &PropertyValue::String(entity_key.to_string()))
7119 .ok()
7120 .and_then(|nodes| nodes.first().map(|n| n.id));
7121
7122 let Some(id) = node_id else {
7123 return false;
7124 };
7125
7126 graph
7127 .edges_of(id, Direction::Both)
7128 .is_ok_and(|edges| !edges.is_empty())
7129 }
7130
7131 #[test]
7134 fn routes_select_to_relational() {
7135 let router = QueryRouter::new();
7136
7137 router
7139 .execute("CREATE TABLE users (name string, age int)")
7140 .unwrap();
7141 router
7142 .execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
7143 .unwrap();
7144
7145 let result = router.execute("SELECT * FROM users").unwrap();
7146 match result {
7147 QueryResult::Rows(rows) => {
7148 assert_eq!(rows.len(), 1);
7149 },
7150 _ => panic!("Expected Rows result"),
7151 }
7152 }
7153
7154 #[test]
7155 fn routes_node_to_graph() {
7156 let router = QueryRouter::new();
7157
7158 let result = router
7159 .execute("NODE CREATE person { name: 'Bob' }")
7160 .unwrap();
7161 match result {
7162 QueryResult::Ids(ids) => {
7163 assert_eq!(ids.len(), 1);
7164 },
7165 _ => panic!("Expected Ids result"),
7166 }
7167 }
7168
7169 #[test]
7170 fn routes_embed_to_vector() {
7171 let router = QueryRouter::new();
7172
7173 let result = router.execute("EMBED doc1 [1.0, 0.0, 0.0]").unwrap();
7174 match result {
7175 QueryResult::Empty => {},
7176 _ => panic!("Expected Empty result"),
7177 }
7178
7179 assert!(router.vector().exists("doc1"));
7180 }
7181
7182 #[test]
7183 fn routes_similar_to_vector() {
7184 let router = QueryRouter::new();
7185
7186 router.execute("EMBED doc1 [1.0, 0.0, 0.0]").unwrap();
7187 router.execute("EMBED doc2 [0.0, 1.0, 0.0]").unwrap();
7188 router.execute("EMBED doc3 [0.9, 0.1, 0.0]").unwrap();
7189
7190 let result = router.execute("SIMILAR doc1 TOP 2").unwrap();
7191 match result {
7192 QueryResult::Similar(results) => {
7193 assert_eq!(results.len(), 2);
7194 assert_eq!(results[0].key, "doc1"); },
7196 _ => panic!("Expected Similar result"),
7197 }
7198 }
7199
7200 #[test]
7203 fn handles_unified_query_find_nodes() {
7204 let router = QueryRouter::new();
7205
7206 router
7208 .execute("NODE CREATE post { title: 'Post 1' }")
7209 .unwrap();
7210 router
7211 .execute("NODE CREATE post { title: 'Post 2' }")
7212 .unwrap();
7213 router
7214 .execute("NODE CREATE post { title: 'Post 3' }")
7215 .unwrap();
7216
7217 let result = router.execute("FIND NODES post").unwrap();
7218 match result {
7219 QueryResult::Unified(unified) => {
7220 assert!(unified.description.contains("node"));
7221 assert_eq!(unified.items.len(), 3);
7223 },
7224 _ => panic!("Expected Unified result"),
7225 }
7226 }
7227
7228 #[test]
7229 fn handles_unified_query_connected() {
7230 let router = QueryRouter::new();
7231
7232 let user_id = match router
7234 .execute("NODE CREATE user { name: 'Alice' }")
7235 .unwrap()
7236 {
7237 QueryResult::Ids(ids) => ids[0],
7238 _ => panic!("Expected Ids"),
7239 };
7240
7241 let post_id = match router
7242 .execute("NODE CREATE post { title: 'Hello' }")
7243 .unwrap()
7244 {
7245 QueryResult::Ids(ids) => ids[0],
7246 _ => panic!("Expected Ids"),
7247 };
7248
7249 router
7250 .execute(&format!(
7251 "EDGE CREATE {} -> {} : authored",
7252 user_id, post_id
7253 ))
7254 .unwrap();
7255
7256 router
7258 .execute("EMBED STORE 'post' [1.0, 0.0, 0.0]")
7259 .unwrap();
7260
7261 let result = router.execute("FIND NODES post").unwrap();
7264 match result {
7265 QueryResult::Unified(_) => {},
7266 _ => panic!("Expected Unified result"),
7267 }
7268 }
7269
7270 #[test]
7273 fn returns_error_for_malformed_command() {
7274 let router = QueryRouter::new();
7275
7276 let result = router.execute("");
7277 assert!(matches!(result, Err(RouterError::ParseError(_))));
7278
7279 let result = router.execute(" ");
7280 assert!(matches!(result, Err(RouterError::ParseError(_))));
7281 }
7282
7283 #[test]
7284 fn returns_error_for_unknown_command() {
7285 let router = QueryRouter::new();
7286
7287 let result = router.execute("UNKNOWN something");
7288 assert!(matches!(result, Err(RouterError::UnknownCommand(_))));
7289 }
7290
7291 #[test]
7292 fn returns_error_for_missing_arguments() {
7293 let router = QueryRouter::new();
7294
7295 let result = router.execute("SELECT");
7296 assert!(matches!(result, Err(RouterError::ParseError(_))));
7297
7298 let result = router.execute("NODE");
7299 assert!(matches!(result, Err(RouterError::ParseError(_))));
7300
7301 let result = router.execute("EMBED");
7302 assert!(matches!(result, Err(RouterError::ParseError(_))));
7303 }
7304
7305 #[test]
7306 fn does_not_crash_on_unexpected_input() {
7307 let router = QueryRouter::new();
7308
7309 let inputs = [
7311 "SELECT * FROM FROM WHERE",
7312 "INSERT INTO VALUES",
7313 "NODE CREATE",
7314 "EDGE 123 -> 456",
7315 "SIMILAR [not, valid, floats]",
7316 "FIND something WITH random KEYWORDS",
7317 ";;;",
7318 "SELECT * FROM users; DROP TABLE users;--",
7319 "SELECT * FROM users WHERE name = 'O'Brien'",
7320 "\n\t\r",
7321 ];
7322
7323 for input in inputs {
7324 let _ = router.execute(input);
7326 }
7327 }
7328
7329 #[test]
7330 fn handles_table_not_found() {
7331 let router = QueryRouter::new();
7332
7333 let result = router.execute("SELECT * FROM nonexistent");
7334 assert!(matches!(result, Err(RouterError::RelationalError(_))));
7335 }
7336
7337 #[test]
7338 fn handles_node_not_found() {
7339 let router = QueryRouter::new();
7340
7341 let result = router.execute("NODE GET 99999");
7342 assert!(matches!(result, Err(RouterError::GraphError(_))));
7343 }
7344
7345 #[test]
7346 fn handles_embedding_not_found() {
7347 let router = QueryRouter::new();
7348
7349 let result = router.execute("SIMILAR nonexistent TOP 5");
7350 assert!(matches!(result, Err(RouterError::VectorError(_))));
7351 }
7352
7353 #[test]
7356 fn create_table_and_insert() {
7357 let router = QueryRouter::new();
7358
7359 router
7360 .execute("CREATE TABLE products (name string, price float)")
7361 .unwrap();
7362 router
7363 .execute("INSERT INTO products (name, price) VALUES ('Widget', 9.99)")
7364 .unwrap();
7365
7366 let result = router.execute("SELECT * FROM products").unwrap();
7367 match result {
7368 QueryResult::Rows(rows) => {
7369 assert_eq!(rows.len(), 1);
7370 assert_eq!(rows[0].get("name"), Some(&Value::String("Widget".into())));
7371 },
7372 _ => panic!("Expected Rows"),
7373 }
7374 }
7375
7376 #[test]
7377 fn select_with_where() {
7378 let router = QueryRouter::new();
7379
7380 router
7381 .execute("CREATE TABLE items (name string, qty int)")
7382 .unwrap();
7383 router
7384 .execute("INSERT INTO items (name, qty) VALUES ('A', 10)")
7385 .unwrap();
7386 router
7387 .execute("INSERT INTO items (name, qty) VALUES ('B', 20)")
7388 .unwrap();
7389 router
7390 .execute("INSERT INTO items (name, qty) VALUES ('C', 30)")
7391 .unwrap();
7392
7393 let result = router
7394 .execute("SELECT * FROM items WHERE qty > 15")
7395 .unwrap();
7396 match result {
7397 QueryResult::Rows(rows) => {
7398 assert_eq!(rows.len(), 2);
7399 },
7400 _ => panic!("Expected Rows"),
7401 }
7402 }
7403
7404 #[test]
7405 fn update_rows() {
7406 let router = QueryRouter::new();
7407
7408 router
7409 .execute("CREATE TABLE counters (name string, value int)")
7410 .unwrap();
7411 router
7412 .execute("INSERT INTO counters (name, value) VALUES ('hits', 0)")
7413 .unwrap();
7414
7415 let result = router
7416 .execute("UPDATE counters SET value=100 WHERE name=\"hits\"")
7417 .unwrap();
7418 match result {
7419 QueryResult::Count(n) => assert_eq!(n, 1),
7420 _ => panic!("Expected Count"),
7421 }
7422 }
7423
7424 #[test]
7425 fn delete_rows() {
7426 let router = QueryRouter::new();
7427
7428 router.execute("CREATE TABLE temp (id int)").unwrap();
7429 router.execute("INSERT INTO temp (id) VALUES (1)").unwrap();
7430 router.execute("INSERT INTO temp (id) VALUES (2)").unwrap();
7431
7432 let result = router.execute("DELETE FROM temp WHERE id=1").unwrap();
7433 match result {
7434 QueryResult::Count(n) => assert_eq!(n, 1),
7435 _ => panic!("Expected Count"),
7436 }
7437 }
7438
7439 #[test]
7440 fn create_and_drop_index() {
7441 let router = QueryRouter::new();
7442
7443 router.execute("CREATE TABLE indexed (col int)").unwrap();
7444 router
7445 .execute("CREATE INDEX idx_col ON indexed(col)")
7446 .unwrap();
7447
7448 assert!(router.relational().has_index("indexed", "col"));
7449
7450 router.execute("DROP INDEX ON indexed(col)").unwrap();
7451 assert!(!router.relational().has_index("indexed", "col"));
7452 }
7453
7454 #[test]
7455 fn drop_table() {
7456 let router = QueryRouter::new();
7457
7458 router.execute("CREATE TABLE todrop (x int)").unwrap();
7459 assert!(router.relational().table_exists("todrop"));
7460
7461 router.execute("DROP TABLE todrop").unwrap();
7462 assert!(!router.relational().table_exists("todrop"));
7463 }
7464
7465 #[test]
7468 fn node_create_get_delete() {
7469 let router = QueryRouter::new();
7470
7471 let id = match router
7472 .execute("NODE CREATE person { name: 'Test' }")
7473 .unwrap()
7474 {
7475 QueryResult::Ids(ids) => ids[0],
7476 _ => panic!("Expected Ids"),
7477 };
7478
7479 let result = router.execute(&format!("NODE GET {}", id)).unwrap();
7480 match result {
7481 QueryResult::Nodes(nodes) => {
7482 assert_eq!(nodes.len(), 1);
7483 assert_eq!(nodes[0].label, "person");
7484 },
7485 _ => panic!("Expected Nodes"),
7486 }
7487
7488 router.execute(&format!("NODE DELETE {}", id)).unwrap();
7489 }
7490
7491 #[test]
7492 fn edge_create_and_get() {
7493 let router = QueryRouter::new();
7494
7495 let n1 = match router.execute("NODE CREATE a").unwrap() {
7496 QueryResult::Ids(ids) => ids[0],
7497 _ => panic!("Expected Ids"),
7498 };
7499 let n2 = match router.execute("NODE CREATE b").unwrap() {
7500 QueryResult::Ids(ids) => ids[0],
7501 _ => panic!("Expected Ids"),
7502 };
7503
7504 let edge_id = match router
7505 .execute(&format!("EDGE CREATE {} -> {} : connects", n1, n2))
7506 .unwrap()
7507 {
7508 QueryResult::Ids(ids) => ids[0],
7509 _ => panic!("Expected Ids"),
7510 };
7511
7512 let result = router.execute(&format!("EDGE GET {}", edge_id)).unwrap();
7513 match result {
7514 QueryResult::Edges(edges) => {
7515 assert_eq!(edges.len(), 1);
7516 assert_eq!(edges[0].label, "connects");
7517 },
7518 _ => panic!("Expected Edges"),
7519 }
7520 }
7521
7522 #[test]
7523 fn neighbors_query() {
7524 let router = QueryRouter::new();
7525
7526 let center = match router.execute("NODE CREATE center").unwrap() {
7527 QueryResult::Ids(ids) => ids[0],
7528 _ => panic!("Expected Ids"),
7529 };
7530 let leaf1 = match router.execute("NODE CREATE leaf1").unwrap() {
7531 QueryResult::Ids(ids) => ids[0],
7532 _ => panic!("Expected Ids"),
7533 };
7534 let leaf2 = match router.execute("NODE CREATE leaf2").unwrap() {
7535 QueryResult::Ids(ids) => ids[0],
7536 _ => panic!("Expected Ids"),
7537 };
7538
7539 router
7540 .execute(&format!("EDGE CREATE {} -> {}", center, leaf1))
7541 .unwrap();
7542 router
7543 .execute(&format!("EDGE CREATE {} -> {}", center, leaf2))
7544 .unwrap();
7545
7546 let result = router
7547 .execute(&format!("NEIGHBORS {} OUTGOING", center))
7548 .unwrap();
7549 match result {
7550 QueryResult::Ids(ids) => {
7551 assert_eq!(ids.len(), 2);
7552 },
7553 _ => panic!("Expected Ids"),
7554 }
7555 }
7556
7557 #[test]
7558 fn path_query() {
7559 let router = QueryRouter::new();
7560
7561 let a = match router.execute("NODE CREATE a").unwrap() {
7562 QueryResult::Ids(ids) => ids[0],
7563 _ => panic!("Expected Ids"),
7564 };
7565 let b = match router.execute("NODE CREATE b").unwrap() {
7566 QueryResult::Ids(ids) => ids[0],
7567 _ => panic!("Expected Ids"),
7568 };
7569 let c = match router.execute("NODE CREATE c").unwrap() {
7570 QueryResult::Ids(ids) => ids[0],
7571 _ => panic!("Expected Ids"),
7572 };
7573
7574 router
7575 .execute(&format!("EDGE CREATE {} -> {}", a, b))
7576 .unwrap();
7577 router
7578 .execute(&format!("EDGE CREATE {} -> {}", b, c))
7579 .unwrap();
7580
7581 let result = router.execute(&format!("PATH {} -> {}", a, c)).unwrap();
7582 match result {
7583 QueryResult::Path(path) => {
7584 assert_eq!(path.len(), 3);
7585 assert_eq!(path[0], a);
7586 assert_eq!(path[2], c);
7587 },
7588 _ => panic!("Expected Path"),
7589 }
7590 }
7591
7592 #[test]
7595 fn embed_and_similar_inline() {
7596 let router = QueryRouter::new();
7597
7598 router.execute("EMBED v1 [1.0, 0.0]").unwrap();
7599 router.execute("EMBED v2 [0.0, 1.0]").unwrap();
7600
7601 let result = router.execute("SIMILAR [1.0, 0.0] TOP 1").unwrap();
7602 match result {
7603 QueryResult::Similar(results) => {
7604 assert_eq!(results.len(), 1);
7605 assert_eq!(results[0].key, "v1");
7606 },
7607 _ => panic!("Expected Similar"),
7608 }
7609 }
7610
7611 #[test]
7614 fn can_access_underlying_engines() {
7615 let router = QueryRouter::new();
7616
7617 let _ = router.relational();
7619 let _ = router.graph();
7620 let _ = router.vector();
7621 }
7622
7623 #[test]
7624 fn with_engines_constructor() {
7625 let rel = Arc::new(RelationalEngine::new());
7626 let graph = Arc::new(GraphEngine::new());
7627 let vec = Arc::new(VectorEngine::new());
7628
7629 let router = QueryRouter::with_engines(rel, graph, vec);
7630 assert!(router.execute("EMBED test [1.0]").is_ok());
7631 }
7632
7633 #[test]
7634 fn build_vector_index() {
7635 let mut router = QueryRouter::new();
7636
7637 router.execute("EMBED a [1.0, 0.0]").unwrap();
7638 router.execute("EMBED b [0.0, 1.0]").unwrap();
7639
7640 router.build_vector_index().unwrap();
7641
7642 let result = router.execute("SIMILAR a TOP 2").unwrap();
7644 match result {
7645 QueryResult::Similar(results) => {
7646 assert_eq!(results.len(), 2);
7647 },
7648 _ => panic!("Expected Similar"),
7649 }
7650 }
7651
7652 #[test]
7655 fn error_display() {
7656 let e = RouterError::ParseError("test".into());
7657 assert!(e.to_string().contains("Parse error"));
7658
7659 let e = RouterError::UnknownCommand("FOO".into());
7660 assert!(e.to_string().contains("Unknown command"));
7661
7662 let e = RouterError::RelationalError("db error".into());
7663 assert!(e.to_string().contains("Relational error"));
7664
7665 let e = RouterError::GraphError("graph error".into());
7666 assert!(e.to_string().contains("Graph error"));
7667
7668 let e = RouterError::VectorError("vec error".into());
7669 assert!(e.to_string().contains("Vector error"));
7670
7671 let e = RouterError::InvalidArgument("bad arg".into());
7672 assert!(e.to_string().contains("Invalid argument"));
7673
7674 let e = RouterError::MissingArgument("missing".into());
7675 assert!(e.to_string().contains("Missing argument"));
7676
7677 let e = RouterError::TypeMismatch("type".into());
7678 assert!(e.to_string().contains("Type mismatch"));
7679 }
7680
7681 #[test]
7682 fn error_clone_and_eq() {
7683 let e1 = RouterError::ParseError("test".into());
7684 let e2 = e1.clone();
7685 assert_eq!(e1, e2);
7686 }
7687
7688 #[test]
7689 fn error_is_std_error() {
7690 let error: Box<dyn std::error::Error> = Box::new(RouterError::ParseError("test".into()));
7691 assert!(error.to_string().contains("Parse"));
7692 }
7693
7694 #[test]
7695 fn default_trait() {
7696 let router = QueryRouter::default();
7697 assert!(router.execute("EMBED x [1.0]").is_ok());
7698 }
7699
7700 #[test]
7703 fn parse_compound_conditions() {
7704 let router = QueryRouter::new();
7705
7706 router.execute("CREATE TABLE data (a int, b int)").unwrap();
7707 router
7708 .execute("INSERT INTO data (a, b) VALUES (1, 2)")
7709 .unwrap();
7710 router
7711 .execute("INSERT INTO data (a, b) VALUES (3, 4)")
7712 .unwrap();
7713 router
7714 .execute("INSERT INTO data (a, b) VALUES (5, 6)")
7715 .unwrap();
7716
7717 let result = router
7719 .execute("SELECT * FROM data WHERE a > 2 AND b < 6")
7720 .unwrap();
7721 match result {
7722 QueryResult::Rows(rows) => {
7723 assert_eq!(rows.len(), 1);
7724 },
7725 _ => panic!("Expected Rows"),
7726 }
7727
7728 let result = router
7730 .execute("SELECT * FROM data WHERE a = 1 OR a = 5")
7731 .unwrap();
7732 match result {
7733 QueryResult::Rows(rows) => {
7734 assert_eq!(rows.len(), 2);
7735 },
7736 _ => panic!("Expected Rows"),
7737 }
7738 }
7739
7740 #[test]
7741 fn parse_nullable_columns() {
7742 let router = QueryRouter::new();
7743
7744 router
7745 .execute("CREATE TABLE nullable (required string, optional text)")
7746 .unwrap();
7747 router
7748 .execute("INSERT INTO nullable (required, optional) VALUES ('test', NULL)")
7749 .unwrap();
7750
7751 let result = router.execute("SELECT * FROM nullable").unwrap();
7752 match result {
7753 QueryResult::Rows(rows) => {
7754 assert_eq!(rows.len(), 1);
7755 assert_eq!(rows[0].get("optional"), Some(&Value::Null));
7756 },
7757 _ => panic!("Expected Rows"),
7758 }
7759 }
7760
7761 #[test]
7764 fn update_without_where() {
7765 let router = QueryRouter::new();
7766 router.execute("CREATE TABLE t (x int)").unwrap();
7767 router.execute("INSERT INTO t (x) VALUES (1)").unwrap();
7768 let result = router.execute("UPDATE t x=2");
7770 assert!(result.is_err());
7771 }
7772
7773 #[test]
7774 fn delete_without_where_clause() {
7775 let router = QueryRouter::new();
7776 router.execute("CREATE TABLE del (x int)").unwrap();
7777 router.execute("INSERT INTO del (x) VALUES (1)").unwrap();
7778 router.execute("INSERT INTO del (x) VALUES (2)").unwrap();
7779 let result = router.execute("DELETE FROM del").unwrap();
7781 match result {
7782 QueryResult::Count(n) => assert_eq!(n, 2),
7783 _ => panic!("Expected Count"),
7784 }
7785 }
7786
7787 #[test]
7788 fn create_table_with_bool() {
7789 let router = QueryRouter::new();
7790 router
7791 .execute("CREATE TABLE flags (name string, active bool)")
7792 .unwrap();
7793 router
7794 .execute("INSERT INTO flags (name, active) VALUES ('test', true)")
7795 .unwrap();
7796 let result = router.execute("SELECT * FROM flags").unwrap();
7797 match result {
7798 QueryResult::Rows(rows) => {
7799 assert_eq!(rows.len(), 1);
7800 assert_eq!(rows[0].get("active"), Some(&Value::Bool(true)));
7801 },
7802 _ => panic!("Expected Rows"),
7803 }
7804 }
7805
7806 #[test]
7807 fn create_table_with_float() {
7808 let router = QueryRouter::new();
7809 router.execute("CREATE TABLE nums (val double)").unwrap();
7810 router
7811 .execute("INSERT INTO nums (val) VALUES (3.14)")
7812 .unwrap();
7813 let result = router.execute("SELECT * FROM nums").unwrap();
7814 match result {
7815 QueryResult::Rows(rows) => {
7816 assert_eq!(rows.len(), 1);
7817 },
7818 _ => panic!("Expected Rows"),
7819 }
7820 }
7821
7822 #[test]
7823 fn invalid_create_missing_parens() {
7824 let router = QueryRouter::new();
7825 let result = router.execute("CREATE TABLE bad x int");
7826 assert!(result.is_err());
7827 }
7828
7829 #[test]
7830 fn invalid_create_command() {
7831 let router = QueryRouter::new();
7832 let result = router.execute("CREATE SOMETHING bad");
7833 assert!(result.is_err());
7834 }
7835
7836 #[test]
7837 fn invalid_drop_command() {
7838 let router = QueryRouter::new();
7839 let result = router.execute("DROP SOMETHING bad");
7840 assert!(result.is_err());
7841 }
7842
7843 #[test]
7844 fn path_not_found() {
7845 let router = QueryRouter::new();
7846 let n1 = match router.execute("NODE CREATE a").unwrap() {
7847 QueryResult::Ids(ids) => ids[0],
7848 _ => panic!("Expected Ids"),
7849 };
7850 let n2 = match router.execute("NODE CREATE b").unwrap() {
7851 QueryResult::Ids(ids) => ids[0],
7852 _ => panic!("Expected Ids"),
7853 };
7854 let result = router.execute(&format!("PATH {} -> {}", n1, n2)).unwrap();
7856 match result {
7857 QueryResult::Path(path) => assert!(path.is_empty()),
7858 _ => panic!("Expected Path"),
7859 }
7860 }
7861
7862 #[test]
7863 fn neighbors_in_direction() {
7864 let router = QueryRouter::new();
7865 let n1 = match router.execute("NODE CREATE a").unwrap() {
7866 QueryResult::Ids(ids) => ids[0],
7867 _ => panic!("Expected Ids"),
7868 };
7869 let n2 = match router.execute("NODE CREATE b").unwrap() {
7870 QueryResult::Ids(ids) => ids[0],
7871 _ => panic!("Expected Ids"),
7872 };
7873 router
7874 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
7875 .unwrap();
7876
7877 let result = router
7879 .execute(&format!("NEIGHBORS {} INCOMING", n2))
7880 .unwrap();
7881 match result {
7882 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
7883 _ => panic!("Expected Ids"),
7884 }
7885 }
7886
7887 #[test]
7888 fn neighbors_invalid_direction() {
7889 let router = QueryRouter::new();
7890 let n1 = match router.execute("NODE CREATE a").unwrap() {
7891 QueryResult::Ids(ids) => ids[0],
7892 _ => panic!("Expected Ids"),
7893 };
7894 let result = router.execute(&format!("NEIGHBORS {n1} INVALID"));
7897 assert!(result.is_err());
7898 }
7899
7900 #[test]
7901 fn node_with_typed_properties() {
7902 let router = QueryRouter::new();
7903 let result = router
7905 .execute("NODE CREATE person { age: 30, score: 95.5, active: true }")
7906 .unwrap();
7907 match result {
7908 QueryResult::Ids(ids) => {
7909 let node_result = router.execute(&format!("NODE GET {}", ids[0])).unwrap();
7910 match node_result {
7911 QueryResult::Nodes(nodes) => {
7912 assert_eq!(nodes[0].properties.get("age"), Some(&"30".to_string()));
7913 },
7914 _ => panic!("Expected Nodes"),
7915 }
7916 },
7917 _ => panic!("Expected Ids"),
7918 }
7919 }
7920
7921 #[test]
7922 fn edge_undirected() {
7923 let router = QueryRouter::new();
7924 let n1 = match router.execute("NODE CREATE a").unwrap() {
7925 QueryResult::Ids(ids) => ids[0],
7926 _ => panic!("Expected Ids"),
7927 };
7928 let n2 = match router.execute("NODE CREATE b").unwrap() {
7929 QueryResult::Ids(ids) => ids[0],
7930 _ => panic!("Expected Ids"),
7931 };
7932 router
7935 .execute(&format!("EDGE CREATE {} -> {} : rel_link", n1, n2))
7936 .unwrap();
7937 }
7938
7939 #[test]
7940 fn condition_all_operators() {
7941 let router = QueryRouter::new();
7942 router.execute("CREATE TABLE ops (x int)").unwrap();
7943 router.execute("INSERT INTO ops (x) VALUES (5)").unwrap();
7944
7945 let result = router.execute("SELECT * FROM ops WHERE x != 10").unwrap();
7947 match result {
7948 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
7949 _ => panic!("Expected Rows"),
7950 }
7951
7952 let result = router.execute("SELECT * FROM ops WHERE x <= 5").unwrap();
7954 match result {
7955 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
7956 _ => panic!("Expected Rows"),
7957 }
7958
7959 let result = router.execute("SELECT * FROM ops WHERE x >= 5").unwrap();
7961 match result {
7962 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
7963 _ => panic!("Expected Rows"),
7964 }
7965 }
7966
7967 #[test]
7968 fn invalid_condition() {
7969 let router = QueryRouter::new();
7970 router.execute("CREATE TABLE t (x int)").unwrap();
7971 let result = router.execute("SELECT * FROM t WHERE invalid");
7972 assert!(result.is_err());
7973 }
7974
7975 #[test]
7976 fn invalid_insert_values() {
7977 let router = QueryRouter::new();
7978 router.execute("CREATE TABLE t (x int)").unwrap();
7979 let result = router.execute("INSERT t invalid");
7980 assert!(result.is_err());
7981 }
7982
7983 #[test]
7984 fn invalid_node_id() {
7985 let router = QueryRouter::new();
7986 let result = router.execute("NODE GET notanumber");
7987 assert!(result.is_err());
7988 }
7989
7990 #[test]
7991 fn invalid_edge_id() {
7992 let router = QueryRouter::new();
7993 let result = router.execute("EDGE GET notanumber");
7994 assert!(result.is_err());
7995 }
7996
7997 #[test]
7998 fn invalid_neighbors_id() {
7999 let router = QueryRouter::new();
8000 let result = router.execute("NEIGHBORS notanumber");
8001 assert!(result.is_err());
8002 }
8003
8004 #[test]
8005 fn invalid_path_ids() {
8006 let router = QueryRouter::new();
8007 let result = router.execute("PATH notanumber -> 1");
8008 assert!(result.is_err());
8009
8010 let result = router.execute("PATH 1 -> notanumber");
8011 assert!(result.is_err());
8012 }
8013
8014 #[test]
8015 fn invalid_vector() {
8016 let router = QueryRouter::new();
8017 let result = router.execute("EMBED key [not, valid]");
8018 assert!(result.is_err());
8019 }
8020
8021 #[test]
8022 fn empty_vector() {
8023 let router = QueryRouter::new();
8024 let result = router.execute("EMBED key []");
8025 assert!(result.is_err());
8026 }
8027
8028 #[test]
8029 fn similar_with_inline_vector() {
8030 let router = QueryRouter::new();
8031 router.execute("EMBED v1 [1.0, 0.0, 0.0]").unwrap();
8032 let result = router.execute("SIMILAR [0.9, 0.1, 0.0] TOP 1").unwrap();
8033 match result {
8034 QueryResult::Similar(results) => assert_eq!(results.len(), 1),
8035 _ => panic!("Expected Similar"),
8036 }
8037 }
8038
8039 #[test]
8040 fn unknown_edge_subcommand() {
8041 let router = QueryRouter::new();
8042 let result = router.execute("EDGE UNKNOWN 1");
8043 assert!(result.is_err());
8044 }
8045
8046 #[test]
8047 fn unknown_node_subcommand() {
8048 let router = QueryRouter::new();
8049 let result = router.execute("NODE UNKNOWN label");
8050 assert!(result.is_err());
8051 }
8052
8053 #[test]
8054 fn find_with_where_clause() {
8055 let router = QueryRouter::new();
8056 router.execute("NODE CREATE item { x: 10 }").unwrap();
8058 router.execute("NODE CREATE item { x: 3 }").unwrap();
8059 router.execute("NODE CREATE item { x: 7 }").unwrap();
8060
8061 let result = router.execute("FIND NODES item WHERE x > 5").unwrap();
8063 match result {
8064 QueryResult::Unified(u) => {
8065 assert!(u.items.len() <= 3);
8067 },
8068 _ => panic!("Expected Unified"),
8069 }
8070 }
8071
8072 #[test]
8073 fn property_value_null() {
8074 let router = QueryRouter::new();
8075 router.execute("NODE CREATE test { val: NULL }").unwrap();
8076 }
8077
8078 #[test]
8079 fn property_value_false() {
8080 let router = QueryRouter::new();
8081 router.execute("NODE CREATE test { val: false }").unwrap();
8082 }
8083
8084 #[test]
8085 fn missing_edge_definition() {
8086 let router = QueryRouter::new();
8087 let result = router.execute("EDGE CREATE");
8088 assert!(result.is_err());
8089 }
8090
8091 #[test]
8092 fn missing_path_args() {
8093 let router = QueryRouter::new();
8094 let result = router.execute("PATH 1");
8095 assert!(result.is_err());
8096 }
8097
8098 #[test]
8099 fn missing_embed_args() {
8100 let router = QueryRouter::new();
8101 let result = router.execute("EMBED");
8102 assert!(result.is_err());
8103 }
8104
8105 #[test]
8106 fn missing_similar_args() {
8107 let router = QueryRouter::new();
8108 let result = router.execute("SIMILAR");
8109 assert!(result.is_err());
8110 }
8111
8112 #[test]
8113 fn find_without_args_returns_all_nodes() {
8114 let router = QueryRouter::new();
8115 router.execute("NODE CREATE test { name: 'A' }").unwrap();
8117 router.execute("NODE CREATE test { name: 'B' }").unwrap();
8118
8119 let result = router.execute("FIND").unwrap();
8121 match result {
8122 QueryResult::Unified(u) => {
8123 assert_eq!(u.items.len(), 2);
8124 },
8125 _ => panic!("Expected Unified"),
8126 }
8127 }
8128
8129 #[test]
8130 fn create_index_missing_args() {
8131 let router = QueryRouter::new();
8132 let result = router.execute("CREATE INDEX t");
8133 assert!(result.is_err());
8134 }
8135
8136 #[test]
8137 fn drop_index_missing_args() {
8138 let router = QueryRouter::new();
8139 let result = router.execute("DROP INDEX t");
8140 assert!(result.is_err());
8141 }
8142
8143 #[test]
8144 fn invalid_top_value() {
8145 let router = QueryRouter::new();
8146 router.execute("EMBED v [1.0]").unwrap();
8147 let result = router.execute("SIMILAR v TOP notanumber");
8148 assert!(result.is_err());
8149 }
8150
8151 #[test]
8152 fn hnsw_similar_search() {
8153 let mut router = QueryRouter::new();
8154 router.execute("EMBED a [1.0, 0.0]").unwrap();
8155 router.execute("EMBED b [0.0, 1.0]").unwrap();
8156 router.build_vector_index().unwrap();
8157
8158 let result = router.execute("SIMILAR a TOP 2").unwrap();
8159 match result {
8160 QueryResult::Similar(results) => assert_eq!(results.len(), 2),
8161 _ => panic!("Expected Similar"),
8162 }
8163 }
8164
8165 #[test]
8166 fn invalid_edge_nodes() {
8167 let router = QueryRouter::new();
8168 let result = router.execute("EDGE CREATE notanumber -> 1");
8169 assert!(result.is_err());
8170
8171 let n1 = match router.execute("NODE CREATE a").unwrap() {
8172 QueryResult::Ids(ids) => ids[0],
8173 _ => panic!("Expected Ids"),
8174 };
8175 let result = router.execute(&format!("EDGE CREATE {} -> notanumber", n1));
8176 assert!(result.is_err());
8177 }
8178
8179 #[test]
8180 fn missing_insert_table() {
8181 let router = QueryRouter::new();
8182 let result = router.execute("INSERT");
8183 assert!(result.is_err());
8184 }
8185
8186 #[test]
8187 fn missing_delete_table() {
8188 let router = QueryRouter::new();
8189 let result = router.execute("DELETE");
8190 assert!(result.is_err());
8191 }
8192
8193 #[test]
8194 fn update_with_set_no_where() {
8195 let router = QueryRouter::new();
8196 router.execute("CREATE TABLE t (x int)").unwrap();
8197 router.execute("INSERT INTO t (x) VALUES (1)").unwrap();
8198 router.execute("INSERT INTO t (x) VALUES (2)").unwrap();
8199 let result = router.execute("UPDATE t SET x=99").unwrap();
8201 match result {
8202 QueryResult::Count(n) => assert_eq!(n, 2),
8203 _ => panic!("Expected Count"),
8204 }
8205 }
8206
8207 #[test]
8208 fn invalid_column_definition() {
8209 let router = QueryRouter::new();
8210 let result = router.execute("CREATE TABLE bad (invalid)");
8211 assert!(result.is_err());
8212 }
8213
8214 #[test]
8215 fn unknown_column_type() {
8216 let router = QueryRouter::new();
8217 let result = router.execute("CREATE TABLE bad (x unknowntype)");
8218 assert!(result.is_err());
8219 }
8220
8221 #[test]
8222 fn node_get_missing_id() {
8223 let router = QueryRouter::new();
8224 let result = router.execute("NODE GET");
8225 assert!(result.is_err());
8226 }
8227
8228 #[test]
8229 fn node_delete_missing_id() {
8230 let router = QueryRouter::new();
8231 let result = router.execute("NODE DELETE");
8232 assert!(result.is_err());
8233 }
8234
8235 #[test]
8236 fn edge_missing_subcommand() {
8237 let router = QueryRouter::new();
8238 let result = router.execute("EDGE");
8239 assert!(result.is_err());
8240 }
8241
8242 #[test]
8243 fn edge_get_missing_id() {
8244 let router = QueryRouter::new();
8245 let result = router.execute("EDGE GET");
8246 assert!(result.is_err());
8247 }
8248
8249 #[test]
8250 fn neighbors_default_direction() {
8251 let router = QueryRouter::new();
8252 let n1 = match router.execute("NODE CREATE a").unwrap() {
8253 QueryResult::Ids(ids) => ids[0],
8254 _ => panic!("Expected Ids"),
8255 };
8256 let result = router.execute(&format!("NEIGHBORS {}", n1)).unwrap();
8258 match result {
8259 QueryResult::Ids(_) => {},
8260 _ => panic!("Expected Ids"),
8261 }
8262 }
8263
8264 #[test]
8265 fn invalid_edge_definition_format() {
8266 let router = QueryRouter::new();
8267 let result = router.execute("EDGE CREATE 1 2 label");
8269 assert!(result.is_err());
8270 }
8271
8272 #[test]
8273 fn edge_directed_keyword() {
8274 let router = QueryRouter::new();
8275 let n1 = match router.execute("NODE CREATE a").unwrap() {
8276 QueryResult::Ids(ids) => ids[0],
8277 _ => panic!("Expected Ids"),
8278 };
8279 let n2 = match router.execute("NODE CREATE b").unwrap() {
8280 QueryResult::Ids(ids) => ids[0],
8281 _ => panic!("Expected Ids"),
8282 };
8283 router
8285 .execute(&format!("EDGE CREATE {} -> {} : rel_link", n1, n2))
8286 .unwrap();
8287 }
8288
8289 #[test]
8290 fn value_parsing_false_lowercase() {
8291 let router = QueryRouter::new();
8292 router.execute("CREATE TABLE t (flag bool)").unwrap();
8293 router
8294 .execute("INSERT INTO t (flag) VALUES (FALSE)")
8295 .unwrap();
8296 let result = router.execute("SELECT * FROM t").unwrap();
8297 match result {
8298 QueryResult::Rows(rows) => {
8299 assert_eq!(rows[0].get("flag"), Some(&Value::Bool(false)));
8300 },
8301 _ => panic!("Expected Rows"),
8302 }
8303 }
8304
8305 #[test]
8306 fn value_parsing_string_variants() {
8307 let router = QueryRouter::new();
8308 router.execute("CREATE TABLE t (s string)").unwrap();
8309 router
8311 .execute("INSERT INTO t (s) VALUES ('hello')")
8312 .unwrap();
8313 let result = router.execute("SELECT * FROM t").unwrap();
8314 match result {
8315 QueryResult::Rows(rows) => {
8316 assert_eq!(rows[0].get("s"), Some(&Value::String("hello".into())));
8317 },
8318 _ => panic!("Expected Rows"),
8319 }
8320 }
8321
8322 #[test]
8323 fn property_null_to_string() {
8324 let router = QueryRouter::new();
8325 let result = router.execute("NODE CREATE test { prop: NULL }").unwrap();
8326 match result {
8327 QueryResult::Ids(ids) => {
8328 let node = router.execute(&format!("NODE GET {}", ids[0])).unwrap();
8329 match node {
8330 QueryResult::Nodes(nodes) => {
8331 assert_eq!(nodes[0].properties.get("prop"), Some(&"null".to_string()));
8332 },
8333 _ => panic!("Expected Nodes"),
8334 }
8335 },
8336 _ => panic!("Expected Ids"),
8337 }
8338 }
8339
8340 #[test]
8341 fn missing_neighbors_id() {
8342 let router = QueryRouter::new();
8343 let result = router.execute("NEIGHBORS");
8344 assert!(result.is_err());
8345 }
8346
8347 #[test]
8348 fn select_missing_table() {
8349 let router = QueryRouter::new();
8350 let result = router.execute("SELECT");
8351 assert!(result.is_err());
8352 }
8353
8354 #[test]
8355 fn node_missing_subcommand() {
8356 let router = QueryRouter::new();
8357 let result = router.execute("NODE");
8358 assert!(result.is_err());
8359 }
8360
8361 #[test]
8362 fn unquoted_string_value() {
8363 let router = QueryRouter::new();
8364 router.execute("CREATE TABLE t (s string)").unwrap();
8365 router
8367 .execute("INSERT INTO t (s) VALUES (bareword)")
8368 .unwrap();
8369 let result = router.execute("SELECT * FROM t").unwrap();
8370 match result {
8371 QueryResult::Rows(rows) => {
8372 assert_eq!(rows[0].get("s"), Some(&Value::String("bareword".into())));
8373 },
8374 _ => panic!("Expected Rows"),
8375 }
8376 }
8377
8378 #[test]
8379 fn whitespace_only_command() {
8380 let router = QueryRouter::new();
8381 let result = router.execute(" ");
8382 assert!(result.is_err());
8383 }
8384
8385 #[test]
8386 fn path_graph_error() {
8387 let router = QueryRouter::new();
8388 let result = router.execute("PATH 99999 -> 99998");
8390 assert!(result.is_err());
8391 }
8392
8393 #[test]
8394 fn node_property_unquoted_string() {
8395 let router = QueryRouter::new();
8396 let result = router
8397 .execute("NODE CREATE test { prop: somevalue }")
8398 .unwrap();
8399 match result {
8400 QueryResult::Ids(ids) => {
8401 let node = router.execute(&format!("NODE GET {}", ids[0])).unwrap();
8402 match node {
8403 QueryResult::Nodes(nodes) => {
8404 assert_eq!(
8405 nodes[0].properties.get("prop"),
8406 Some(&"somevalue".to_string())
8407 );
8408 },
8409 _ => panic!("Expected Nodes"),
8410 }
8411 },
8412 _ => panic!("Expected Ids"),
8413 }
8414 }
8415
8416 #[test]
8417 fn edge_missing_arrow_definition() {
8418 let router = QueryRouter::new();
8419 let n1 = match router.execute("NODE CREATE a").unwrap() {
8420 QueryResult::Ids(ids) => ids[0],
8421 _ => panic!("Expected Ids"),
8422 };
8423 let result = router.execute(&format!("EDGE CREATE {} {} label", n1, n1 + 1));
8425 assert!(result.is_err());
8426 }
8427
8428 #[test]
8429 fn embed_with_empty_brackets() {
8430 let router = QueryRouter::new();
8431 let result = router.execute("EMBED emptykey []");
8432 assert!(result.is_err());
8433 }
8434
8435 #[test]
8436 fn show_vector_index_empty() {
8437 let router = QueryRouter::new();
8438 let result = router.execute("SHOW VECTOR INDEX").unwrap();
8439 match result {
8440 QueryResult::Value(s) => {
8441 assert!(s.contains("No HNSW index built"));
8442 },
8443 _ => panic!("Expected Value result"),
8444 }
8445 }
8446
8447 #[test]
8448 fn show_vector_index_after_build() {
8449 let mut router = QueryRouter::new();
8450 router.execute("EMBED v1 [1.0, 0.0, 0.0]").unwrap();
8451 router.execute("EMBED v2 [0.0, 1.0, 0.0]").unwrap();
8452 router.execute("EMBED v3 [0.0, 0.0, 1.0]").unwrap();
8453
8454 router.build_vector_index().unwrap();
8456
8457 let result = router.execute("SHOW VECTOR INDEX").unwrap();
8459 match result {
8460 QueryResult::Value(s) => {
8461 assert!(s.contains("HNSW index"));
8462 assert!(s.contains("3"));
8463 },
8464 _ => panic!("Expected Value result"),
8465 }
8466 }
8467
8468 #[test]
8469 fn insert_table_only() {
8470 let router = QueryRouter::new();
8471 router.execute("CREATE TABLE t (x int)").unwrap();
8472 let result = router.execute("INSERT t");
8473 assert!(result.is_err());
8474 }
8475
8476 #[test]
8477 fn edge_definition_too_short() {
8478 let router = QueryRouter::new();
8479 let n1 = match router.execute("NODE CREATE a").unwrap() {
8480 QueryResult::Ids(ids) => ids[0],
8481 _ => panic!("Expected Ids"),
8482 };
8483 let result = router.execute(&format!("EDGE CREATE {} ->", n1));
8485 assert!(result.is_err());
8486 }
8487
8488 #[test]
8489 fn find_edges_returns_items() {
8490 let router = QueryRouter::new();
8491
8492 let user_id = match router
8494 .execute("NODE CREATE user { name: 'Alice' }")
8495 .unwrap()
8496 {
8497 QueryResult::Ids(ids) => ids[0],
8498 _ => panic!("Expected Ids"),
8499 };
8500 let post_id = match router
8501 .execute("NODE CREATE post { title: 'Hello' }")
8502 .unwrap()
8503 {
8504 QueryResult::Ids(ids) => ids[0],
8505 _ => panic!("Expected Ids"),
8506 };
8507 router
8508 .execute(&format!(
8509 "EDGE CREATE {} -> {} : authored",
8510 user_id, post_id
8511 ))
8512 .unwrap();
8513
8514 let result = router.execute("FIND EDGES authored").unwrap();
8515 match result {
8516 QueryResult::Unified(unified) => {
8517 assert!(!unified.items.is_empty());
8518 assert_eq!(unified.items[0].source, "graph");
8519 },
8520 _ => panic!("Expected Unified"),
8521 }
8522 }
8523
8524 #[test]
8525 fn similar_no_results() {
8526 let router = QueryRouter::new();
8527 let result = router.execute("SIMILAR nonexistent TOP 5");
8529 assert!(result.is_err());
8530 }
8531
8532 #[test]
8535 fn parsed_select_basic() {
8536 let router = QueryRouter::new();
8537 router
8538 .execute("CREATE TABLE users (id int, name string)")
8539 .unwrap();
8540 router
8541 .execute("INSERT INTO users (id, name) VALUES (1, 'alice')")
8542 .unwrap();
8543
8544 let result = router.execute_parsed("SELECT * FROM users").unwrap();
8545 match result {
8546 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
8547 _ => panic!("Expected Rows"),
8548 }
8549 }
8550
8551 #[test]
8552 fn parsed_select_with_where() {
8553 let router = QueryRouter::new();
8554 router
8555 .execute("CREATE TABLE products (id int, price int)")
8556 .unwrap();
8557 router
8558 .execute("INSERT INTO products (id, price) VALUES (1, 100)")
8559 .unwrap();
8560 router
8561 .execute("INSERT INTO products (id, price) VALUES (2, 200)")
8562 .unwrap();
8563
8564 let result = router
8565 .execute_parsed("SELECT * FROM products WHERE price > 150")
8566 .unwrap();
8567 match result {
8568 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
8569 _ => panic!("Expected Rows"),
8570 }
8571 }
8572
8573 #[test]
8574 fn parsed_insert_values() {
8575 let router = QueryRouter::new();
8576 router
8577 .execute("CREATE TABLE items (id int, name string)")
8578 .unwrap();
8579
8580 let result = router
8581 .execute_parsed("INSERT INTO items (id, name) VALUES (1, 'test')")
8582 .unwrap();
8583 match result {
8584 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
8585 _ => panic!("Expected Ids"),
8586 }
8587 }
8588
8589 #[test]
8590 fn parsed_update() {
8591 let router = QueryRouter::new();
8592 router
8593 .execute("CREATE TABLE scores (id int, val int)")
8594 .unwrap();
8595 router
8596 .execute("INSERT INTO scores (id, val) VALUES (1, 10)")
8597 .unwrap();
8598
8599 let result = router
8600 .execute_parsed("UPDATE scores SET val = 20 WHERE id = 1")
8601 .unwrap();
8602 match result {
8603 QueryResult::Count(n) => assert_eq!(n, 1),
8604 _ => panic!("Expected Count"),
8605 }
8606 }
8607
8608 #[test]
8609 fn parsed_update_no_where() {
8610 let router = QueryRouter::new();
8611 router.execute("CREATE TABLE t (x int)").unwrap();
8612 router.execute("INSERT INTO t (x) VALUES (1)").unwrap();
8613 router.execute("INSERT INTO t (x) VALUES (2)").unwrap();
8614
8615 let result = router.execute_parsed("UPDATE t SET x = 99").unwrap();
8616 match result {
8617 QueryResult::Count(n) => assert_eq!(n, 2),
8618 _ => panic!("Expected Count"),
8619 }
8620 }
8621
8622 #[test]
8623 fn parsed_delete() {
8624 let router = QueryRouter::new();
8625 router.execute("CREATE TABLE temps (id int)").unwrap();
8626 router.execute("INSERT INTO temps (id) VALUES (1)").unwrap();
8627 router.execute("INSERT INTO temps (id) VALUES (2)").unwrap();
8628
8629 let result = router
8630 .execute_parsed("DELETE FROM temps WHERE id = 1")
8631 .unwrap();
8632 match result {
8633 QueryResult::Count(n) => assert_eq!(n, 1),
8634 _ => panic!("Expected Count"),
8635 }
8636 }
8637
8638 #[test]
8639 fn parsed_delete_no_where() {
8640 let router = QueryRouter::new();
8641 router.execute("CREATE TABLE t (x int)").unwrap();
8642 router.execute("INSERT INTO t (x) VALUES (1)").unwrap();
8643 router.execute("INSERT INTO t (x) VALUES (2)").unwrap();
8644
8645 let result = router.execute_parsed("DELETE FROM t").unwrap();
8646 match result {
8647 QueryResult::Count(n) => assert_eq!(n, 2),
8648 _ => panic!("Expected Count"),
8649 }
8650 }
8651
8652 #[test]
8653 fn parsed_create_table() {
8654 let router = QueryRouter::new();
8655 let result = router
8656 .execute_parsed("CREATE TABLE newtbl (id INTEGER, name VARCHAR(100))")
8657 .unwrap();
8658 assert!(matches!(result, QueryResult::Empty));
8659
8660 router
8662 .execute("INSERT INTO newtbl (id, name) VALUES (1, 'test')")
8663 .unwrap();
8664 }
8665
8666 #[test]
8667 fn parsed_create_table_not_null() {
8668 let router = QueryRouter::new();
8669 router
8670 .execute_parsed("CREATE TABLE required (id INT NOT NULL, name TEXT)")
8671 .unwrap();
8672 }
8673
8674 #[test]
8675 fn parsed_drop_table() {
8676 let router = QueryRouter::new();
8677 router.execute("CREATE TABLE todrop (id int)").unwrap();
8678
8679 let result = router.execute_parsed("DROP TABLE todrop").unwrap();
8680 assert!(matches!(result, QueryResult::Empty));
8681 }
8682
8683 #[test]
8684 fn parsed_create_index() {
8685 let router = QueryRouter::new();
8686 router
8687 .execute("CREATE TABLE indexed (id int, val int)")
8688 .unwrap();
8689
8690 let result = router
8691 .execute_parsed("CREATE INDEX idx ON indexed (val)")
8692 .unwrap();
8693 assert!(matches!(result, QueryResult::Empty));
8694 }
8695
8696 #[test]
8697 fn parsed_drop_index_not_supported() {
8698 let router = QueryRouter::new();
8699 let result = router.execute_parsed("DROP INDEX myindex");
8700 assert!(result.is_err());
8701 }
8702
8703 #[test]
8704 fn parsed_node_create() {
8705 let router = QueryRouter::new();
8706 let result = router
8707 .execute_parsed("NODE CREATE person { name: 'Alice', age: 30 }")
8708 .unwrap();
8709 match result {
8710 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
8711 _ => panic!("Expected Ids"),
8712 }
8713 }
8714
8715 #[test]
8716 fn parsed_node_get() {
8717 let router = QueryRouter::new();
8718 let id = match router.execute("NODE CREATE test").unwrap() {
8719 QueryResult::Ids(ids) => ids[0],
8720 _ => panic!("Expected Ids"),
8721 };
8722
8723 let result = router.execute_parsed(&format!("NODE GET {}", id)).unwrap();
8724 match result {
8725 QueryResult::Nodes(nodes) => assert_eq!(nodes.len(), 1),
8726 _ => panic!("Expected Nodes"),
8727 }
8728 }
8729
8730 #[test]
8731 fn parsed_node_delete() {
8732 let router = QueryRouter::new();
8733 let id = match router.execute("NODE CREATE todelete").unwrap() {
8734 QueryResult::Ids(ids) => ids[0],
8735 _ => panic!("Expected Ids"),
8736 };
8737
8738 let result = router
8739 .execute_parsed(&format!("NODE DELETE {}", id))
8740 .unwrap();
8741 match result {
8742 QueryResult::Count(n) => assert_eq!(n, 1),
8743 _ => panic!("Expected Count"),
8744 }
8745 }
8746
8747 #[test]
8748 fn parsed_node_list() {
8749 let router = QueryRouter::new();
8750 router.execute("NODE CREATE label1").unwrap();
8751
8752 let result = router.execute_parsed("NODE LIST").unwrap();
8753 assert!(matches!(result, QueryResult::Nodes(_)));
8754 }
8755
8756 #[test]
8757 fn parsed_edge_create() {
8758 let router = QueryRouter::new();
8759 let n1 = match router.execute("NODE CREATE a").unwrap() {
8760 QueryResult::Ids(ids) => ids[0],
8761 _ => panic!("Expected Ids"),
8762 };
8763 let n2 = match router.execute("NODE CREATE b").unwrap() {
8764 QueryResult::Ids(ids) => ids[0],
8765 _ => panic!("Expected Ids"),
8766 };
8767
8768 let result = router
8769 .execute_parsed(&format!(
8770 "EDGE CREATE {} -> {} : knows {{ since: 2020 }}",
8771 n1, n2
8772 ))
8773 .unwrap();
8774 match result {
8775 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
8776 _ => panic!("Expected Ids"),
8777 }
8778 }
8779
8780 #[test]
8781 fn parsed_edge_get() {
8782 let router = QueryRouter::new();
8783 let n1 = match router.execute("NODE CREATE x").unwrap() {
8784 QueryResult::Ids(ids) => ids[0],
8785 _ => panic!("Expected Ids"),
8786 };
8787 let n2 = match router.execute("NODE CREATE y").unwrap() {
8788 QueryResult::Ids(ids) => ids[0],
8789 _ => panic!("Expected Ids"),
8790 };
8791 let edge_id = match router
8792 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
8793 .unwrap()
8794 {
8795 QueryResult::Ids(ids) => ids[0],
8796 _ => panic!("Expected Ids"),
8797 };
8798
8799 let result = router
8800 .execute_parsed(&format!("EDGE GET {}", edge_id))
8801 .unwrap();
8802 match result {
8803 QueryResult::Edges(edges) => assert_eq!(edges.len(), 1),
8804 _ => panic!("Expected Edges"),
8805 }
8806 }
8807
8808 #[test]
8809 fn parsed_edge_list() {
8810 let router = QueryRouter::new();
8811 let result = router.execute_parsed("EDGE LIST").unwrap();
8812 assert!(matches!(result, QueryResult::Edges(_)));
8813 }
8814
8815 #[test]
8816 fn parsed_edge_delete_nonexistent() {
8817 let router = QueryRouter::new();
8818 let result = router.execute_parsed("EDGE DELETE 999999");
8820 assert!(result.is_err());
8821 }
8822
8823 #[test]
8824 fn parsed_edge_delete_success() {
8825 let router = QueryRouter::new();
8826
8827 let a = match router.execute("NODE CREATE TestNode").unwrap() {
8829 QueryResult::Ids(ids) => ids[0],
8830 _ => panic!("Expected Ids"),
8831 };
8832 let b = match router.execute("NODE CREATE TestNode").unwrap() {
8833 QueryResult::Ids(ids) => ids[0],
8834 _ => panic!("Expected Ids"),
8835 };
8836
8837 let edge_id = match router
8838 .execute(&format!(
8839 "EDGE CREATE {} -> {} : test_edge {{ weight: 0.5 }}",
8840 a, b
8841 ))
8842 .unwrap()
8843 {
8844 QueryResult::Ids(ids) => ids[0],
8845 _ => panic!("Expected Ids"),
8846 };
8847
8848 let result = router.execute_parsed(&format!("EDGE DELETE {}", edge_id));
8850 assert!(result.is_ok());
8851 match result.unwrap() {
8852 QueryResult::Count(c) => assert_eq!(c, 1),
8853 _ => panic!("Expected Count result"),
8854 }
8855 }
8856
8857 #[test]
8858 fn parsed_neighbors() {
8859 let router = QueryRouter::new();
8860 let n1 = match router.execute("NODE CREATE start").unwrap() {
8861 QueryResult::Ids(ids) => ids[0],
8862 _ => panic!("Expected Ids"),
8863 };
8864 let n2 = match router.execute("NODE CREATE neighbor").unwrap() {
8865 QueryResult::Ids(ids) => ids[0],
8866 _ => panic!("Expected Ids"),
8867 };
8868 router
8869 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
8870 .unwrap();
8871
8872 let result = router
8873 .execute_parsed(&format!("NEIGHBORS {} OUTGOING", n1))
8874 .unwrap();
8875 match result {
8876 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
8877 _ => panic!("Expected Ids"),
8878 }
8879 }
8880
8881 #[test]
8882 fn parsed_neighbors_incoming() {
8883 let router = QueryRouter::new();
8884 let n1 = match router.execute("NODE CREATE a").unwrap() {
8885 QueryResult::Ids(ids) => ids[0],
8886 _ => panic!("Expected Ids"),
8887 };
8888 let n2 = match router.execute("NODE CREATE b").unwrap() {
8889 QueryResult::Ids(ids) => ids[0],
8890 _ => panic!("Expected Ids"),
8891 };
8892 router
8893 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
8894 .unwrap();
8895
8896 let result = router
8897 .execute_parsed(&format!("NEIGHBORS {} INCOMING", n2))
8898 .unwrap();
8899 match result {
8900 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
8901 _ => panic!("Expected Ids"),
8902 }
8903 }
8904
8905 #[test]
8906 fn parsed_neighbors_both() {
8907 let router = QueryRouter::new();
8908 let n1 = match router.execute("NODE CREATE a").unwrap() {
8909 QueryResult::Ids(ids) => ids[0],
8910 _ => panic!("Expected Ids"),
8911 };
8912 let n2 = match router.execute("NODE CREATE b").unwrap() {
8913 QueryResult::Ids(ids) => ids[0],
8914 _ => panic!("Expected Ids"),
8915 };
8916 router
8917 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
8918 .unwrap();
8919
8920 let result = router
8921 .execute_parsed(&format!("NEIGHBORS {} BOTH", n1))
8922 .unwrap();
8923 assert!(matches!(result, QueryResult::Ids(_)));
8924 }
8925
8926 #[test]
8927 fn parsed_path() {
8928 let router = QueryRouter::new();
8929 let n1 = match router.execute("NODE CREATE source").unwrap() {
8930 QueryResult::Ids(ids) => ids[0],
8931 _ => panic!("Expected Ids"),
8932 };
8933 let n2 = match router.execute("NODE CREATE target").unwrap() {
8934 QueryResult::Ids(ids) => ids[0],
8935 _ => panic!("Expected Ids"),
8936 };
8937 router
8938 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
8939 .unwrap();
8940
8941 let result = router
8942 .execute_parsed(&format!("PATH SHORTEST {} -> {}", n1, n2))
8943 .unwrap();
8944 match result {
8945 QueryResult::Path(path) => assert!(!path.is_empty()),
8946 _ => panic!("Expected Path"),
8947 }
8948 }
8949
8950 #[test]
8951 fn parsed_embed_store() {
8952 let router = QueryRouter::new();
8953 let result = router
8954 .execute_parsed("EMBED STORE 'key1' [1.0, 2.0, 3.0]")
8955 .unwrap();
8956 assert!(matches!(result, QueryResult::Empty));
8957 }
8958
8959 #[test]
8960 fn parsed_embed_get() {
8961 let router = QueryRouter::new();
8962 router.execute("EMBED vec1 [1.0, 2.0, 3.0]").unwrap();
8963
8964 let result = router.execute_parsed("EMBED GET 'vec1'").unwrap();
8965 match result {
8966 QueryResult::Value(s) => assert!(s.contains("1")),
8967 _ => panic!("Expected Value"),
8968 }
8969 }
8970
8971 #[test]
8972 fn parsed_embed_delete() {
8973 let router = QueryRouter::new();
8974 router.execute("EMBED todelete [1.0, 2.0]").unwrap();
8975
8976 let result = router.execute_parsed("EMBED DELETE 'todelete'").unwrap();
8977 match result {
8978 QueryResult::Count(n) => assert_eq!(n, 1),
8979 _ => panic!("Expected Count"),
8980 }
8981 }
8982
8983 #[test]
8984 fn parsed_similar_by_key() {
8985 let router = QueryRouter::new();
8986 router.execute("EMBED item1 [1.0, 0.0, 0.0]").unwrap();
8987 router.execute("EMBED item2 [0.9, 0.1, 0.0]").unwrap();
8988
8989 let result = router.execute_parsed("SIMILAR 'item1' LIMIT 5").unwrap();
8990 match result {
8991 QueryResult::Similar(results) => assert!(!results.is_empty()),
8992 _ => panic!("Expected Similar"),
8993 }
8994 }
8995
8996 #[test]
8997 fn parsed_similar_by_vector() {
8998 let router = QueryRouter::new();
8999 router.execute("EMBED vec1 [1.0, 0.0, 0.0]").unwrap();
9000 router.execute("EMBED vec2 [0.0, 1.0, 0.0]").unwrap();
9001
9002 let result = router
9003 .execute_parsed("SIMILAR [1.0, 0.0, 0.0] LIMIT 5")
9004 .unwrap();
9005 match result {
9006 QueryResult::Similar(results) => assert!(!results.is_empty()),
9007 _ => panic!("Expected Similar"),
9008 }
9009 }
9010
9011 #[test]
9012 fn parsed_similar_with_hnsw() {
9013 let mut router = QueryRouter::new();
9014 router.execute("EMBED a [1.0, 0.0]").unwrap();
9015 router.execute("EMBED b [0.0, 1.0]").unwrap();
9016 router.build_vector_index().unwrap();
9017
9018 let result = router.execute_parsed("SIMILAR 'a' LIMIT 2").unwrap();
9019 match result {
9020 QueryResult::Similar(results) => assert_eq!(results.len(), 2),
9021 _ => panic!("Expected Similar"),
9022 }
9023 }
9024
9025 #[test]
9026 fn parsed_similar_cosine_metric() {
9027 let router = QueryRouter::new();
9028 router.execute("EMBED cos_a [1.0, 0.0]").unwrap();
9029 router.execute("EMBED cos_b [0.0, 1.0]").unwrap();
9030 router.execute("EMBED cos_c [0.707, 0.707]").unwrap();
9031
9032 let result = router
9034 .execute_parsed("SIMILAR [1.0, 0.0] COSINE LIMIT 3")
9035 .unwrap();
9036 match result {
9037 QueryResult::Similar(results) => {
9038 assert_eq!(results.len(), 3);
9039 assert_eq!(results[0].key, "cos_a");
9041 },
9042 _ => panic!("Expected Similar"),
9043 }
9044 }
9045
9046 #[test]
9047 fn parsed_similar_euclidean_metric() {
9048 let router = QueryRouter::new();
9049 router.execute("EMBED euc_a [1.0, 0.0]").unwrap();
9050 router.execute("EMBED euc_b [2.0, 0.0]").unwrap();
9051 router.execute("EMBED euc_c [10.0, 0.0]").unwrap();
9052
9053 let result = router
9055 .execute_parsed("SIMILAR [1.0, 0.0] EUCLIDEAN LIMIT 3")
9056 .unwrap();
9057 match result {
9058 QueryResult::Similar(results) => {
9059 assert_eq!(results.len(), 3);
9060 assert_eq!(results[0].key, "euc_a");
9062 },
9063 _ => panic!("Expected Similar"),
9064 }
9065 }
9066
9067 #[test]
9068 fn parsed_similar_euclidean_zero_query() {
9069 let router = QueryRouter::new();
9070 router.execute("EMBED zero_origin [0.0, 0.0]").unwrap();
9071 router.execute("EMBED zero_unit [1.0, 0.0]").unwrap();
9072 router.execute("EMBED zero_far [10.0, 0.0]").unwrap();
9073
9074 let result = router
9076 .execute_parsed("SIMILAR [0.0, 0.0] EUCLIDEAN LIMIT 3")
9077 .unwrap();
9078 match result {
9079 QueryResult::Similar(results) => {
9080 assert_eq!(
9081 results.len(),
9082 3,
9083 "Should return 3 results for EUCLIDEAN with zero query"
9084 );
9085 assert_eq!(results[0].key, "zero_origin");
9087 assert!((results[0].score - 1.0).abs() < 0.01);
9089 },
9090 _ => panic!("Expected Similar"),
9091 }
9092 }
9093
9094 #[test]
9095 fn parsed_similar_dot_product_metric() {
9096 let router = QueryRouter::new();
9097 router.execute("EMBED dot_a [1.0, 0.0]").unwrap();
9098 router.execute("EMBED dot_b [2.0, 0.0]").unwrap();
9099 router.execute("EMBED dot_c [0.5, 0.0]").unwrap();
9100
9101 let result = router
9103 .execute_parsed("SIMILAR [1.0, 0.0] DOT_PRODUCT LIMIT 3")
9104 .unwrap();
9105 match result {
9106 QueryResult::Similar(results) => {
9107 assert_eq!(results.len(), 3);
9108 assert_eq!(results[0].key, "dot_b");
9110 },
9111 _ => panic!("Expected Similar"),
9112 }
9113 }
9114
9115 #[test]
9116 fn parsed_similar_hnsw_falls_back_for_non_cosine() {
9117 let mut router = QueryRouter::new();
9118 router.execute("EMBED hnsw_a [1.0, 0.0]").unwrap();
9119 router.execute("EMBED hnsw_b [2.0, 0.0]").unwrap();
9120 router.build_vector_index().unwrap();
9121
9122 let result = router
9124 .execute_parsed("SIMILAR [1.0, 0.0] EUCLIDEAN LIMIT 2")
9125 .unwrap();
9126 match result {
9127 QueryResult::Similar(results) => {
9128 assert_eq!(results.len(), 2);
9129 assert_eq!(results[0].key, "hnsw_a");
9131 },
9132 _ => panic!("Expected Similar"),
9133 }
9134 }
9135
9136 #[test]
9137 fn parsed_find_nodes() {
9138 let router = QueryRouter::new();
9139 let result = router.execute_parsed("FIND NODE person").unwrap();
9140 match result {
9141 QueryResult::Unified(unified) => {
9142 assert!(unified.description.contains("node"));
9143 assert!(unified.description.contains("'person'"));
9144 },
9145 _ => panic!("Expected Unified"),
9146 }
9147 }
9148
9149 #[test]
9150 fn parsed_find_edges() {
9151 let router = QueryRouter::new();
9152 let result = router.execute_parsed("FIND EDGE knows").unwrap();
9153 match result {
9154 QueryResult::Unified(unified) => {
9155 assert!(unified.description.contains("edge"));
9156 assert!(unified.description.contains("'knows'"));
9157 },
9158 _ => panic!("Expected Unified"),
9159 }
9160 }
9161
9162 #[test]
9163 fn parsed_find_with_where() {
9164 let router = QueryRouter::new();
9165 router
9167 .execute_parsed("NODE CREATE person { name: 'Alice', age: 25 }")
9168 .unwrap();
9169 let result = router.execute_parsed("FIND NODE WHERE age > 18").unwrap();
9170 match result {
9171 QueryResult::Unified(unified) => {
9172 assert!(unified.description.contains("node"));
9174 },
9175 _ => panic!("Expected Unified"),
9176 }
9177 }
9178
9179 #[test]
9180 fn parsed_find_nodes_with_where_eq() {
9181 let router = QueryRouter::new();
9182 router
9183 .execute_parsed("NODE CREATE user { name: 'Bob', status: 'active' }")
9184 .unwrap();
9185 router
9186 .execute_parsed("NODE CREATE user { name: 'Eve', status: 'inactive' }")
9187 .unwrap();
9188
9189 let result = router
9190 .execute_parsed("FIND NODE WHERE status = 'active'")
9191 .unwrap();
9192 match result {
9193 QueryResult::Unified(u) => {
9194 assert!(u
9196 .items
9197 .iter()
9198 .any(|item| item.data.get("name") == Some(&"Bob".to_string())));
9199 },
9200 _ => panic!("Expected Unified"),
9201 }
9202 }
9203
9204 #[test]
9205 fn parsed_find_nodes_with_where_gt() {
9206 let router = QueryRouter::new();
9207 router
9208 .execute_parsed("NODE CREATE person { name: 'Young', age: 15 }")
9209 .unwrap();
9210 router
9211 .execute_parsed("NODE CREATE person { name: 'Adult', age: 30 }")
9212 .unwrap();
9213
9214 let result = router.execute_parsed("FIND NODE WHERE age > 20").unwrap();
9215 match result {
9216 QueryResult::Unified(u) => {
9217 assert!(!u.items.is_empty());
9219 assert!(u
9220 .items
9221 .iter()
9222 .any(|item| item.data.get("name") == Some(&"Adult".to_string())));
9223 },
9224 _ => panic!("Expected Unified"),
9225 }
9226 }
9227
9228 #[test]
9229 fn parsed_find_nodes_with_where_and() {
9230 let router = QueryRouter::new();
9231 router
9232 .execute_parsed("NODE CREATE user { name: 'Alice', age: 25, role: 'admin' }")
9233 .unwrap();
9234 router
9235 .execute_parsed("NODE CREATE user { name: 'Bob', age: 35, role: 'user' }")
9236 .unwrap();
9237
9238 let result = router
9239 .execute_parsed("FIND NODE WHERE age > 20 AND role = 'admin'")
9240 .unwrap();
9241 match result {
9242 QueryResult::Unified(u) => {
9243 assert!(u
9245 .items
9246 .iter()
9247 .any(|item| item.data.get("name") == Some(&"Alice".to_string())));
9248 },
9249 _ => panic!("Expected Unified"),
9250 }
9251 }
9252
9253 #[test]
9254 fn parsed_find_nodes_with_where_lt() {
9255 let router = QueryRouter::new();
9256 router
9257 .execute_parsed("NODE CREATE person { name: 'Young', age: 15 }")
9258 .unwrap();
9259 router
9260 .execute_parsed("NODE CREATE person { name: 'Adult', age: 30 }")
9261 .unwrap();
9262
9263 let result = router.execute_parsed("FIND NODE WHERE age < 20").unwrap();
9264 match result {
9265 QueryResult::Unified(u) => {
9266 assert!(u
9268 .items
9269 .iter()
9270 .any(|item| item.data.get("name") == Some(&"Young".to_string())));
9271 assert!(!u
9273 .items
9274 .iter()
9275 .any(|item| item.data.get("name") == Some(&"Adult".to_string())));
9276 },
9277 _ => panic!("Expected Unified"),
9278 }
9279 }
9280
9281 #[test]
9282 fn parsed_find_nodes_with_where_le() {
9283 let router = QueryRouter::new();
9284 router
9285 .execute_parsed("NODE CREATE person { name: 'Young', age: 20 }")
9286 .unwrap();
9287 router
9288 .execute_parsed("NODE CREATE person { name: 'Adult', age: 30 }")
9289 .unwrap();
9290
9291 let result = router.execute_parsed("FIND NODE WHERE age <= 20").unwrap();
9292 match result {
9293 QueryResult::Unified(u) => {
9294 assert!(u
9296 .items
9297 .iter()
9298 .any(|item| item.data.get("name") == Some(&"Young".to_string())));
9299 },
9300 _ => panic!("Expected Unified"),
9301 }
9302 }
9303
9304 #[test]
9305 fn parsed_find_nodes_with_where_ge() {
9306 let router = QueryRouter::new();
9307 router
9308 .execute_parsed("NODE CREATE person { name: 'Young', age: 15 }")
9309 .unwrap();
9310 router
9311 .execute_parsed("NODE CREATE person { name: 'Adult', age: 30 }")
9312 .unwrap();
9313
9314 let result = router.execute_parsed("FIND NODE WHERE age >= 30").unwrap();
9315 match result {
9316 QueryResult::Unified(u) => {
9317 assert!(u
9319 .items
9320 .iter()
9321 .any(|item| item.data.get("name") == Some(&"Adult".to_string())));
9322 },
9323 _ => panic!("Expected Unified"),
9324 }
9325 }
9326
9327 #[test]
9328 fn parsed_find_nodes_with_where_or() {
9329 let router = QueryRouter::new();
9330 router
9331 .execute_parsed("NODE CREATE user { name: 'Alice', role: 'admin' }")
9332 .unwrap();
9333 router
9334 .execute_parsed("NODE CREATE user { name: 'Bob', role: 'guest' }")
9335 .unwrap();
9336 router
9337 .execute_parsed("NODE CREATE user { name: 'Eve', role: 'user' }")
9338 .unwrap();
9339
9340 let result = router
9341 .execute_parsed("FIND NODE WHERE role = 'admin' OR role = 'guest'")
9342 .unwrap();
9343 match result {
9344 QueryResult::Unified(u) => {
9345 assert!(u
9347 .items
9348 .iter()
9349 .any(|item| item.data.get("name") == Some(&"Alice".to_string())));
9350 assert!(u
9351 .items
9352 .iter()
9353 .any(|item| item.data.get("name") == Some(&"Bob".to_string())));
9354 },
9355 _ => panic!("Expected Unified"),
9356 }
9357 }
9358
9359 #[test]
9360 fn parsed_find_nodes_with_id_condition() {
9361 let router = QueryRouter::new();
9362 router
9363 .execute_parsed("NODE CREATE user { name: 'First' }")
9364 .unwrap();
9365 router
9366 .execute_parsed("NODE CREATE user { name: 'Second' }")
9367 .unwrap();
9368
9369 let result = router.execute_parsed("FIND NODE WHERE id = 1").unwrap();
9370 match result {
9371 QueryResult::Unified(u) => {
9372 assert_eq!(u.items.len(), 1);
9373 assert!(u
9374 .items
9375 .iter()
9376 .any(|item| item.data.get("name") == Some(&"First".to_string())));
9377 },
9378 _ => panic!("Expected Unified"),
9379 }
9380 }
9381
9382 #[test]
9383 fn parsed_find_nodes_condition_no_match() {
9384 let router = QueryRouter::new();
9385 router
9386 .execute_parsed("NODE CREATE user { name: 'Test', age: 25 }")
9387 .unwrap();
9388
9389 let result = router
9391 .execute_parsed("FIND NODE WHERE nonexistent = 'value'")
9392 .unwrap();
9393 match result {
9394 QueryResult::Unified(u) => {
9395 assert!(u.items.is_empty());
9396 },
9397 _ => panic!("Expected Unified"),
9398 }
9399 }
9400
9401 #[test]
9402 fn vault_accessor() {
9403 let router = QueryRouter::new();
9404 assert!(router.vault().is_none());
9406 }
9407
9408 #[test]
9409 fn error_from_cache_error() {
9410 let cache_err = tensor_cache::CacheError::NotFound("test".to_string());
9411 let router_err: RouterError = cache_err.into();
9412 assert!(matches!(router_err, RouterError::CacheError(_)));
9413 }
9414
9415 #[test]
9416 fn parsed_find_edge_by_type() {
9417 let router = QueryRouter::new();
9418 router
9419 .execute_parsed("NODE CREATE x { name: 'X' }")
9420 .unwrap();
9421 router
9422 .execute_parsed("NODE CREATE y { name: 'Y' }")
9423 .unwrap();
9424 router
9425 .execute_parsed("EDGE CREATE 1 -> 2 : special_type")
9426 .unwrap();
9427
9428 let result = router
9429 .execute_parsed("FIND EDGE WHERE edge_type = 'special_type'")
9430 .unwrap();
9431 match result {
9432 QueryResult::Unified(u) => {
9433 assert!(u.description.contains("edge"));
9434 },
9435 _ => panic!("Expected Unified"),
9436 }
9437 }
9438
9439 #[test]
9440 fn blobs_similar_to_key() {
9441 let mut router = QueryRouter::new();
9442 router.init_blob().unwrap();
9443
9444 router
9446 .execute_parsed("EMBED STORE 'blob_a' [1.0, 0.0, 0.0, 0.0]")
9447 .unwrap();
9448 router
9449 .execute_parsed("EMBED STORE 'blob_b' [0.9, 0.1, 0.0, 0.0]")
9450 .unwrap();
9451 router
9452 .execute_parsed("EMBED STORE 'blob_c' [0.0, 1.0, 0.0, 0.0]")
9453 .unwrap();
9454
9455 let result = router.execute_parsed("BLOBS SIMILAR TO 'blob_a' LIMIT 2");
9457 assert!(result.is_ok() || result.is_err());
9459 }
9460
9461 #[test]
9462 fn blob_put_with_full_options() {
9463 let mut router = QueryRouter::new();
9464 router.init_blob().unwrap();
9465
9466 let result = router.execute_parsed(
9468 "BLOB PUT 'test_file.json' DATA '{\"key\":\"value\"}' TYPE 'application/json' BY 'testuser'",
9469 );
9470 assert!(result.is_ok() || result.is_err());
9472 }
9473
9474 #[test]
9475 fn parsed_find_edges_with_edge_props() {
9476 let router = QueryRouter::new();
9477 router
9478 .execute_parsed("NODE CREATE person { name: 'X' }")
9479 .unwrap();
9480 router
9481 .execute_parsed("NODE CREATE person { name: 'Y' }")
9482 .unwrap();
9483 router
9484 .execute_parsed("EDGE CREATE 1 -> 2 : works_at { department: 'engineering', level: 3 }")
9485 .unwrap();
9486
9487 let result = router.execute_parsed("FIND EDGE works_at").unwrap();
9489 match result {
9490 QueryResult::Unified(u) => {
9491 assert!(u.description.contains("edge"));
9492 assert!(!u.items.is_empty());
9493 },
9494 _ => panic!("Expected Unified"),
9495 }
9496 }
9497
9498 #[test]
9499 fn parsed_find_nodes_scan_with_properties() {
9500 let router = QueryRouter::new();
9501 router
9503 .execute_parsed("NODE CREATE item { name: 'Item1', price: 100, active: true }")
9504 .unwrap();
9505 router
9506 .execute_parsed("NODE CREATE item { name: 'Item2', price: 200, active: false }")
9507 .unwrap();
9508
9509 let result = router.execute_parsed("FIND NODE item").unwrap();
9511 match result {
9512 QueryResult::Unified(u) => {
9513 assert!(u.items.len() >= 2);
9515 assert!(u.items.iter().any(|item| item.data.contains_key("name")));
9517 },
9518 _ => panic!("Expected Unified"),
9519 }
9520 }
9521
9522 #[test]
9523 fn parsed_find_edges_with_where() {
9524 let router = QueryRouter::new();
9525 router
9527 .execute_parsed("NODE CREATE person { name: 'A' }")
9528 .unwrap();
9529 router
9530 .execute_parsed("NODE CREATE person { name: 'B' }")
9531 .unwrap();
9532 router
9534 .execute_parsed("EDGE CREATE 1 -> 2 : friend { strength: 10 }")
9535 .unwrap();
9536
9537 let result = router
9538 .execute_parsed("FIND EDGE WHERE strength > 5")
9539 .unwrap();
9540 match result {
9541 QueryResult::Unified(u) => {
9542 assert!(u.description.contains("edge"));
9543 },
9544 _ => panic!("Expected Unified"),
9545 }
9546 }
9547
9548 #[test]
9549 fn parsed_find_edges_with_type_eq() {
9550 let router = QueryRouter::new();
9551 router
9552 .execute_parsed("NODE CREATE x { name: 'X' }")
9553 .unwrap();
9554 router
9555 .execute_parsed("NODE CREATE y { name: 'Y' }")
9556 .unwrap();
9557 router
9558 .execute_parsed("EDGE CREATE 1 -> 2 : knows { since: 2020 }")
9559 .unwrap();
9560 router
9561 .execute_parsed("EDGE CREATE 1 -> 2 : works { since: 2021 }")
9562 .unwrap();
9563
9564 let result = router.execute_parsed("FIND EDGE knows").unwrap();
9566 match result {
9567 QueryResult::Unified(u) => {
9568 assert!(!u.items.is_empty());
9569 },
9570 _ => panic!("Expected Unified"),
9571 }
9572 }
9573
9574 #[test]
9575 fn parsed_find_edges_with_and_condition() {
9576 let router = QueryRouter::new();
9577 router
9578 .execute_parsed("NODE CREATE a { name: 'A' }")
9579 .unwrap();
9580 router
9581 .execute_parsed("NODE CREATE b { name: 'B' }")
9582 .unwrap();
9583 router
9584 .execute_parsed("EDGE CREATE 1 -> 2 : rel { weight: 50, active: true }")
9585 .unwrap();
9586 router
9587 .execute_parsed("EDGE CREATE 1 -> 2 : rel { weight: 10, active: false }")
9588 .unwrap();
9589
9590 let result = router
9591 .execute_parsed("FIND EDGE WHERE weight > 20 AND active = true")
9592 .unwrap();
9593 match result {
9594 QueryResult::Unified(u) => {
9595 assert!(u.description.contains("edge"));
9597 },
9598 _ => panic!("Expected Unified"),
9599 }
9600 }
9601
9602 #[test]
9603 fn parsed_find_edges_with_or_condition() {
9604 let router = QueryRouter::new();
9605 router
9606 .execute_parsed("NODE CREATE a { name: 'A' }")
9607 .unwrap();
9608 router
9609 .execute_parsed("NODE CREATE b { name: 'B' }")
9610 .unwrap();
9611 router
9612 .execute_parsed("EDGE CREATE 1 -> 2 : rel { status: 'active' }")
9613 .unwrap();
9614 router
9615 .execute_parsed("EDGE CREATE 1 -> 2 : rel { status: 'pending' }")
9616 .unwrap();
9617 router
9618 .execute_parsed("EDGE CREATE 1 -> 2 : rel { status: 'archived' }")
9619 .unwrap();
9620
9621 let result = router
9622 .execute_parsed("FIND EDGE WHERE status = 'active' OR status = 'pending'")
9623 .unwrap();
9624 match result {
9625 QueryResult::Unified(u) => {
9626 assert!(u.description.contains("edge"));
9627 },
9628 _ => panic!("Expected Unified"),
9629 }
9630 }
9631
9632 #[test]
9633 fn parsed_find_nodes_with_ne_condition() {
9634 let router = QueryRouter::new();
9635 router
9636 .execute_parsed("NODE CREATE user { name: 'Admin', role: 'admin' }")
9637 .unwrap();
9638 router
9639 .execute_parsed("NODE CREATE user { name: 'User', role: 'user' }")
9640 .unwrap();
9641
9642 let result = router
9644 .execute_parsed("FIND NODE WHERE role != 'admin'")
9645 .unwrap();
9646 match result {
9647 QueryResult::Unified(u) => {
9648 assert!(u.description.contains("node"));
9650 assert!(u
9651 .items
9652 .iter()
9653 .any(|item| item.data.get("name") == Some(&"User".to_string())));
9654 },
9655 _ => panic!("Expected Unified"),
9656 }
9657 }
9658
9659 #[test]
9660 fn parsed_find_edges_with_ne_condition() {
9661 let router = QueryRouter::new();
9662 router
9663 .execute_parsed("NODE CREATE n { name: 'N1' }")
9664 .unwrap();
9665 router
9666 .execute_parsed("NODE CREATE n { name: 'N2' }")
9667 .unwrap();
9668 router
9669 .execute_parsed("EDGE CREATE 1 -> 2 : rel { status: 'complete' }")
9670 .unwrap();
9671 router
9672 .execute_parsed("EDGE CREATE 1 -> 2 : rel { status: 'pending' }")
9673 .unwrap();
9674
9675 let result = router
9676 .execute_parsed("FIND EDGE WHERE status != 'complete'")
9677 .unwrap();
9678 match result {
9679 QueryResult::Unified(u) => {
9680 assert!(u.description.contains("edge"));
9681 },
9682 _ => panic!("Expected Unified"),
9683 }
9684 }
9685
9686 #[test]
9687 fn parsed_find_edges_with_lt_condition() {
9688 let router = QueryRouter::new();
9689 router
9690 .execute_parsed("NODE CREATE n { name: 'N1' }")
9691 .unwrap();
9692 router
9693 .execute_parsed("NODE CREATE n { name: 'N2' }")
9694 .unwrap();
9695 router
9696 .execute_parsed("EDGE CREATE 1 -> 2 : rel { weight: 100 }")
9697 .unwrap();
9698 router
9699 .execute_parsed("EDGE CREATE 1 -> 2 : rel { weight: 10 }")
9700 .unwrap();
9701
9702 let result = router
9703 .execute_parsed("FIND EDGE WHERE weight < 50")
9704 .unwrap();
9705 match result {
9706 QueryResult::Unified(u) => {
9707 assert!(u.description.contains("edge"));
9708 },
9709 _ => panic!("Expected Unified"),
9710 }
9711 }
9712
9713 #[test]
9714 fn parsed_find_edges_with_ge_condition() {
9715 let router = QueryRouter::new();
9716 router
9717 .execute_parsed("NODE CREATE n { name: 'N1' }")
9718 .unwrap();
9719 router
9720 .execute_parsed("NODE CREATE n { name: 'N2' }")
9721 .unwrap();
9722 router
9723 .execute_parsed("EDGE CREATE 1 -> 2 : rel { priority: 5 }")
9724 .unwrap();
9725 router
9726 .execute_parsed("EDGE CREATE 1 -> 2 : rel { priority: 10 }")
9727 .unwrap();
9728
9729 let result = router
9730 .execute_parsed("FIND EDGE WHERE priority >= 5")
9731 .unwrap();
9732 match result {
9733 QueryResult::Unified(u) => {
9734 assert!(u.description.contains("edge"));
9735 },
9736 _ => panic!("Expected Unified"),
9737 }
9738 }
9739
9740 #[test]
9741 fn parsed_find_edges_with_le_condition() {
9742 let router = QueryRouter::new();
9743 router
9744 .execute_parsed("NODE CREATE n { name: 'N1' }")
9745 .unwrap();
9746 router
9747 .execute_parsed("NODE CREATE n { name: 'N2' }")
9748 .unwrap();
9749 router
9750 .execute_parsed("EDGE CREATE 1 -> 2 : rel { score: 3 }")
9751 .unwrap();
9752 router
9753 .execute_parsed("EDGE CREATE 1 -> 2 : rel { score: 8 }")
9754 .unwrap();
9755
9756 let result = router.execute_parsed("FIND EDGE WHERE score <= 5").unwrap();
9757 match result {
9758 QueryResult::Unified(u) => {
9759 assert!(u.description.contains("edge"));
9760 },
9761 _ => panic!("Expected Unified"),
9762 }
9763 }
9764
9765 #[test]
9766 fn parsed_find_with_limit_verified() {
9767 let router = QueryRouter::new();
9768 for i in 0..10 {
9770 router
9771 .execute_parsed(&format!("NODE CREATE item {{ idx: {} }}", i))
9772 .unwrap();
9773 }
9774
9775 let result = router.execute_parsed("FIND NODE item LIMIT 3").unwrap();
9776 match result {
9777 QueryResult::Unified(u) => {
9778 assert!(u.items.len() <= 3);
9779 },
9780 _ => panic!("Expected Unified"),
9781 }
9782 }
9783
9784 #[test]
9785 fn parsed_node_list_with_data() {
9786 let router = QueryRouter::new();
9787 router
9788 .execute_parsed("NODE CREATE employee { name: 'John', dept: 'sales' }")
9789 .unwrap();
9790 router
9791 .execute_parsed("NODE CREATE employee { name: 'Jane', dept: 'eng' }")
9792 .unwrap();
9793 router
9794 .execute_parsed("NODE CREATE manager { name: 'Boss', level: 5 }")
9795 .unwrap();
9796
9797 let result = router.execute_parsed("NODE LIST employee").unwrap();
9799 match result {
9800 QueryResult::Nodes(nodes) => {
9801 assert_eq!(nodes.len(), 2); },
9803 _ => panic!("Expected Nodes"),
9804 }
9805
9806 let all = router.execute_parsed("NODE LIST").unwrap();
9808 match all {
9809 QueryResult::Nodes(nodes) => {
9810 assert_eq!(nodes.len(), 3); },
9812 _ => panic!("Expected Nodes"),
9813 }
9814 }
9815
9816 #[test]
9817 fn parsed_edge_list_with_data() {
9818 let router = QueryRouter::new();
9819 router
9821 .execute_parsed("NODE CREATE person { name: 'X' }")
9822 .unwrap();
9823 router
9824 .execute_parsed("NODE CREATE person { name: 'Y' }")
9825 .unwrap();
9826 router
9827 .execute_parsed("NODE CREATE person { name: 'Z' }")
9828 .unwrap();
9829 router
9831 .execute_parsed("EDGE CREATE 1 -> 2 : friend")
9832 .unwrap();
9833 router
9834 .execute_parsed("EDGE CREATE 2 -> 3 : colleague")
9835 .unwrap();
9836 router
9837 .execute_parsed("EDGE CREATE 1 -> 3 : friend")
9838 .unwrap();
9839
9840 let result = router.execute_parsed("EDGE LIST friend").unwrap();
9842 match result {
9843 QueryResult::Edges(edges) => {
9844 assert_eq!(edges.len(), 2); },
9846 _ => panic!("Expected Edges"),
9847 }
9848
9849 let all = router.execute_parsed("EDGE LIST").unwrap();
9851 match all {
9852 QueryResult::Edges(edges) => {
9853 assert_eq!(edges.len(), 3); },
9855 _ => panic!("Expected Edges"),
9856 }
9857 }
9858
9859 #[test]
9860 fn parsed_empty_statement() {
9861 let router = QueryRouter::new();
9862 let result = router.execute_parsed(";").unwrap();
9863 assert!(matches!(result, QueryResult::Empty));
9864 }
9865
9866 #[test]
9867 fn parsed_parse_error() {
9868 let router = QueryRouter::new();
9869 let result = router.execute_parsed("INVALID SYNTAX HERE @#$");
9870 assert!(result.is_err());
9871 if let Err(RouterError::ParseError(msg)) = result {
9872 assert!(!msg.is_empty());
9873 } else {
9874 panic!("Expected ParseError");
9875 }
9876 }
9877
9878 #[test]
9879 fn parsed_select_missing_from() {
9880 let router = QueryRouter::new();
9881 let result = router.execute_parsed("SELECT *");
9882 assert!(result.is_err());
9883 }
9884
9885 #[test]
9886 fn parsed_insert_select_basic() {
9887 let router = QueryRouter::new();
9888 router
9889 .execute_parsed("CREATE TABLE src (id INT, name TEXT)")
9890 .unwrap();
9891 router
9892 .execute_parsed("CREATE TABLE dst (id INT, name TEXT)")
9893 .unwrap();
9894
9895 router
9897 .execute_parsed("INSERT INTO src VALUES (1, 'Alice')")
9898 .unwrap();
9899 router
9900 .execute_parsed("INSERT INTO src VALUES (2, 'Bob')")
9901 .unwrap();
9902
9903 let result = router.execute_parsed("INSERT INTO dst SELECT * FROM src");
9905 assert!(result.is_ok());
9906
9907 let rows = router.execute_parsed("SELECT * FROM dst").unwrap();
9909 match rows {
9910 QueryResult::Rows(r) => {
9911 assert_eq!(r.len(), 2);
9912 },
9913 _ => panic!("expected Rows"),
9914 }
9915 }
9916
9917 #[test]
9918 fn parsed_condition_operators() {
9919 let router = QueryRouter::new();
9920 router.execute("CREATE TABLE vals (id int, x int)").unwrap();
9921 router
9922 .execute("INSERT INTO vals (id, x) VALUES (1, 10)")
9923 .unwrap();
9924 router
9925 .execute("INSERT INTO vals (id, x) VALUES (2, 20)")
9926 .unwrap();
9927 router
9928 .execute("INSERT INTO vals (id, x) VALUES (3, 30)")
9929 .unwrap();
9930
9931 let eq = router
9932 .execute_parsed("SELECT * FROM vals WHERE x = 20")
9933 .unwrap();
9934 assert!(matches!(eq, QueryResult::Rows(r) if r.len() == 1));
9935
9936 let ne = router
9937 .execute_parsed("SELECT * FROM vals WHERE x != 20")
9938 .unwrap();
9939 assert!(matches!(ne, QueryResult::Rows(r) if r.len() == 2));
9940
9941 let lt = router
9942 .execute_parsed("SELECT * FROM vals WHERE x < 20")
9943 .unwrap();
9944 assert!(matches!(lt, QueryResult::Rows(r) if r.len() == 1));
9945
9946 let le = router
9947 .execute_parsed("SELECT * FROM vals WHERE x <= 20")
9948 .unwrap();
9949 assert!(matches!(le, QueryResult::Rows(r) if r.len() == 2));
9950
9951 let gt = router
9952 .execute_parsed("SELECT * FROM vals WHERE x > 20")
9953 .unwrap();
9954 assert!(matches!(gt, QueryResult::Rows(r) if r.len() == 1));
9955
9956 let ge = router
9957 .execute_parsed("SELECT * FROM vals WHERE x >= 20")
9958 .unwrap();
9959 assert!(matches!(ge, QueryResult::Rows(r) if r.len() == 2));
9960 }
9961
9962 #[test]
9963 fn parsed_condition_and_or() {
9964 let router = QueryRouter::new();
9965 router.execute("CREATE TABLE multi (a int, b int)").unwrap();
9966 router
9967 .execute("INSERT INTO multi (a, b) VALUES (1, 1)")
9968 .unwrap();
9969 router
9970 .execute("INSERT INTO multi (a, b) VALUES (1, 2)")
9971 .unwrap();
9972 router
9973 .execute("INSERT INTO multi (a, b) VALUES (2, 1)")
9974 .unwrap();
9975
9976 let and_result = router
9977 .execute_parsed("SELECT * FROM multi WHERE a = 1 AND b = 1")
9978 .unwrap();
9979 assert!(matches!(and_result, QueryResult::Rows(r) if r.len() == 1));
9980
9981 let or_result = router
9982 .execute_parsed("SELECT * FROM multi WHERE a = 2 OR b = 2")
9983 .unwrap();
9984 assert!(matches!(or_result, QueryResult::Rows(r) if r.len() == 2));
9985 }
9986
9987 #[test]
9988 fn parsed_data_types() {
9989 let router = QueryRouter::new();
9990 router
9991 .execute_parsed(
9992 "CREATE TABLE types (
9993 i INT,
9994 bi BIGINT,
9995 si SMALLINT,
9996 f FLOAT,
9997 d DOUBLE,
9998 r REAL,
9999 dec DECIMAL(10, 2),
10000 num NUMERIC(5),
10001 vc VARCHAR(255),
10002 c CHAR(10),
10003 t TEXT,
10004 b BOOLEAN
10005 )",
10006 )
10007 .unwrap();
10008 }
10009
10010 #[test]
10011 fn parsed_expr_to_value_types() {
10012 let router = QueryRouter::new();
10013 router
10014 .execute("CREATE TABLE vals (n int, f double, s string, b bool)")
10015 .unwrap();
10016
10017 router
10019 .execute_parsed("INSERT INTO vals (n, f, s, b) VALUES (42, 3.14, 'hello', true)")
10020 .unwrap();
10021 router
10022 .execute_parsed("INSERT INTO vals (n, f, s, b) VALUES (0, 0.0, 'world', false)")
10023 .unwrap();
10024
10025 let result = router.execute("SELECT * FROM vals").unwrap();
10026 match result {
10027 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
10028 _ => panic!("Expected Rows"),
10029 }
10030 }
10031
10032 #[test]
10033 fn parsed_neighbors_with_edge_type() {
10034 let router = QueryRouter::new();
10035 let n1 = match router.execute("NODE CREATE a").unwrap() {
10036 QueryResult::Ids(ids) => ids[0],
10037 _ => panic!("Expected Ids"),
10038 };
10039 let n2 = match router.execute("NODE CREATE b").unwrap() {
10040 QueryResult::Ids(ids) => ids[0],
10041 _ => panic!("Expected Ids"),
10042 };
10043 router
10044 .execute(&format!("EDGE CREATE {} -> {} : knows", n1, n2))
10045 .unwrap();
10046
10047 let result = router
10048 .execute_parsed(&format!("NEIGHBORS {} OUTGOING : knows", n1))
10049 .unwrap();
10050 assert!(matches!(result, QueryResult::Ids(_)));
10051 }
10052
10053 #[test]
10054 fn parsed_find_with_limit() {
10055 let router = QueryRouter::new();
10056 let result = router.execute_parsed("FIND NODE person LIMIT 5").unwrap();
10057 assert!(matches!(result, QueryResult::Unified(_)));
10058 }
10059
10060 #[test]
10061 fn parsed_insert_null_value() {
10062 let router = QueryRouter::new();
10063 router
10065 .execute_parsed("CREATE TABLE ntest (id INT NOT NULL, val TEXT)")
10066 .unwrap();
10067 router
10068 .execute_parsed("INSERT INTO ntest (id, val) VALUES (1, NULL)")
10069 .unwrap();
10070 }
10071
10072 #[test]
10073 fn parsed_node_create_with_properties() {
10074 let router = QueryRouter::new();
10075 let result = router
10077 .execute_parsed(
10078 "NODE CREATE person { name: 'John', age: 30, score: 95.5, active: true }",
10079 )
10080 .unwrap();
10081 assert!(matches!(result, QueryResult::Ids(_)));
10082 }
10083
10084 #[test]
10085 fn parsed_path_not_found() {
10086 let router = QueryRouter::new();
10087 let n1 = match router.execute("NODE CREATE a").unwrap() {
10088 QueryResult::Ids(ids) => ids[0],
10089 _ => panic!("Expected Ids"),
10090 };
10091 let n2 = match router.execute("NODE CREATE b").unwrap() {
10092 QueryResult::Ids(ids) => ids[0],
10093 _ => panic!("Expected Ids"),
10094 };
10095 let result = router
10097 .execute_parsed(&format!("PATH SHORTEST {} -> {}", n1, n2))
10098 .unwrap();
10099 match result {
10100 QueryResult::Path(path) => assert!(path.is_empty()),
10101 _ => panic!("Expected Path"),
10102 }
10103 }
10104
10105 #[test]
10106 fn parsed_select_qualified_column() {
10107 let router = QueryRouter::new();
10108 router.execute("CREATE TABLE t (x int)").unwrap();
10109 router.execute("INSERT INTO t (x) VALUES (1)").unwrap();
10110
10111 let result = router.execute_parsed("SELECT t.x FROM t").unwrap();
10113 assert!(matches!(result, QueryResult::Rows(_)));
10114 }
10115
10116 #[test]
10117 fn parsed_insert_with_ident_value() {
10118 let router = QueryRouter::new();
10119 router.execute("CREATE TABLE t (name string)").unwrap();
10120
10121 let result = router.execute_parsed("INSERT INTO t (name) VALUES (someident)");
10123 assert!(result.is_ok() || result.is_err());
10125 }
10126
10127 #[test]
10128 fn parsed_similar_with_limit_expr() {
10129 let router = QueryRouter::new();
10130 router.execute("EMBED v1 [1.0, 0.0]").unwrap();
10131 router.execute("EMBED v2 [0.0, 1.0]").unwrap();
10132
10133 let result = router.execute_parsed("SIMILAR 'v1' LIMIT 10").unwrap();
10135 assert!(matches!(result, QueryResult::Similar(_)));
10136 }
10137
10138 #[test]
10139 fn parsed_embed_store_with_list() {
10140 let router = QueryRouter::new();
10141 router
10143 .execute_parsed("EMBED STORE 'stored_vec' [1.0, 2.0, 3.0]")
10144 .unwrap();
10145
10146 let result = router.execute_parsed("EMBED GET 'stored_vec'").unwrap();
10148 assert!(matches!(result, QueryResult::Value(_)));
10149 }
10150
10151 #[test]
10152 fn parsed_empty_command() {
10153 let router = QueryRouter::new();
10154 let result = router.execute_parsed("");
10156 assert!(result.is_ok() || result.is_err());
10158 }
10159
10160 #[test]
10161 fn parsed_whitespace_only() {
10162 let router = QueryRouter::new();
10163 let result = router.execute_parsed(" ");
10164 assert!(result.is_ok() || result.is_err());
10166 }
10167
10168 #[test]
10169 fn parsed_create_index_empty_columns() {
10170 let router = QueryRouter::new();
10171 router.execute("CREATE TABLE t (x int)").unwrap();
10172 let result = router.execute_parsed("CREATE INDEX idx ON t (x)");
10174 assert!(result.is_ok());
10175 }
10176
10177 #[test]
10178 fn parsed_find_path_pattern() {
10179 let router = QueryRouter::new();
10180 let result = router.execute_parsed("FIND a -[e]-> b");
10182 assert!(result.is_ok() || result.is_err());
10184 }
10185
10186 #[test]
10187 fn parsed_edge_create_with_type_and_props() {
10188 let router = QueryRouter::new();
10189 let n1 = match router.execute("NODE CREATE a").unwrap() {
10190 QueryResult::Ids(ids) => ids[0],
10191 _ => panic!("Expected Ids"),
10192 };
10193 let n2 = match router.execute("NODE CREATE b").unwrap() {
10194 QueryResult::Ids(ids) => ids[0],
10195 _ => panic!("Expected Ids"),
10196 };
10197
10198 let result = router
10200 .execute_parsed(&format!(
10201 "EDGE CREATE {} -> {} : friend {{ since: 2020 }}",
10202 n1, n2
10203 ))
10204 .unwrap();
10205 assert!(matches!(result, QueryResult::Ids(_)));
10206 }
10207
10208 #[test]
10209 fn parsed_node_create_null_property() {
10210 let router = QueryRouter::new();
10211 let result = router
10213 .execute_parsed("NODE CREATE test { val: NULL }")
10214 .unwrap();
10215 assert!(matches!(result, QueryResult::Ids(_)));
10216 }
10217
10218 #[test]
10219 fn parsed_node_create_bool_property() {
10220 let router = QueryRouter::new();
10221 let result = router
10222 .execute_parsed("NODE CREATE test { active: false }")
10223 .unwrap();
10224 assert!(matches!(result, QueryResult::Ids(_)));
10225 }
10226
10227 #[test]
10228 fn parsed_node_create_float_property() {
10229 let router = QueryRouter::new();
10230 let result = router
10231 .execute_parsed("NODE CREATE test { score: 3.14 }")
10232 .unwrap();
10233 assert!(matches!(result, QueryResult::Ids(_)));
10234 }
10235
10236 #[test]
10237 fn parsed_embed_with_int_values() {
10238 let router = QueryRouter::new();
10239 router
10241 .execute_parsed("EMBED STORE 'intvec' [1, 2, 3]")
10242 .unwrap();
10243 }
10244
10245 #[test]
10246 fn parsed_node_with_ident_property() {
10247 let router = QueryRouter::new();
10248 let result = router
10250 .execute_parsed("NODE CREATE test { mykey: somevalue }")
10251 .unwrap();
10252 assert!(matches!(result, QueryResult::Ids(_)));
10253 }
10254
10255 #[test]
10256 fn execute_empty_after_trim() {
10257 let router = QueryRouter::new();
10258 let result = router.execute(" \t\n ");
10260 assert!(result.is_err());
10261 }
10262
10263 #[test]
10264 fn parsed_select_with_qualified_where() {
10265 let router = QueryRouter::new();
10266 router.execute("CREATE TABLE t (x int)").unwrap();
10267 router.execute("INSERT INTO t (x) VALUES (5)").unwrap();
10268 let result = router
10270 .execute_parsed("SELECT * FROM t WHERE t.x = 5")
10271 .unwrap();
10272 assert!(matches!(result, QueryResult::Rows(_)));
10273 }
10274
10275 #[test]
10276 fn parsed_unsupported_operator_in_where() {
10277 let router = QueryRouter::new();
10278 router.execute("CREATE TABLE t (x int)").unwrap();
10279 let result = router.execute_parsed("SELECT * FROM t WHERE x + 1");
10281 assert!(result.is_err());
10282 }
10283
10284 #[test]
10285 fn parsed_literal_in_where() {
10286 let router = QueryRouter::new();
10287 router.execute("CREATE TABLE t (x int)").unwrap();
10288 let result = router.execute_parsed("SELECT * FROM t WHERE 1");
10290 assert!(result.is_err());
10291 }
10292
10293 #[test]
10294 fn parsed_insert_with_complex_expr() {
10295 let router = QueryRouter::new();
10296 router.execute("CREATE TABLE t (x int)").unwrap();
10297 let result = router.execute_parsed("INSERT INTO t (x) VALUES (1 + 2)");
10299 assert!(result.is_err());
10300 }
10301
10302 #[test]
10303 fn parsed_create_unsupported_type() {
10304 let router = QueryRouter::new();
10305 let result = router.execute_parsed("CREATE TABLE t (data jsonb)");
10307 assert!(result.is_err());
10308 }
10309
10310 #[test]
10311 fn parsed_similar_limit_not_integer() {
10312 let router = QueryRouter::new();
10313 router.execute("EMBED v [1.0, 2.0]").unwrap();
10314 let result = router.execute_parsed("SIMILAR 'v' LIMIT 'ten'");
10316 assert!(result.is_err());
10317 }
10318
10319 #[test]
10320 fn parsed_neighbors_negative_id() {
10321 let router = QueryRouter::new();
10322 let result = router.execute_parsed("NEIGHBORS -1 OUTGOING");
10324 assert!(result.is_ok() || result.is_err());
10326 }
10327
10328 #[test]
10329 fn parsed_path_negative_ids() {
10330 let router = QueryRouter::new();
10331 let result = router.execute_parsed("PATH SHORTEST -1 -> -2");
10333 assert!(result.is_ok() || result.is_err());
10334 }
10335
10336 #[test]
10337 fn parsed_find_edges_plain() {
10338 let router = QueryRouter::new();
10339 let result = router.execute_parsed("FIND EDGE").unwrap();
10341 match result {
10342 QueryResult::Unified(u) => assert!(u.description.contains("edge")),
10343 _ => panic!("Expected Unified"),
10344 }
10345 }
10346
10347 #[test]
10348 fn parsed_find_nodes_plain() {
10349 let router = QueryRouter::new();
10350 let result = router.execute_parsed("FIND NODE").unwrap();
10352 match result {
10353 QueryResult::Unified(u) => assert!(u.description.contains("node")),
10354 _ => panic!("Expected Unified"),
10355 }
10356 }
10357
10358 #[test]
10359 fn parsed_embed_get_with_ident_key() {
10360 let router = QueryRouter::new();
10361 router.execute("EMBED mykey [1.0, 2.0]").unwrap();
10362 let result = router.execute_parsed("EMBED GET mykey").unwrap();
10364 assert!(matches!(result, QueryResult::Value(_)));
10365 }
10366
10367 #[test]
10368 fn parsed_similar_with_ident_key() {
10369 let router = QueryRouter::new();
10370 router.execute("EMBED vec1 [1.0, 0.0]").unwrap();
10371 let result = router.execute_parsed("SIMILAR vec1 LIMIT 5").unwrap();
10373 assert!(matches!(result, QueryResult::Similar(_)));
10374 }
10375
10376 #[test]
10377 fn parsed_node_get_nonexistent() {
10378 let router = QueryRouter::new();
10379 let result = router.execute_parsed("NODE GET 999999");
10381 assert!(result.is_err());
10382 }
10383
10384 #[test]
10385 fn parsed_path_nonexistent_nodes() {
10386 let router = QueryRouter::new();
10387 let result = router.execute_parsed("PATH SHORTEST 999999 -> 999998");
10389 assert!(result.is_err());
10390 }
10391
10392 #[test]
10393 fn parsed_neighbors_nonexistent_node() {
10394 let router = QueryRouter::new();
10395 let result = router.execute_parsed("NEIGHBORS 999999 OUTGOING");
10397 assert!(result.is_err());
10398 }
10399
10400 #[test]
10401 fn parsed_edge_get_nonexistent() {
10402 let router = QueryRouter::new();
10403 let result = router.execute_parsed("EDGE GET 999999");
10405 assert!(result.is_err());
10406 }
10407
10408 #[test]
10409 fn parsed_node_delete_nonexistent() {
10410 let router = QueryRouter::new();
10411 let result = router.execute_parsed("NODE DELETE 999999");
10413 assert!(result.is_err());
10414 }
10415
10416 #[test]
10417 fn parsed_embed_delete_nonexistent() {
10418 let router = QueryRouter::new();
10419 let result = router.execute_parsed("EMBED DELETE 'nonexistent'");
10421 assert!(result.is_err());
10422 }
10423
10424 #[test]
10425 fn parsed_select_nonexistent_table() {
10426 let router = QueryRouter::new();
10427 let result = router.execute_parsed("SELECT * FROM nonexistent");
10429 assert!(result.is_err());
10430 }
10431
10432 #[test]
10433 fn parsed_update_nonexistent_table() {
10434 let router = QueryRouter::new();
10435 let result = router.execute_parsed("UPDATE nonexistent SET x = 1");
10437 assert!(result.is_err());
10438 }
10439
10440 #[test]
10441 fn parsed_delete_nonexistent_table() {
10442 let router = QueryRouter::new();
10443 let result = router.execute_parsed("DELETE FROM nonexistent");
10445 assert!(result.is_err());
10446 }
10447
10448 #[test]
10449 fn execute_only_whitespace() {
10450 let router = QueryRouter::new();
10451 let result = router.execute("\t\n \r\n");
10453 assert!(result.is_err());
10454 }
10455
10456 #[test]
10457 fn parsed_embed_list() {
10458 let router = QueryRouter::new();
10459 router.execute("EMBED a [1.0]").unwrap();
10460 router.execute("EMBED b [2.0]").unwrap();
10461 let result = router.execute_parsed("EMBED LIST");
10462 assert!(result.is_ok() || result.is_err());
10464 }
10465
10466 #[test]
10467 fn parsed_insert_into_nonexistent() {
10468 let router = QueryRouter::new();
10469 let result = router.execute_parsed("INSERT INTO nonexistent (x) VALUES (1)");
10470 assert!(result.is_err());
10471 }
10472
10473 #[test]
10474 fn parsed_drop_nonexistent_table() {
10475 let router = QueryRouter::new();
10476 let result = router.execute_parsed("DROP TABLE nonexistent");
10477 assert!(result.is_err());
10478 }
10479
10480 #[test]
10481 fn execute_tab_only() {
10482 let router = QueryRouter::new();
10483 let result = router.execute("\t");
10485 assert!(result.is_err());
10486 }
10487
10488 #[test]
10489 fn parsed_embed_non_number_vector() {
10490 let router = QueryRouter::new();
10491 let result = router.execute_parsed("EMBED STORE 'k' ['a', 'b']");
10493 assert!(result.is_err());
10494 }
10495
10496 #[test]
10497 fn parsed_similar_non_string_key() {
10498 let router = QueryRouter::new();
10499 let result = router.execute_parsed("SIMILAR [1,2,3] LIMIT 5");
10501 assert!(result.is_ok() || result.is_err());
10503 }
10504
10505 #[test]
10506 fn parsed_where_complex_column() {
10507 let router = QueryRouter::new();
10508 router.execute("CREATE TABLE t (x int)").unwrap();
10509 let result = router.execute_parsed("SELECT * FROM t WHERE (1+2) = 3");
10511 assert!(result.is_err());
10512 }
10513
10514 #[test]
10515 fn parsed_node_invalid_property_expr() {
10516 let router = QueryRouter::new();
10517 let result = router.execute_parsed("NODE CREATE test { val: (1+2) }");
10519 assert!(result.is_err());
10520 }
10521
10522 #[test]
10525 fn show_tables_empty() {
10526 let router = QueryRouter::new();
10527 let result = router.execute_parsed("SHOW TABLES").unwrap();
10528 match result {
10529 QueryResult::TableList(tables) => {
10530 assert!(tables.is_empty());
10531 },
10532 _ => panic!("Expected TableList"),
10533 }
10534 }
10535
10536 #[test]
10537 fn show_tables_with_tables() {
10538 let router = QueryRouter::new();
10539 router
10540 .execute_parsed("CREATE TABLE users (id INT)")
10541 .unwrap();
10542 router
10543 .execute_parsed("CREATE TABLE products (id INT)")
10544 .unwrap();
10545
10546 let result = router.execute_parsed("SHOW TABLES").unwrap();
10547 match result {
10548 QueryResult::TableList(tables) => {
10549 assert_eq!(tables.len(), 2);
10550 assert!(tables.contains(&"users".to_string()));
10551 assert!(tables.contains(&"products".to_string()));
10552 },
10553 _ => panic!("Expected TableList"),
10554 }
10555 }
10556
10557 #[test]
10558 fn show_without_tables_error() {
10559 let router = QueryRouter::new();
10560 let result = router.execute_parsed("SHOW");
10561 assert!(result.is_err());
10562 }
10563
10564 #[test]
10565 fn insert_without_columns() {
10566 let router = QueryRouter::new();
10567 router
10568 .execute_parsed("CREATE TABLE users (id INT, name TEXT)")
10569 .unwrap();
10570
10571 router
10573 .execute_parsed("INSERT INTO users VALUES (1, 'Alice')")
10574 .unwrap();
10575 router
10576 .execute_parsed("INSERT INTO users VALUES (2, 'Bob')")
10577 .unwrap();
10578
10579 let result = router.execute_parsed("SELECT * FROM users").unwrap();
10580 match result {
10581 QueryResult::Rows(rows) => {
10582 assert_eq!(rows.len(), 2);
10583 },
10584 _ => panic!("Expected Rows"),
10585 }
10586 }
10587
10588 #[test]
10591 fn with_shared_store_creates_unified_router() {
10592 let store = tensor_store::TensorStore::new();
10593 let router = QueryRouter::with_shared_store(store);
10594
10595 assert!(router.relational().list_tables().is_empty());
10597 }
10598
10599 #[test]
10600 fn with_shared_store_initializes_unified_engine() {
10601 let store = tensor_store::TensorStore::new();
10602 let router = QueryRouter::with_shared_store(store);
10603
10604 assert!(router.unified().is_some());
10606 }
10607
10608 #[test]
10609 fn new_router_has_unified_engine() {
10610 let router = QueryRouter::new();
10611
10612 assert!(router.unified().is_some());
10614 }
10615
10616 #[test]
10617 fn unified_engine_delegates_find_neighbors_by_similarity() {
10618 let store = tensor_store::TensorStore::new();
10619 let router = QueryRouter::with_shared_store(store);
10620
10621 router
10623 .vector()
10624 .set_entity_embedding("center", vec![1.0, 0.0, 0.0])
10625 .unwrap();
10626 router
10627 .vector()
10628 .set_entity_embedding("neighbor1", vec![0.9, 0.1, 0.0])
10629 .unwrap();
10630 router
10631 .vector()
10632 .set_entity_embedding("neighbor2", vec![0.5, 0.5, 0.0])
10633 .unwrap();
10634
10635 add_test_edge(router.graph(), "center", "neighbor1", "connected");
10637 add_test_edge(router.graph(), "center", "neighbor2", "connected");
10638
10639 let query = vec![1.0, 0.0, 0.0];
10641 let results = router
10642 .find_neighbors_by_similarity("center", &query, 5)
10643 .unwrap();
10644
10645 assert_eq!(results.len(), 2);
10647 assert_eq!(results[0].id, "neighbor1");
10649 assert!(results[0].score.unwrap() > results[1].score.unwrap());
10650 }
10651
10652 #[test]
10653 fn unified_engine_delegates_find_similar_connected() {
10654 let store = tensor_store::TensorStore::new();
10655 let router = QueryRouter::with_shared_store(store);
10656
10657 router
10659 .vector()
10660 .set_entity_embedding("query", vec![1.0, 0.0, 0.0])
10661 .unwrap();
10662 router
10663 .vector()
10664 .set_entity_embedding("connected1", vec![0.95, 0.05, 0.0])
10665 .unwrap();
10666 router
10667 .vector()
10668 .set_entity_embedding("connected2", vec![0.8, 0.2, 0.0])
10669 .unwrap();
10670 router
10671 .vector()
10672 .set_entity_embedding("not_connected", vec![0.99, 0.01, 0.0])
10673 .unwrap();
10674
10675 add_test_edge(router.graph(), "hub", "connected1", "links");
10677 add_test_edge(router.graph(), "hub", "connected2", "links");
10678 let results = router.find_similar_connected("query", "hub", 5).unwrap();
10682
10683 assert!(results.len() <= 2);
10685 for item in &results {
10686 assert!(item.id == "connected1" || item.id == "connected2");
10687 assert!(item.score.is_some());
10688 }
10689 }
10690
10691 #[test]
10692 fn create_unified_entity_stores_embedding() {
10693 let store = tensor_store::TensorStore::new();
10694 let router = QueryRouter::with_shared_store(store);
10695
10696 let fields = HashMap::from([("name".to_string(), "Alice".to_string())]);
10697 let embedding = vec![1.0, 0.0, 0.0];
10698
10699 router
10700 .create_unified_entity("user:1", fields, Some(embedding.clone()))
10701 .unwrap();
10702
10703 let retrieved = router.vector().get_entity_embedding("user:1").unwrap();
10704 assert_eq!(retrieved, embedding);
10705 }
10706
10707 #[test]
10708 fn create_unified_entity_without_embedding() {
10709 let store = tensor_store::TensorStore::new();
10710 let router = QueryRouter::with_shared_store(store);
10711
10712 let fields = HashMap::from([("name".to_string(), "Alice".to_string())]);
10713
10714 router
10715 .create_unified_entity("user:1", fields, None)
10716 .unwrap();
10717
10718 assert!(!router.vector().entity_has_embedding("user:1"));
10720 }
10721
10722 #[test]
10723 fn connect_entities_creates_edge() {
10724 let store = tensor_store::TensorStore::new();
10725 let router = QueryRouter::with_shared_store(store);
10726
10727 let edge_key = router
10728 .connect_entities("user:1", "user:2", "follows")
10729 .unwrap();
10730
10731 assert!(edge_key.starts_with("edge:follows:"));
10732
10733 let neighbors = get_neighbors_out(router.graph(), "user:1");
10734 assert_eq!(neighbors.len(), 1);
10735 assert_eq!(neighbors[0], "user:2");
10736 }
10737
10738 #[test]
10739 fn find_similar_connected_returns_intersection() {
10740 let store = tensor_store::TensorStore::new();
10741 let router = QueryRouter::with_shared_store(store);
10742
10743 router
10745 .vector()
10746 .set_entity_embedding("query", vec![1.0, 0.0, 0.0])
10747 .unwrap();
10748 router
10749 .vector()
10750 .set_entity_embedding("user:1", vec![0.9, 0.1, 0.0])
10751 .unwrap();
10752 router
10753 .vector()
10754 .set_entity_embedding("user:2", vec![0.8, 0.2, 0.0])
10755 .unwrap();
10756 router
10757 .vector()
10758 .set_entity_embedding("user:3", vec![0.0, 1.0, 0.0])
10759 .unwrap();
10760
10761 add_test_edge(router.graph(), "hub", "user:1", "connects");
10763 add_test_edge(router.graph(), "hub", "user:2", "connects");
10764 let results = router.find_similar_connected("query", "hub", 5).unwrap();
10767
10768 assert!(results.len() <= 2);
10770 for item in &results {
10771 assert!(item.id == "user:1" || item.id == "user:2");
10772 assert!(item.score.is_some());
10773 assert_eq!(item.source, "vector+graph");
10774 }
10775 }
10776
10777 #[test]
10778 fn find_similar_connected_no_embedding() {
10779 let store = tensor_store::TensorStore::new();
10780 let router = QueryRouter::with_shared_store(store);
10781
10782 let result = router.find_similar_connected("nonexistent", "hub", 5);
10784 assert!(result.is_ok());
10785 assert!(result.unwrap().is_empty());
10786 }
10787
10788 #[test]
10789 fn find_neighbors_by_similarity() {
10790 let store = tensor_store::TensorStore::new();
10791 let router = QueryRouter::with_shared_store(store);
10792
10793 router
10795 .vector()
10796 .set_entity_embedding("user:1", vec![1.0, 0.0, 0.0])
10797 .unwrap();
10798 router
10799 .vector()
10800 .set_entity_embedding("user:2", vec![0.0, 1.0, 0.0])
10801 .unwrap();
10802 router
10803 .vector()
10804 .set_entity_embedding("user:3", vec![0.5, 0.5, 0.0])
10805 .unwrap();
10806
10807 add_test_edge(router.graph(), "center", "user:1", "knows");
10809 add_test_edge(router.graph(), "center", "user:2", "knows");
10810 add_test_edge(router.graph(), "center", "user:3", "knows");
10811
10812 let query = vec![1.0, 0.0, 0.0];
10814 let results = router
10815 .find_neighbors_by_similarity("center", &query, 3)
10816 .unwrap();
10817
10818 assert_eq!(results.len(), 3);
10819 assert_eq!(results[0].id, "user:1");
10821 assert_eq!(results[0].source, "graph+vector");
10822 }
10823
10824 #[test]
10825 fn find_neighbors_by_similarity_no_entity() {
10826 let store = tensor_store::TensorStore::new();
10827 let router = QueryRouter::with_shared_store(store);
10828
10829 let result = router.find_neighbors_by_similarity("nonexistent", &[1.0, 0.0], 5);
10831 assert!(result.is_ok());
10832 assert!(result.unwrap().is_empty());
10833 }
10834
10835 #[test]
10836 fn find_neighbors_by_similarity_filters_dimension_mismatch() {
10837 let store = tensor_store::TensorStore::new();
10838 let router = QueryRouter::with_shared_store(store);
10839
10840 router
10842 .vector()
10843 .set_entity_embedding("user:1", vec![1.0, 0.0])
10844 .unwrap();
10845 router
10846 .vector()
10847 .set_entity_embedding("user:2", vec![1.0, 0.0, 0.0])
10848 .unwrap(); add_test_edge(router.graph(), "center", "user:1", "knows");
10851 add_test_edge(router.graph(), "center", "user:2", "knows");
10852
10853 let query = vec![1.0, 0.0]; let results = router
10855 .find_neighbors_by_similarity("center", &query, 5)
10856 .unwrap();
10857
10858 assert_eq!(results.len(), 1);
10860 assert_eq!(results[0].id, "user:1");
10861 }
10862
10863 #[test]
10864 fn shared_store_engines_share_data() {
10865 let store = tensor_store::TensorStore::new();
10866 let router = QueryRouter::with_shared_store(store);
10867
10868 router
10870 .vector()
10871 .set_entity_embedding("entity:1", vec![1.0, 2.0])
10872 .unwrap();
10873
10874 add_test_edge(router.graph(), "entity:1", "entity:2", "relates");
10876
10877 assert!(router.vector().entity_has_embedding("entity:1"));
10879 assert!(entity_has_edges(router.graph(), "entity:1"));
10880 }
10881
10882 #[test]
10883 fn test_cache_init() {
10884 let mut router = QueryRouter::new();
10885 router.init_cache();
10886 assert!(router.cache().is_some());
10887 }
10888
10889 #[test]
10890 fn test_cache_stats() {
10891 let mut router = QueryRouter::new();
10892 router.init_cache();
10893 router.set_identity("user:test");
10894 let result = router.execute_parsed("CACHE STATS");
10895 assert!(result.is_ok());
10896 let output = unwrap_qr_value(result.unwrap());
10897 assert!(output.contains("Cache Statistics"));
10898 }
10899
10900 #[test]
10901 fn test_cache_init_command() {
10902 let mut router = QueryRouter::new();
10903 router.init_cache();
10904 router.set_identity("user:test");
10905 let result = router.execute_parsed("CACHE INIT");
10906 assert!(result.is_ok());
10907 let output = unwrap_qr_value(result.unwrap());
10908 assert!(output.contains("Cache initialized"));
10909 }
10910
10911 #[test]
10912 fn test_cache_clear() {
10913 let mut router = QueryRouter::new();
10914 router.init_cache();
10915 router.set_identity("user:test");
10916 let result = router.execute_parsed("CACHE CLEAR");
10917 assert!(result.is_ok());
10918 let output = unwrap_qr_value(result.unwrap());
10919 assert!(output.contains("Cache cleared"));
10920 }
10921
10922 #[test]
10923 fn test_cache_without_init() {
10924 let router = QueryRouter::new();
10925 let result = router.execute_parsed("CACHE STATS");
10926 assert!(result.is_err());
10927 }
10928
10929 #[test]
10930 fn test_cache_evict() {
10931 let mut router = QueryRouter::new();
10932 router.init_cache();
10933 router.set_identity("user:test");
10934 let result = router.execute_parsed("CACHE EVICT");
10935 assert!(result.is_ok());
10936 let output = unwrap_qr_value(result.unwrap());
10937 assert!(output.contains("Evicted"));
10938 }
10939
10940 #[test]
10941 fn test_cache_evict_with_count() {
10942 let mut router = QueryRouter::new();
10943 router.init_cache();
10944 router.set_identity("user:test");
10945 let result = router.execute_parsed("CACHE EVICT 50");
10946 assert!(result.is_ok());
10947 let output = unwrap_qr_value(result.unwrap());
10948 assert!(output.contains("Evicted"));
10949 }
10950
10951 #[test]
10952 fn test_cache_put_get() {
10953 let mut router = QueryRouter::new();
10954 router.init_cache();
10955 router.set_identity("user:test");
10956
10957 let result = router.execute_parsed("CACHE PUT 'testkey' 'testvalue'");
10959 assert!(result.is_ok());
10960 assert!(matches!(result.unwrap(), QueryResult::Value(s) if s == "OK"));
10961
10962 let result = router.execute_parsed("CACHE GET 'testkey'");
10964 assert!(result.is_ok());
10965 let output = unwrap_qr_value(result.unwrap());
10966 assert_eq!(output, "testvalue");
10967 }
10968
10969 #[test]
10970 fn test_cache_get_not_found() {
10971 let mut router = QueryRouter::new();
10972 router.init_cache();
10973 router.set_identity("user:test");
10974
10975 let result = router.execute_parsed("CACHE GET 'nonexistent'");
10976 assert!(result.is_ok());
10977 let output = unwrap_qr_value(result.unwrap());
10978 assert_eq!(output, "(not found)");
10979 }
10980
10981 #[test]
10982 #[ignore] fn test_query_cache_select() {
10984 let mut router = QueryRouter::new();
10985 router.init_cache();
10986
10987 router
10989 .execute_parsed("CREATE TABLE cached_test (id INT, name TEXT)")
10990 .unwrap();
10991 router
10992 .execute_parsed("INSERT INTO cached_test (id, name) VALUES (1, 'Alice')")
10993 .unwrap();
10994
10995 let result1 = router.execute_parsed("SELECT * FROM cached_test").unwrap();
10997 assert!(matches!(result1, QueryResult::Rows(_)));
10998
10999 let result2 = router.execute_parsed("SELECT * FROM cached_test").unwrap();
11001 assert!(matches!(result2, QueryResult::Rows(_)));
11002
11003 let stats = router.cache.as_ref().unwrap().stats();
11005 assert!(stats.hits(CacheLayer::Exact) > 0);
11006 }
11007
11008 #[test]
11009 #[ignore] fn test_query_cache_invalidation() {
11011 let mut router = QueryRouter::new();
11012 router.init_cache();
11013
11014 router
11016 .execute_parsed("CREATE TABLE invalidate_test (id INT)")
11017 .unwrap();
11018 router
11019 .execute_parsed("INSERT INTO invalidate_test (id) VALUES (1)")
11020 .unwrap();
11021
11022 let _ = router.execute_parsed("SELECT * FROM invalidate_test");
11024
11025 let _hits_before = router
11027 .cache
11028 .as_ref()
11029 .unwrap()
11030 .stats()
11031 .hits(CacheLayer::Exact);
11032
11033 router
11035 .execute_parsed("INSERT INTO invalidate_test (id) VALUES (2)")
11036 .unwrap();
11037
11038 let _ = router.execute_parsed("SELECT * FROM invalidate_test");
11040
11041 let misses_after = router
11044 .cache
11045 .as_ref()
11046 .unwrap()
11047 .stats()
11048 .misses(CacheLayer::Exact);
11049 assert!(misses_after > 0);
11050 }
11051
11052 #[test]
11053 fn test_is_write_statement_sql_writes() {
11054 let cases = [
11055 ("INSERT INTO t (x) VALUES (1)", true),
11056 ("UPDATE t SET x = 1", true),
11057 ("DELETE FROM t WHERE x = 1", true),
11058 ("CREATE TABLE t (id INT)", true),
11059 ("DROP TABLE t", true),
11060 ("CREATE INDEX idx ON t (x)", true),
11061 ("DROP INDEX ON t(x)", true),
11062 ];
11063 for (query, expected) in &cases {
11064 let stmt = parser::parse(query).unwrap();
11065 assert_eq!(
11066 QueryRouter::is_write_statement(&stmt),
11067 *expected,
11068 "Failed for: {query}"
11069 );
11070 }
11071 }
11072
11073 #[test]
11074 fn test_is_write_statement_reads_are_false() {
11075 let cases = [
11076 "SELECT * FROM t",
11077 "SHOW TABLES",
11078 "DESCRIBE TABLE t",
11079 "SHOW EMBEDDINGS",
11080 ];
11081 for query in &cases {
11082 let stmt = parser::parse(query).unwrap();
11083 assert!(
11084 !QueryRouter::is_write_statement(&stmt),
11085 "Should be false for: {query}"
11086 );
11087 }
11088 }
11089
11090 #[test]
11091 fn test_is_write_statement_graph_ops() {
11092 let writes = [
11093 "NODE CREATE label1 { name: 'test' }",
11094 "NODE DELETE 1",
11095 "EDGE CREATE 1 -> 2 : knows",
11096 "EDGE DELETE 1",
11097 ];
11098 for query in &writes {
11099 let stmt = parser::parse(query).unwrap();
11100 assert!(
11101 QueryRouter::is_write_statement(&stmt),
11102 "Should be write: {query}"
11103 );
11104 }
11105
11106 let reads = ["NODE GET 1", "EDGE GET 1", "NODE LIST", "EDGE LIST"];
11107 for query in &reads {
11108 let stmt = parser::parse(query).unwrap();
11109 assert!(
11110 !QueryRouter::is_write_statement(&stmt),
11111 "Should be read: {query}"
11112 );
11113 }
11114 }
11115
11116 #[test]
11117 fn test_is_write_statement_embed_ops() {
11118 let writes = [
11119 "EMBED STORE 'key1' [1.0, 2.0, 3.0]",
11120 "EMBED DELETE 'key1'",
11121 "EMBED BATCH [('a', [1.0, 2.0]), ('b', [3.0, 4.0])]",
11122 ];
11123 for query in &writes {
11124 let stmt = parser::parse(query).unwrap();
11125 assert!(
11126 QueryRouter::is_write_statement(&stmt),
11127 "Should be write: {query}"
11128 );
11129 }
11130
11131 let reads = ["EMBED GET 'key1'"];
11132 for query in &reads {
11133 let stmt = parser::parse(query).unwrap();
11134 assert!(
11135 !QueryRouter::is_write_statement(&stmt),
11136 "Should be read: {query}"
11137 );
11138 }
11139 }
11140
11141 #[test]
11142 fn test_is_write_statement_entity_ops() {
11143 let writes = [
11144 "ENTITY CREATE 'e1' { name: 'test' }",
11145 "ENTITY UPDATE 'e1' { name: 'updated' }",
11146 "ENTITY DELETE 'e1'",
11147 "ENTITY CONNECT 'e1' -> 'e2' : related",
11148 ];
11149 for query in &writes {
11150 let stmt = parser::parse(query).unwrap();
11151 assert!(
11152 QueryRouter::is_write_statement(&stmt),
11153 "Should be write: {query}"
11154 );
11155 }
11156
11157 let reads = ["ENTITY GET 'e1'"];
11158 for query in &reads {
11159 let stmt = parser::parse(query).unwrap();
11160 assert!(
11161 !QueryRouter::is_write_statement(&stmt),
11162 "Should be read: {query}"
11163 );
11164 }
11165 }
11166
11167 #[test]
11168 fn test_is_write_statement_cache_never_invalidates() {
11169 let cases = [
11170 "CACHE PUT 'key' 'value'",
11171 "CACHE GET 'key'",
11172 "CACHE CLEAR",
11173 "CACHE STATS",
11174 ];
11175 for query in &cases {
11176 let stmt = parser::parse(query).unwrap();
11177 assert!(
11178 !QueryRouter::is_write_statement(&stmt),
11179 "Cache should never invalidate: {query}"
11180 );
11181 }
11182 }
11183
11184 #[test]
11185 fn test_is_write_statement_rollback_is_write() {
11186 let stmt = parser::parse("ROLLBACK TO 'checkpoint_id'").unwrap();
11187 assert!(QueryRouter::is_write_statement(&stmt));
11188 }
11189
11190 #[test]
11191 fn test_is_write_statement_checkpoint_is_not_write() {
11192 let stmt = parser::parse("CHECKPOINT 'snap1'").unwrap();
11193 assert!(!QueryRouter::is_write_statement(&stmt));
11194 }
11195
11196 #[test]
11197 fn test_is_write_statement_spatial_ops() {
11198 let writes = [
11199 "SPATIAL INSERT 'loc1' BOUNDS 10 20 30 40",
11200 "SPATIAL DELETE 'loc1' BOUNDS 10 20 30 40",
11201 ];
11202 for query in &writes {
11203 let stmt = parser::parse(query).unwrap();
11204 assert!(
11205 QueryRouter::is_write_statement(&stmt),
11206 "Should be write: {query}"
11207 );
11208 }
11209
11210 let reads = [
11211 "SPATIAL WITHIN 5.0 10.0 RADIUS 25.0",
11212 "SPATIAL NEAREST 5.0 10.0 LIMIT 3",
11213 ];
11214 for query in &reads {
11215 let stmt = parser::parse(query).unwrap();
11216 assert!(
11217 !QueryRouter::is_write_statement(&stmt),
11218 "Should be read: {query}"
11219 );
11220 }
11221 }
11222
11223 #[test]
11224 fn test_is_write_statement_entity_batch() {
11225 let stmt =
11226 parser::parse("ENTITY BATCH CREATE [{key: 'e1', name: 'a'}, {key: 'e2', name: 'b'}]")
11227 .unwrap();
11228 assert!(QueryRouter::is_write_statement(&stmt));
11229 }
11230
11231 #[test]
11232 fn test_is_write_statement_entity_get_is_read() {
11233 let stmt = parser::parse("ENTITY GET 'e1'").unwrap();
11234 assert!(!QueryRouter::is_write_statement(&stmt));
11235 }
11236
11237 #[test]
11238 fn test_is_write_statement_vault_ops() {
11239 let writes = ["VAULT SET 'secret' 'value'", "VAULT DELETE 'secret'"];
11240 for query in &writes {
11241 let stmt = parser::parse(query).unwrap();
11242 assert!(
11243 QueryRouter::is_write_statement(&stmt),
11244 "Should be write: {query}"
11245 );
11246 }
11247
11248 let reads = ["VAULT GET 'secret'", "VAULT LIST"];
11249 for query in &reads {
11250 let stmt = parser::parse(query).unwrap();
11251 assert!(
11252 !QueryRouter::is_write_statement(&stmt),
11253 "Should be read: {query}"
11254 );
11255 }
11256 }
11257
11258 #[test]
11259 fn test_is_write_statement_blob_ops() {
11260 let writes = [
11261 "BLOB PUT 'test.txt' FROM '/tmp/test.txt'",
11262 "BLOB DELETE 'abc123'",
11263 ];
11264 for query in &writes {
11265 let stmt = parser::parse(query).unwrap();
11266 assert!(
11267 QueryRouter::is_write_statement(&stmt),
11268 "Should be write: {query}"
11269 );
11270 }
11271
11272 let reads = ["BLOB GET 'abc123'"];
11273 for query in &reads {
11274 let stmt = parser::parse(query).unwrap();
11275 assert!(
11276 !QueryRouter::is_write_statement(&stmt),
11277 "Should be read: {query}"
11278 );
11279 }
11280 }
11281
11282 #[test]
11283 #[ignore] fn test_query_cache_case_insensitive() {
11285 let mut router = QueryRouter::new();
11286 router.init_cache();
11287
11288 router
11289 .execute_parsed("CREATE TABLE case_test (id INT)")
11290 .unwrap();
11291 router
11292 .execute_parsed("INSERT INTO case_test (id) VALUES (1)")
11293 .unwrap();
11294
11295 let _ = router.execute_parsed("SELECT * FROM case_test");
11297
11298 let _ = router.execute_parsed("select * from case_test");
11300
11301 let stats = router.cache.as_ref().unwrap().stats();
11302 assert!(stats.hits(CacheLayer::Exact) > 0);
11303 }
11304
11305 #[test]
11308 fn test_vault_not_initialized() {
11309 let router = QueryRouter::new();
11310 let result = router.execute_parsed("VAULT SET 'key' 'value'");
11311 assert!(result.is_err());
11312 let err = result.unwrap_err();
11313 assert!(err.to_string().contains("not initialized"));
11314 }
11315
11316 #[test]
11317 fn test_vault_set_get() {
11318 let mut router = QueryRouter::new();
11319 router.init_vault(b"test_master_key_32bytes!").unwrap();
11320 router.set_identity(Vault::ROOT); router
11323 .execute_parsed("VAULT SET 'secret_key' 'secret_value'")
11324 .unwrap();
11325 let result = router.execute_parsed("VAULT GET 'secret_key'").unwrap();
11326 match result {
11327 QueryResult::Value(v) => assert_eq!(v, "secret_value"),
11328 _ => panic!("Expected Value result"),
11329 }
11330 }
11331
11332 #[test]
11333 fn test_vault_delete() {
11334 let mut router = QueryRouter::new();
11335 router.init_vault(b"test_master_key_32bytes!").unwrap();
11336 router.set_identity(Vault::ROOT);
11337
11338 router
11339 .execute_parsed("VAULT SET 'to_delete' 'value'")
11340 .unwrap();
11341 router.execute_parsed("VAULT DELETE 'to_delete'").unwrap();
11342 let result = router.execute_parsed("VAULT GET 'to_delete'");
11343 assert!(result.is_err());
11344 }
11345
11346 #[test]
11347 fn test_vault_list() {
11348 let mut router = QueryRouter::new();
11349 router.init_vault(b"test_master_key_32bytes!").unwrap();
11350 router.set_identity(Vault::ROOT);
11351
11352 router.execute_parsed("VAULT SET 'key1' 'v1'").unwrap();
11353 router.execute_parsed("VAULT SET 'key2' 'v2'").unwrap();
11354 let result = router.execute_parsed("VAULT LIST").unwrap();
11355 match result {
11356 QueryResult::Value(v) => {
11357 assert!(v.contains("key1"));
11358 assert!(v.contains("key2"));
11359 },
11360 _ => panic!("Expected Value result"),
11361 }
11362 }
11363
11364 #[test]
11365 fn test_vault_list_with_pattern() {
11366 let mut router = QueryRouter::new();
11367 router.init_vault(b"test_master_key_32bytes!").unwrap();
11368 router.set_identity(Vault::ROOT);
11369
11370 router.execute_parsed("VAULT SET 'db_pass' 'v1'").unwrap();
11371 router.execute_parsed("VAULT SET 'db_user' 'v2'").unwrap();
11372 router.execute_parsed("VAULT SET 'api_key' 'v3'").unwrap();
11373 let result = router.execute_parsed("VAULT LIST 'db_*'").unwrap();
11374 match result {
11375 QueryResult::Value(v) => {
11376 assert!(v.contains("db_pass"));
11377 assert!(v.contains("db_user"));
11378 },
11379 _ => panic!("Expected Value result"),
11380 }
11381 }
11382
11383 #[test]
11384 fn test_vault_rotate() {
11385 let mut router = QueryRouter::new();
11386 router.init_vault(b"test_master_key_32bytes!").unwrap();
11387 router.set_identity(Vault::ROOT);
11388
11389 router
11390 .execute_parsed("VAULT SET 'rotate_key' 'old_value'")
11391 .unwrap();
11392 router
11393 .execute_parsed("VAULT ROTATE 'rotate_key' 'new_value'")
11394 .unwrap();
11395 let result = router.execute_parsed("VAULT GET 'rotate_key'").unwrap();
11396 match result {
11397 QueryResult::Value(v) => assert_eq!(v, "new_value"),
11398 _ => panic!("Expected Value result"),
11399 }
11400 }
11401
11402 #[test]
11403 fn test_vault_grant_revoke() {
11404 let mut router = QueryRouter::new();
11405 router.init_vault(b"test_master_key_32bytes!").unwrap();
11406 router.set_identity(Vault::ROOT);
11407
11408 router
11409 .execute_parsed("VAULT SET 'shared_key' 'shared_value'")
11410 .unwrap();
11411 let grant_result = router.execute_parsed("VAULT GRANT 'user:bob' 'shared_key'");
11413 assert!(grant_result.is_ok() || grant_result.is_err());
11415
11416 let revoke_result = router.execute_parsed("VAULT REVOKE 'user:bob' 'shared_key'");
11418 assert!(revoke_result.is_ok() || revoke_result.is_err());
11419 }
11420
11421 #[test]
11424 fn test_blob_not_initialized() {
11425 let mut router = QueryRouter::new();
11426 router.set_identity("user:test");
11427 let result = router.execute_parsed("BLOB PUT 'test.txt' 'hello'");
11428 assert!(result.is_err());
11429 let err = result.unwrap_err();
11430 assert!(err.to_string().contains("not initialized"));
11431 }
11432
11433 #[test]
11434 fn test_blob_put_get_delete() {
11435 let mut router = QueryRouter::new();
11436 router.init_blob().unwrap();
11437 router.set_identity("user:test");
11438
11439 let put_result = router
11441 .execute_parsed("BLOB PUT 'test.txt' 'Hello, World!'")
11442 .unwrap();
11443 let artifact_id = match put_result {
11444 QueryResult::Value(id) => id,
11445 _ => panic!("Expected Value result with artifact ID"),
11446 };
11447
11448 let get_result = router
11450 .execute_parsed(&format!("BLOB GET '{}'", artifact_id))
11451 .unwrap();
11452 match get_result {
11453 QueryResult::Blob(data) => {
11454 assert_eq!(String::from_utf8_lossy(&data), "Hello, World!");
11455 },
11456 _ => panic!("Expected Blob result"),
11457 }
11458
11459 router
11461 .execute_parsed(&format!("BLOB DELETE '{}'", artifact_id))
11462 .unwrap();
11463
11464 let get_after_delete = router.execute_parsed(&format!("BLOB GET '{}'", artifact_id));
11466 assert!(get_after_delete.is_err());
11467 }
11468
11469 #[test]
11470 fn test_blob_info() {
11471 let mut router = QueryRouter::new();
11472 router.init_blob().unwrap();
11473 router.set_identity("user:test");
11474
11475 let put_result = router
11476 .execute_parsed("BLOB PUT 'info_test.txt' 'test data'")
11477 .unwrap();
11478 let artifact_id = match put_result {
11479 QueryResult::Value(id) => id,
11480 _ => panic!("Expected Value result"),
11481 };
11482
11483 let info_result = router
11484 .execute_parsed(&format!("BLOB INFO '{}'", artifact_id))
11485 .unwrap();
11486 match info_result {
11487 QueryResult::ArtifactInfo(info) => {
11488 assert_eq!(info.filename, "info_test.txt");
11489 assert_eq!(info.size, 9);
11490 },
11491 _ => panic!("Expected ArtifactInfo result"),
11492 }
11493 }
11494
11495 #[test]
11496 fn test_blob_link_unlink() {
11497 let mut router = QueryRouter::new();
11498 router.init_blob().unwrap();
11499 router.set_identity("user:test");
11500
11501 let put_result = router
11502 .execute_parsed("BLOB PUT 'link_test.txt' 'data'")
11503 .unwrap();
11504 let artifact_id = match put_result {
11505 QueryResult::Value(id) => id,
11506 _ => panic!("Expected Value result"),
11507 };
11508
11509 router
11511 .execute_parsed(&format!("BLOB LINK '{}' TO 'task:123'", artifact_id))
11512 .unwrap();
11513
11514 let links_result = router
11516 .execute_parsed(&format!("BLOB LINKS '{}'", artifact_id))
11517 .unwrap();
11518 match links_result {
11519 QueryResult::ArtifactList(links) => {
11520 assert!(links.contains(&"task:123".to_string()));
11521 },
11522 _ => panic!("Expected ArtifactList result"),
11523 }
11524
11525 router
11527 .execute_parsed(&format!("BLOB UNLINK '{}' FROM 'task:123'", artifact_id))
11528 .unwrap();
11529 }
11530
11531 #[test]
11532 fn test_blob_tag_untag() {
11533 let mut router = QueryRouter::new();
11534 router.init_blob().unwrap();
11535 router.set_identity("user:test");
11536
11537 let put_result = router
11538 .execute_parsed("BLOB PUT 'tag_test.txt' 'data'")
11539 .unwrap();
11540 let artifact_id = match put_result {
11541 QueryResult::Value(id) => id,
11542 _ => panic!("Expected Value result"),
11543 };
11544
11545 router
11547 .execute_parsed(&format!("BLOB TAG '{}' 'important'", artifact_id))
11548 .unwrap();
11549
11550 let info = router
11552 .execute_parsed(&format!("BLOB INFO '{}'", artifact_id))
11553 .unwrap();
11554 match info {
11555 QueryResult::ArtifactInfo(info) => {
11556 assert!(info.tags.contains(&"important".to_string()));
11557 },
11558 _ => panic!("Expected ArtifactInfo"),
11559 }
11560
11561 router
11563 .execute_parsed(&format!("BLOB UNTAG '{}' 'important'", artifact_id))
11564 .unwrap();
11565 }
11566
11567 #[test]
11568 fn test_blob_verify() {
11569 let mut router = QueryRouter::new();
11570 router.init_blob().unwrap();
11571 router.set_identity("user:test");
11572
11573 let put_result = router
11574 .execute_parsed("BLOB PUT 'verify_test.txt' 'verify me'")
11575 .unwrap();
11576 let artifact_id = match put_result {
11577 QueryResult::Value(id) => id,
11578 _ => panic!("Expected Value result"),
11579 };
11580
11581 let verify_result = router
11582 .execute_parsed(&format!("BLOB VERIFY '{}'", artifact_id))
11583 .unwrap();
11584 match verify_result {
11585 QueryResult::Value(v) => assert_eq!(v, "OK"),
11586 _ => panic!("Expected Value result"),
11587 }
11588 }
11589
11590 #[test]
11591 fn test_blob_gc() {
11592 let mut router = QueryRouter::new();
11593 router.init_blob().unwrap();
11594 router.set_identity("user:test");
11595
11596 let gc_result = router.execute_parsed("BLOB GC").unwrap();
11597 match gc_result {
11598 QueryResult::Value(v) => {
11599 assert!(v.contains("Deleted"));
11600 assert!(v.contains("freed"));
11601 },
11602 _ => panic!("Expected Value result"),
11603 }
11604 }
11605
11606 #[test]
11607 fn test_blob_gc_full() {
11608 let mut router = QueryRouter::new();
11609 router.init_blob().unwrap();
11610 router.set_identity("user:test");
11611
11612 let gc_result = router.execute_parsed("BLOB GC FULL").unwrap();
11613 match gc_result {
11614 QueryResult::Value(v) => {
11615 assert!(v.contains("Deleted"));
11616 assert!(v.contains("freed"));
11617 },
11618 _ => panic!("Expected Value result"),
11619 }
11620 }
11621
11622 #[test]
11623 fn test_blob_repair() {
11624 let mut router = QueryRouter::new();
11625 router.init_blob().unwrap();
11626 router.set_identity("user:test");
11627
11628 let repair_result = router.execute_parsed("BLOB REPAIR").unwrap();
11629 match repair_result {
11630 QueryResult::Value(v) => {
11631 assert!(v.contains("Fixed"));
11632 assert!(v.contains("orphans"));
11633 },
11634 _ => panic!("Expected Value result"),
11635 }
11636 }
11637
11638 #[test]
11639 fn test_blob_stats() {
11640 let mut router = QueryRouter::new();
11641 router.init_blob().unwrap();
11642 router.set_identity("user:test");
11643
11644 let stats_result = router.execute_parsed("BLOB STATS").unwrap();
11645 match stats_result {
11646 QueryResult::BlobStats(stats) => {
11647 assert_eq!(stats.artifact_count, 0);
11648 },
11649 _ => panic!("Expected BlobStats result"),
11650 }
11651 }
11652
11653 #[test]
11654 fn test_blob_meta_set_get() {
11655 let mut router = QueryRouter::new();
11656 router.init_blob().unwrap();
11657 router.set_identity("user:test");
11658
11659 let put_result = router
11660 .execute_parsed("BLOB PUT 'meta_test.txt' 'data'")
11661 .unwrap();
11662 let artifact_id = match put_result {
11663 QueryResult::Value(id) => id,
11664 _ => panic!("Expected Value result"),
11665 };
11666
11667 router
11669 .execute_parsed(&format!("BLOB META SET '{}' 'author' 'alice'", artifact_id))
11670 .unwrap();
11671
11672 let meta_result = router
11674 .execute_parsed(&format!("BLOB META GET '{}' 'author'", artifact_id))
11675 .unwrap();
11676 match meta_result {
11677 QueryResult::Value(v) => assert_eq!(v, "alice"),
11678 _ => panic!("Expected Value result"),
11679 }
11680
11681 let missing_meta = router
11683 .execute_parsed(&format!("BLOB META GET '{}' 'nonexistent'", artifact_id))
11684 .unwrap();
11685 match missing_meta {
11686 QueryResult::Value(v) => assert_eq!(v, "(not found)"),
11687 _ => panic!("Expected Value result"),
11688 }
11689 }
11690
11691 #[test]
11692 fn test_blob_put_missing_data() {
11693 let mut router = QueryRouter::new();
11694 router.init_blob().unwrap();
11695 router.set_identity("user:test");
11696
11697 let result = router.execute_parsed("BLOB PUT 'missing.txt'");
11699 assert!(result.is_err());
11700 }
11701
11702 #[test]
11705 fn test_blobs_list() {
11706 let mut router = QueryRouter::new();
11707 router.init_blob().unwrap();
11708 router.set_identity("user:test");
11709
11710 router
11712 .execute_parsed("BLOB PUT 'file1.txt' 'data1'")
11713 .unwrap();
11714 router
11715 .execute_parsed("BLOB PUT 'file2.txt' 'data2'")
11716 .unwrap();
11717
11718 let list_result = router.execute_parsed("BLOBS").unwrap();
11720 match list_result {
11721 QueryResult::ArtifactList(list) => {
11722 assert_eq!(list.len(), 2);
11723 },
11724 _ => panic!("Expected ArtifactList result"),
11725 }
11726 }
11727
11728 #[test]
11729 fn test_blobs_list_with_pattern() {
11730 let mut router = QueryRouter::new();
11731 router.init_blob().unwrap();
11732 router.set_identity("user:test");
11733
11734 let list_result = router.execute_parsed("BLOBS 'some_prefix'");
11736 match list_result {
11737 Ok(QueryResult::ArtifactList(_)) => {},
11738 _ => panic!("Expected ArtifactList result"),
11739 }
11740 }
11741
11742 #[test]
11743 fn test_blobs_find_by_link() {
11744 let mut router = QueryRouter::new();
11745 router.init_blob().unwrap();
11746 router.set_identity("user:test");
11747
11748 let put_result = router
11749 .execute_parsed("BLOB PUT 'linked.txt' 'data'")
11750 .unwrap();
11751 let artifact_id = match put_result {
11752 QueryResult::Value(id) => id,
11753 _ => panic!("Expected Value result"),
11754 };
11755 router
11756 .execute_parsed(&format!("BLOB LINK '{}' TO 'project:alpha'", artifact_id))
11757 .unwrap();
11758
11759 let find_result = router.execute_parsed("BLOBS FOR 'project:alpha'").unwrap();
11761 match find_result {
11762 QueryResult::ArtifactList(list) => {
11763 assert!(!list.is_empty());
11764 },
11765 _ => panic!("Expected ArtifactList result"),
11766 }
11767 }
11768
11769 #[test]
11770 fn test_blobs_find_by_tag() {
11771 let mut router = QueryRouter::new();
11772 router.init_blob().unwrap();
11773 router.set_identity("user:test");
11774
11775 let put_result = router
11776 .execute_parsed("BLOB PUT 'tagged.txt' 'data'")
11777 .unwrap();
11778 let artifact_id = match put_result {
11779 QueryResult::Value(id) => id,
11780 _ => panic!("Expected Value result"),
11781 };
11782 router
11783 .execute_parsed(&format!("BLOB TAG '{}' 'urgent'", artifact_id))
11784 .unwrap();
11785
11786 let find_result = router.execute_parsed("BLOBS BY TAG 'urgent'").unwrap();
11788 match find_result {
11789 QueryResult::ArtifactList(list) => {
11790 assert!(!list.is_empty());
11791 },
11792 _ => panic!("Expected ArtifactList result"),
11793 }
11794 }
11795
11796 #[test]
11797 fn test_blobs_not_initialized() {
11798 let mut router = QueryRouter::new();
11799 router.set_identity("user:test");
11800 let result = router.execute_parsed("BLOBS LIST");
11801 assert!(result.is_err());
11802 }
11803
11804 #[test]
11807 fn test_vault_get_not_found() {
11808 let mut router = QueryRouter::new();
11809 router.init_vault(b"test_master_key_32bytes!").unwrap();
11810 router.set_identity(Vault::ROOT);
11811
11812 let result = router.execute_parsed("VAULT GET 'nonexistent'");
11813 assert!(result.is_err());
11814 }
11815
11816 #[test]
11817 fn test_blob_get_not_found() {
11818 let mut router = QueryRouter::new();
11819 router.init_blob().unwrap();
11820 router.set_identity("user:test");
11821
11822 let result = router.execute_parsed("BLOB GET 'artifact:nonexistent'");
11823 assert!(result.is_err());
11824 }
11825
11826 #[test]
11827 fn test_blob_delete_not_found() {
11828 let mut router = QueryRouter::new();
11829 router.init_blob().unwrap();
11830 router.set_identity("user:test");
11831
11832 let result = router.execute_parsed("BLOB DELETE 'artifact:nonexistent'");
11833 assert!(result.is_err());
11834 }
11835
11836 #[test]
11837 fn test_blob_info_not_found() {
11838 let mut router = QueryRouter::new();
11839 router.init_blob().unwrap();
11840 router.set_identity("user:test");
11841
11842 let result = router.execute_parsed("BLOB INFO 'artifact:nonexistent'");
11843 assert!(result.is_err());
11844 }
11845
11846 #[test]
11847 fn test_blob_verify_not_found() {
11848 let mut router = QueryRouter::new();
11849 router.init_blob().unwrap();
11850 router.set_identity("user:test");
11851
11852 let result = router.execute_parsed("BLOB VERIFY 'artifact:nonexistent'");
11853 assert!(result.is_err());
11854 }
11855
11856 #[test]
11857 fn test_start_blob_not_initialized() {
11858 let mut router = QueryRouter::new();
11859 let result = router.start_blob();
11860 assert!(result.is_err());
11861 }
11862
11863 #[test]
11864 fn test_blob_put_with_options() {
11865 let mut router = QueryRouter::new();
11866 router.init_blob().unwrap();
11867 router.set_identity("user:test");
11868
11869 let result = router
11871 .execute_parsed("BLOB PUT 'options_test.txt' 'data' LINK 'task:123' TAG 'important'");
11872 assert!(result.is_ok());
11873
11874 let artifact_id = match result.unwrap() {
11875 QueryResult::Value(id) => id,
11876 _ => panic!("Expected Value result"),
11877 };
11878
11879 let links = router
11881 .execute_parsed(&format!("BLOB LINKS '{}'", artifact_id))
11882 .unwrap();
11883 match links {
11884 QueryResult::ArtifactList(l) => {
11885 assert!(l.contains(&"task:123".to_string()));
11886 },
11887 _ => panic!("Expected ArtifactList"),
11888 }
11889
11890 let info = router
11892 .execute_parsed(&format!("BLOB INFO '{}'", artifact_id))
11893 .unwrap();
11894 match info {
11895 QueryResult::ArtifactInfo(i) => {
11896 assert!(i.tags.contains(&"important".to_string()));
11897 },
11898 _ => panic!("Expected ArtifactInfo"),
11899 }
11900 }
11901
11902 #[test]
11903 fn test_blobs_similar() {
11904 let mut router = QueryRouter::new();
11905 router.init_blob().unwrap();
11906
11907 let result = router.execute_parsed("BLOBS SIMILAR TO 'artifact:test' LIMIT 5");
11909 assert!(result.is_err() || result.is_ok());
11911 }
11912
11913 #[test]
11914 fn test_blobs_for_entity() {
11915 let mut router = QueryRouter::new();
11916 router.init_blob().unwrap();
11917 router.set_identity("user:test");
11918
11919 let result = router.execute_parsed("BLOBS FOR 'task:123'");
11920 match result {
11921 Ok(QueryResult::ArtifactList(_)) => {},
11922 _ => panic!("Expected ArtifactList result"),
11923 }
11924 }
11925
11926 #[test]
11927 fn test_blobs_by_type() {
11928 let mut router = QueryRouter::new();
11929 router.init_blob().unwrap();
11930 router.set_identity("user:test");
11931
11932 let result = router.execute_parsed("BLOBS WHERE TYPE = 'text/plain'");
11933 match result {
11934 Ok(QueryResult::ArtifactList(_)) => {},
11935 _ => panic!("Expected ArtifactList result"),
11936 }
11937 }
11938
11939 #[test]
11942 fn test_shutdown_blob() {
11943 let mut router = QueryRouter::new();
11944 router.init_blob().unwrap();
11945
11946 let result = router.shutdown_blob();
11948 assert!(result.is_ok());
11949 }
11950
11951 #[test]
11952 fn test_shutdown_blob_not_initialized() {
11953 let mut router = QueryRouter::new();
11954 let result = router.shutdown_blob();
11956 assert!(result.is_ok());
11957 }
11958
11959 #[test]
11960 fn test_set_identity() {
11961 let mut router = QueryRouter::new();
11962 assert_eq!(router.current_identity(), None);
11964 assert!(!router.is_authenticated());
11965
11966 router.set_identity("user:alice");
11967 assert_eq!(router.current_identity(), Some("user:alice"));
11968 assert!(router.is_authenticated());
11969 }
11970
11971 #[test]
11972 fn test_vault_requires_authentication() {
11973 let mut router = QueryRouter::new();
11974 router.init_vault(b"test_master_key_32bytes!").unwrap();
11975
11976 let result = router.execute_parsed("VAULT GET 'api_key'");
11978 assert!(result.is_err());
11979 let err = result.unwrap_err();
11980 assert!(
11981 matches!(err, RouterError::AuthenticationRequired),
11982 "Expected AuthenticationRequired, got: {:?}",
11983 err
11984 );
11985
11986 router.set_identity("user:alice");
11988 let result = router.execute_parsed("VAULT GET 'nonexistent_key'");
11989 match result {
11991 Err(RouterError::AuthenticationRequired) => {
11992 panic!("Should not get AuthenticationRequired after set_identity")
11993 },
11994 _ => {}, }
11996 }
11997
11998 #[test]
11999 fn test_cache_requires_authentication() {
12000 let mut router = QueryRouter::new();
12001 router.init_cache_default().unwrap();
12002
12003 let result = router.execute_parsed("CACHE STATS");
12005 assert!(result.is_err());
12006 let err = result.unwrap_err();
12007 assert!(
12008 matches!(err, RouterError::AuthenticationRequired),
12009 "Expected AuthenticationRequired, got: {:?}",
12010 err
12011 );
12012
12013 router.set_identity("user:test");
12015 let result = router.execute_parsed("CACHE STATS");
12016 if let Err(RouterError::AuthenticationRequired) = result {
12017 panic!("Should not get AuthenticationRequired after set_identity")
12018 }
12019 }
12020
12021 #[test]
12022 fn test_blob_requires_authentication() {
12023 let mut router = QueryRouter::new();
12024 router.init_blob().unwrap();
12025
12026 let result = router.execute_parsed("BLOB INIT");
12028 assert!(result.is_err());
12029 let err = result.unwrap_err();
12030 assert!(
12031 matches!(err, RouterError::AuthenticationRequired),
12032 "Expected AuthenticationRequired, got: {:?}",
12033 err
12034 );
12035
12036 router.set_identity("user:test");
12038 let result = router.execute_parsed("BLOB INIT");
12039 if let Err(RouterError::AuthenticationRequired) = result {
12040 panic!("Should not get AuthenticationRequired after set_identity")
12041 }
12042 }
12043
12044 #[test]
12045 fn test_blobs_requires_authentication() {
12046 let mut router = QueryRouter::new();
12047 router.init_blob().unwrap();
12048
12049 let result = router.execute_parsed("BLOBS");
12051 assert!(result.is_err());
12052 let err = result.unwrap_err();
12053 assert!(
12054 matches!(err, RouterError::AuthenticationRequired),
12055 "Expected AuthenticationRequired, got: {:?}",
12056 err
12057 );
12058
12059 router.set_identity("user:test");
12061 let result = router.execute_parsed("BLOBS");
12062 if let Err(RouterError::AuthenticationRequired) = result {
12063 panic!("Should not get AuthenticationRequired after set_identity")
12064 }
12065 }
12066
12067 #[test]
12068 fn test_chain_requires_authentication() {
12069 let mut router = QueryRouter::new();
12070 router.init_chain("test_node").unwrap();
12071
12072 let result = router.execute_parsed("CHAIN HEIGHT");
12074 assert!(result.is_err());
12075 let err = result.unwrap_err();
12076 assert!(
12077 matches!(err, RouterError::AuthenticationRequired),
12078 "Expected AuthenticationRequired, got: {:?}",
12079 err
12080 );
12081
12082 router.set_identity("user:test");
12084 let result = router.execute_parsed("CHAIN HEIGHT");
12085 if let Err(RouterError::AuthenticationRequired) = result {
12086 panic!("Should not get AuthenticationRequired after set_identity")
12087 }
12088 }
12089
12090 #[test]
12091 fn test_init_cache_default() {
12092 let mut router = QueryRouter::new();
12093 let result = router.init_cache_default();
12094 assert!(result.is_ok());
12095 assert!(router.cache().is_some());
12096 }
12097
12098 #[test]
12099 fn test_init_cache_with_config() {
12100 let mut router = QueryRouter::new();
12101 let config = tensor_cache::CacheConfig::default();
12102 let _ = router.init_cache_with_config(config);
12103 assert!(router.cache().is_some());
12104 }
12105
12106 #[test]
12107 fn test_blob_accessor() {
12108 let mut router = QueryRouter::new();
12109 assert!(router.blob().is_none());
12110
12111 router.init_blob().unwrap();
12112 assert!(router.blob().is_some());
12113 }
12114
12115 #[test]
12116 fn test_error_display_all_variants() {
12117 let errors = vec![
12118 RouterError::ParseError("parse msg".to_string()),
12119 RouterError::UnknownCommand("unknown".to_string()),
12120 RouterError::RelationalError("rel msg".to_string()),
12121 RouterError::GraphError("graph msg".to_string()),
12122 RouterError::VectorError("vec msg".to_string()),
12123 RouterError::VaultError("vault msg".to_string()),
12124 RouterError::CacheError("cache msg".to_string()),
12125 RouterError::BlobError("blob msg".to_string()),
12126 RouterError::InvalidArgument("invalid msg".to_string()),
12127 RouterError::TypeMismatch("type msg".to_string()),
12128 RouterError::MissingArgument("missing msg".to_string()),
12129 ];
12130
12131 for e in errors {
12132 let display = format!("{}", e);
12133 assert!(!display.is_empty());
12134 }
12135 }
12136
12137 #[test]
12138 fn test_blob_from_path() {
12139 let mut router = QueryRouter::new();
12140 router.init_blob().unwrap();
12141 router.set_identity("user:test");
12142
12143 let result = router.execute_parsed("BLOB PUT 'from_path.txt' FROM '/nonexistent/path'");
12145 assert!(result.is_err());
12146 }
12147
12148 #[test]
12149 fn test_blob_get_to_path() {
12150 let mut router = QueryRouter::new();
12151 router.init_blob().unwrap();
12152 router.set_identity("user:test");
12153
12154 let put_result = router
12156 .execute_parsed("BLOB PUT 'get_to.txt' 'test data'")
12157 .unwrap();
12158 let artifact_id = match put_result {
12159 QueryResult::Value(id) => id,
12160 _ => panic!("Expected Value result"),
12161 };
12162
12163 let result = router.execute_parsed(&format!(
12165 "BLOB GET '{}' TO '/nonexistent/dir/file.txt'",
12166 artifact_id
12167 ));
12168 assert!(result.is_err());
12169 }
12170
12171 #[test]
12172 fn test_init_vault() {
12173 let mut router = QueryRouter::new();
12174 let result = router.init_vault(b"32_byte_master_key_for_testing!");
12175 assert!(result.is_ok());
12176 }
12177
12178 #[test]
12179 fn test_vault_rotate_nonexistent() {
12180 let mut router = QueryRouter::new();
12181 router
12182 .init_vault(b"32_byte_master_key_for_testing!")
12183 .unwrap();
12184 router.set_identity(Vault::ROOT);
12185
12186 let result = router.execute_parsed("VAULT ROTATE 'nonexistent' 'new_value'");
12187 assert!(result.is_err());
12188 }
12189
12190 #[test]
12191 fn test_vault_delete_nonexistent() {
12192 let mut router = QueryRouter::new();
12193 router
12194 .init_vault(b"32_byte_master_key_for_testing!")
12195 .unwrap();
12196 router.set_identity(Vault::ROOT);
12197
12198 let result = router.execute_parsed("VAULT DELETE 'nonexistent'");
12199 assert!(result.is_err());
12200 }
12201
12202 #[test]
12203 fn test_blob_link_nonexistent() {
12204 let mut router = QueryRouter::new();
12205 router.init_blob().unwrap();
12206
12207 let result = router.execute_parsed("BLOB LINK 'nonexistent' TO 'entity'");
12208 assert!(result.is_err());
12209 }
12210
12211 #[test]
12212 fn test_blob_unlink_nonexistent() {
12213 let mut router = QueryRouter::new();
12214 router.init_blob().unwrap();
12215
12216 let result = router.execute_parsed("BLOB UNLINK 'nonexistent' FROM 'entity'");
12217 assert!(result.is_err());
12218 }
12219
12220 #[test]
12221 fn test_blob_tag_nonexistent() {
12222 let mut router = QueryRouter::new();
12223 router.init_blob().unwrap();
12224
12225 let result = router.execute_parsed("BLOB TAG 'nonexistent' 'tag'");
12226 assert!(result.is_err());
12227 }
12228
12229 #[test]
12230 fn test_blob_untag_nonexistent() {
12231 let mut router = QueryRouter::new();
12232 router.init_blob().unwrap();
12233
12234 let result = router.execute_parsed("BLOB UNTAG 'nonexistent' 'tag'");
12235 assert!(result.is_err());
12236 }
12237
12238 #[test]
12239 fn test_blob_links_nonexistent() {
12240 let mut router = QueryRouter::new();
12241 router.init_blob().unwrap();
12242
12243 let result = router.execute_parsed("BLOB LINKS 'nonexistent'");
12244 assert!(result.is_err());
12245 }
12246
12247 #[test]
12248 fn test_blob_meta_set_nonexistent() {
12249 let mut router = QueryRouter::new();
12250 router.init_blob().unwrap();
12251
12252 let result = router.execute_parsed("BLOB META SET 'nonexistent' 'key' 'value'");
12253 assert!(result.is_err());
12254 }
12255
12256 #[test]
12257 fn test_blob_meta_get_nonexistent() {
12258 let mut router = QueryRouter::new();
12259 router.init_blob().unwrap();
12260
12261 let result = router.execute_parsed("BLOB META GET 'nonexistent' 'key'");
12262 assert!(result.is_err());
12263 }
12264
12265 #[test]
12266 fn test_blob_get_to_valid_path() {
12267 let mut router = QueryRouter::new();
12268 router.init_blob().unwrap();
12269 router.set_identity("user:test");
12270
12271 let put_result = router
12272 .execute_parsed("BLOB PUT 'get_to_valid.txt' 'test'")
12273 .unwrap();
12274 let artifact_id = match put_result {
12275 QueryResult::Value(id) => id,
12276 _ => panic!("Expected Value result"),
12277 };
12278
12279 let temp_dir = std::env::temp_dir();
12281 let temp_path = temp_dir.join("neumann_test_blob_output.txt");
12282 let temp_str = temp_path.to_string_lossy().replace('\\', "/");
12284 let result = router.execute_parsed(&format!("BLOB GET '{artifact_id}' TO '{temp_str}'"));
12285 assert!(result.is_ok(), "BLOB GET TO failed: {result:?}");
12286
12287 let _ = std::fs::remove_file(&temp_path);
12289 }
12290
12291 #[test]
12292 fn test_find_similar_connected_no_embedding() {
12293 let router = QueryRouter::new();
12294 let result = router.find_similar_connected("nonexistent", "other", 5);
12296 assert!(result.is_ok());
12297 assert!(result.unwrap().is_empty());
12298 }
12299
12300 #[test]
12301 fn test_query_result_debug() {
12302 let result = QueryResult::Empty;
12304 let debug_str = format!("{:?}", result);
12305 assert!(!debug_str.is_empty());
12306
12307 let result = QueryResult::Value("test".to_string());
12308 let debug_str = format!("{:?}", result);
12309 assert!(debug_str.contains("test"));
12310 }
12311
12312 #[test]
12313 fn test_error_from_conversions() {
12314 let rel_err = relational_engine::RelationalError::TableNotFound("test".to_string());
12316 let router_err: RouterError = rel_err.into();
12317 assert!(matches!(router_err, RouterError::RelationalError(_)));
12318
12319 let graph_err = graph_engine::GraphError::NodeNotFound(1);
12320 let router_err: RouterError = graph_err.into();
12321 assert!(matches!(router_err, RouterError::GraphError(_)));
12322
12323 let vec_err = vector_engine::VectorError::NotFound("test".to_string());
12324 let router_err: RouterError = vec_err.into();
12325 assert!(matches!(router_err, RouterError::VectorError(_)));
12326 }
12327
12328 #[test]
12331 fn parsed_entity_create_basic() {
12332 let router = QueryRouter::new();
12333 let result = router.execute_parsed("ENTITY CREATE 'user:1' { name: 'Alice' }");
12334 assert!(result.is_ok());
12335 match result.unwrap() {
12336 QueryResult::Value(msg) => {
12337 assert!(msg.contains("Entity 'user:1' created"));
12338 },
12339 _ => panic!("expected Value result"),
12340 }
12341 }
12342
12343 #[test]
12344 fn parsed_entity_create_with_embedding() {
12345 let router = QueryRouter::new();
12346 let result =
12347 router.execute_parsed("ENTITY CREATE 'doc:1' { title: 'Test' } EMBEDDING [1.0, 0.0]");
12348 assert!(result.is_ok());
12349
12350 let emb = router.vector().get_entity_embedding("doc:1");
12352 assert!(emb.is_ok());
12353 assert_eq!(emb.unwrap(), vec![1.0, 0.0]);
12354 }
12355
12356 #[test]
12357 fn parsed_entity_connect() {
12358 let router = QueryRouter::new();
12359
12360 let result = router.execute_parsed("ENTITY CONNECT 'user:1' -> 'user:2' : follows");
12362 assert!(result.is_ok());
12363 match result.unwrap() {
12364 QueryResult::Value(msg) => {
12365 assert!(msg.contains("Connected 'user:1' -> 'user:2'"));
12366 },
12367 _ => panic!("expected Value result"),
12368 }
12369 }
12370
12371 #[test]
12372 fn parsed_similar_connected_to() {
12373 let router = QueryRouter::new();
12374
12375 router
12377 .vector()
12378 .set_entity_embedding("query", vec![1.0, 0.0, 0.0])
12379 .unwrap();
12380 router
12381 .vector()
12382 .set_entity_embedding("user:1", vec![0.9, 0.1, 0.0])
12383 .unwrap();
12384 router
12385 .vector()
12386 .set_entity_embedding("user:2", vec![0.8, 0.2, 0.0])
12387 .unwrap();
12388
12389 add_test_edge(router.graph(), "hub", "user:1", "connects");
12391 add_test_edge(router.graph(), "hub", "user:2", "connects");
12392
12393 let result = router.execute_parsed("SIMILAR 'query' CONNECTED TO 'hub' LIMIT 5");
12395 assert!(result.is_ok());
12396 match result.unwrap() {
12397 QueryResult::Similar(results) => {
12398 assert!(!results.is_empty());
12399 },
12400 _ => panic!("expected Similar result"),
12401 }
12402 }
12403
12404 #[test]
12405 fn parsed_similar_connected_to_requires_key() {
12406 let router = QueryRouter::new();
12407
12408 let result = router.execute_parsed("SIMILAR [1.0, 0.0] CONNECTED TO 'hub'");
12410 assert!(result.is_err());
12411 let err = result.unwrap_err();
12412 assert!(err.to_string().contains("requires a key"));
12413 }
12414
12415 #[test]
12416 fn parsed_neighbors_by_similarity() {
12417 let router = QueryRouter::new();
12418
12419 router
12421 .vector()
12422 .set_entity_embedding("user:1", vec![1.0, 0.0])
12423 .unwrap();
12424 router
12425 .vector()
12426 .set_entity_embedding("user:2", vec![0.0, 1.0])
12427 .unwrap();
12428
12429 add_test_edge(router.graph(), "center", "user:1", "knows");
12431 add_test_edge(router.graph(), "center", "user:2", "knows");
12432
12433 let result = router.execute_parsed("NEIGHBORS 'center' BY SIMILAR [1.0, 0.0] LIMIT 5");
12435 assert!(result.is_ok());
12436 match result.unwrap() {
12437 QueryResult::Similar(results) => {
12438 assert!(!results.is_empty());
12440 assert_eq!(results[0].key, "user:1");
12442 },
12443 _ => panic!("expected Similar result"),
12444 }
12445 }
12446
12447 #[test]
12448 fn parsed_entity_create_empty_properties() {
12449 let router = QueryRouter::new();
12450 let result = router.execute_parsed("ENTITY CREATE 'empty:1' {}");
12451 assert!(result.is_ok());
12452 }
12453
12454 #[test]
12455 fn parsed_entity_create_multiple_properties() {
12456 let router = QueryRouter::new();
12457 let result =
12458 router.execute_parsed("ENTITY CREATE 'user:2' { name: 'Bob', age: 30, active: true }");
12459 assert!(result.is_ok());
12460 }
12461
12462 #[test]
12463 fn parser_entity_statement() {
12464 let result = parser::parse("ENTITY CREATE 'key' { prop: 'value' }");
12466 assert!(result.is_ok());
12467 let stmt = result.unwrap();
12468 assert!(matches!(stmt.kind, StatementKind::Entity(_)));
12469 }
12470
12471 #[test]
12472 fn parser_entity_connect_statement() {
12473 let result = parser::parse("ENTITY CONNECT 'from' -> 'to' : type");
12474 assert!(result.is_ok());
12475 let stmt = result.unwrap();
12476 if let StatementKind::Entity(entity) = stmt.kind {
12477 assert!(matches!(entity.operation, EntityOp::Connect { .. }));
12478 } else {
12479 panic!("expected Entity statement");
12480 }
12481 }
12482
12483 #[test]
12484 fn parser_similar_connected_to() {
12485 let result = parser::parse("SIMILAR 'key' CONNECTED TO 'hub' LIMIT 10");
12486 assert!(result.is_ok());
12487 let stmt = result.unwrap();
12488 if let StatementKind::Similar(similar) = stmt.kind {
12489 assert!(similar.connected_to.is_some());
12490 } else {
12491 panic!("expected Similar statement");
12492 }
12493 }
12494
12495 #[test]
12496 fn parser_neighbors_by_similarity() {
12497 let result = parser::parse("NEIGHBORS 'entity' BY SIMILAR [1.0, 0.0] LIMIT 5");
12498 assert!(result.is_ok());
12499 let stmt = result.unwrap();
12500 if let StatementKind::Neighbors(neighbors) = stmt.kind {
12501 assert!(neighbors.by_similarity.is_some());
12502 assert!(neighbors.limit.is_some());
12503 } else {
12504 panic!("expected Neighbors statement");
12505 }
12506 }
12507
12508 #[test]
12511 fn parsed_drop_index_on_table_column() {
12512 let router = QueryRouter::new();
12513
12514 router
12516 .execute_parsed("CREATE TABLE products (id INT, name TEXT)")
12517 .unwrap();
12518 router
12519 .execute_parsed("CREATE INDEX idx_name ON products(name)")
12520 .unwrap();
12521 assert!(router.relational().has_index("products", "name"));
12522
12523 let result = router.execute_parsed("DROP INDEX ON products(name)");
12525 assert!(result.is_ok());
12526 assert!(!router.relational().has_index("products", "name"));
12527 }
12528
12529 #[test]
12530 fn parsed_drop_index_if_exists() {
12531 let router = QueryRouter::new();
12532
12533 router
12535 .execute_parsed("CREATE TABLE items (id INT)")
12536 .unwrap();
12537
12538 let result = router.execute_parsed("DROP INDEX IF EXISTS ON items(id)");
12540 assert!(result.is_ok());
12541 }
12542
12543 #[test]
12544 fn parsed_drop_index_not_found() {
12545 let router = QueryRouter::new();
12546
12547 router
12548 .execute_parsed("CREATE TABLE data (col INT)")
12549 .unwrap();
12550
12551 let result = router.execute_parsed("DROP INDEX ON data(col)");
12553 assert!(result.is_err());
12554 }
12555
12556 #[test]
12557 fn parsed_drop_index_named_not_supported() {
12558 let router = QueryRouter::new();
12559
12560 let result = router.execute_parsed("DROP INDEX my_index");
12562 assert!(result.is_err());
12563 let err = result.unwrap_err();
12564 assert!(err.to_string().contains("not supported"));
12565 }
12566
12567 #[test]
12568 fn parser_drop_index_on_syntax() {
12569 let result = parser::parse("DROP INDEX ON users(email)");
12570 assert!(result.is_ok());
12571 let stmt = result.unwrap();
12572 if let StatementKind::DropIndex(drop) = stmt.kind {
12573 assert!(drop.table.is_some());
12574 assert_eq!(drop.table.unwrap().name, "users");
12575 assert!(drop.column.is_some());
12576 assert_eq!(drop.column.unwrap().name, "email");
12577 } else {
12578 panic!("expected DropIndex");
12579 }
12580 }
12581
12582 #[test]
12583 fn parser_drop_index_if_exists_on() {
12584 let result = parser::parse("DROP INDEX IF EXISTS ON products(sku)");
12585 assert!(result.is_ok());
12586 let stmt = result.unwrap();
12587 if let StatementKind::DropIndex(drop) = stmt.kind {
12588 assert!(drop.if_exists);
12589 assert!(drop.table.is_some());
12590 } else {
12591 panic!("expected DropIndex");
12592 }
12593 }
12594
12595 #[test]
12598 fn parsed_insert_select_with_where() {
12599 let router = QueryRouter::new();
12600 router
12601 .execute_parsed("CREATE TABLE employees (id INT, dept TEXT)")
12602 .unwrap();
12603 router
12604 .execute_parsed("CREATE TABLE engineers (id INT, dept TEXT)")
12605 .unwrap();
12606
12607 router
12608 .execute_parsed("INSERT INTO employees VALUES (1, 'eng')")
12609 .unwrap();
12610 router
12611 .execute_parsed("INSERT INTO employees VALUES (2, 'sales')")
12612 .unwrap();
12613 router
12614 .execute_parsed("INSERT INTO employees VALUES (3, 'eng')")
12615 .unwrap();
12616
12617 let result = router
12619 .execute_parsed("INSERT INTO engineers SELECT * FROM employees WHERE dept = 'eng'");
12620 assert!(result.is_ok());
12621
12622 let rows = router.execute_parsed("SELECT * FROM engineers").unwrap();
12623 match rows {
12624 QueryResult::Rows(r) => {
12625 assert_eq!(r.len(), 2);
12626 },
12627 _ => panic!("expected Rows"),
12628 }
12629 }
12630
12631 #[test]
12632 fn parsed_insert_select_empty_result() {
12633 let router = QueryRouter::new();
12634 router
12635 .execute_parsed("CREATE TABLE source (id INT)")
12636 .unwrap();
12637 router
12638 .execute_parsed("CREATE TABLE target (id INT)")
12639 .unwrap();
12640
12641 let result =
12643 router.execute_parsed("INSERT INTO target SELECT * FROM source WHERE id > 100");
12644 assert!(result.is_ok());
12645
12646 match result.unwrap() {
12647 QueryResult::Ids(ids) => {
12648 assert!(ids.is_empty());
12649 },
12650 _ => panic!("expected Ids"),
12651 }
12652 }
12653
12654 #[test]
12655 fn parsed_insert_select_with_columns() {
12656 let router = QueryRouter::new();
12657 router
12658 .execute_parsed("CREATE TABLE complete (id INT, name TEXT, age INT)")
12659 .unwrap();
12660 router
12661 .execute_parsed("CREATE TABLE partial (id INT, name TEXT)")
12662 .unwrap();
12663
12664 router
12665 .execute_parsed("INSERT INTO complete VALUES (1, 'Alice', 30)")
12666 .unwrap();
12667
12668 let result =
12670 router.execute_parsed("INSERT INTO partial (id, name) SELECT id, name FROM complete");
12671 assert!(result.is_ok());
12672
12673 let rows = router.execute_parsed("SELECT * FROM partial").unwrap();
12674 match rows {
12675 QueryResult::Rows(r) => {
12676 assert_eq!(r.len(), 1);
12677 },
12678 _ => panic!("expected Rows"),
12679 }
12680 }
12681
12682 #[test]
12683 fn parsed_blob_init_not_initialized() {
12684 let mut router = QueryRouter::new();
12685 router.set_identity("user:test");
12686 let result = router.execute_parsed("BLOB INIT");
12687 assert!(result.is_err());
12688 let err = result.unwrap_err();
12689 assert!(
12690 err.to_string().contains("init_blob"),
12691 "should mention init_blob()"
12692 );
12693 }
12694
12695 #[test]
12696 fn parsed_blob_init_already_initialized() {
12697 let mut router = QueryRouter::new();
12698 router.init_blob().unwrap();
12699 router.set_identity("user:test");
12700
12701 let result = router.execute_parsed("BLOB INIT");
12702 assert!(result.is_ok());
12703 match result.unwrap() {
12704 QueryResult::Value(v) => {
12705 assert!(
12706 v.contains("already initialized"),
12707 "should say already initialized"
12708 );
12709 },
12710 _ => panic!("expected Value"),
12711 }
12712 }
12713
12714 #[test]
12715 fn parsed_embed_build_index_not_built() {
12716 let router = QueryRouter::new();
12717 let result = router.execute_parsed("EMBED BUILD INDEX");
12718 assert!(result.is_err());
12719 let err = result.unwrap_err();
12720 assert!(
12721 err.to_string().contains("build_vector_index"),
12722 "should mention build_vector_index()"
12723 );
12724 }
12725
12726 #[test]
12727 fn parsed_embed_build_index_already_built() {
12728 let mut router = QueryRouter::new();
12729 router
12731 .execute_parsed("EMBED STORE 'key1' [1.0, 0.0]")
12732 .unwrap();
12733 router
12734 .execute_parsed("EMBED STORE 'key2' [0.0, 1.0]")
12735 .unwrap();
12736 router.build_vector_index().unwrap();
12737
12738 let result = router.execute_parsed("EMBED BUILD INDEX");
12739 assert!(result.is_ok());
12740 match result.unwrap() {
12741 QueryResult::Value(v) => {
12742 assert!(v.contains("already built"), "should say already built");
12743 },
12744 _ => panic!("expected Value"),
12745 }
12746 }
12747
12748 #[test]
12751 fn parsed_embed_batch_basic() {
12752 let router = QueryRouter::new();
12753 let result = router.execute_parsed(
12754 "EMBED BATCH [('doc1', [1.0, 0.0]), ('doc2', [0.0, 1.0]), ('doc3', [0.5, 0.5])]",
12755 );
12756 assert!(result.is_ok());
12757 match result.unwrap() {
12758 QueryResult::Count(n) => {
12759 assert_eq!(n, 3, "should store 3 embeddings");
12760 },
12761 _ => panic!("expected Count"),
12762 }
12763
12764 let result = router.execute_parsed("EMBED GET 'doc1'");
12766 assert!(result.is_ok());
12767 }
12768
12769 #[test]
12770 fn parsed_embed_batch_empty() {
12771 let router = QueryRouter::new();
12772 let result = router.execute_parsed("EMBED BATCH []");
12773 assert!(result.is_ok());
12774 match result.unwrap() {
12775 QueryResult::Count(n) => {
12776 assert_eq!(n, 0, "empty batch should return 0");
12777 },
12778 _ => panic!("expected Count"),
12779 }
12780 }
12781
12782 #[test]
12783 fn parsed_cache_semantic_put() {
12784 let mut router = QueryRouter::new();
12785 let mut config = CacheConfig::default();
12787 config.embedding_dim = 3;
12788 let _ = router.init_cache_with_config(config);
12789 router.set_identity("user:test");
12790
12791 let result = router.execute_parsed(
12792 "CACHE SEMANTIC PUT 'What is 2+2?' 'The answer is 4' EMBEDDING [1.0, 0.0, 0.0]",
12793 );
12794 assert!(result.is_ok());
12795 match result.unwrap() {
12796 QueryResult::Value(v) => {
12797 assert_eq!(v, "OK");
12798 },
12799 _ => panic!("expected Value"),
12800 }
12801 }
12802
12803 #[test]
12804 fn parsed_cache_semantic_get() {
12805 let mut router = QueryRouter::new();
12806 let mut config = CacheConfig::default();
12808 config.embedding_dim = 2;
12809 let _ = router.init_cache_with_config(config);
12810 router.set_identity("user:test");
12811
12812 router
12814 .execute_parsed("CACHE SEMANTIC PUT 'hello' 'world' EMBEDDING [1.0, 0.0]")
12815 .unwrap();
12816
12817 router
12819 .execute_parsed("EMBED STORE 'hello' [1.0, 0.0]")
12820 .unwrap();
12821
12822 let result = router.execute_parsed("CACHE SEMANTIC GET 'hello'");
12824 assert!(result.is_ok());
12825 }
12826
12827 #[test]
12828 fn parsed_cache_semantic_get_with_threshold() {
12829 let mut router = QueryRouter::new();
12830 router.init_cache();
12831 router.set_identity("user:test");
12832
12833 let result = router.execute_parsed("CACHE SEMANTIC GET 'unknown query' THRESHOLD 0.9");
12834 assert!(result.is_ok());
12835 match result.unwrap() {
12836 QueryResult::Value(v) => {
12837 assert!(v.contains("not found"));
12838 },
12839 _ => panic!("expected Value"),
12840 }
12841 }
12842
12843 #[test]
12844 fn parsed_describe_table() {
12845 let router = QueryRouter::new();
12846 router
12847 .execute_parsed("CREATE TABLE users (id INT NOT NULL, name TEXT, active BOOLEAN)")
12848 .unwrap();
12849
12850 let result = router.execute_parsed("DESCRIBE TABLE users");
12851 assert!(result.is_ok());
12852 match result.unwrap() {
12853 QueryResult::Value(v) => {
12854 assert!(v.contains("Table: users"));
12855 assert!(v.contains("id"));
12856 assert!(v.contains("name"));
12857 assert!(v.contains("active"));
12858 },
12859 _ => panic!("expected Value"),
12860 }
12861 }
12862
12863 #[test]
12864 fn parsed_describe_node() {
12865 let router = QueryRouter::new();
12866 router
12867 .execute_parsed("NODE CREATE person {name: 'Alice'}")
12868 .unwrap();
12869
12870 let result = router.execute_parsed("DESCRIBE NODE person");
12871 assert!(result.is_ok());
12872 match result.unwrap() {
12873 QueryResult::Value(v) => {
12874 assert!(v.contains("Node label 'person'"));
12875 },
12876 _ => panic!("expected Value"),
12877 }
12878 }
12879
12880 #[test]
12881 fn parsed_describe_edge() {
12882 let router = QueryRouter::new();
12883
12884 let result = router.execute_parsed("DESCRIBE EDGE follows");
12885 assert!(result.is_ok());
12886 match result.unwrap() {
12887 QueryResult::Value(v) => {
12888 assert!(v.contains("Edge type 'follows'"));
12889 },
12890 _ => panic!("expected Value"),
12891 }
12892 }
12893
12894 #[test]
12895 fn parsed_show_embeddings() {
12896 let router = QueryRouter::new();
12897 router
12898 .execute_parsed("EMBED STORE 'emb1' [1.0, 0.0]")
12899 .unwrap();
12900 router
12901 .execute_parsed("EMBED STORE 'emb2' [0.0, 1.0]")
12902 .unwrap();
12903
12904 let result = router.execute_parsed("SHOW EMBEDDINGS");
12905 assert!(result.is_ok());
12906 match result.unwrap() {
12907 QueryResult::Value(v) => {
12908 assert!(v.contains("emb1") || v.contains("emb2"));
12909 },
12910 _ => panic!("expected Value"),
12911 }
12912 }
12913
12914 #[test]
12915 fn parsed_show_embeddings_with_limit() {
12916 let router = QueryRouter::new();
12917 for i in 0..10 {
12918 router
12919 .execute_parsed(&format!("EMBED STORE 'key{}' [{}]", i, i as f32))
12920 .unwrap();
12921 }
12922
12923 let result = router.execute_parsed("SHOW EMBEDDINGS LIMIT 5");
12924 assert!(result.is_ok());
12925 }
12926
12927 #[test]
12928 fn parsed_count_embeddings() {
12929 let router = QueryRouter::new();
12930 router.execute_parsed("EMBED STORE 'a' [1.0]").unwrap();
12931 router.execute_parsed("EMBED STORE 'b' [2.0]").unwrap();
12932 router.execute_parsed("EMBED STORE 'c' [3.0]").unwrap();
12933
12934 let result = router.execute_parsed("COUNT EMBEDDINGS");
12935 assert!(result.is_ok());
12936 match result.unwrap() {
12937 QueryResult::Count(n) => {
12938 assert_eq!(n, 3);
12939 },
12940 _ => panic!("expected Count"),
12941 }
12942 }
12943
12944 #[test]
12945 fn test_query_result_to_json() {
12946 let result = QueryResult::Value("test".to_string());
12947 let json = result.to_json();
12948 assert!(json.contains("Value"));
12949 assert!(json.contains("test"));
12950 }
12951
12952 #[test]
12953 fn test_query_result_to_pretty_json() {
12954 let result = QueryResult::Count(42);
12955 let json = result.to_pretty_json();
12956 assert!(json.contains("Count"));
12957 assert!(json.contains("42"));
12958 }
12959
12960 #[test]
12961 fn test_query_result_is_empty() {
12962 assert!(QueryResult::Empty.is_empty());
12963 assert!(!QueryResult::Value("x".to_string()).is_empty());
12964 }
12965
12966 #[test]
12967 fn test_query_result_as_count() {
12968 assert_eq!(QueryResult::Count(10).as_count(), Some(10));
12969 assert_eq!(QueryResult::Empty.as_count(), None);
12970 }
12971
12972 #[test]
12973 fn test_query_result_as_value() {
12974 let result = QueryResult::Value("hello".to_string());
12975 assert_eq!(result.as_value(), Some("hello"));
12976 assert_eq!(QueryResult::Empty.as_value(), None);
12977 }
12978
12979 #[test]
12980 fn test_query_result_as_rows() {
12981 let values = vec![("name".to_string(), Value::String("test".to_string()))];
12982 let rows = vec![Row { id: 1, values }];
12983 let result = QueryResult::Rows(rows);
12984 assert!(result.as_rows().is_some());
12985 assert_eq!(result.as_rows().unwrap().len(), 1);
12986 assert!(QueryResult::Empty.as_rows().is_none());
12987 }
12988
12989 #[test]
12992 fn test_ensure_cache_auto_init() {
12993 let mut router = QueryRouter::new();
12994 assert!(router.cache().is_none());
12995
12996 let cache = router.ensure_cache();
12998 assert_eq!(cache.stats().total_entries(), 0);
12999
13000 let cache2 = router.ensure_cache();
13002 assert_eq!(cache2.stats().total_entries(), 0);
13003 }
13004
13005 #[test]
13006 fn test_ensure_blob_auto_init() {
13007 let mut router = QueryRouter::new();
13008 assert!(router.blob().is_none());
13009
13010 let result = router.ensure_blob();
13012 assert!(result.is_ok());
13013
13014 let result2 = router.ensure_blob();
13016 assert!(result2.is_ok());
13017 }
13018
13019 #[test]
13020 fn test_ensure_vault_no_env_key() {
13021 let mut router = QueryRouter::new();
13022 assert!(router.vault().is_none());
13023
13024 let saved = std::env::var("NEUMANN_VAULT_KEY").ok();
13026 std::env::remove_var("NEUMANN_VAULT_KEY");
13027
13028 let result = router.ensure_vault();
13030 assert!(result.is_err());
13031 if let Err(err) = result {
13032 assert!(err.to_string().contains("not initialized"));
13033 }
13034
13035 if let Some(key) = saved {
13037 std::env::set_var("NEUMANN_VAULT_KEY", key);
13038 }
13039 }
13040
13041 #[test]
13042 fn test_ensure_vault_with_pre_init() {
13043 let mut router = QueryRouter::new();
13044 router
13045 .init_vault(b"32_byte_master_key_for_testing!")
13046 .unwrap();
13047
13048 let result = router.ensure_vault();
13050 assert!(result.is_ok());
13051 }
13052
13053 #[test]
13054 fn test_ensure_cache_idempotent() {
13055 let mut router = QueryRouter::new();
13056
13057 let _ = router.ensure_cache();
13059 let _ = router.ensure_cache();
13060 let _ = router.ensure_cache();
13061
13062 assert!(router.cache().is_some());
13064 }
13065
13066 #[test]
13067 fn test_ensure_blob_idempotent() {
13068 let mut router = QueryRouter::new();
13069
13070 let _ = router.ensure_blob();
13072 let _ = router.ensure_blob();
13073 let _ = router.ensure_blob();
13074
13075 assert!(router.blob().is_some());
13077 }
13078
13079 #[tokio::test]
13082 async fn test_execute_parsed_async_basic() {
13083 let router = QueryRouter::new();
13084
13085 let result = router
13087 .execute_parsed_async("CREATE TABLE async_test (id INT, name VARCHAR(100))")
13088 .await;
13089 assert!(result.is_ok());
13090
13091 let result = router
13093 .execute_parsed_async("INSERT INTO async_test (id, name) VALUES (1, 'test')")
13094 .await;
13095 assert!(result.is_ok());
13096
13097 let result = router
13099 .execute_parsed_async("SELECT * FROM async_test")
13100 .await;
13101 assert!(result.is_ok());
13102 if let QueryResult::Rows(rows) = result.unwrap() {
13103 assert_eq!(rows.len(), 1);
13104 }
13105 }
13106
13107 #[tokio::test]
13108 async fn test_execute_statement_async_delegates() {
13109 let router = QueryRouter::new();
13110
13111 let stmt = parser::parse("NODE CREATE user { name: 'Alice' }").unwrap();
13113
13114 let result = router.execute_statement_async(&stmt).await;
13116 assert!(result.is_ok());
13117 }
13118
13119 #[tokio::test]
13120 async fn test_embed_batch_parallel() {
13121 let router = QueryRouter::new();
13122
13123 let items: Vec<(String, Vec<f32>)> = (0..10)
13125 .map(|i| (format!("parallel:{}", i), vec![i as f32 / 10.0; 4]))
13126 .collect();
13127
13128 let result = router.embed_batch_parallel(items).await;
13130 assert!(result.is_ok());
13131 assert_eq!(result.unwrap(), 10);
13132
13133 for i in 0..10 {
13135 let key = format!("parallel:{}", i);
13136 let emb = router.vector().get_embedding(&key);
13137 assert!(emb.is_ok());
13138 }
13139 }
13140
13141 #[tokio::test]
13142 async fn test_find_similar_connected_async() {
13143 let router = QueryRouter::new();
13144
13145 router
13147 .vector()
13148 .set_entity_embedding("query", vec![1.0, 0.0, 0.0])
13149 .unwrap();
13150 router
13151 .vector()
13152 .set_entity_embedding("user:1", vec![0.9, 0.1, 0.0])
13153 .unwrap();
13154 router
13155 .vector()
13156 .set_entity_embedding("user:2", vec![0.8, 0.2, 0.0])
13157 .unwrap();
13158
13159 add_test_edge(router.graph(), "hub", "user:1", "connects");
13161 add_test_edge(router.graph(), "hub", "user:2", "connects");
13162
13163 let result = router.find_similar_connected_async("query", "hub", 5).await;
13165 assert!(result.is_ok());
13166 let items = result.unwrap();
13167 assert!(!items.is_empty());
13168 }
13169
13170 #[tokio::test]
13171 async fn test_find_neighbors_by_similarity_async() {
13172 let router = QueryRouter::new();
13173
13174 add_test_edge(router.graph(), "center", "neighbor:1", "links");
13176 add_test_edge(router.graph(), "center", "neighbor:2", "links");
13177 add_test_edge(router.graph(), "center", "neighbor:3", "links");
13178
13179 router
13180 .vector()
13181 .set_entity_embedding("neighbor:1", vec![1.0, 0.0, 0.0])
13182 .unwrap();
13183 router
13184 .vector()
13185 .set_entity_embedding("neighbor:2", vec![0.9, 0.1, 0.0])
13186 .unwrap();
13187 router
13188 .vector()
13189 .set_entity_embedding("neighbor:3", vec![0.0, 1.0, 0.0])
13190 .unwrap();
13191
13192 let query = vec![1.0, 0.0, 0.0];
13194 let result = router
13195 .find_neighbors_by_similarity_async("center", &query, 3)
13196 .await;
13197 assert!(result.is_ok());
13198 let items = result.unwrap();
13199 assert_eq!(items.len(), 3);
13200 assert!(items[0].id.contains("neighbor:1") || items[0].score.unwrap() > 0.9);
13202 }
13203
13204 #[test]
13205 fn test_block_on_helper() {
13206 let mut router = QueryRouter::new();
13209 router.init_blob().unwrap();
13210
13211 let result = router.block_on(async { 42 + 1 });
13213 assert!(result.is_ok());
13214 assert_eq!(result.unwrap(), 43);
13215 }
13216
13217 #[test]
13218 fn test_runtime_accessor() {
13219 let router = QueryRouter::new();
13220 assert!(router.runtime().is_none());
13222
13223 let mut router = QueryRouter::new();
13224 router.init_blob().unwrap();
13225 assert!(router.runtime().is_some());
13226 }
13227
13228 #[tokio::test]
13229 async fn test_execute_parsed_async_with_cache() {
13230 let mut router = QueryRouter::new();
13231 router.init_cache();
13232
13233 router
13235 .execute_parsed_async("CREATE TABLE cached (x INT)")
13236 .await
13237 .unwrap();
13238 router
13239 .execute_parsed_async("INSERT INTO cached (x) VALUES (1)")
13240 .await
13241 .unwrap();
13242
13243 let result1 = router.execute_parsed_async("SELECT * FROM cached").await;
13245 assert!(result1.is_ok());
13246
13247 let result2 = router.execute_parsed_async("SELECT * FROM cached").await;
13249 assert!(result2.is_ok());
13250 }
13251
13252 #[tokio::test]
13253 async fn test_embed_batch_parallel_empty() {
13254 let router = QueryRouter::new();
13255
13256 let result = router.embed_batch_parallel(vec![]).await;
13258 assert!(result.is_ok());
13259 assert_eq!(result.unwrap(), 0);
13260 }
13261
13262 #[tokio::test]
13263 async fn test_execute_parsed_async_error() {
13264 let router = QueryRouter::new();
13265
13266 let result = router.execute_parsed_async("INVALID QUERY XYZ").await;
13268 assert!(result.is_err());
13269 }
13270
13271 #[test]
13275 fn test_exec_blob_async_put_get() {
13276 let mut router = QueryRouter::new();
13277 router.init_blob().unwrap();
13278
13279 router
13280 .block_on(async {
13281 let stmt = parser::parse("BLOB PUT 'test.txt' 'hello world'").unwrap();
13283 let result = router.execute_statement_async(&stmt).await;
13284 assert!(result.is_ok());
13285 let artifact_id = match result.unwrap() {
13286 QueryResult::Value(id) => id,
13287 _ => panic!("Expected Value result"),
13288 };
13289
13290 let stmt = parser::parse(&format!("BLOB GET '{}'", artifact_id)).unwrap();
13292 let result = router.execute_statement_async(&stmt).await;
13293 assert!(result.is_ok());
13294 if let QueryResult::Blob(data) = result.unwrap() {
13295 assert_eq!(String::from_utf8(data).unwrap(), "hello world");
13296 }
13297 })
13298 .unwrap();
13299 }
13300
13301 #[test]
13302 fn test_exec_blob_async_info() {
13303 let mut router = QueryRouter::new();
13304 router.init_blob().unwrap();
13305
13306 router
13307 .block_on(async {
13308 let stmt = parser::parse("BLOB PUT 'info.txt' 'test data'").unwrap();
13310 let result = router.execute_statement_async(&stmt).await.unwrap();
13311 let artifact_id = match result {
13312 QueryResult::Value(id) => id,
13313 _ => panic!("Expected Value result"),
13314 };
13315
13316 let stmt = parser::parse(&format!("BLOB INFO '{}'", artifact_id)).unwrap();
13318 let result = router.execute_statement_async(&stmt).await;
13319 assert!(result.is_ok());
13320 if let QueryResult::ArtifactInfo(info) = result.unwrap() {
13321 assert_eq!(info.filename, "info.txt");
13322 assert_eq!(info.size, 9); }
13324 })
13325 .unwrap();
13326 }
13327
13328 #[test]
13329 fn test_exec_blob_async_link_unlink() {
13330 let mut router = QueryRouter::new();
13331 router.init_blob().unwrap();
13332
13333 router
13334 .block_on(async {
13335 let stmt = parser::parse("BLOB PUT 'linked.txt' 'link test'").unwrap();
13337 let result = router.execute_statement_async(&stmt).await.unwrap();
13338 let artifact_id = match result {
13339 QueryResult::Value(id) => id,
13340 _ => panic!("Expected Value result"),
13341 };
13342
13343 let stmt =
13345 parser::parse(&format!("BLOB LINK '{}' TO 'entity:1'", artifact_id)).unwrap();
13346 let result = router.execute_statement_async(&stmt).await;
13347 assert!(result.is_ok());
13348
13349 let stmt = parser::parse(&format!("BLOB LINKS '{}'", artifact_id)).unwrap();
13351 let result = router.execute_statement_async(&stmt).await;
13352 assert!(result.is_ok());
13353 if let QueryResult::ArtifactList(links) = result.unwrap() {
13354 assert!(links.contains(&"entity:1".to_string()));
13355 }
13356
13357 let stmt = parser::parse(&format!("BLOB UNLINK '{}' FROM 'entity:1'", artifact_id))
13359 .unwrap();
13360 let result = router.execute_statement_async(&stmt).await;
13361 assert!(result.is_ok());
13362 })
13363 .unwrap();
13364 }
13365
13366 #[test]
13367 fn test_exec_blob_async_tag_untag() {
13368 let mut router = QueryRouter::new();
13369 router.init_blob().unwrap();
13370
13371 router
13372 .block_on(async {
13373 let stmt = parser::parse("BLOB PUT 'tagged.txt' 'tag test'").unwrap();
13375 let result = router.execute_statement_async(&stmt).await.unwrap();
13376 let artifact_id = match result {
13377 QueryResult::Value(id) => id,
13378 _ => panic!("Expected Value result"),
13379 };
13380
13381 let stmt =
13383 parser::parse(&format!("BLOB TAG '{}' 'important'", artifact_id)).unwrap();
13384 let result = router.execute_statement_async(&stmt).await;
13385 assert!(result.is_ok());
13386
13387 let stmt =
13389 parser::parse(&format!("BLOB UNTAG '{}' 'important'", artifact_id)).unwrap();
13390 let result = router.execute_statement_async(&stmt).await;
13391 assert!(result.is_ok());
13392 })
13393 .unwrap();
13394 }
13395
13396 #[test]
13397 fn test_exec_blob_async_verify() {
13398 let mut router = QueryRouter::new();
13399 router.init_blob().unwrap();
13400
13401 router
13402 .block_on(async {
13403 let stmt = parser::parse("BLOB PUT 'verify.txt' 'verify test'").unwrap();
13405 let result = router.execute_statement_async(&stmt).await.unwrap();
13406 let artifact_id = match result {
13407 QueryResult::Value(id) => id,
13408 _ => panic!("Expected Value result"),
13409 };
13410
13411 let stmt = parser::parse(&format!("BLOB VERIFY '{}'", artifact_id)).unwrap();
13413 let result = router.execute_statement_async(&stmt).await;
13414 assert!(result.is_ok());
13415 if let QueryResult::Value(v) = result.unwrap() {
13416 assert_eq!(v, "OK");
13417 }
13418 })
13419 .unwrap();
13420 }
13421
13422 #[test]
13423 fn test_exec_blob_async_stats() {
13424 let mut router = QueryRouter::new();
13425 router.init_blob().unwrap();
13426
13427 router
13428 .block_on(async {
13429 let stmt = parser::parse("BLOB STATS").unwrap();
13431 let result = router.execute_statement_async(&stmt).await;
13432 assert!(result.is_ok());
13433 if let QueryResult::BlobStats(stats) = result.unwrap() {
13434 assert!(stats.dedup_ratio >= 0.0);
13436 }
13437 })
13438 .unwrap();
13439 }
13440
13441 #[test]
13442 fn test_exec_blob_async_gc() {
13443 let mut router = QueryRouter::new();
13444 router.init_blob().unwrap();
13445
13446 router
13447 .block_on(async {
13448 let stmt = parser::parse("BLOB GC").unwrap();
13450 let result = router.execute_statement_async(&stmt).await;
13451 assert!(result.is_ok());
13452 })
13453 .unwrap();
13454 }
13455
13456 #[test]
13457 fn test_exec_blob_async_delete() {
13458 let mut router = QueryRouter::new();
13459 router.init_blob().unwrap();
13460
13461 router
13462 .block_on(async {
13463 let stmt = parser::parse("BLOB PUT 'delete.txt' 'delete test'").unwrap();
13465 let result = router.execute_statement_async(&stmt).await.unwrap();
13466 let artifact_id = match result {
13467 QueryResult::Value(id) => id,
13468 _ => panic!("Expected Value result"),
13469 };
13470
13471 let stmt = parser::parse(&format!("BLOB DELETE '{}'", artifact_id)).unwrap();
13473 let result = router.execute_statement_async(&stmt).await;
13474 assert!(result.is_ok());
13475 })
13476 .unwrap();
13477 }
13478
13479 #[test]
13480 fn test_exec_blob_async_meta() {
13481 let mut router = QueryRouter::new();
13482 router.init_blob().unwrap();
13483
13484 router
13485 .block_on(async {
13486 let stmt = parser::parse("BLOB PUT 'meta.txt' 'meta test'").unwrap();
13488 let result = router.execute_statement_async(&stmt).await.unwrap();
13489 let artifact_id = match result {
13490 QueryResult::Value(id) => id,
13491 _ => panic!("Expected Value result"),
13492 };
13493
13494 let stmt = parser::parse(&format!("BLOB META SET '{}' 'key' 'value'", artifact_id))
13496 .unwrap();
13497 let result = router.execute_statement_async(&stmt).await;
13498 assert!(result.is_ok());
13499
13500 let stmt =
13502 parser::parse(&format!("BLOB META GET '{}' 'key'", artifact_id)).unwrap();
13503 let result = router.execute_statement_async(&stmt).await;
13504 assert!(result.is_ok());
13505 if let QueryResult::Value(v) = result.unwrap() {
13506 assert_eq!(v, "value");
13507 }
13508 })
13509 .unwrap();
13510 }
13511
13512 #[test]
13513 fn test_exec_blob_async_repair() {
13514 let mut router = QueryRouter::new();
13515 router.init_blob().unwrap();
13516
13517 router
13518 .block_on(async {
13519 let stmt = parser::parse("BLOB REPAIR").unwrap();
13521 let result = router.execute_statement_async(&stmt).await;
13522 assert!(result.is_ok());
13523 })
13524 .unwrap();
13525 }
13526
13527 #[test]
13528 fn test_exec_blobs_async_list() {
13529 let mut router = QueryRouter::new();
13530 router.init_blob().unwrap();
13531
13532 router
13533 .block_on(async {
13534 let stmt = parser::parse("BLOB PUT 'list1.txt' 'data1'").unwrap();
13536 router.execute_statement_async(&stmt).await.unwrap();
13537 let stmt = parser::parse("BLOB PUT 'list2.txt' 'data2'").unwrap();
13538 router.execute_statement_async(&stmt).await.unwrap();
13539
13540 let stmt = parser::parse("BLOBS").unwrap();
13542 let result = router.execute_statement_async(&stmt).await;
13543 assert!(result.is_ok());
13544 if let QueryResult::ArtifactList(ids) = result.unwrap() {
13545 assert!(ids.len() >= 2);
13546 }
13547 })
13548 .unwrap();
13549 }
13550
13551 #[test]
13552 fn test_exec_blobs_async_for_entity() {
13553 let mut router = QueryRouter::new();
13554 router.init_blob().unwrap();
13555
13556 router
13557 .block_on(async {
13558 let stmt = parser::parse("BLOB PUT 'entity.txt' 'entity data'").unwrap();
13560 let result = router.execute_statement_async(&stmt).await.unwrap();
13561 let artifact_id = match result {
13562 QueryResult::Value(id) => id,
13563 _ => panic!("Expected Value result"),
13564 };
13565
13566 let stmt =
13567 parser::parse(&format!("BLOB LINK '{}' TO 'myentity'", artifact_id)).unwrap();
13568 router.execute_statement_async(&stmt).await.unwrap();
13569
13570 let stmt = parser::parse("BLOBS FOR 'myentity'").unwrap();
13572 let result = router.execute_statement_async(&stmt).await;
13573 assert!(result.is_ok());
13574 if let QueryResult::ArtifactList(ids) = result.unwrap() {
13575 assert!(ids.contains(&artifact_id));
13576 }
13577 })
13578 .unwrap();
13579 }
13580
13581 #[test]
13582 fn test_exec_blobs_async_by_tag() {
13583 let mut router = QueryRouter::new();
13584 router.init_blob().unwrap();
13585
13586 router
13587 .block_on(async {
13588 let stmt = parser::parse("BLOB PUT 'bytag.txt' 'tag data'").unwrap();
13590 let result = router.execute_statement_async(&stmt).await.unwrap();
13591 let artifact_id = match result {
13592 QueryResult::Value(id) => id,
13593 _ => panic!("Expected Value result"),
13594 };
13595
13596 let stmt = parser::parse(&format!("BLOB TAG '{}' 'mytag'", artifact_id)).unwrap();
13597 router.execute_statement_async(&stmt).await.unwrap();
13598
13599 let stmt = parser::parse("BLOBS BY TAG 'mytag'").unwrap();
13601 let result = router.execute_statement_async(&stmt).await;
13602 assert!(result.is_ok());
13603 if let QueryResult::ArtifactList(ids) = result.unwrap() {
13604 assert!(ids.contains(&artifact_id));
13605 }
13606 })
13607 .unwrap();
13608 }
13609
13610 #[test]
13611 fn test_exec_blob_async_init_already_initialized() {
13612 let mut router = QueryRouter::new();
13613 router.init_blob().unwrap();
13614
13615 router
13616 .block_on(async {
13617 let stmt = parser::parse("BLOB INIT").unwrap();
13619 let result = router.execute_statement_async(&stmt).await;
13620 assert!(result.is_ok());
13621 if let QueryResult::Value(v) = result.unwrap() {
13622 assert!(v.contains("already initialized"));
13623 }
13624 })
13625 .unwrap();
13626 }
13627
13628 #[test]
13629 fn test_exec_blob_async_not_initialized() {
13630 let router = QueryRouter::new();
13631 let stmt = parser::parse("BLOB STATS").unwrap();
13634 let result = router.execute_statement(&stmt);
13635 assert!(result.is_err());
13636 }
13637
13638 #[test]
13641 fn test_init_checkpoint_requires_dir() {
13642 let mut router = QueryRouter::new();
13643 let result = router.init_checkpoint();
13645 assert!(result.is_err());
13646 if let Err(RouterError::CheckpointError(msg)) = result {
13647 assert!(msg.contains("Checkpoint directory must be set"));
13648 }
13649 }
13650
13651 #[test]
13652 fn test_init_checkpoint_with_dir() {
13653 let dir = tempfile::tempdir().unwrap();
13654 let mut router = QueryRouter::new();
13655 router.set_checkpoint_dir(dir.path().to_path_buf());
13656 let result = router.init_checkpoint();
13657 assert!(result.is_ok());
13658 }
13659
13660 #[test]
13661 fn test_init_checkpoint_with_config() {
13662 let dir = tempfile::tempdir().unwrap();
13663 let mut router = QueryRouter::new();
13664 router.set_checkpoint_dir(dir.path().to_path_buf());
13665 let config = CheckpointConfig::default().with_max_checkpoints(5);
13666 let result = router.init_checkpoint_with_config(config);
13667 assert!(result.is_ok());
13668 }
13669
13670 #[test]
13671 fn test_ensure_checkpoint_auto_init() {
13672 let dir = tempfile::tempdir().unwrap();
13673 let mut router = QueryRouter::new();
13674 router.set_checkpoint_dir(dir.path().to_path_buf());
13675 let result = router.ensure_checkpoint();
13677 assert!(result.is_ok());
13678 }
13679
13680 #[test]
13681 fn test_ensure_checkpoint_already_initialized() {
13682 let dir = tempfile::tempdir().unwrap();
13683 let mut router = QueryRouter::new();
13684 router.set_checkpoint_dir(dir.path().to_path_buf());
13685 router.init_checkpoint().unwrap();
13686 let result = router.ensure_checkpoint();
13688 assert!(result.is_ok());
13689 }
13690
13691 #[test]
13692 fn test_exec_checkpoint_not_initialized() {
13693 let router = QueryRouter::new();
13694 let stmt = parser::parse("CHECKPOINT").unwrap();
13695 let result = router.execute_statement(&stmt);
13696 assert!(result.is_err());
13697 if let Err(RouterError::CheckpointError(msg)) = result {
13698 assert!(msg.contains("not initialized"));
13699 }
13700 }
13701
13702 #[test]
13703 fn test_exec_checkpoint_create() {
13704 let dir = tempfile::tempdir().unwrap();
13705 let mut router = QueryRouter::new();
13706 router.set_checkpoint_dir(dir.path().to_path_buf());
13707 router.init_checkpoint().unwrap();
13708
13709 let stmt = parser::parse("CHECKPOINT").unwrap();
13710 let result = router.execute_statement(&stmt);
13711 assert!(result.is_ok());
13712 if let QueryResult::Value(v) = result.unwrap() {
13713 assert!(v.contains("Checkpoint created"));
13714 }
13715 }
13716
13717 #[test]
13718 fn test_exec_checkpoint_with_name() {
13719 let dir = tempfile::tempdir().unwrap();
13720 let mut router = QueryRouter::new();
13721 router.set_checkpoint_dir(dir.path().to_path_buf());
13722 router.init_checkpoint().unwrap();
13723
13724 let stmt = parser::parse("CHECKPOINT 'my-checkpoint'").unwrap();
13725 let result = router.execute_statement(&stmt);
13726 assert!(result.is_ok());
13727 if let QueryResult::Value(v) = result.unwrap() {
13728 assert!(v.contains("Checkpoint created"));
13729 }
13730 }
13731
13732 #[test]
13733 fn test_exec_checkpoints_list() {
13734 let dir = tempfile::tempdir().unwrap();
13735 let mut router = QueryRouter::new();
13736 router.set_checkpoint_dir(dir.path().to_path_buf());
13737 router.init_checkpoint().unwrap();
13738
13739 let stmt = parser::parse("CHECKPOINT 'test-cp'").unwrap();
13741 router.execute_statement(&stmt).unwrap();
13742
13743 let stmt = parser::parse("CHECKPOINTS").unwrap();
13745 let result = router.execute_statement(&stmt);
13746 assert!(result.is_ok());
13747 if let QueryResult::CheckpointList(list) = result.unwrap() {
13748 assert!(!list.is_empty());
13749 assert_eq!(list[0].name, "test-cp");
13750 }
13751 }
13752
13753 #[test]
13754 fn test_exec_checkpoints_with_limit() {
13755 let dir = tempfile::tempdir().unwrap();
13756 let mut router = QueryRouter::new();
13757 router.set_checkpoint_dir(dir.path().to_path_buf());
13758 router.init_checkpoint().unwrap();
13759
13760 for i in 0..5 {
13762 let stmt = parser::parse(&format!("CHECKPOINT 'cp-{}'", i)).unwrap();
13763 router.execute_statement(&stmt).unwrap();
13764 }
13765
13766 let stmt = parser::parse("CHECKPOINTS LIMIT 3").unwrap();
13768 let result = router.execute_statement(&stmt);
13769 assert!(result.is_ok());
13770 if let QueryResult::CheckpointList(list) = result.unwrap() {
13771 assert_eq!(list.len(), 3);
13772 }
13773 }
13774
13775 #[test]
13776 fn test_exec_checkpoints_not_initialized() {
13777 let router = QueryRouter::new();
13778 let stmt = parser::parse("CHECKPOINTS").unwrap();
13779 let result = router.execute_statement(&stmt);
13780 assert!(result.is_err());
13781 }
13782
13783 #[test]
13784 fn test_exec_rollback_not_initialized() {
13785 let router = QueryRouter::new();
13786 let stmt = parser::parse("ROLLBACK TO 'some-id'").unwrap();
13787 let result = router.execute_statement(&stmt);
13788 assert!(result.is_err());
13789 if let Err(RouterError::CheckpointError(msg)) = result {
13790 assert!(msg.contains("not initialized"));
13791 }
13792 }
13793
13794 #[test]
13795 fn test_exec_rollback_success() {
13796 let dir = tempfile::tempdir().unwrap();
13797 let mut router = QueryRouter::new();
13798 router.set_checkpoint_dir(dir.path().to_path_buf());
13799 router.init_checkpoint().unwrap();
13800
13801 router.execute("EMBED testkey [1.0, 2.0, 3.0]").unwrap();
13803
13804 let cp_stmt = parser::parse("CHECKPOINT 'before-delete'").unwrap();
13806 router.execute_statement(&cp_stmt).unwrap();
13807
13808 router.execute_parsed("EMBED DELETE 'testkey'").unwrap();
13810 assert!(!router.vector().exists("testkey"));
13811
13812 let rb_stmt = parser::parse("ROLLBACK TO 'before-delete'").unwrap();
13814 let result = router.execute_statement(&rb_stmt);
13815 assert!(result.is_ok());
13816 if let QueryResult::Value(v) = result.unwrap() {
13817 assert!(v.contains("Rolled back"));
13818 }
13819
13820 assert!(router.vector().exists("testkey"));
13822 }
13823
13824 #[test]
13825 fn test_exec_rollback_not_found() {
13826 let dir = tempfile::tempdir().unwrap();
13827 let mut router = QueryRouter::new();
13828 router.set_checkpoint_dir(dir.path().to_path_buf());
13829 router.init_checkpoint().unwrap();
13830
13831 let stmt = parser::parse("ROLLBACK TO 'nonexistent'").unwrap();
13832 let result = router.execute_statement(&stmt);
13833 assert!(result.is_err());
13834 }
13835
13836 #[test]
13837 fn test_checkpoint_info_is_auto() {
13838 let dir = tempfile::tempdir().unwrap();
13839 let mut router = QueryRouter::new();
13840 router.set_checkpoint_dir(dir.path().to_path_buf());
13841 router.init_checkpoint().unwrap();
13842
13843 let stmt = parser::parse("CHECKPOINT 'manual'").unwrap();
13845 router.execute_statement(&stmt).unwrap();
13846
13847 let stmt = parser::parse("CHECKPOINTS").unwrap();
13848 let result = router.execute_statement(&stmt).unwrap();
13849 if let QueryResult::CheckpointList(list) = result {
13850 assert!(!list[0].is_auto);
13851 }
13852 }
13853
13854 #[test]
13855 fn test_checkpoint_error_display() {
13856 let e = RouterError::CheckpointError("test error".into());
13857 assert!(e.to_string().contains("Checkpoint error"));
13858 assert!(e.to_string().contains("test error"));
13859 }
13860
13861 #[test]
13862 fn test_exec_checkpoint_sync_success() {
13863 let dir = tempfile::tempdir().unwrap();
13864 let mut router = QueryRouter::new();
13865 router.set_checkpoint_dir(dir.path().to_path_buf());
13866 router.init_checkpoint().unwrap();
13867
13868 let stmt = parser::parse("CHECKPOINT 'sync-test'").unwrap();
13869 let result = router.execute_statement(&stmt);
13870 assert!(result.is_ok());
13871 if let QueryResult::Value(v) = result.unwrap() {
13872 assert!(v.contains("Checkpoint created"));
13873 }
13874 }
13875
13876 #[test]
13877 fn test_exec_checkpoints_sync_success() {
13878 let dir = tempfile::tempdir().unwrap();
13879 let mut router = QueryRouter::new();
13880 router.set_checkpoint_dir(dir.path().to_path_buf());
13881 router.init_checkpoint().unwrap();
13882
13883 let stmt = parser::parse("CHECKPOINT 'for-list'").unwrap();
13885 router.execute_statement(&stmt).unwrap();
13886
13887 let stmt = parser::parse("CHECKPOINTS").unwrap();
13888 let result = router.execute_statement(&stmt);
13889 assert!(result.is_ok());
13890 if let QueryResult::CheckpointList(list) = result.unwrap() {
13891 assert!(!list.is_empty());
13892 }
13893 }
13894
13895 #[test]
13896 fn test_exec_rollback_sync_success() {
13897 let dir = tempfile::tempdir().unwrap();
13898 let mut router = QueryRouter::new();
13899 router.set_checkpoint_dir(dir.path().to_path_buf());
13900 router.init_checkpoint().unwrap();
13901
13902 router.execute("EMBED synckey [1.0, 2.0]").unwrap();
13904 let stmt = parser::parse("CHECKPOINT 'sync-rollback'").unwrap();
13905 router.execute_statement(&stmt).unwrap();
13906
13907 router.execute_parsed("EMBED DELETE 'synckey'").unwrap();
13909 assert!(!router.vector().exists("synckey"));
13910
13911 let stmt = parser::parse("ROLLBACK TO 'sync-rollback'").unwrap();
13912 let result = router.execute_statement(&stmt);
13913 assert!(result.is_ok());
13914
13915 assert!(router.vector().exists("synckey"));
13917 }
13918
13919 #[test]
13920 fn test_checkpoint_with_limit() {
13921 let dir = tempfile::tempdir().unwrap();
13922 let mut router = QueryRouter::new();
13923 router.set_checkpoint_dir(dir.path().to_path_buf());
13924 router.init_checkpoint().unwrap();
13925
13926 let stmt = parser::parse("CHECKPOINTS LIMIT 5").unwrap();
13927 let result = router.execute_statement(&stmt);
13928 assert!(result.is_ok());
13929 }
13930
13931 #[test]
13932 fn test_checkpoint_list_empty() {
13933 let dir = tempfile::tempdir().unwrap();
13934 let mut router = QueryRouter::new();
13935 router.set_checkpoint_dir(dir.path().to_path_buf());
13936 router.init_checkpoint().unwrap();
13937
13938 let stmt = parser::parse("CHECKPOINTS").unwrap();
13940 let result = router.execute_statement(&stmt);
13941 assert!(result.is_ok());
13942 if let QueryResult::CheckpointList(list) = result.unwrap() {
13943 assert!(list.is_empty());
13944 }
13945 }
13946
13947 #[test]
13948 fn test_checkpoint_with_double_quoted_name() {
13949 let dir = tempfile::tempdir().unwrap();
13950 let mut router = QueryRouter::new();
13951 router.set_checkpoint_dir(dir.path().to_path_buf());
13952 router.init_checkpoint().unwrap();
13953
13954 let stmt = parser::parse("CHECKPOINT \"double-quoted\"").unwrap();
13955 let result = router.execute_statement(&stmt);
13956 assert!(result.is_ok());
13957 }
13958
13959 #[test]
13960 fn test_rollback_sync_by_id() {
13961 let dir = tempfile::tempdir().unwrap();
13962 let mut router = QueryRouter::new();
13963 router.set_checkpoint_dir(dir.path().to_path_buf());
13964 router.init_checkpoint().unwrap();
13965
13966 let stmt = parser::parse("CHECKPOINT 'rollback-by-id'").unwrap();
13968 router.execute_statement(&stmt).unwrap();
13969
13970 let stmt = parser::parse("CHECKPOINTS").unwrap();
13971 let result = router.execute_statement(&stmt).unwrap();
13972 let checkpoint_id = if let QueryResult::CheckpointList(list) = result {
13973 list[0].id.clone()
13974 } else {
13975 panic!("Expected CheckpointList");
13976 };
13977
13978 let stmt = parser::parse(&format!("ROLLBACK TO '{}'", checkpoint_id)).unwrap();
13980 let result = router.execute_statement(&stmt);
13981 assert!(result.is_ok());
13982 }
13983
13984 #[test]
13985 fn test_multiple_checkpoints_ordering() {
13986 let dir = tempfile::tempdir().unwrap();
13987 let mut router = QueryRouter::new();
13988 router.set_checkpoint_dir(dir.path().to_path_buf());
13989 router.init_checkpoint().unwrap();
13990
13991 router
13993 .execute_statement(&parser::parse("CHECKPOINT 'first'").unwrap())
13994 .unwrap();
13995 router
13996 .execute_statement(&parser::parse("CHECKPOINT 'second'").unwrap())
13997 .unwrap();
13998 router
13999 .execute_statement(&parser::parse("CHECKPOINT 'third'").unwrap())
14000 .unwrap();
14001
14002 let result = router
14004 .execute_statement(&parser::parse("CHECKPOINTS").unwrap())
14005 .unwrap();
14006 if let QueryResult::CheckpointList(list) = result {
14007 assert_eq!(list.len(), 3);
14008 }
14009 }
14010
14011 #[test]
14012 fn test_checkpoint_via_execute_parsed() {
14013 let dir = tempfile::tempdir().unwrap();
14014 let mut router = QueryRouter::new();
14015 router.set_checkpoint_dir(dir.path().to_path_buf());
14016 router.init_checkpoint().unwrap();
14017
14018 let result = router.execute_parsed("CHECKPOINT 'parsed-test'");
14019 assert!(result.is_ok());
14020 }
14021
14022 #[test]
14023 fn test_checkpoints_via_execute_parsed() {
14024 let dir = tempfile::tempdir().unwrap();
14025 let mut router = QueryRouter::new();
14026 router.set_checkpoint_dir(dir.path().to_path_buf());
14027 router.init_checkpoint().unwrap();
14028
14029 router.execute_parsed("CHECKPOINT 'test1'").unwrap();
14030 router.execute_parsed("CHECKPOINT 'test2'").unwrap();
14031
14032 let result = router.execute_parsed("CHECKPOINTS");
14033 assert!(result.is_ok());
14034 }
14035
14036 #[test]
14037 fn test_rollback_via_execute_parsed() {
14038 let dir = tempfile::tempdir().unwrap();
14039 let mut router = QueryRouter::new();
14040 router.set_checkpoint_dir(dir.path().to_path_buf());
14041 router.init_checkpoint().unwrap();
14042
14043 router
14044 .execute_parsed("CHECKPOINT 'rollback-parsed'")
14045 .unwrap();
14046 let result = router.execute_parsed("ROLLBACK TO 'rollback-parsed'");
14047 assert!(result.is_ok());
14048 }
14049
14050 #[test]
14051 fn test_checkpoint_default_name() {
14052 let dir = tempfile::tempdir().unwrap();
14053 let mut router = QueryRouter::new();
14054 router.set_checkpoint_dir(dir.path().to_path_buf());
14055 router.init_checkpoint().unwrap();
14056
14057 let stmt = parser::parse("CHECKPOINT").unwrap();
14059 let result = router.execute_statement(&stmt);
14060 assert!(result.is_ok());
14061
14062 let list_result = router
14063 .execute_statement(&parser::parse("CHECKPOINTS").unwrap())
14064 .unwrap();
14065 if let QueryResult::CheckpointList(list) = list_result {
14066 assert_eq!(list.len(), 1);
14067 assert!(list[0].name.starts_with("checkpoint-"));
14069 }
14070 }
14071
14072 #[test]
14075 fn test_chain_not_initialized() {
14076 let router = QueryRouter::new();
14077 let stmt = parser::parse("CHAIN HEIGHT").unwrap();
14078 let result = router.execute_statement(&stmt);
14079 assert!(result.is_err());
14080 if let Err(RouterError::ChainError(msg)) = result {
14081 assert!(msg.contains("not initialized"));
14082 }
14083 }
14084
14085 #[test]
14086 fn test_chain_height() {
14087 let mut router = QueryRouter::new();
14088 router.init_chain("test_node").unwrap();
14089 router.set_identity("user:test");
14090
14091 let stmt = parser::parse("CHAIN HEIGHT").unwrap();
14092 let result = router.execute_statement(&stmt).unwrap();
14093
14094 if let QueryResult::Chain(ChainResult::Height(h)) = result {
14095 assert_eq!(h, 0);
14096 } else {
14097 panic!("expected CHAIN HEIGHT result");
14098 }
14099 }
14100
14101 #[test]
14102 fn test_chain_tip() {
14103 let mut router = QueryRouter::new();
14104 router.init_chain("test_node").unwrap();
14105 router.set_identity("user:test");
14106
14107 let stmt = parser::parse("CHAIN TIP").unwrap();
14108 let result = router.execute_statement(&stmt).unwrap();
14109
14110 if let QueryResult::Chain(ChainResult::Tip { height, .. }) = result {
14111 assert_eq!(height, 0);
14112 } else {
14113 panic!("expected CHAIN TIP result");
14114 }
14115 }
14116
14117 #[test]
14118 fn test_chain_verify() {
14119 let mut router = QueryRouter::new();
14120 router.init_chain("test_node").unwrap();
14121 router.set_identity("user:test");
14122
14123 let stmt = parser::parse("CHAIN VERIFY").unwrap();
14124 let result = router.execute_statement(&stmt).unwrap();
14125
14126 if let QueryResult::Chain(ChainResult::Verified { ok, errors }) = result {
14127 assert!(ok);
14128 assert!(errors.is_empty());
14129 } else {
14130 panic!("expected CHAIN VERIFY result");
14131 }
14132 }
14133
14134 #[test]
14135 fn test_chain_block_not_found() {
14136 let mut router = QueryRouter::new();
14137 router.init_chain("test_node").unwrap();
14138 router.set_identity("user:test");
14139
14140 let stmt = parser::parse("CHAIN BLOCK 999").unwrap();
14141 let result = router.execute_statement(&stmt);
14142 assert!(result.is_err());
14143 }
14144
14145 #[test]
14146 fn test_chain_block_genesis() {
14147 let mut router = QueryRouter::new();
14148 router.init_chain("test_node").unwrap();
14149 router.set_identity("user:test");
14150
14151 let stmt = parser::parse("CHAIN BLOCK 0").unwrap();
14153 let result = router.execute_statement(&stmt);
14154
14155 match result {
14156 Ok(QueryResult::Chain(ChainResult::Block(info))) => {
14157 assert_eq!(info.height, 0);
14158 assert!(!info.hash.is_empty());
14159 },
14160 _ => panic!("Expected CHAIN BLOCK result, got {:?}", result),
14161 }
14162 }
14163
14164 #[test]
14165 fn test_chain_history() {
14166 let mut router = QueryRouter::new();
14167 router.init_chain("test_node").unwrap();
14168 router.set_identity("user:test");
14169
14170 let stmt = parser::parse("CHAIN HISTORY 'test_key'").unwrap();
14171 let result = router.execute_statement(&stmt).unwrap();
14172
14173 if let QueryResult::Chain(ChainResult::History(entries)) = result {
14174 assert!(entries.is_empty());
14176 } else {
14177 panic!("expected CHAIN HISTORY result");
14178 }
14179 }
14180
14181 #[test]
14182 fn test_chain_drift() {
14183 let mut router = QueryRouter::new();
14184 router.init_chain("test_node").unwrap();
14185 router.set_identity("user:test");
14186
14187 let stmt = parser::parse("CHAIN DRIFT FROM 0 TO 100").unwrap();
14188 let result = router.execute_statement(&stmt).unwrap();
14189
14190 if let QueryResult::Chain(ChainResult::Drift(drift)) = result {
14191 assert_eq!(drift.from_height, 0);
14192 assert_eq!(drift.to_height, 100);
14193 } else {
14194 panic!("expected CHAIN DRIFT result");
14195 }
14196 }
14197
14198 #[test]
14199 fn test_chain_begin() {
14200 let mut router = QueryRouter::new();
14201 router.init_chain("test_node").unwrap();
14202 router.set_identity("user:test");
14203
14204 let stmt = parser::parse("BEGIN CHAIN TRANSACTION").unwrap();
14205 let result = router.execute_statement(&stmt).unwrap();
14206
14207 if let QueryResult::Chain(ChainResult::TransactionBegun { tx_id }) = result {
14208 assert!(!tx_id.is_empty());
14209 } else {
14210 panic!("expected CHAIN BEGIN result");
14211 }
14212 }
14213
14214 #[test]
14215 fn test_show_codebook_global() {
14216 let mut router = QueryRouter::new();
14217 router.init_chain("test_node").unwrap();
14218 router.set_identity("user:test");
14219
14220 let stmt = parser::parse("SHOW CODEBOOK GLOBAL").unwrap();
14221 let result = router.execute_statement(&stmt).unwrap();
14222
14223 if let QueryResult::Chain(ChainResult::Codebook(info)) = result {
14224 assert_eq!(info.scope, "global");
14225 } else {
14226 panic!("expected SHOW CODEBOOK GLOBAL result");
14227 }
14228 }
14229
14230 #[test]
14231 fn test_show_codebook_local() {
14232 let mut router = QueryRouter::new();
14233 router.init_chain("test_node").unwrap();
14234 router.set_identity("user:test");
14235
14236 let stmt = parser::parse("SHOW CODEBOOK LOCAL 'users'").unwrap();
14237 let result = router.execute_statement(&stmt).unwrap();
14238
14239 if let QueryResult::Chain(ChainResult::Codebook(info)) = result {
14240 assert_eq!(info.scope, "local");
14241 assert_eq!(info.domain, Some("users".to_string()));
14242 } else {
14243 panic!("expected SHOW CODEBOOK LOCAL result");
14244 }
14245 }
14246
14247 #[test]
14248 fn test_analyze_codebook_transitions() {
14249 let mut router = QueryRouter::new();
14250 router.init_chain("test_node").unwrap();
14251 router.set_identity("user:test");
14252
14253 let stmt = parser::parse("ANALYZE CODEBOOK TRANSITIONS").unwrap();
14254 let result = router.execute_statement(&stmt).unwrap();
14255
14256 if let QueryResult::Chain(ChainResult::TransitionAnalysis(analysis)) = result {
14257 assert_eq!(analysis.total_transitions, 0);
14258 } else {
14259 panic!("expected ANALYZE CODEBOOK TRANSITIONS result");
14260 }
14261 }
14262
14263 fn setup_join_tables(router: &QueryRouter) {
14266 router
14267 .execute_parsed("CREATE TABLE users (id INT, name TEXT)")
14268 .unwrap();
14269 router
14270 .execute_parsed("CREATE TABLE orders (id INT, user_id INT, amount INT)")
14271 .unwrap();
14272
14273 router
14274 .execute_parsed("INSERT INTO users (id, name) VALUES (1, 'Alice')")
14275 .unwrap();
14276 router
14277 .execute_parsed("INSERT INTO users (id, name) VALUES (2, 'Bob')")
14278 .unwrap();
14279 router
14280 .execute_parsed("INSERT INTO users (id, name) VALUES (3, 'Charlie')")
14281 .unwrap();
14282
14283 router
14284 .execute_parsed("INSERT INTO orders (id, user_id, amount) VALUES (101, 1, 100)")
14285 .unwrap();
14286 router
14287 .execute_parsed("INSERT INTO orders (id, user_id, amount) VALUES (102, 1, 200)")
14288 .unwrap();
14289 router
14290 .execute_parsed("INSERT INTO orders (id, user_id, amount) VALUES (103, 2, 150)")
14291 .unwrap();
14292 router
14293 .execute_parsed("INSERT INTO orders (id, user_id, amount) VALUES (104, 99, 50)")
14294 .unwrap();
14295 }
14296
14297 #[test]
14298 fn test_inner_join_via_router() {
14299 let router = QueryRouter::new();
14300 setup_join_tables(&router);
14301
14302 let stmt =
14303 parser::parse("SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id")
14304 .unwrap();
14305 let result = router.execute_statement(&stmt).unwrap();
14306
14307 let rows = unwrap_qr_rows(result);
14308 assert_eq!(rows.len(), 3);
14309 let alice_orders: Vec<_> = rows
14311 .iter()
14312 .filter(|r| {
14313 r.values
14314 .iter()
14315 .any(|(k, v)| k == "users.name" && v == &Value::String("Alice".to_string()))
14316 })
14317 .collect();
14318 assert_eq!(alice_orders.len(), 2);
14319 }
14320
14321 #[test]
14322 fn test_left_join_via_router() {
14323 let router = QueryRouter::new();
14324 setup_join_tables(&router);
14325
14326 let stmt =
14327 parser::parse("SELECT * FROM users LEFT JOIN orders ON users.id = orders.user_id")
14328 .unwrap();
14329 let result = router.execute_statement(&stmt).unwrap();
14330
14331 let rows = unwrap_qr_rows(result);
14332 assert_eq!(rows.len(), 4);
14334
14335 let charlie_row = rows
14337 .iter()
14338 .find(|r| {
14339 r.values
14340 .iter()
14341 .any(|(k, v)| k == "users.name" && v == &Value::String("Charlie".to_string()))
14342 })
14343 .expect("Charlie should be in result");
14344
14345 let has_orders_id = charlie_row.values.iter().any(|(k, _)| k == "orders._id");
14347 assert!(!has_orders_id, "Charlie should not have orders._id");
14348 }
14349
14350 #[test]
14351 fn test_right_join_via_router() {
14352 let router = QueryRouter::new();
14353 setup_join_tables(&router);
14354
14355 let stmt =
14356 parser::parse("SELECT * FROM users RIGHT JOIN orders ON users.id = orders.user_id")
14357 .unwrap();
14358 let result = router.execute_statement(&stmt).unwrap();
14359
14360 let rows = unwrap_qr_rows(result);
14361 assert_eq!(rows.len(), 4);
14363
14364 let orphan_order = rows
14366 .iter()
14367 .find(|r| {
14368 r.values
14369 .iter()
14370 .any(|(k, v)| k == "orders.id" && v == &Value::Int(104))
14371 })
14372 .expect("Order 104 should be in result");
14373
14374 let has_user_id = orphan_order.values.iter().any(|(k, _)| k == "users._id");
14375 assert!(!has_user_id, "Orphan order should not have users._id");
14376 }
14377
14378 #[test]
14379 fn test_full_join_via_router() {
14380 let router = QueryRouter::new();
14381 setup_join_tables(&router);
14382
14383 let stmt =
14384 parser::parse("SELECT * FROM users FULL JOIN orders ON users.id = orders.user_id")
14385 .unwrap();
14386 let result = router.execute_statement(&stmt).unwrap();
14387
14388 let rows = unwrap_qr_rows(result);
14389 assert_eq!(rows.len(), 5);
14391 }
14392
14393 #[test]
14394 fn test_cross_join_via_router() {
14395 let router = QueryRouter::new();
14396 setup_join_tables(&router);
14397
14398 let stmt = parser::parse("SELECT * FROM users CROSS JOIN orders").unwrap();
14399 let result = router.execute_statement(&stmt).unwrap();
14400
14401 let rows = unwrap_qr_rows(result);
14402 assert_eq!(rows.len(), 12);
14404 }
14405
14406 #[test]
14407 fn test_natural_join_via_router() {
14408 let router = QueryRouter::new();
14409
14410 router
14411 .execute_parsed("CREATE TABLE departments (dept_id INT, name TEXT)")
14412 .unwrap();
14413 router
14414 .execute_parsed("CREATE TABLE employees (emp_id INT, dept_id INT, name TEXT)")
14415 .unwrap();
14416
14417 router
14418 .execute_parsed("INSERT INTO departments (dept_id, name) VALUES (1, 'Engineering')")
14419 .unwrap();
14420 router
14421 .execute_parsed("INSERT INTO departments (dept_id, name) VALUES (2, 'Sales')")
14422 .unwrap();
14423
14424 router
14425 .execute_parsed(
14426 "INSERT INTO employees (emp_id, dept_id, name) VALUES (100, 1, 'Alice')",
14427 )
14428 .unwrap();
14429 router
14430 .execute_parsed("INSERT INTO employees (emp_id, dept_id, name) VALUES (101, 1, 'Bob')")
14431 .unwrap();
14432 router
14433 .execute_parsed(
14434 "INSERT INTO employees (emp_id, dept_id, name) VALUES (102, 2, 'Charlie')",
14435 )
14436 .unwrap();
14437
14438 let stmt = parser::parse("SELECT * FROM departments NATURAL JOIN employees").unwrap();
14439 let result = router.execute_statement(&stmt).unwrap();
14440
14441 let rows = unwrap_qr_rows(result);
14442 assert_eq!(rows.len(), 0);
14447 }
14448
14449 #[test]
14450 fn test_join_with_where_clause() {
14451 let router = QueryRouter::new();
14452 setup_join_tables(&router);
14453
14454 let stmt = parser::parse(
14455 "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id WHERE orders.amount > 100"
14456 ).unwrap();
14457 let result = router.execute_statement(&stmt).unwrap();
14458
14459 let rows = unwrap_qr_rows(result);
14460 assert_eq!(rows.len(), 2);
14462 }
14463
14464 #[test]
14465 fn test_join_with_limit() {
14466 let router = QueryRouter::new();
14467 setup_join_tables(&router);
14468
14469 let stmt = parser::parse(
14470 "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id LIMIT 2",
14471 )
14472 .unwrap();
14473 let result = router.execute_statement(&stmt).unwrap();
14474
14475 let rows = unwrap_qr_rows(result);
14476 assert_eq!(rows.len(), 2);
14477 }
14478
14479 #[test]
14480 fn test_join_using_clause() {
14481 let router = QueryRouter::new();
14482
14483 router
14484 .execute_parsed("CREATE TABLE products (product_id INT, name TEXT)")
14485 .unwrap();
14486 router
14487 .execute_parsed("CREATE TABLE sales (sale_id INT, product_id INT, qty INT)")
14488 .unwrap();
14489
14490 router
14491 .execute_parsed("INSERT INTO products (product_id, name) VALUES (1, 'Widget')")
14492 .unwrap();
14493 router
14494 .execute_parsed("INSERT INTO products (product_id, name) VALUES (2, 'Gadget')")
14495 .unwrap();
14496 router
14497 .execute_parsed("INSERT INTO sales (sale_id, product_id, qty) VALUES (100, 1, 10)")
14498 .unwrap();
14499 router
14500 .execute_parsed("INSERT INTO sales (sale_id, product_id, qty) VALUES (101, 1, 5)")
14501 .unwrap();
14502
14503 let stmt =
14504 parser::parse("SELECT * FROM products INNER JOIN sales USING (product_id)").unwrap();
14505 let result = router.execute_statement(&stmt).unwrap();
14506
14507 let rows = unwrap_qr_rows(result);
14508 assert_eq!(rows.len(), 2);
14510 }
14511
14512 #[test]
14515 fn test_order_by_asc() {
14516 let router = QueryRouter::new();
14517 router
14518 .execute_parsed("CREATE TABLE items (id INT, name TEXT, price INT)")
14519 .unwrap();
14520 router
14521 .execute_parsed("INSERT INTO items (id, name, price) VALUES (1, 'Apple', 100)")
14522 .unwrap();
14523 router
14524 .execute_parsed("INSERT INTO items (id, name, price) VALUES (2, 'Banana', 50)")
14525 .unwrap();
14526 router
14527 .execute_parsed("INSERT INTO items (id, name, price) VALUES (3, 'Cherry', 200)")
14528 .unwrap();
14529
14530 let stmt = parser::parse("SELECT * FROM items ORDER BY price ASC").unwrap();
14531 let result = router.execute_statement(&stmt).unwrap();
14532
14533 let rows = unwrap_qr_rows(result);
14534 assert_eq!(rows.len(), 3);
14535 assert_eq!(
14537 rows[0].values.iter().find(|(k, _)| k == "name").unwrap().1,
14538 Value::String("Banana".to_string())
14539 );
14540 assert_eq!(
14541 rows[1].values.iter().find(|(k, _)| k == "name").unwrap().1,
14542 Value::String("Apple".to_string())
14543 );
14544 assert_eq!(
14545 rows[2].values.iter().find(|(k, _)| k == "name").unwrap().1,
14546 Value::String("Cherry".to_string())
14547 );
14548 }
14549
14550 #[test]
14551 fn test_order_by_desc() {
14552 let router = QueryRouter::new();
14553 router
14554 .execute_parsed("CREATE TABLE items (id INT, name TEXT, price INT)")
14555 .unwrap();
14556 router
14557 .execute_parsed("INSERT INTO items (id, name, price) VALUES (1, 'Apple', 100)")
14558 .unwrap();
14559 router
14560 .execute_parsed("INSERT INTO items (id, name, price) VALUES (2, 'Banana', 50)")
14561 .unwrap();
14562 router
14563 .execute_parsed("INSERT INTO items (id, name, price) VALUES (3, 'Cherry', 200)")
14564 .unwrap();
14565
14566 let stmt = parser::parse("SELECT * FROM items ORDER BY price DESC").unwrap();
14567 let result = router.execute_statement(&stmt).unwrap();
14568
14569 let rows = unwrap_qr_rows(result);
14570 assert_eq!(rows.len(), 3);
14571 assert_eq!(
14573 rows[0].values.iter().find(|(k, _)| k == "name").unwrap().1,
14574 Value::String("Cherry".to_string())
14575 );
14576 assert_eq!(
14577 rows[1].values.iter().find(|(k, _)| k == "name").unwrap().1,
14578 Value::String("Apple".to_string())
14579 );
14580 assert_eq!(
14581 rows[2].values.iter().find(|(k, _)| k == "name").unwrap().1,
14582 Value::String("Banana".to_string())
14583 );
14584 }
14585
14586 #[test]
14587 fn test_order_by_string() {
14588 let router = QueryRouter::new();
14589 router
14590 .execute_parsed("CREATE TABLE items (id INT, name TEXT)")
14591 .unwrap();
14592 router
14593 .execute_parsed("INSERT INTO items (id, name) VALUES (1, 'Cherry')")
14594 .unwrap();
14595 router
14596 .execute_parsed("INSERT INTO items (id, name) VALUES (2, 'Apple')")
14597 .unwrap();
14598 router
14599 .execute_parsed("INSERT INTO items (id, name) VALUES (3, 'Banana')")
14600 .unwrap();
14601
14602 let stmt = parser::parse("SELECT * FROM items ORDER BY name").unwrap();
14603 let result = router.execute_statement(&stmt).unwrap();
14604
14605 let rows = unwrap_qr_rows(result);
14606 assert_eq!(rows.len(), 3);
14607 assert_eq!(
14609 rows[0].values.iter().find(|(k, _)| k == "name").unwrap().1,
14610 Value::String("Apple".to_string())
14611 );
14612 assert_eq!(
14613 rows[1].values.iter().find(|(k, _)| k == "name").unwrap().1,
14614 Value::String("Banana".to_string())
14615 );
14616 assert_eq!(
14617 rows[2].values.iter().find(|(k, _)| k == "name").unwrap().1,
14618 Value::String("Cherry".to_string())
14619 );
14620 }
14621
14622 #[test]
14623 fn test_order_by_multiple_columns() {
14624 let router = QueryRouter::new();
14625 router
14626 .execute_parsed("CREATE TABLE items (id INT, category TEXT, price INT)")
14627 .unwrap();
14628 router
14629 .execute_parsed("INSERT INTO items (id, category, price) VALUES (1, 'Fruit', 100)")
14630 .unwrap();
14631 router
14632 .execute_parsed("INSERT INTO items (id, category, price) VALUES (2, 'Fruit', 50)")
14633 .unwrap();
14634 router
14635 .execute_parsed("INSERT INTO items (id, category, price) VALUES (3, 'Veggie', 75)")
14636 .unwrap();
14637
14638 let stmt = parser::parse("SELECT * FROM items ORDER BY category ASC, price DESC").unwrap();
14639 let result = router.execute_statement(&stmt).unwrap();
14640
14641 let rows = unwrap_qr_rows(result);
14642 assert_eq!(rows.len(), 3);
14643 assert_eq!(
14645 rows[0].values.iter().find(|(k, _)| k == "id").unwrap().1,
14646 Value::Int(1)
14647 ); assert_eq!(
14649 rows[1].values.iter().find(|(k, _)| k == "id").unwrap().1,
14650 Value::Int(2)
14651 ); assert_eq!(
14653 rows[2].values.iter().find(|(k, _)| k == "id").unwrap().1,
14654 Value::Int(3)
14655 ); }
14657
14658 #[test]
14659 fn test_offset() {
14660 let router = QueryRouter::new();
14661 router
14662 .execute_parsed("CREATE TABLE items (id INT, name TEXT)")
14663 .unwrap();
14664 router
14665 .execute_parsed("INSERT INTO items (id, name) VALUES (1, 'A')")
14666 .unwrap();
14667 router
14668 .execute_parsed("INSERT INTO items (id, name) VALUES (2, 'B')")
14669 .unwrap();
14670 router
14671 .execute_parsed("INSERT INTO items (id, name) VALUES (3, 'C')")
14672 .unwrap();
14673 router
14674 .execute_parsed("INSERT INTO items (id, name) VALUES (4, 'D')")
14675 .unwrap();
14676
14677 let stmt = parser::parse("SELECT * FROM items ORDER BY id OFFSET 2").unwrap();
14678 let result = router.execute_statement(&stmt).unwrap();
14679
14680 let rows = unwrap_qr_rows(result);
14681 assert_eq!(rows.len(), 2);
14682 assert_eq!(
14683 rows[0].values.iter().find(|(k, _)| k == "name").unwrap().1,
14684 Value::String("C".to_string())
14685 );
14686 assert_eq!(
14687 rows[1].values.iter().find(|(k, _)| k == "name").unwrap().1,
14688 Value::String("D".to_string())
14689 );
14690 }
14691
14692 #[test]
14693 fn test_order_by_with_limit_and_offset() {
14694 let router = QueryRouter::new();
14695 router
14696 .execute_parsed("CREATE TABLE items (id INT, name TEXT)")
14697 .unwrap();
14698 router
14699 .execute_parsed("INSERT INTO items (id, name) VALUES (1, 'A')")
14700 .unwrap();
14701 router
14702 .execute_parsed("INSERT INTO items (id, name) VALUES (2, 'B')")
14703 .unwrap();
14704 router
14705 .execute_parsed("INSERT INTO items (id, name) VALUES (3, 'C')")
14706 .unwrap();
14707 router
14708 .execute_parsed("INSERT INTO items (id, name) VALUES (4, 'D')")
14709 .unwrap();
14710 router
14711 .execute_parsed("INSERT INTO items (id, name) VALUES (5, 'E')")
14712 .unwrap();
14713
14714 let stmt = parser::parse("SELECT * FROM items ORDER BY id LIMIT 2 OFFSET 1").unwrap();
14715 let result = router.execute_statement(&stmt).unwrap();
14716
14717 let rows = unwrap_qr_rows(result);
14718 assert_eq!(rows.len(), 2);
14720 assert_eq!(
14721 rows[0].values.iter().find(|(k, _)| k == "name").unwrap().1,
14722 Value::String("B".to_string())
14723 );
14724 assert_eq!(
14725 rows[1].values.iter().find(|(k, _)| k == "name").unwrap().1,
14726 Value::String("C".to_string())
14727 );
14728 }
14729
14730 #[test]
14731 fn test_offset_beyond_rows() {
14732 let router = QueryRouter::new();
14733 router
14734 .execute_parsed("CREATE TABLE items (id INT)")
14735 .unwrap();
14736 router
14737 .execute_parsed("INSERT INTO items (id) VALUES (1)")
14738 .unwrap();
14739 router
14740 .execute_parsed("INSERT INTO items (id) VALUES (2)")
14741 .unwrap();
14742
14743 let stmt = parser::parse("SELECT * FROM items OFFSET 10").unwrap();
14744 let result = router.execute_statement(&stmt).unwrap();
14745
14746 let rows = unwrap_qr_rows(result);
14747 assert_eq!(rows.len(), 0);
14748 }
14749
14750 #[test]
14751 fn test_order_by_with_join() {
14752 let router = QueryRouter::new();
14753 setup_join_tables(&router);
14754
14755 let stmt = parser::parse(
14756 "SELECT * FROM users INNER JOIN orders ON users.id = orders.user_id ORDER BY orders.amount DESC"
14757 ).unwrap();
14758 let result = router.execute_statement(&stmt).unwrap();
14759
14760 let rows = unwrap_qr_rows(result);
14761 assert_eq!(rows.len(), 3);
14762 let amounts: Vec<_> = rows
14764 .iter()
14765 .map(|r| {
14766 r.values
14767 .iter()
14768 .find(|(k, _)| k == "orders.amount")
14769 .unwrap()
14770 .1
14771 .clone()
14772 })
14773 .collect();
14774 assert_eq!(
14775 amounts,
14776 vec![Value::Int(200), Value::Int(150), Value::Int(100)]
14777 );
14778 }
14779
14780 fn setup_aggregate_table(router: &QueryRouter) {
14783 router
14784 .execute_parsed("CREATE TABLE sales (id INT, product TEXT, amount INT, price FLOAT)")
14785 .unwrap();
14786 router
14787 .execute_parsed(
14788 "INSERT INTO sales (id, product, amount, price) VALUES (1, 'Apple', 10, 1.50)",
14789 )
14790 .unwrap();
14791 router
14792 .execute_parsed(
14793 "INSERT INTO sales (id, product, amount, price) VALUES (2, 'Banana', 20, 0.75)",
14794 )
14795 .unwrap();
14796 router
14797 .execute_parsed(
14798 "INSERT INTO sales (id, product, amount, price) VALUES (3, 'Cherry', 15, 2.00)",
14799 )
14800 .unwrap();
14801 router
14802 .execute_parsed(
14803 "INSERT INTO sales (id, product, amount, price) VALUES (4, 'Apple', 5, 1.50)",
14804 )
14805 .unwrap();
14806 }
14807
14808 #[test]
14809 fn test_count_star() {
14810 let router = QueryRouter::new();
14811 setup_aggregate_table(&router);
14812
14813 let stmt = parser::parse("SELECT COUNT(*) FROM sales").unwrap();
14814 let result = router.execute_statement(&stmt).unwrap();
14815
14816 let rows = unwrap_qr_rows(result);
14817 assert_eq!(rows.len(), 1);
14818 let count = rows[0]
14819 .values
14820 .iter()
14821 .find(|(k, _)| k == "COUNT(*)")
14822 .unwrap()
14823 .1
14824 .clone();
14825 assert_eq!(count, Value::Int(4));
14826 }
14827
14828 #[test]
14829 fn test_count_column() {
14830 let router = QueryRouter::new();
14831 setup_aggregate_table(&router);
14832
14833 let stmt = parser::parse("SELECT COUNT(product) FROM sales").unwrap();
14834 let result = router.execute_statement(&stmt).unwrap();
14835
14836 let rows = unwrap_qr_rows(result);
14837 assert_eq!(rows.len(), 1);
14838 let count = rows[0]
14839 .values
14840 .iter()
14841 .find(|(k, _)| k == "COUNT(product)")
14842 .unwrap()
14843 .1
14844 .clone();
14845 assert_eq!(count, Value::Int(4));
14846 }
14847
14848 #[test]
14849 fn test_sum() {
14850 let router = QueryRouter::new();
14851 setup_aggregate_table(&router);
14852
14853 let stmt = parser::parse("SELECT SUM(amount) FROM sales").unwrap();
14854 let result = router.execute_statement(&stmt).unwrap();
14855
14856 let rows = unwrap_qr_rows(result);
14857 assert_eq!(rows.len(), 1);
14858 let sum = rows[0]
14859 .values
14860 .iter()
14861 .find(|(k, _)| k == "SUM(amount)")
14862 .unwrap()
14863 .1
14864 .clone();
14865 assert_eq!(sum, Value::Float(50.0)); }
14867
14868 #[test]
14869 fn test_avg() {
14870 let router = QueryRouter::new();
14871 setup_aggregate_table(&router);
14872
14873 let stmt = parser::parse("SELECT AVG(amount) FROM sales").unwrap();
14874 let result = router.execute_statement(&stmt).unwrap();
14875
14876 let rows = unwrap_qr_rows(result);
14877 assert_eq!(rows.len(), 1);
14878 let avg = rows[0]
14879 .values
14880 .iter()
14881 .find(|(k, _)| k == "AVG(amount)")
14882 .unwrap()
14883 .1
14884 .clone();
14885 assert_eq!(avg, Value::Float(12.5)); }
14887
14888 #[test]
14889 fn test_min() {
14890 let router = QueryRouter::new();
14891 setup_aggregate_table(&router);
14892
14893 let stmt = parser::parse("SELECT MIN(amount) FROM sales").unwrap();
14894 let result = router.execute_statement(&stmt).unwrap();
14895
14896 let rows = unwrap_qr_rows(result);
14897 assert_eq!(rows.len(), 1);
14898 let min = rows[0]
14899 .values
14900 .iter()
14901 .find(|(k, _)| k == "MIN(amount)")
14902 .unwrap()
14903 .1
14904 .clone();
14905 assert_eq!(min, Value::Int(5));
14906 }
14907
14908 #[test]
14909 fn test_max() {
14910 let router = QueryRouter::new();
14911 setup_aggregate_table(&router);
14912
14913 let stmt = parser::parse("SELECT MAX(amount) FROM sales").unwrap();
14914 let result = router.execute_statement(&stmt).unwrap();
14915
14916 let rows = unwrap_qr_rows(result);
14917 assert_eq!(rows.len(), 1);
14918 let max = rows[0]
14919 .values
14920 .iter()
14921 .find(|(k, _)| k == "MAX(amount)")
14922 .unwrap()
14923 .1
14924 .clone();
14925 assert_eq!(max, Value::Int(20));
14926 }
14927
14928 #[test]
14929 fn test_multiple_aggregates() {
14930 let router = QueryRouter::new();
14931 setup_aggregate_table(&router);
14932
14933 let stmt = parser::parse("SELECT COUNT(*), SUM(amount), AVG(price) FROM sales").unwrap();
14934 let result = router.execute_statement(&stmt).unwrap();
14935
14936 let rows = unwrap_qr_rows(result);
14937 assert_eq!(rows.len(), 1);
14938 let count = rows[0]
14939 .values
14940 .iter()
14941 .find(|(k, _)| k == "COUNT(*)")
14942 .unwrap()
14943 .1
14944 .clone();
14945 let sum = rows[0]
14946 .values
14947 .iter()
14948 .find(|(k, _)| k == "SUM(amount)")
14949 .unwrap()
14950 .1
14951 .clone();
14952 let avg = rows[0]
14953 .values
14954 .iter()
14955 .find(|(k, _)| k == "AVG(price)")
14956 .unwrap()
14957 .1
14958 .clone();
14959 assert_eq!(count, Value::Int(4));
14960 assert_eq!(sum, Value::Float(50.0));
14961 if let Value::Float(f) = avg {
14963 assert!((f - 1.4375).abs() < 0.0001);
14964 } else {
14965 panic!("expected Float");
14966 }
14967 }
14968
14969 #[test]
14970 fn test_aggregate_with_where() {
14971 let router = QueryRouter::new();
14972 setup_aggregate_table(&router);
14973
14974 let stmt = parser::parse("SELECT COUNT(*), SUM(amount) FROM sales WHERE product = 'Apple'")
14975 .unwrap();
14976 let result = router.execute_statement(&stmt).unwrap();
14977
14978 let rows = unwrap_qr_rows(result);
14979 assert_eq!(rows.len(), 1);
14980 let count = rows[0]
14981 .values
14982 .iter()
14983 .find(|(k, _)| k == "COUNT(*)")
14984 .unwrap()
14985 .1
14986 .clone();
14987 let sum = rows[0]
14988 .values
14989 .iter()
14990 .find(|(k, _)| k == "SUM(amount)")
14991 .unwrap()
14992 .1
14993 .clone();
14994 assert_eq!(count, Value::Int(2)); assert_eq!(sum, Value::Float(15.0)); }
14997
14998 #[test]
14999 fn test_aggregate_with_alias() {
15000 let router = QueryRouter::new();
15001 setup_aggregate_table(&router);
15002
15003 let stmt = parser::parse("SELECT COUNT(*) AS total_count FROM sales").unwrap();
15004 let result = router.execute_statement(&stmt).unwrap();
15005
15006 let rows = unwrap_qr_rows(result);
15007 assert_eq!(rows.len(), 1);
15008 let count = rows[0]
15009 .values
15010 .iter()
15011 .find(|(k, _)| k == "total_count")
15012 .unwrap()
15013 .1
15014 .clone();
15015 assert_eq!(count, Value::Int(4));
15016 }
15017
15018 #[test]
15019 fn test_min_max_string() {
15020 let router = QueryRouter::new();
15021 setup_aggregate_table(&router);
15022
15023 let stmt = parser::parse("SELECT MIN(product), MAX(product) FROM sales").unwrap();
15024 let result = router.execute_statement(&stmt).unwrap();
15025
15026 let rows = unwrap_qr_rows(result);
15027 assert_eq!(rows.len(), 1);
15028 let min = rows[0]
15029 .values
15030 .iter()
15031 .find(|(k, _)| k == "MIN(product)")
15032 .unwrap()
15033 .1
15034 .clone();
15035 let max = rows[0]
15036 .values
15037 .iter()
15038 .find(|(k, _)| k == "MAX(product)")
15039 .unwrap()
15040 .1
15041 .clone();
15042 assert_eq!(min, Value::String("Apple".to_string()));
15043 assert_eq!(max, Value::String("Cherry".to_string()));
15044 }
15045
15046 #[test]
15047 fn test_group_by_single_column() {
15048 let router = QueryRouter::new();
15049 setup_aggregate_table(&router);
15050
15051 let stmt = parser::parse("SELECT product, COUNT(*) FROM sales GROUP BY product").unwrap();
15053 let result = router.execute_statement(&stmt).unwrap();
15054
15055 let rows = unwrap_qr_rows(result);
15056 assert_eq!(rows.len(), 3); let get_count = |product: &str| -> i64 {
15060 rows.iter()
15061 .find(|r| {
15062 r.values
15063 .iter()
15064 .any(|(k, v)| k == "product" && *v == Value::String(product.to_string()))
15065 })
15066 .and_then(|r| r.values.iter().find(|(k, _)| k == "COUNT(*)"))
15067 .map(|(_, v)| if let Value::Int(i) = v { *i } else { 0 })
15068 .unwrap_or(0)
15069 };
15070
15071 assert_eq!(get_count("Apple"), 2);
15072 assert_eq!(get_count("Banana"), 1);
15073 assert_eq!(get_count("Cherry"), 1);
15074 }
15075
15076 #[test]
15077 fn test_group_by_with_sum() {
15078 let router = QueryRouter::new();
15079 setup_aggregate_table(&router);
15080
15081 let stmt =
15082 parser::parse("SELECT product, SUM(amount) FROM sales GROUP BY product").unwrap();
15083 let result = router.execute_statement(&stmt).unwrap();
15084
15085 let rows = unwrap_qr_rows(result);
15086 assert_eq!(rows.len(), 3);
15087
15088 let get_sum = |product: &str| -> f64 {
15089 rows.iter()
15090 .find(|r| {
15091 r.values
15092 .iter()
15093 .any(|(k, v)| k == "product" && *v == Value::String(product.to_string()))
15094 })
15095 .and_then(|r| r.values.iter().find(|(k, _)| k == "SUM(amount)"))
15096 .map(|(_, v)| if let Value::Float(f) = v { *f } else { 0.0 })
15097 .unwrap_or(0.0)
15098 };
15099
15100 assert_eq!(get_sum("Apple"), 15.0); assert_eq!(get_sum("Banana"), 20.0); assert_eq!(get_sum("Cherry"), 15.0); }
15104
15105 #[test]
15106 fn test_group_by_with_avg() {
15107 let router = QueryRouter::new();
15108 setup_aggregate_table(&router);
15109
15110 let stmt =
15111 parser::parse("SELECT product, AVG(amount) FROM sales GROUP BY product").unwrap();
15112 let result = router.execute_statement(&stmt).unwrap();
15113
15114 let rows = unwrap_qr_rows(result);
15115 assert_eq!(rows.len(), 3);
15116
15117 let get_avg = |product: &str| -> f64 {
15118 rows.iter()
15119 .find(|r| {
15120 r.values
15121 .iter()
15122 .any(|(k, v)| k == "product" && *v == Value::String(product.to_string()))
15123 })
15124 .and_then(|r| r.values.iter().find(|(k, _)| k == "AVG(amount)"))
15125 .map(|(_, v)| if let Value::Float(f) = v { *f } else { 0.0 })
15126 .unwrap_or(0.0)
15127 };
15128
15129 assert_eq!(get_avg("Apple"), 7.5); assert_eq!(get_avg("Banana"), 20.0); assert_eq!(get_avg("Cherry"), 15.0); }
15133
15134 #[test]
15135 fn test_group_by_with_having() {
15136 let router = QueryRouter::new();
15137 setup_aggregate_table(&router);
15138
15139 let stmt = parser::parse(
15141 "SELECT product, COUNT(*) FROM sales GROUP BY product HAVING COUNT(*) > 1",
15142 )
15143 .unwrap();
15144 let result = router.execute_statement(&stmt).unwrap();
15145
15146 let rows = unwrap_qr_rows(result);
15147 assert_eq!(rows.len(), 1); let product = rows[0]
15149 .values
15150 .iter()
15151 .find(|(k, _)| k == "product")
15152 .unwrap()
15153 .1
15154 .clone();
15155 assert_eq!(product, Value::String("Apple".to_string()));
15156 }
15157
15158 #[test]
15159 fn test_group_by_with_having_sum() {
15160 let router = QueryRouter::new();
15161 setup_aggregate_table(&router);
15162
15163 let stmt = parser::parse(
15165 "SELECT product, SUM(amount) FROM sales GROUP BY product HAVING SUM(amount) > 15",
15166 )
15167 .unwrap();
15168 let result = router.execute_statement(&stmt).unwrap();
15169
15170 let rows = unwrap_qr_rows(result);
15171 assert_eq!(rows.len(), 1); let products: Vec<String> = rows
15174 .iter()
15175 .filter_map(|r| r.values.iter().find(|(k, _)| k == "product"))
15176 .filter_map(|(_, v)| {
15177 if let Value::String(s) = v {
15178 Some(s.clone())
15179 } else {
15180 None
15181 }
15182 })
15183 .collect();
15184
15185 assert!(products.contains(&"Banana".to_string()));
15186 }
15187
15188 #[test]
15189 fn test_group_by_with_where_and_having() {
15190 let router = QueryRouter::new();
15191 setup_aggregate_table(&router);
15192
15193 let stmt = parser::parse("SELECT product, COUNT(*), SUM(amount) FROM sales WHERE amount > 5 GROUP BY product HAVING COUNT(*) >= 1").unwrap();
15195 let result = router.execute_statement(&stmt).unwrap();
15196
15197 let rows = unwrap_qr_rows(result);
15198 assert_eq!(rows.len(), 3);
15201 }
15202
15203 #[test]
15204 fn test_group_by_multiple_aggregates() {
15205 let router = QueryRouter::new();
15206 setup_aggregate_table(&router);
15207
15208 let stmt = parser::parse("SELECT product, COUNT(*), SUM(amount), AVG(amount), MIN(amount), MAX(amount) FROM sales GROUP BY product").unwrap();
15209 let result = router.execute_statement(&stmt).unwrap();
15210
15211 let rows = unwrap_qr_rows(result);
15212 assert_eq!(rows.len(), 3);
15213
15214 let apple_row = rows
15216 .iter()
15217 .find(|r| {
15218 r.values
15219 .iter()
15220 .any(|(k, v)| k == "product" && *v == Value::String("Apple".to_string()))
15221 })
15222 .expect("Apple row not found");
15223
15224 let count = apple_row
15225 .values
15226 .iter()
15227 .find(|(k, _)| k == "COUNT(*)")
15228 .unwrap()
15229 .1
15230 .clone();
15231 let sum = apple_row
15232 .values
15233 .iter()
15234 .find(|(k, _)| k == "SUM(amount)")
15235 .unwrap()
15236 .1
15237 .clone();
15238 let avg = apple_row
15239 .values
15240 .iter()
15241 .find(|(k, _)| k == "AVG(amount)")
15242 .unwrap()
15243 .1
15244 .clone();
15245 let min = apple_row
15246 .values
15247 .iter()
15248 .find(|(k, _)| k == "MIN(amount)")
15249 .unwrap()
15250 .1
15251 .clone();
15252 let max = apple_row
15253 .values
15254 .iter()
15255 .find(|(k, _)| k == "MAX(amount)")
15256 .unwrap()
15257 .1
15258 .clone();
15259
15260 assert_eq!(count, Value::Int(2));
15261 assert_eq!(sum, Value::Float(15.0));
15262 assert_eq!(avg, Value::Float(7.5));
15263 assert_eq!(min, Value::Int(5));
15265 assert_eq!(max, Value::Int(10));
15266 }
15267
15268 #[test]
15269 fn test_having_without_matching_groups() {
15270 let router = QueryRouter::new();
15271 setup_aggregate_table(&router);
15272
15273 let stmt = parser::parse(
15275 "SELECT product, COUNT(*) FROM sales GROUP BY product HAVING COUNT(*) > 10",
15276 )
15277 .unwrap();
15278 let result = router.execute_statement(&stmt).unwrap();
15279
15280 let rows = unwrap_qr_rows(result);
15281 assert_eq!(rows.len(), 0); }
15283
15284 #[test]
15287 fn test_delete_without_checkpoint_manager() {
15288 let router = QueryRouter::new();
15290
15291 router
15292 .execute("CREATE TABLE temp (id int, name string)")
15293 .unwrap();
15294 router
15295 .execute("INSERT INTO temp (id, name) VALUES (1, 'test')")
15296 .unwrap();
15297
15298 let result = router.execute("DELETE FROM temp WHERE id = 1");
15300 assert!(result.is_ok(), "Delete failed: {result:?}");
15301 if let Ok(QueryResult::Count(n)) = result {
15302 assert_eq!(n, 1);
15303 }
15304 }
15305
15306 #[test]
15307 fn test_delete_creates_auto_checkpoint() {
15308 use tensor_checkpoint::{AutoConfirm, CheckpointConfig};
15309
15310 let dir = tempfile::tempdir().unwrap();
15311 let mut router = QueryRouter::new();
15312 router.set_checkpoint_dir(dir.path().to_path_buf());
15313
15314 let config = CheckpointConfig::default()
15315 .with_auto_checkpoint(true)
15316 .with_interactive_confirm(true);
15317 router.init_checkpoint_with_config(config).unwrap();
15318
15319 router
15320 .set_confirmation_handler(Arc::new(AutoConfirm))
15321 .unwrap();
15322
15323 router
15324 .execute("CREATE TABLE users (id int, name string)")
15325 .unwrap();
15326 router
15327 .execute("INSERT INTO users (id, name) VALUES (1, 'Alice')")
15328 .unwrap();
15329 router
15330 .execute("INSERT INTO users (id, name) VALUES (2, 'Bob')")
15331 .unwrap();
15332
15333 let result = router.execute("DELETE FROM users WHERE id = 1");
15335 assert!(result.is_ok());
15336
15337 let checkpoints = router.execute_parsed("CHECKPOINTS").unwrap();
15339 let list = unwrap_qr_checkpointlist(checkpoints);
15340 assert!(!list.is_empty());
15341 assert!(list[0].name.contains("auto-before-delete"));
15342 }
15343
15344 #[test]
15345 fn test_delete_cancelled_preserves_data() {
15346 use tensor_checkpoint::{AutoReject, CheckpointConfig};
15347
15348 let dir = tempfile::tempdir().unwrap();
15349 let mut router = QueryRouter::new();
15350 router.set_checkpoint_dir(dir.path().to_path_buf());
15351
15352 let config = CheckpointConfig::default()
15353 .with_auto_checkpoint(true)
15354 .with_interactive_confirm(true);
15355 router.init_checkpoint_with_config(config).unwrap();
15356
15357 router
15358 .set_confirmation_handler(Arc::new(AutoReject))
15359 .unwrap();
15360
15361 router
15362 .execute("CREATE TABLE users (id int, name string)")
15363 .unwrap();
15364 router
15365 .execute("INSERT INTO users (id, name) VALUES (1, 'Alice')")
15366 .unwrap();
15367
15368 let result = router.execute("DELETE FROM users WHERE id = 1");
15370 assert!(result.is_err());
15371 let err = result.unwrap_err();
15372 assert!(err.to_string().contains("cancelled"));
15373
15374 let select = router.execute("SELECT * FROM users WHERE id = 1").unwrap();
15376 let rows = unwrap_qr_rows(select);
15377 assert_eq!(rows.len(), 1);
15378 }
15379
15380 #[test]
15381 fn test_delete_with_auto_checkpoint_disabled() {
15382 use tensor_checkpoint::{AutoReject, CheckpointConfig};
15383
15384 let dir = tempfile::tempdir().unwrap();
15385 let mut router = QueryRouter::new();
15386 router.set_checkpoint_dir(dir.path().to_path_buf());
15387
15388 let config = CheckpointConfig::default()
15389 .with_auto_checkpoint(false)
15390 .with_interactive_confirm(false);
15391 router.init_checkpoint_with_config(config).unwrap();
15392
15393 router
15394 .set_confirmation_handler(Arc::new(AutoReject))
15395 .unwrap();
15396
15397 router.execute("CREATE TABLE temp (id int)").unwrap();
15398 router.execute("INSERT INTO temp (id) VALUES (1)").unwrap();
15399
15400 let result = router.execute("DELETE FROM temp WHERE id = 1");
15401 assert!(result.is_ok());
15402 }
15403
15404 #[test]
15405 fn test_drop_table_creates_checkpoint() {
15406 use tensor_checkpoint::{AutoConfirm, CheckpointConfig};
15407
15408 let dir = tempfile::tempdir().unwrap();
15409 let mut router = QueryRouter::new();
15410 router.set_checkpoint_dir(dir.path().to_path_buf());
15411
15412 let config = CheckpointConfig::default()
15413 .with_auto_checkpoint(true)
15414 .with_interactive_confirm(true);
15415 router.init_checkpoint_with_config(config).unwrap();
15416 router
15417 .set_confirmation_handler(Arc::new(AutoConfirm))
15418 .unwrap();
15419
15420 router.execute("CREATE TABLE to_drop (id int)").unwrap();
15421 router
15422 .execute("INSERT INTO to_drop (id) VALUES (1)")
15423 .unwrap();
15424
15425 let result = router.execute("DROP TABLE to_drop");
15426 assert!(result.is_ok());
15427
15428 let checkpoints = router.execute_parsed("CHECKPOINTS").unwrap();
15429 let list = unwrap_qr_checkpointlist(checkpoints);
15430 assert!(!list.is_empty());
15431 assert!(list[0].name.contains("auto-before-drop-table"));
15432 }
15433
15434 #[test]
15435 fn test_node_delete_creates_checkpoint() {
15436 use tensor_checkpoint::{AutoConfirm, CheckpointConfig};
15437
15438 let dir = tempfile::tempdir().unwrap();
15439 let mut router = QueryRouter::new();
15440 router.set_checkpoint_dir(dir.path().to_path_buf());
15441
15442 let config = CheckpointConfig::default()
15443 .with_auto_checkpoint(true)
15444 .with_interactive_confirm(true);
15445 router.init_checkpoint_with_config(config).unwrap();
15446 router
15447 .set_confirmation_handler(Arc::new(AutoConfirm))
15448 .unwrap();
15449
15450 let node_id = match router
15451 .execute("NODE CREATE Person { name: 'Alice' }")
15452 .unwrap()
15453 {
15454 QueryResult::Ids(ids) => ids[0],
15455 _ => panic!("Expected Ids result"),
15456 };
15457
15458 let result = router.execute(&format!("NODE DELETE {node_id}"));
15459 assert!(result.is_ok());
15460
15461 let checkpoints = router.execute_parsed("CHECKPOINTS").unwrap();
15462 let list = unwrap_qr_checkpointlist(checkpoints);
15463 assert!(!list.is_empty());
15464 assert!(list[0].name.contains("auto-before-node-delete"));
15465 }
15466
15467 #[test]
15468 fn test_collect_delete_sample() {
15469 let router = QueryRouter::new();
15470
15471 router
15472 .execute("CREATE TABLE users (id int, name string)")
15473 .unwrap();
15474 router
15475 .execute("INSERT INTO users (id, name) VALUES (1, 'Alice')")
15476 .unwrap();
15477 router
15478 .execute("INSERT INTO users (id, name) VALUES (2, 'Bob')")
15479 .unwrap();
15480 router
15481 .execute("INSERT INTO users (id, name) VALUES (3, 'Charlie')")
15482 .unwrap();
15483
15484 let condition = relational_engine::Condition::True;
15485 let (count, samples) = router.collect_delete_sample("users", &condition, 5);
15486
15487 assert_eq!(count, 3);
15488 assert!(!samples.is_empty());
15489 assert!(samples.len() <= 5);
15490 }
15491
15492 #[test]
15493 fn test_collect_table_sample() {
15494 let router = QueryRouter::new();
15495
15496 router.execute("CREATE TABLE items (id int)").unwrap();
15497 router.execute("INSERT INTO items (id) VALUES (1)").unwrap();
15498 router.execute("INSERT INTO items (id) VALUES (2)").unwrap();
15499
15500 let (count, samples) = router.collect_table_sample("items", 3);
15501
15502 assert_eq!(count, 2);
15503 assert!(!samples.is_empty());
15504 }
15505
15506 #[test]
15507 fn test_collect_node_info() {
15508 let router = QueryRouter::new();
15509
15510 let alice_id = match router
15511 .execute("NODE CREATE Person { name: 'Alice' }")
15512 .unwrap()
15513 {
15514 QueryResult::Ids(ids) => ids[0],
15515 _ => panic!("Expected Ids"),
15516 };
15517 let bob_id = match router
15518 .execute("NODE CREATE Person { name: 'Bob' }")
15519 .unwrap()
15520 {
15521 QueryResult::Ids(ids) => ids[0],
15522 _ => panic!("Expected Ids"),
15523 };
15524
15525 router
15526 .execute(&format!("EDGE CREATE {alice_id} -> {bob_id} : KNOWS"))
15527 .unwrap();
15528
15529 let (edge_count, info) = router.collect_node_info(alice_id);
15530
15531 assert_eq!(edge_count, 1);
15532 assert!(!info.is_empty());
15533 }
15534
15535 #[test]
15540 fn test_graph_pagerank() {
15541 let router = QueryRouter::new();
15542
15543 let a = match router.execute("NODE CREATE Page { url: 'a' }").unwrap() {
15545 QueryResult::Ids(ids) => ids[0],
15546 _ => panic!("Expected Ids"),
15547 };
15548 let b = match router.execute("NODE CREATE Page { url: 'b' }").unwrap() {
15549 QueryResult::Ids(ids) => ids[0],
15550 _ => panic!("Expected Ids"),
15551 };
15552 let c = match router.execute("NODE CREATE Page { url: 'c' }").unwrap() {
15553 QueryResult::Ids(ids) => ids[0],
15554 _ => panic!("Expected Ids"),
15555 };
15556
15557 router
15558 .execute(&format!("EDGE CREATE {} -> {} : linked", a, b))
15559 .unwrap();
15560 router
15561 .execute(&format!("EDGE CREATE {} -> {} : linked", b, c))
15562 .unwrap();
15563 router
15564 .execute(&format!("EDGE CREATE {} -> {} : linked", c, a))
15565 .unwrap();
15566
15567 let result = router.execute("GRAPH PAGERANK").unwrap();
15568 match result {
15569 QueryResult::PageRank(pr) => {
15570 assert_eq!(pr.items.len(), 3);
15571 for item in &pr.items {
15572 assert!(item.score > 0.0);
15573 }
15574 },
15575 _ => panic!("Expected PageRank result"),
15576 }
15577 }
15578
15579 #[test]
15580 fn test_graph_pagerank_with_options() {
15581 let router = QueryRouter::new();
15582
15583 let a = match router.execute("NODE CREATE Page").unwrap() {
15585 QueryResult::Ids(ids) => ids[0],
15586 _ => panic!("Expected Ids"),
15587 };
15588 let b = match router.execute("NODE CREATE Page").unwrap() {
15589 QueryResult::Ids(ids) => ids[0],
15590 _ => panic!("Expected Ids"),
15591 };
15592
15593 router
15594 .execute(&format!("EDGE CREATE {} -> {} : linked", a, b))
15595 .unwrap();
15596
15597 let result = router
15598 .execute("GRAPH PAGERANK DAMPING 0.85 ITERATIONS 50")
15599 .unwrap();
15600 assert!(matches!(result, QueryResult::PageRank(_)));
15601 }
15602
15603 #[test]
15604 fn test_graph_betweenness_centrality() {
15605 let router = QueryRouter::new();
15606
15607 let a = match router.execute("NODE CREATE nd").unwrap() {
15609 QueryResult::Ids(ids) => ids[0],
15610 _ => panic!("Expected Ids"),
15611 };
15612 let b = match router.execute("NODE CREATE nd").unwrap() {
15613 QueryResult::Ids(ids) => ids[0],
15614 _ => panic!("Expected Ids"),
15615 };
15616 let c = match router.execute("NODE CREATE nd").unwrap() {
15617 QueryResult::Ids(ids) => ids[0],
15618 _ => panic!("Expected Ids"),
15619 };
15620
15621 router
15622 .execute(&format!("EDGE CREATE {} -> {} : conn", a, b))
15623 .unwrap();
15624 router
15625 .execute(&format!("EDGE CREATE {} -> {} : conn", b, c))
15626 .unwrap();
15627
15628 let result = router.execute("GRAPH BETWEENNESS CENTRALITY").unwrap();
15629 match result {
15630 QueryResult::Centrality(scores) => {
15631 assert_eq!(scores.items.len(), 3);
15632 },
15633 _ => panic!("Expected Centrality result"),
15634 }
15635 }
15636
15637 #[test]
15638 fn test_graph_closeness_centrality() {
15639 let router = QueryRouter::new();
15640
15641 let a = match router.execute("NODE CREATE nd").unwrap() {
15642 QueryResult::Ids(ids) => ids[0],
15643 _ => panic!("Expected Ids"),
15644 };
15645 let b = match router.execute("NODE CREATE nd").unwrap() {
15646 QueryResult::Ids(ids) => ids[0],
15647 _ => panic!("Expected Ids"),
15648 };
15649
15650 router
15651 .execute(&format!("EDGE CREATE {} -> {} : conn", a, b))
15652 .unwrap();
15653
15654 let result = router.execute("GRAPH CLOSENESS CENTRALITY").unwrap();
15655 assert!(matches!(result, QueryResult::Centrality(_)));
15656 }
15657
15658 #[test]
15659 fn test_graph_eigenvector_centrality() {
15660 let router = QueryRouter::new();
15661
15662 let a = match router.execute("NODE CREATE nd").unwrap() {
15663 QueryResult::Ids(ids) => ids[0],
15664 _ => panic!("Expected Ids"),
15665 };
15666 let b = match router.execute("NODE CREATE nd").unwrap() {
15667 QueryResult::Ids(ids) => ids[0],
15668 _ => panic!("Expected Ids"),
15669 };
15670 let c = match router.execute("NODE CREATE nd").unwrap() {
15671 QueryResult::Ids(ids) => ids[0],
15672 _ => panic!("Expected Ids"),
15673 };
15674
15675 router
15676 .execute(&format!("EDGE CREATE {} -> {} : conn", a, b))
15677 .unwrap();
15678 router
15679 .execute(&format!("EDGE CREATE {} -> {} : conn", b, c))
15680 .unwrap();
15681
15682 let result = router.execute("GRAPH EIGENVECTOR CENTRALITY").unwrap();
15683 match result {
15684 QueryResult::Centrality(c) => {
15685 assert_eq!(c.centrality_type, CentralityType::Eigenvector);
15686 assert!(!c.items.is_empty());
15687 },
15688 _ => panic!("Expected Centrality result"),
15689 }
15690 }
15691
15692 #[test]
15693 fn test_graph_eigenvector_centrality_with_options() {
15694 let router = QueryRouter::new();
15695
15696 for i in 0..5 {
15697 router
15698 .execute(&format!("NODE CREATE nd {{ id: {} }}", i))
15699 .unwrap();
15700 }
15701 for i in 0..4 {
15702 let from = i + 1;
15703 let to = i + 2;
15704 router
15705 .execute(&format!("EDGE CREATE {} -> {} : conn", from, to))
15706 .unwrap();
15707 }
15708
15709 let result = router
15710 .execute("GRAPH EIGENVECTOR CENTRALITY ITERATIONS 50 TOLERANCE 0.001")
15711 .unwrap();
15712 assert!(matches!(result, QueryResult::Centrality(_)));
15713 }
15714
15715 #[test]
15716 fn test_graph_louvain_communities() {
15717 let router = QueryRouter::new();
15718
15719 let a1 = match router.execute("NODE CREATE nd").unwrap() {
15721 QueryResult::Ids(ids) => ids[0],
15722 _ => panic!("Expected Ids"),
15723 };
15724 let a2 = match router.execute("NODE CREATE nd").unwrap() {
15725 QueryResult::Ids(ids) => ids[0],
15726 _ => panic!("Expected Ids"),
15727 };
15728
15729 router
15730 .execute(&format!("EDGE CREATE {} -> {} : rel", a1, a2))
15731 .unwrap();
15732 router
15733 .execute(&format!("EDGE CREATE {} -> {} : rel", a2, a1))
15734 .unwrap();
15735
15736 let result = router.execute("GRAPH LOUVAIN COMMUNITIES").unwrap();
15737 assert!(matches!(result, QueryResult::Communities(_)));
15738 }
15739
15740 #[test]
15741 fn test_graph_label_propagation() {
15742 let router = QueryRouter::new();
15743
15744 let a = match router.execute("NODE CREATE nd").unwrap() {
15745 QueryResult::Ids(ids) => ids[0],
15746 _ => panic!("Expected Ids"),
15747 };
15748 let b = match router.execute("NODE CREATE nd").unwrap() {
15749 QueryResult::Ids(ids) => ids[0],
15750 _ => panic!("Expected Ids"),
15751 };
15752
15753 router
15754 .execute(&format!("EDGE CREATE {} -> {} : rel", a, b))
15755 .unwrap();
15756
15757 let result = router.execute("GRAPH LABEL PROPAGATION").unwrap();
15758 assert!(matches!(result, QueryResult::Communities(_)));
15759 }
15760
15761 #[test]
15766 fn test_graph_index_create_node_property() {
15767 let router = QueryRouter::new();
15768
15769 router
15771 .execute("NODE CREATE Person { name: 'Test' }")
15772 .unwrap();
15773
15774 let result = router
15775 .execute("GRAPH INDEX CREATE ON NODE PROPERTY name")
15776 .unwrap();
15777 assert!(matches!(result, QueryResult::Empty));
15778 }
15779
15780 #[test]
15781 fn test_graph_index_show() {
15782 let router = QueryRouter::new();
15783
15784 let result = router.execute("GRAPH INDEX SHOW ON NODE").unwrap();
15785 assert!(matches!(result, QueryResult::GraphIndexes(_)));
15786 }
15787
15788 #[test]
15793 fn test_constraint_create_list_get_drop() {
15794 let router = QueryRouter::new();
15795
15796 let result = router
15798 .execute("CONSTRAINT CREATE email_unique ON NODE PROPERTY email UNIQUE")
15799 .unwrap();
15800 assert!(matches!(result, QueryResult::Empty));
15801
15802 let result = router.execute("CONSTRAINT LIST").unwrap();
15804 match result {
15805 QueryResult::Constraints(constraints) => {
15806 assert!(!constraints.is_empty());
15807 },
15808 _ => panic!("Expected Constraints result"),
15809 }
15810
15811 let result = router.execute("CONSTRAINT GET email_unique").unwrap();
15813 match result {
15814 QueryResult::Constraints(c) => assert!(!c.is_empty()),
15815 _ => panic!("Expected Constraints result"),
15816 }
15817
15818 let result = router.execute("CONSTRAINT DROP email_unique").unwrap();
15820 assert!(matches!(result, QueryResult::Empty));
15821 }
15822
15823 #[test]
15828 fn test_batch_create_nodes() {
15829 let router = QueryRouter::new();
15830
15831 let result = router
15832 .execute("BATCH CREATE NODES [{labels: [Person], name: 'Alice'}, {labels: [Person], name: 'Bob'}]")
15833 .unwrap();
15834
15835 match result {
15836 QueryResult::BatchResult(batch) => {
15837 assert_eq!(batch.affected_count, 2);
15838 assert!(batch.created_ids.is_some());
15839 },
15840 _ => panic!("Expected BatchResult"),
15841 }
15842 }
15843
15844 #[test]
15845 fn test_batch_create_edges() {
15846 let router = QueryRouter::new();
15847
15848 let a = match router.execute("NODE CREATE Person { name: 'A' }").unwrap() {
15850 QueryResult::Ids(ids) => ids[0],
15851 _ => panic!("Expected Ids"),
15852 };
15853 let b = match router.execute("NODE CREATE Person { name: 'B' }").unwrap() {
15854 QueryResult::Ids(ids) => ids[0],
15855 _ => panic!("Expected Ids"),
15856 };
15857
15858 let result = router
15859 .execute(&format!(
15860 "BATCH CREATE EDGES [{{from: {}, to: {}, type: knows}}]",
15861 a, b
15862 ))
15863 .unwrap();
15864
15865 match result {
15866 QueryResult::BatchResult(batch) => {
15867 assert_eq!(batch.affected_count, 1);
15868 },
15869 _ => panic!("Expected BatchResult"),
15870 }
15871 }
15872
15873 #[test]
15874 fn test_batch_delete_nodes() {
15875 let router = QueryRouter::new();
15876
15877 let a = match router.execute("NODE CREATE Temp").unwrap() {
15879 QueryResult::Ids(ids) => ids[0],
15880 _ => panic!("Expected Ids"),
15881 };
15882 let b = match router.execute("NODE CREATE Temp").unwrap() {
15883 QueryResult::Ids(ids) => ids[0],
15884 _ => panic!("Expected Ids"),
15885 };
15886
15887 let result = router
15888 .execute(&format!("BATCH DELETE NODES [{}, {}]", a, b))
15889 .unwrap();
15890
15891 match result {
15892 QueryResult::BatchResult(batch) => {
15893 assert_eq!(batch.affected_count, 2);
15894 },
15895 _ => panic!("Expected BatchResult"),
15896 }
15897 }
15898
15899 #[test]
15900 fn test_batch_delete_edges() {
15901 let router = QueryRouter::new();
15902
15903 let a = match router.execute("NODE CREATE nd").unwrap() {
15905 QueryResult::Ids(ids) => ids[0],
15906 _ => panic!("Expected Ids"),
15907 };
15908 let b = match router.execute("NODE CREATE nd").unwrap() {
15909 QueryResult::Ids(ids) => ids[0],
15910 _ => panic!("Expected Ids"),
15911 };
15912
15913 let edge_id = match router
15914 .execute(&format!("EDGE CREATE {} -> {} : test_edge", a, b))
15915 .unwrap()
15916 {
15917 QueryResult::Ids(ids) => ids[0],
15918 _ => panic!("Expected Ids"),
15919 };
15920
15921 let result = router
15922 .execute(&format!("BATCH DELETE EDGES [{}]", edge_id))
15923 .unwrap();
15924
15925 match result {
15926 QueryResult::BatchResult(batch) => {
15927 assert_eq!(batch.affected_count, 1);
15928 },
15929 _ => panic!("Expected BatchResult"),
15930 }
15931 }
15932
15933 #[test]
15934 fn test_batch_update_nodes() {
15935 let router = QueryRouter::new();
15936
15937 let a = match router
15939 .execute("NODE CREATE Person { name: 'Alice' }")
15940 .unwrap()
15941 {
15942 QueryResult::Ids(ids) => ids[0],
15943 _ => panic!("Expected Ids"),
15944 };
15945 let b = match router
15946 .execute("NODE CREATE Person { name: 'Bob' }")
15947 .unwrap()
15948 {
15949 QueryResult::Ids(ids) => ids[0],
15950 _ => panic!("Expected Ids"),
15951 };
15952
15953 let result = router
15955 .execute(&format!(
15956 "BATCH UPDATE NODES [{{id: {}, age: 30}}, {{id: {}, age: 25}}]",
15957 a, b
15958 ))
15959 .unwrap();
15960
15961 match result {
15962 QueryResult::BatchResult(batch) => {
15963 assert_eq!(batch.operation, "UPDATE_NODES");
15964 assert_eq!(batch.affected_count, 2);
15965 },
15966 _ => panic!("Expected BatchResult"),
15967 }
15968 }
15969
15970 #[test]
15975 fn test_aggregate_node_property() {
15976 let router = QueryRouter::new();
15977
15978 router.execute("NODE CREATE Person { age: 25 }").unwrap();
15980 router.execute("NODE CREATE Person { age: 30 }").unwrap();
15981 router.execute("NODE CREATE Person { age: 35 }").unwrap();
15982
15983 let result = router.execute("AGGREGATE NODE PROPERTY age SUM").unwrap();
15984 match result {
15985 QueryResult::Aggregate(AggregateResultValue::Sum(s)) => {
15986 assert!((s - 90.0).abs() < 0.001);
15988 },
15989 _ => panic!("Expected Aggregate Sum result"),
15990 }
15991 }
15992
15993 #[test]
15994 fn test_aggregate_node_property_avg() {
15995 let router = QueryRouter::new();
15996
15997 router.execute("NODE CREATE Person { age: 20 }").unwrap();
15998 router.execute("NODE CREATE Person { age: 40 }").unwrap();
15999
16000 let result = router.execute("AGGREGATE NODE PROPERTY age AVG").unwrap();
16001 match result {
16002 QueryResult::Aggregate(AggregateResultValue::Avg(a)) => {
16003 assert!((a - 30.0).abs() < 0.001);
16004 },
16005 _ => panic!("Expected Aggregate Avg result"),
16006 }
16007 }
16008
16009 #[test]
16010 fn test_aggregate_edge_property() {
16011 let router = QueryRouter::new();
16012
16013 let a = match router.execute("NODE CREATE nd").unwrap() {
16014 QueryResult::Ids(ids) => ids[0],
16015 _ => panic!("Expected Ids"),
16016 };
16017 let b = match router.execute("NODE CREATE nd").unwrap() {
16018 QueryResult::Ids(ids) => ids[0],
16019 _ => panic!("Expected Ids"),
16020 };
16021
16022 router
16023 .execute(&format!(
16024 "EDGE CREATE {} -> {} : conn {{ weight: 0.5 }}",
16025 a, b
16026 ))
16027 .unwrap();
16028
16029 let result = router
16030 .execute("AGGREGATE EDGE PROPERTY weight SUM")
16031 .unwrap();
16032 assert!(matches!(
16033 result,
16034 QueryResult::Aggregate(AggregateResultValue::Sum(_))
16035 ));
16036 }
16037
16038 #[test]
16039 fn test_aggregate_edge_property_avg() {
16040 let router = QueryRouter::new();
16041
16042 let a = match router.execute("NODE CREATE nd").unwrap() {
16043 QueryResult::Ids(ids) => ids[0],
16044 _ => panic!("Expected Ids"),
16045 };
16046 let b = match router.execute("NODE CREATE nd").unwrap() {
16047 QueryResult::Ids(ids) => ids[0],
16048 _ => panic!("Expected Ids"),
16049 };
16050
16051 router
16052 .execute(&format!(
16053 "EDGE CREATE {} -> {} : conn {{ weight: 0.5 }}",
16054 a, b
16055 ))
16056 .unwrap();
16057
16058 let result = router
16059 .execute("AGGREGATE EDGE PROPERTY weight AVG")
16060 .unwrap();
16061 assert!(matches!(
16063 result,
16064 QueryResult::Aggregate(AggregateResultValue::Avg(_))
16065 ));
16066 }
16067
16068 #[test]
16069 fn test_aggregate_edge_property_min() {
16070 let router = QueryRouter::new();
16071
16072 let a = match router.execute("NODE CREATE nd").unwrap() {
16073 QueryResult::Ids(ids) => ids[0],
16074 _ => panic!("Expected Ids"),
16075 };
16076 let b = match router.execute("NODE CREATE nd").unwrap() {
16077 QueryResult::Ids(ids) => ids[0],
16078 _ => panic!("Expected Ids"),
16079 };
16080
16081 router
16082 .execute(&format!(
16083 "EDGE CREATE {} -> {} : conn {{ score: 0.5 }}",
16084 a, b
16085 ))
16086 .unwrap();
16087
16088 let result = router.execute("AGGREGATE EDGE PROPERTY score MIN").unwrap();
16089 assert!(matches!(
16090 result,
16091 QueryResult::Aggregate(AggregateResultValue::Min(_))
16092 ));
16093 }
16094
16095 #[test]
16096 fn test_aggregate_edge_property_max() {
16097 let router = QueryRouter::new();
16098
16099 let a = match router.execute("NODE CREATE nd").unwrap() {
16100 QueryResult::Ids(ids) => ids[0],
16101 _ => panic!("Expected Ids"),
16102 };
16103 let b = match router.execute("NODE CREATE nd").unwrap() {
16104 QueryResult::Ids(ids) => ids[0],
16105 _ => panic!("Expected Ids"),
16106 };
16107
16108 router
16109 .execute(&format!(
16110 "EDGE CREATE {} -> {} : conn {{ value: 0.75 }}",
16111 a, b
16112 ))
16113 .unwrap();
16114
16115 let result = router.execute("AGGREGATE EDGE PROPERTY value MAX").unwrap();
16116 assert!(matches!(
16117 result,
16118 QueryResult::Aggregate(AggregateResultValue::Max(_))
16119 ));
16120 }
16121
16122 #[test]
16123 fn test_aggregate_edge_property_count() {
16124 let router = QueryRouter::new();
16125
16126 let a = match router.execute("NODE CREATE nd").unwrap() {
16127 QueryResult::Ids(ids) => ids[0],
16128 _ => panic!("Expected Ids"),
16129 };
16130 let b = match router.execute("NODE CREATE nd").unwrap() {
16131 QueryResult::Ids(ids) => ids[0],
16132 _ => panic!("Expected Ids"),
16133 };
16134
16135 router
16136 .execute(&format!(
16137 "EDGE CREATE {} -> {} : conn {{ prop: 0.1 }}",
16138 a, b
16139 ))
16140 .unwrap();
16141
16142 let result = router
16143 .execute("AGGREGATE EDGE PROPERTY prop COUNT")
16144 .unwrap();
16145 assert!(matches!(
16146 result,
16147 QueryResult::Aggregate(AggregateResultValue::Count(_))
16148 ));
16149 }
16150
16151 #[test]
16154 fn parsed_embed_store_into_collection() {
16155 let router = QueryRouter::new();
16156
16157 router
16159 .execute_parsed("EMBED STORE 'doc1' [1.0, 2.0, 3.0] INTO my_collection")
16160 .unwrap();
16161
16162 let result = router
16164 .execute_parsed("EMBED GET 'doc1' INTO my_collection")
16165 .unwrap();
16166 assert!(matches!(result, QueryResult::Value(_)));
16167 }
16168
16169 #[test]
16170 fn parsed_embed_delete_from_collection() {
16171 let router = QueryRouter::new();
16172
16173 router
16175 .execute_parsed("EMBED STORE 'to_delete' [1.0, 2.0] INTO test_coll")
16176 .unwrap();
16177
16178 let result = router
16180 .execute_parsed("EMBED DELETE 'to_delete' INTO test_coll")
16181 .unwrap();
16182 assert!(matches!(result, QueryResult::Count(1)));
16183
16184 let get_result = router.execute_parsed("EMBED GET 'to_delete' INTO test_coll");
16186 assert!(get_result.is_err());
16187 }
16188
16189 #[test]
16190 fn parsed_similar_into_collection() {
16191 let router = QueryRouter::new();
16192
16193 router
16195 .execute_parsed("EMBED STORE 'vec_a' [1.0, 0.0, 0.0] INTO vectors")
16196 .unwrap();
16197 router
16198 .execute_parsed("EMBED STORE 'vec_b' [0.9, 0.1, 0.0] INTO vectors")
16199 .unwrap();
16200 router
16201 .execute_parsed("EMBED STORE 'vec_c' [0.0, 1.0, 0.0] INTO vectors")
16202 .unwrap();
16203
16204 let result = router
16206 .execute_parsed("SIMILAR [1.0, 0.0, 0.0] LIMIT 3 INTO vectors")
16207 .unwrap();
16208
16209 match result {
16210 QueryResult::Similar(results) => {
16211 assert_eq!(results.len(), 3);
16212 assert_eq!(results[0].key, "vec_a");
16214 },
16215 _ => panic!("Expected Similar"),
16216 }
16217 }
16218
16219 #[test]
16220 fn parsed_similar_into_collection_with_where() {
16221 let router = QueryRouter::new();
16222
16223 router
16225 .execute_parsed("EMBED STORE 'item_1' [1.0, 0.0, 0.0] INTO test_coll")
16226 .unwrap();
16227 router
16228 .execute_parsed("EMBED STORE 'item_2' [0.9, 0.1, 0.0] INTO test_coll")
16229 .unwrap();
16230 router
16231 .execute_parsed("EMBED STORE 'item_3' [0.0, 1.0, 0.0] INTO test_coll")
16232 .unwrap();
16233
16234 let result = router.execute_parsed(
16237 "SIMILAR [1.0, 0.0, 0.0] WHERE key CONTAINS 'item' LIMIT 3 INTO test_coll",
16238 );
16239
16240 assert!(result.is_ok() || result.is_err());
16242 }
16243
16244 #[test]
16245 fn parsed_similar_with_where_clause() {
16246 let router = QueryRouter::new();
16247
16248 use std::collections::HashMap;
16250 use tensor_store::ScalarValue;
16251 let mut meta_a = HashMap::new();
16252 meta_a.insert(
16253 "category".to_string(),
16254 tensor_store::TensorValue::Scalar(ScalarValue::String("science".to_string())),
16255 );
16256 router
16257 .vector()
16258 .store_embedding_with_metadata("item_a", vec![1.0, 0.0], meta_a)
16259 .unwrap();
16260
16261 let mut meta_b = HashMap::new();
16262 meta_b.insert(
16263 "category".to_string(),
16264 tensor_store::TensorValue::Scalar(ScalarValue::String("tech".to_string())),
16265 );
16266 router
16267 .vector()
16268 .store_embedding_with_metadata("item_b", vec![0.9, 0.1], meta_b)
16269 .unwrap();
16270
16271 let mut meta_c = HashMap::new();
16272 meta_c.insert(
16273 "category".to_string(),
16274 tensor_store::TensorValue::Scalar(ScalarValue::String("science".to_string())),
16275 );
16276 router
16277 .vector()
16278 .store_embedding_with_metadata("item_c", vec![0.8, 0.2], meta_c)
16279 .unwrap();
16280
16281 let result = router
16283 .execute_parsed("SIMILAR [1.0, 0.0] LIMIT 10 WHERE category = 'science'")
16284 .unwrap();
16285
16286 match result {
16287 QueryResult::Similar(results) => {
16288 assert_eq!(results.len(), 2);
16290 for r in &results {
16291 assert!(r.key == "item_a" || r.key == "item_c");
16292 }
16293 },
16294 _ => panic!("Expected Similar"),
16295 }
16296 }
16297
16298 #[test]
16299 fn parsed_embed_batch_into_collection() {
16300 let router = QueryRouter::new();
16301
16302 let result = router
16304 .execute_parsed("EMBED BATCH [('b1', [1.0, 2.0]), ('b2', [3.0, 4.0])] INTO batch_test")
16305 .unwrap();
16306 assert!(matches!(result, QueryResult::Count(2)));
16307
16308 let r1 = router.execute_parsed("EMBED GET 'b1' INTO batch_test");
16310 let r2 = router.execute_parsed("EMBED GET 'b2' INTO batch_test");
16311 assert!(r1.is_ok());
16312 assert!(r2.is_ok());
16313 }
16314
16315 #[test]
16316 fn parsed_collection_isolation() {
16317 let router = QueryRouter::new();
16318
16319 router
16321 .execute_parsed("EMBED STORE 'shared_key' [1.0, 0.0] INTO coll_a")
16322 .unwrap();
16323 router
16324 .execute_parsed("EMBED STORE 'shared_key' [0.0, 1.0] INTO coll_b")
16325 .unwrap();
16326
16327 let result_a = router
16329 .execute_parsed("EMBED GET 'shared_key' INTO coll_a")
16330 .unwrap();
16331 let result_b = router
16332 .execute_parsed("EMBED GET 'shared_key' INTO coll_b")
16333 .unwrap();
16334
16335 match (result_a, result_b) {
16337 (QueryResult::Value(va), QueryResult::Value(vb)) => {
16338 assert_ne!(va, vb);
16339 },
16340 _ => panic!("Expected Value results"),
16341 }
16342 }
16343
16344 #[test]
16347 fn filter_condition_reexport_accessible() {
16348 let filter = FilterCondition::Eq(
16350 "status".to_string(),
16351 FilterValue::String("active".to_string()),
16352 );
16353 assert!(matches!(filter, FilterCondition::Eq(_, _)));
16354
16355 let int_val = FilterValue::Int(42);
16357 let float_val = FilterValue::Float(3.14);
16358 let bool_val = FilterValue::Bool(true);
16359 assert!(matches!(int_val, FilterValue::Int(42)));
16360 assert!(matches!(float_val, FilterValue::Float(_)));
16361 assert!(matches!(bool_val, FilterValue::Bool(true)));
16362 }
16363
16364 #[test]
16365 fn filter_strategy_reexport_accessible() {
16366 let auto = FilterStrategy::Auto;
16367 let pre = FilterStrategy::PreFilter;
16368 let post = FilterStrategy::PostFilter;
16369 assert!(matches!(auto, FilterStrategy::Auto));
16370 assert!(matches!(pre, FilterStrategy::PreFilter));
16371 assert!(matches!(post, FilterStrategy::PostFilter));
16372 }
16373
16374 #[test]
16375 fn filtered_search_config_reexport_accessible() {
16376 let config = FilteredSearchConfig::default();
16377 assert!(matches!(config.strategy, FilterStrategy::Auto));
16378
16379 let pre_config = FilteredSearchConfig::pre_filter();
16380 assert!(matches!(pre_config.strategy, FilterStrategy::PreFilter));
16381
16382 let post_config = FilteredSearchConfig::post_filter();
16383 assert!(matches!(post_config.strategy, FilterStrategy::PostFilter));
16384 }
16385
16386 #[test]
16387 fn expr_to_filter_condition_public() {
16388 use neumann_parser::parse_expr;
16389
16390 let router = QueryRouter::new();
16391
16392 let expr = parse_expr("status = 'active'").unwrap();
16394 let filter = router.expr_to_filter_condition(&expr).unwrap();
16395 assert!(matches!(filter, FilterCondition::Eq(_, _)));
16396 }
16397
16398 #[test]
16399 fn expr_to_filter_condition_comparisons() {
16400 use neumann_parser::parse_expr;
16401
16402 let router = QueryRouter::new();
16403
16404 let lt_expr = parse_expr("age < 30").unwrap();
16406 let lt_filter = router.expr_to_filter_condition(<_expr).unwrap();
16407 assert!(matches!(lt_filter, FilterCondition::Lt(_, _)));
16408
16409 let ge_expr = parse_expr("score >= 80").unwrap();
16411 let ge_filter = router.expr_to_filter_condition(&ge_expr).unwrap();
16412 assert!(matches!(ge_filter, FilterCondition::Ge(_, _)));
16413
16414 let ne_expr = parse_expr("status != 'deleted'").unwrap();
16416 let ne_filter = router.expr_to_filter_condition(&ne_expr).unwrap();
16417 assert!(matches!(ne_filter, FilterCondition::Ne(_, _)));
16418 }
16419
16420 #[test]
16421 fn expr_to_filter_condition_and_or() {
16422 use neumann_parser::parse_expr;
16423
16424 let router = QueryRouter::new();
16425
16426 let and_expr = parse_expr("status = 'active' AND age > 18").unwrap();
16428 let and_filter = router.expr_to_filter_condition(&and_expr).unwrap();
16429 assert!(matches!(and_filter, FilterCondition::And(_, _)));
16430
16431 let or_expr = parse_expr("status = 'active' OR status = 'pending'").unwrap();
16433 let or_filter = router.expr_to_filter_condition(&or_expr).unwrap();
16434 assert!(matches!(or_filter, FilterCondition::Or(_, _)));
16435 }
16436
16437 #[test]
16438 fn expr_to_filter_value_types() {
16439 use neumann_parser::parse_expr;
16440
16441 let router = QueryRouter::new();
16442
16443 let int_expr = parse_expr("42").unwrap();
16445 let int_val = router.expr_to_filter_value(&int_expr).unwrap();
16446 assert!(matches!(int_val, FilterValue::Int(42)));
16447
16448 let float_expr = parse_expr("3.14").unwrap();
16450 let float_val = router.expr_to_filter_value(&float_expr).unwrap();
16451 assert!(matches!(float_val, FilterValue::Float(_)));
16452
16453 let str_expr = parse_expr("'hello'").unwrap();
16455 let str_val = router.expr_to_filter_value(&str_expr).unwrap();
16456 assert!(matches!(str_val, FilterValue::String(_)));
16457
16458 let bool_expr = parse_expr("true").unwrap();
16460 let bool_val = router.expr_to_filter_value(&bool_expr).unwrap();
16461 assert!(matches!(bool_val, FilterValue::Bool(true)));
16462 }
16463
16464 #[test]
16465 fn expr_to_column_name_public() {
16466 use neumann_parser::parse_expr;
16467
16468 let router = QueryRouter::new();
16469
16470 let ident_expr = parse_expr("column_name").unwrap();
16472 let col = router.expr_to_column_name(&ident_expr).unwrap();
16473 assert_eq!(col, "column_name");
16474 }
16475
16476 #[test]
16479 fn router_error_display_all_variants() {
16480 let errors = vec![
16481 (
16482 RouterError::RelationalError("rel err".into()),
16483 "Relational error: rel err",
16484 ),
16485 (
16486 RouterError::GraphError("graph err".into()),
16487 "Graph error: graph err",
16488 ),
16489 (
16490 RouterError::VectorError("vec err".into()),
16491 "Vector error: vec err",
16492 ),
16493 (
16494 RouterError::ParseError("parse err".into()),
16495 "Parse error: parse err",
16496 ),
16497 (
16498 RouterError::UnknownCommand("cmd".into()),
16499 "Unknown command: cmd",
16500 ),
16501 (
16502 RouterError::VaultError("vault err".into()),
16503 "Vault error: vault err",
16504 ),
16505 (
16506 RouterError::CacheError("cache err".into()),
16507 "Cache error: cache err",
16508 ),
16509 (
16510 RouterError::BlobError("blob err".into()),
16511 "Blob error: blob err",
16512 ),
16513 (
16514 RouterError::CheckpointError("cp err".into()),
16515 "Checkpoint error: cp err",
16516 ),
16517 (
16518 RouterError::ChainError("chain err".into()),
16519 "Chain error: chain err",
16520 ),
16521 (
16522 RouterError::InvalidArgument("inv arg".into()),
16523 "Invalid argument: inv arg",
16524 ),
16525 (
16526 RouterError::TypeMismatch("type mm".into()),
16527 "Type mismatch: type mm",
16528 ),
16529 (
16530 RouterError::MissingArgument("miss arg".into()),
16531 "Missing argument: miss arg",
16532 ),
16533 (
16534 RouterError::AuthenticationRequired,
16535 "Authentication required: call SET IDENTITY before vault operations",
16536 ),
16537 (
16538 RouterError::NotFound("not found".into()),
16539 "Not found: not found",
16540 ),
16541 ];
16542
16543 for (err, expected) in errors {
16544 assert_eq!(format!("{}", err), expected);
16545 }
16546 }
16547
16548 #[test]
16549 fn router_error_is_std_error() {
16550 let err: Box<dyn std::error::Error> = Box::new(RouterError::ParseError("test".into()));
16551 assert!(err.to_string().contains("Parse error"));
16552 }
16553
16554 #[test]
16555 fn router_error_from_unified_error_variants() {
16556 let unified_rel = UnifiedError::RelationalError("rel".into());
16557 let router_err: RouterError = unified_rel.into();
16558 assert!(matches!(router_err, RouterError::RelationalError(_)));
16559
16560 let unified_graph = UnifiedError::GraphError("graph".into());
16561 let router_err: RouterError = unified_graph.into();
16562 assert!(matches!(router_err, RouterError::GraphError(_)));
16563
16564 let unified_vec = UnifiedError::VectorError("vec".into());
16565 let router_err: RouterError = unified_vec.into();
16566 assert!(matches!(router_err, RouterError::VectorError(_)));
16567
16568 let unified_not_found = UnifiedError::NotFound("key".into());
16569 let router_err: RouterError = unified_not_found.into();
16570 assert!(matches!(router_err, RouterError::VectorError(_)));
16571
16572 let unified_invalid = UnifiedError::InvalidOperation("op".into());
16573 let router_err: RouterError = unified_invalid.into();
16574 assert!(matches!(router_err, RouterError::InvalidArgument(_)));
16575
16576 let unified_batch = UnifiedError::BatchOperationFailed {
16577 index: 0,
16578 key: "k".into(),
16579 cause: "c".into(),
16580 };
16581 let router_err: RouterError = unified_batch.into();
16582 assert!(matches!(router_err, RouterError::VectorError(_)));
16583
16584 let unified_spatial = UnifiedError::SpatialError("bad bounds".into());
16585 let router_err: RouterError = unified_spatial.into();
16586 assert!(matches!(router_err, RouterError::InvalidArgument(_)));
16587 }
16588
16589 #[test]
16590 fn spatial_accessor_and_operations() {
16591 let router = QueryRouter::new();
16592 let spatial = router.spatial().clone();
16593 assert_eq!(spatial.read().len(), 0);
16594
16595 router
16597 .execute("SPATIAL INSERT 'park' BOUNDS 10.0 20.0 5.0 3.0")
16598 .unwrap();
16599 assert_eq!(spatial.read().len(), 1);
16600
16601 let guard = spatial.read();
16603 let results = guard.query_within_radius_with_distances(10.0, 20.0, 50.0);
16604 assert_eq!(results.len(), 1);
16605 assert_eq!(results[0].0.data, "park");
16606 }
16607
16608 #[test]
16609 fn spatial_result_variant() {
16610 let router = QueryRouter::new();
16611 router
16612 .execute("SPATIAL INSERT 'obj' BOUNDS 1.0 2.0 1.0 1.0")
16613 .unwrap();
16614 let result = router
16615 .execute("SPATIAL WITHIN 1.0 2.0 RADIUS 10.0")
16616 .unwrap();
16617 match &result {
16618 QueryResult::Spatial(items) => {
16619 assert!(!items.is_empty());
16620 assert_eq!(items[0].key, "obj");
16621 },
16622 other => panic!("Expected Spatial, got: {other:?}"),
16623 }
16624 }
16625
16626 #[test]
16629 fn query_result_as_rows_none() {
16630 let result = QueryResult::Count(10);
16631 assert!(result.as_rows().is_none());
16632
16633 let result = QueryResult::Value("test".into());
16634 assert!(result.as_rows().is_none());
16635 }
16636
16637 #[test]
16638 fn query_result_as_count_variants() {
16639 let result = QueryResult::Count(42);
16640 assert_eq!(result.as_count(), Some(42));
16641
16642 let result = QueryResult::Rows(vec![]);
16643 assert!(result.as_count().is_none());
16644
16645 let result = QueryResult::Ids(vec![1, 2, 3]);
16646 assert!(result.as_count().is_none());
16647 }
16648
16649 #[test]
16650 fn query_result_as_value_variants() {
16651 let result = QueryResult::Value("hello".into());
16652 assert_eq!(result.as_value(), Some("hello"));
16653
16654 let result = QueryResult::Count(5);
16655 assert!(result.as_value().is_none());
16656 }
16657
16658 #[test]
16659 fn query_result_is_empty_variants() {
16660 assert!(QueryResult::Empty.is_empty());
16662 assert!(!QueryResult::Rows(vec![]).is_empty());
16663 assert!(!QueryResult::Rows(vec![Row {
16664 id: 0,
16665 values: vec![]
16666 }])
16667 .is_empty());
16668 assert!(!QueryResult::Ids(vec![]).is_empty());
16669 assert!(!QueryResult::Count(0).is_empty());
16670 assert!(!QueryResult::Value("test".into()).is_empty());
16671 assert!(!QueryResult::Nodes(vec![]).is_empty());
16672 assert!(!QueryResult::Edges(vec![]).is_empty());
16673 }
16674
16675 #[test]
16676 fn query_result_debug_format() {
16677 let result = QueryResult::Count(5);
16678 let debug = format!("{:?}", result);
16679 assert!(debug.contains("Count"));
16680 }
16681
16682 #[test]
16685 fn execute_empty_string() {
16686 let router = QueryRouter::new();
16687 let result = router.execute("");
16688 assert!(result.is_err());
16689 }
16690
16691 #[test]
16692 fn execute_whitespace_variations() {
16693 let router = QueryRouter::new();
16694
16695 let result = router.execute(" ");
16697 assert!(result.is_err());
16698
16699 let result = router.execute("\t\t");
16701 assert!(result.is_err());
16702 }
16703
16704 #[test]
16705 fn insert_with_null_value() {
16706 let router = QueryRouter::new();
16707 router
16709 .execute("CREATE TABLE nulltest (id int, name text)")
16710 .unwrap();
16711 router
16712 .execute("INSERT INTO nulltest (id, name) VALUES (1, NULL)")
16713 .unwrap();
16714
16715 let result = router.execute("SELECT * FROM nulltest").unwrap();
16716 assert!(matches!(result, QueryResult::Rows(_)));
16717 }
16718
16719 #[test]
16720 fn select_with_where_operators() {
16721 let router = QueryRouter::new();
16722 router
16723 .execute("CREATE TABLE ops (id int, val int)")
16724 .unwrap();
16725 router
16726 .execute("INSERT INTO ops (id, val) VALUES (1, 10)")
16727 .unwrap();
16728 router
16729 .execute("INSERT INTO ops (id, val) VALUES (2, 20)")
16730 .unwrap();
16731 router
16732 .execute("INSERT INTO ops (id, val) VALUES (3, 30)")
16733 .unwrap();
16734
16735 let result = router.execute("SELECT * FROM ops WHERE val < 25").unwrap();
16737 if let QueryResult::Rows(rows) = result {
16738 assert_eq!(rows.len(), 2);
16739 }
16740
16741 let result = router.execute("SELECT * FROM ops WHERE val <= 20").unwrap();
16743 if let QueryResult::Rows(rows) = result {
16744 assert_eq!(rows.len(), 2);
16745 }
16746
16747 let result = router.execute("SELECT * FROM ops WHERE val >= 20").unwrap();
16749 if let QueryResult::Rows(rows) = result {
16750 assert_eq!(rows.len(), 2);
16751 }
16752
16753 let result = router.execute("SELECT * FROM ops WHERE val != 20").unwrap();
16755 if let QueryResult::Rows(rows) = result {
16756 assert_eq!(rows.len(), 2);
16757 }
16758 }
16759
16760 #[test]
16761 fn select_with_and_or_conditions() {
16762 let router = QueryRouter::new();
16763 router.execute("CREATE TABLE logic (a int, b int)").unwrap();
16764 router
16765 .execute("INSERT INTO logic (a, b) VALUES (1, 1)")
16766 .unwrap();
16767 router
16768 .execute("INSERT INTO logic (a, b) VALUES (1, 2)")
16769 .unwrap();
16770 router
16771 .execute("INSERT INTO logic (a, b) VALUES (2, 1)")
16772 .unwrap();
16773
16774 let result = router
16775 .execute("SELECT * FROM logic WHERE a = 1 AND b = 1")
16776 .unwrap();
16777 if let QueryResult::Rows(rows) = result {
16778 assert_eq!(rows.len(), 1);
16779 }
16780
16781 let result = router
16782 .execute("SELECT * FROM logic WHERE a = 1 OR b = 1")
16783 .unwrap();
16784 if let QueryResult::Rows(rows) = result {
16785 assert_eq!(rows.len(), 3);
16786 }
16787 }
16788
16789 #[test]
16790 fn node_create_with_various_property_types() {
16791 let router = QueryRouter::new();
16792
16793 router.execute("NODE CREATE intnode { cnt: 42 }").unwrap();
16795
16796 router
16798 .execute("NODE CREATE floatnode { value: 3.14 }")
16799 .unwrap();
16800
16801 router
16803 .execute("NODE CREATE boolnode { active: true }")
16804 .unwrap();
16805 router
16806 .execute("NODE CREATE boolnode2 { active: false }")
16807 .unwrap();
16808
16809 router
16811 .execute("NODE CREATE strnode { name: 'hello world' }")
16812 .unwrap();
16813 }
16814
16815 #[test]
16816 fn edge_operations_comprehensive() {
16817 let router = QueryRouter::new();
16818
16819 let n1 = match router.execute("NODE CREATE person { name: 'A' }").unwrap() {
16820 QueryResult::Ids(ids) => ids[0],
16821 _ => panic!("Expected Ids"),
16822 };
16823 let n2 = match router.execute("NODE CREATE person { name: 'B' }").unwrap() {
16824 QueryResult::Ids(ids) => ids[0],
16825 _ => panic!("Expected Ids"),
16826 };
16827 let n3 = match router.execute("NODE CREATE person { name: 'C' }").unwrap() {
16828 QueryResult::Ids(ids) => ids[0],
16829 _ => panic!("Expected Ids"),
16830 };
16831
16832 let e1 = match router
16834 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
16835 .unwrap()
16836 {
16837 QueryResult::Ids(ids) => ids[0],
16838 _ => panic!("Expected Ids"),
16839 };
16840 let _e2 = router
16841 .execute(&format!("EDGE CREATE {} -> {}", n2, n3))
16842 .unwrap();
16843
16844 let result = router.execute(&format!("EDGE GET {}", e1)).unwrap();
16846 assert!(matches!(result, QueryResult::Edges(_)));
16847 }
16848
16849 #[test]
16850 fn embed_and_similar_comprehensive() {
16851 let router = QueryRouter::new();
16852
16853 router.execute("EMBED vec1 [1.0, 0.0, 0.0]").unwrap();
16855 router.execute("EMBED vec2 [0.9, 0.1, 0.0]").unwrap();
16856 router.execute("EMBED vec3 [0.0, 1.0, 0.0]").unwrap();
16857 router.execute("EMBED vec4 [0.0, 0.0, 1.0]").unwrap();
16858
16859 let result = router.execute("SIMILAR vec1 TOP 3").unwrap();
16861 if let QueryResult::Similar(results) = result {
16862 assert!(results.len() <= 3);
16863 }
16864
16865 let result = router.execute("SIMILAR [1.0, 0.0, 0.0] TOP 2").unwrap();
16867 if let QueryResult::Similar(results) = result {
16868 assert!(results.len() <= 2);
16869 }
16870 }
16871
16872 #[test]
16873 fn aggregation_functions_comprehensive() {
16874 let router = QueryRouter::new();
16875 router
16876 .execute("CREATE TABLE agg (category string, value int)")
16877 .unwrap();
16878 router
16879 .execute("INSERT INTO agg (category, value) VALUES ('A', 10)")
16880 .unwrap();
16881 router
16882 .execute("INSERT INTO agg (category, value) VALUES ('A', 20)")
16883 .unwrap();
16884 router
16885 .execute("INSERT INTO agg (category, value) VALUES ('B', 30)")
16886 .unwrap();
16887
16888 let result = router.execute("SELECT COUNT(*) FROM agg").unwrap();
16890 assert!(matches!(result, QueryResult::Rows(_)));
16891
16892 let result = router.execute("SELECT SUM(value) FROM agg").unwrap();
16894 assert!(matches!(result, QueryResult::Rows(_)));
16895
16896 let result = router.execute("SELECT AVG(value) FROM agg").unwrap();
16898 assert!(matches!(result, QueryResult::Rows(_)));
16899
16900 let result = router.execute("SELECT MIN(value) FROM agg").unwrap();
16902 assert!(matches!(result, QueryResult::Rows(_)));
16903
16904 let result = router.execute("SELECT MAX(value) FROM agg").unwrap();
16906 assert!(matches!(result, QueryResult::Rows(_)));
16907 }
16908
16909 #[test]
16910 fn delete_from_table() {
16911 let router = QueryRouter::new();
16912 router
16913 .execute("CREATE TABLE deltest (id int, name string)")
16914 .unwrap();
16915 router
16916 .execute("INSERT INTO deltest (id, name) VALUES (1, 'A')")
16917 .unwrap();
16918 router
16919 .execute("INSERT INTO deltest (id, name) VALUES (2, 'B')")
16920 .unwrap();
16921 router
16922 .execute("INSERT INTO deltest (id, name) VALUES (3, 'C')")
16923 .unwrap();
16924
16925 router.execute("DELETE FROM deltest WHERE id = 2").unwrap();
16927
16928 let result = router.execute("SELECT * FROM deltest").unwrap();
16929 if let QueryResult::Rows(rows) = result {
16930 assert_eq!(rows.len(), 2);
16931 }
16932 }
16933
16934 #[test]
16935 fn update_table_rows() {
16936 let router = QueryRouter::new();
16937 router
16938 .execute("CREATE TABLE updtest (id int, status string)")
16939 .unwrap();
16940 router
16941 .execute("INSERT INTO updtest (id, status) VALUES (1, 'pending')")
16942 .unwrap();
16943 router
16944 .execute("INSERT INTO updtest (id, status) VALUES (2, 'pending')")
16945 .unwrap();
16946
16947 router
16948 .execute("UPDATE updtest SET status='done' WHERE id = 1")
16949 .unwrap();
16950
16951 let result = router
16952 .execute("SELECT * FROM updtest WHERE id = 1")
16953 .unwrap();
16954 if let QueryResult::Rows(rows) = result {
16955 assert_eq!(rows.len(), 1);
16956 }
16957 }
16958
16959 #[test]
16960 fn drop_table_coverage() {
16961 let router = QueryRouter::new();
16962 router.execute("CREATE TABLE dropme (id int)").unwrap();
16963 router
16964 .execute("INSERT INTO dropme (id) VALUES (1)")
16965 .unwrap();
16966
16967 router.execute("DROP TABLE dropme").unwrap();
16968
16969 let result = router.execute("SELECT * FROM dropme");
16970 assert!(result.is_err());
16971 }
16972
16973 #[test]
16974 fn index_operations() {
16975 let router = QueryRouter::new();
16976 router
16977 .execute("CREATE TABLE indexed (id int, name string)")
16978 .unwrap();
16979
16980 router
16982 .execute("CREATE INDEX idx_name ON indexed(name)")
16983 .unwrap();
16984
16985 router.execute("DROP INDEX ON indexed(name)").unwrap();
16987 }
16988
16989 #[test]
16990 fn join_operations() {
16991 let router = QueryRouter::new();
16992 router
16993 .execute("CREATE TABLE left_t (id int, val string)")
16994 .unwrap();
16995 router
16996 .execute("CREATE TABLE right_t (id int, data string)")
16997 .unwrap();
16998 router
16999 .execute("INSERT INTO left_t (id, val) VALUES (1, 'a')")
17000 .unwrap();
17001 router
17002 .execute("INSERT INTO left_t (id, val) VALUES (2, 'b')")
17003 .unwrap();
17004 router
17005 .execute("INSERT INTO right_t (id, data) VALUES (1, 'x')")
17006 .unwrap();
17007 router
17008 .execute("INSERT INTO right_t (id, data) VALUES (3, 'y')")
17009 .unwrap();
17010
17011 let result = router
17013 .execute_parsed("SELECT * FROM left_t JOIN right_t ON left_t.id = right_t.id")
17014 .unwrap();
17015 assert!(matches!(result, QueryResult::Rows(_)));
17016 }
17017
17018 #[test]
17019 fn path_operations() {
17020 let router = QueryRouter::new();
17021
17022 let n1 = match router.execute("NODE CREATE city { name: 'A' }").unwrap() {
17023 QueryResult::Ids(ids) => ids[0],
17024 _ => panic!("Expected Ids"),
17025 };
17026 let n2 = match router.execute("NODE CREATE city { name: 'B' }").unwrap() {
17027 QueryResult::Ids(ids) => ids[0],
17028 _ => panic!("Expected Ids"),
17029 };
17030 let n3 = match router.execute("NODE CREATE city { name: 'C' }").unwrap() {
17031 QueryResult::Ids(ids) => ids[0],
17032 _ => panic!("Expected Ids"),
17033 };
17034
17035 router
17036 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
17037 .unwrap();
17038 router
17039 .execute(&format!("EDGE CREATE {} -> {}", n2, n3))
17040 .unwrap();
17041
17042 let result = router.execute(&format!("PATH {} -> {}", n1, n3)).unwrap();
17044 assert!(matches!(result, QueryResult::Path(_)));
17045 }
17046
17047 #[test]
17048 fn node_get_and_delete() {
17049 let router = QueryRouter::new();
17050
17051 let id = match router
17052 .execute("NODE CREATE test { name: 'ToDelete' }")
17053 .unwrap()
17054 {
17055 QueryResult::Ids(ids) => ids[0],
17056 _ => panic!("Expected Ids"),
17057 };
17058
17059 let result = router.execute(&format!("NODE GET {}", id)).unwrap();
17061 assert!(matches!(result, QueryResult::Nodes(_)));
17062
17063 router.execute(&format!("NODE DELETE {}", id)).unwrap();
17065
17066 let result = router.execute(&format!("NODE GET {}", id));
17068 assert!(result.is_err());
17069 }
17070
17071 #[test]
17072 fn edge_get() {
17073 let router = QueryRouter::new();
17074
17075 let n1 = match router.execute("NODE CREATE a { x: 1 }").unwrap() {
17076 QueryResult::Ids(ids) => ids[0],
17077 _ => panic!("Expected Ids"),
17078 };
17079 let n2 = match router.execute("NODE CREATE b { x: 2 }").unwrap() {
17080 QueryResult::Ids(ids) => ids[0],
17081 _ => panic!("Expected Ids"),
17082 };
17083
17084 let edge_id = match router
17085 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
17086 .unwrap()
17087 {
17088 QueryResult::Ids(ids) => ids[0],
17089 _ => panic!("Expected Ids"),
17090 };
17091
17092 let result = router.execute(&format!("EDGE GET {}", edge_id)).unwrap();
17094 assert!(matches!(result, QueryResult::Edges(_)));
17095 }
17096
17097 #[test]
17098 fn neighbors_command() {
17099 let router = QueryRouter::new();
17100
17101 let n1 = match router.execute("NODE CREATE hub { x: 1 }").unwrap() {
17102 QueryResult::Ids(ids) => ids[0],
17103 _ => panic!("Expected Ids"),
17104 };
17105 let n2 = match router.execute("NODE CREATE spoke { x: 2 }").unwrap() {
17106 QueryResult::Ids(ids) => ids[0],
17107 _ => panic!("Expected Ids"),
17108 };
17109
17110 router
17111 .execute(&format!("EDGE CREATE {} -> {}", n1, n2))
17112 .unwrap();
17113
17114 let result = router.execute(&format!("NEIGHBORS {}", n1)).unwrap();
17115 assert!(matches!(result, QueryResult::Ids(_)));
17117 }
17118
17119 #[test]
17120 fn show_tables_test() {
17121 let router = QueryRouter::new();
17122 router
17123 .execute("CREATE TABLE shown (id int, name string)")
17124 .unwrap();
17125
17126 let result = router.execute("SHOW TABLES").unwrap();
17127 assert!(matches!(result, QueryResult::TableList(_)));
17129 }
17130
17131 #[test]
17132 fn count_via_select() {
17133 let router = QueryRouter::new();
17134 router.execute("CREATE TABLE counted (id int)").unwrap();
17135 router
17136 .execute("INSERT INTO counted (id) VALUES (1)")
17137 .unwrap();
17138 router
17139 .execute("INSERT INTO counted (id) VALUES (2)")
17140 .unwrap();
17141
17142 let result = router.execute("SELECT COUNT(*) FROM counted").unwrap();
17144 assert!(matches!(result, QueryResult::Rows(_)));
17145 }
17146
17147 #[test]
17148 fn entity_create_get_update_delete() {
17149 let router = QueryRouter::new();
17150
17151 router
17153 .execute("ENTITY CREATE 'user:1' { name: 'Alice', age: '30' }")
17154 .unwrap();
17155
17156 let result = router.execute("ENTITY GET 'user:1'").unwrap();
17158 assert!(matches!(result, QueryResult::Unified(_)));
17159
17160 router
17162 .execute("ENTITY UPDATE 'user:1' { name: 'Alicia', age: '31' }")
17163 .unwrap();
17164
17165 router.execute("ENTITY DELETE 'user:1'").unwrap();
17167 }
17168
17169 #[test]
17170 fn entity_with_embedding() {
17171 let router = QueryRouter::new();
17172
17173 router
17174 .execute("ENTITY CREATE 'doc:1' { title: 'Test' } EMBEDDING [0.1, 0.2, 0.3]")
17175 .unwrap();
17176
17177 let result = router.execute("ENTITY GET 'doc:1'").unwrap();
17178 assert!(matches!(result, QueryResult::Unified(_)));
17179 }
17180
17181 #[test]
17182 fn entity_batch_create() {
17183 let router = QueryRouter::new();
17184
17185 router.execute("ENTITY BATCH CREATE [{key: 'batch:1', name: 'First'}, {key: 'batch:2', name: 'Second'}]").unwrap();
17186
17187 let result = router.execute("ENTITY GET 'batch:1'").unwrap();
17188 assert!(matches!(result, QueryResult::Unified(_)));
17189 }
17190
17191 #[test]
17192 fn entity_connect() {
17193 let router = QueryRouter::new();
17194
17195 router
17196 .execute("ENTITY CREATE 'user:alice' { name: 'Alice' }")
17197 .unwrap();
17198 router
17199 .execute("ENTITY CREATE 'user:bob' { name: 'Bob' }")
17200 .unwrap();
17201
17202 router
17203 .execute("ENTITY CONNECT 'user:alice' -> 'user:bob' : follows")
17204 .unwrap();
17205 }
17206
17207 #[test]
17208 fn find_nodes_edges_rows() {
17209 let router = QueryRouter::new();
17210
17211 router.execute("NODE CREATE findtest { lbl: 'A' }").unwrap();
17213 router.execute("NODE CREATE findtest { lbl: 'B' }").unwrap();
17214
17215 let result = router.execute("FIND NODES findtest").unwrap();
17217 assert!(matches!(result, QueryResult::Unified(_)));
17218
17219 let result = router.execute("FIND EDGES").unwrap();
17221 assert!(matches!(result, QueryResult::Unified(_)));
17222
17223 router.execute("CREATE TABLE findrows (x int)").unwrap();
17225 router
17226 .execute("INSERT INTO findrows (x) VALUES (1)")
17227 .unwrap();
17228 let result = router.execute("FIND ROWS FROM findrows").unwrap();
17229 assert!(matches!(result, QueryResult::Unified(_)));
17230 }
17231
17232 #[test]
17233 fn cluster_commands() {
17234 let router = QueryRouter::new();
17235
17236 let result = router.execute("CLUSTER STATUS");
17238 assert!(result.is_ok());
17239
17240 let result = router.execute("CLUSTER NODES");
17241 assert!(result.is_ok());
17242
17243 let result = router.execute("CLUSTER LEADER");
17244 assert!(result.is_ok());
17245 }
17246
17247 #[test]
17248 fn chain_commands_basic() {
17249 let router = QueryRouter::new();
17250
17251 let result = router.execute("CHAIN HEIGHT");
17252 let _ = result;
17254
17255 let result = router.execute("CHAIN TIP");
17256 let _ = result;
17257 }
17258
17259 #[test]
17260 fn cache_commands() {
17261 let mut router = QueryRouter::new();
17262 router.init_cache();
17263
17264 router
17266 .execute("CACHE PUT 'test_prompt' 'test_response'")
17267 .ok();
17268
17269 let _ = router.execute("CACHE GET 'test_prompt'");
17271
17272 let _ = router.execute("CACHE STATS");
17274 }
17275
17276 #[test]
17277 fn hnsw_build_command() {
17278 let router = QueryRouter::new();
17279
17280 router.execute("EMBED h1 [1.0, 0.0, 0.0]").unwrap();
17282 router.execute("EMBED h2 [0.0, 1.0, 0.0]").unwrap();
17283
17284 let result = router.execute("BUILD HNSW");
17286 let _ = result;
17288 }
17289
17290 #[test]
17291 fn query_result_to_json_formats() {
17292 let result = QueryResult::Count(42);
17293 let json = result.to_json();
17294 assert!(json.contains("42"));
17295
17296 let json_pretty = result.to_pretty_json();
17297 assert!(json_pretty.contains("42"));
17298
17299 let row = Row {
17300 id: 0,
17301 values: vec![("name".to_string(), Value::String("test".to_string()))],
17302 };
17303 let result = QueryResult::Rows(vec![row]);
17304 let json = result.to_json();
17305 assert!(json.contains("name"));
17306 }
17307
17308 #[test]
17309 fn batch_operation_result_display() {
17310 let batch = BatchOperationResult {
17311 operation: "INSERT".to_string(),
17312 affected_count: 5,
17313 created_ids: Some(vec![1, 2, 3, 4, 5]),
17314 };
17315 let debug = format!("{:?}", batch);
17316 assert!(debug.contains("INSERT"));
17317 assert!(debug.contains("5"));
17318 }
17319
17320 #[test]
17321 fn similar_result_display() {
17322 let similar = SimilarResult {
17323 key: "test_key".to_string(),
17324 score: 0.95,
17325 };
17326 let debug = format!("{:?}", similar);
17327 assert!(debug.contains("test_key"));
17328 }
17329
17330 #[test]
17331 fn unified_result_display() {
17332 let unified = UnifiedResult {
17333 description: "Test results".to_string(),
17334 items: vec![],
17335 };
17336 let debug = format!("{:?}", unified);
17337 assert!(debug.contains("Test results"));
17338 }
17339
17340 #[test]
17341 fn error_conditions_comprehensive() {
17342 let router = QueryRouter::new();
17343
17344 let result = router.execute("FOOBAR xyz");
17346 assert!(result.is_err());
17347
17348 let result = router.execute("SELECT * FROM nonexistent");
17350 assert!(result.is_err());
17351
17352 let result = router.execute("SELECT * FROM FROM");
17354 assert!(result.is_err());
17355
17356 let result = router.execute("EMBED");
17358 assert!(result.is_err());
17359
17360 let result = router.execute("EMBED bad [not,a,vector]");
17362 assert!(result.is_err());
17363 }
17364
17365 #[test]
17366 fn constraint_operations() {
17367 let router = QueryRouter::new();
17368
17369 let result = router.execute("CONSTRAINT ADD person name UNIQUE");
17371 let _ = result;
17372
17373 let result = router.execute("CONSTRAINT LIST");
17375 let _ = result;
17376
17377 let result = router.execute("CONSTRAINT REMOVE person name");
17379 let _ = result;
17380 }
17381
17382 #[test]
17383 fn checkpoint_operations() {
17384 let router = QueryRouter::new();
17385
17386 let result = router.execute("CHECKPOINT CREATE test_checkpoint");
17388 let _ = result;
17389
17390 let result = router.execute("CHECKPOINT LIST");
17392 let _ = result;
17393 }
17394
17395 #[test]
17396 fn rollback_operations() {
17397 let router = QueryRouter::new();
17398
17399 let result = router.execute("ROLLBACK");
17401 let _ = result;
17402 }
17403
17404 #[test]
17405 fn order_by_combinations() {
17406 let router = QueryRouter::new();
17407 router
17408 .execute("CREATE TABLE ordered (id int, name string, score int)")
17409 .unwrap();
17410 router
17411 .execute("INSERT INTO ordered (id, name, score) VALUES (1, 'C', 30)")
17412 .unwrap();
17413 router
17414 .execute("INSERT INTO ordered (id, name, score) VALUES (2, 'A', 10)")
17415 .unwrap();
17416 router
17417 .execute("INSERT INTO ordered (id, name, score) VALUES (3, 'B', 20)")
17418 .unwrap();
17419
17420 let result = router
17422 .execute_parsed("SELECT * FROM ordered ORDER BY name")
17423 .unwrap();
17424 assert!(matches!(result, QueryResult::Rows(_)));
17425
17426 let result = router
17428 .execute_parsed("SELECT * FROM ordered ORDER BY score DESC")
17429 .unwrap();
17430 assert!(matches!(result, QueryResult::Rows(_)));
17431
17432 let result = router
17434 .execute_parsed("SELECT * FROM ordered ORDER BY id LIMIT 2")
17435 .unwrap();
17436 if let QueryResult::Rows(rows) = result {
17437 assert_eq!(rows.len(), 2);
17438 }
17439 }
17440
17441 #[test]
17442 fn distinct_query() {
17443 let router = QueryRouter::new();
17444 router.execute("CREATE TABLE dups (cat string)").unwrap();
17445 router
17446 .execute("INSERT INTO dups (cat) VALUES ('A')")
17447 .unwrap();
17448 router
17449 .execute("INSERT INTO dups (cat) VALUES ('A')")
17450 .unwrap();
17451 router
17452 .execute("INSERT INTO dups (cat) VALUES ('B')")
17453 .unwrap();
17454
17455 let result = router.execute("SELECT DISTINCT cat FROM dups").unwrap();
17456 assert!(matches!(result, QueryResult::Rows(_)));
17457 }
17458
17459 #[test]
17460 fn vector_collections() {
17461 let router = QueryRouter::new();
17462
17463 router.execute("VECTOR COLLECTION CREATE test_coll").ok();
17465
17466 router.execute("EMBED coll_vec1 [1.0, 0.0, 0.0]").ok();
17468 router
17469 .execute("VECTOR COLLECTION ADD test_coll coll_vec1")
17470 .ok();
17471
17472 let _ = router.execute("SIMILAR [1.0, 0.0, 0.0] IN test_coll TOP 5");
17474 }
17475
17476 #[test]
17477 fn metadata_operations() {
17478 let router = QueryRouter::new();
17479
17480 router.execute("EMBED meta_vec [1.0, 0.0]").unwrap();
17482 router
17483 .execute("VECTOR META SET meta_vec category='test'")
17484 .ok();
17485
17486 let _ = router.execute("VECTOR META GET meta_vec");
17488 }
17489
17490 #[test]
17491 fn transaction_commands() {
17492 let router = QueryRouter::new();
17493
17494 let _ = router.execute("BEGIN");
17496
17497 let _ = router.execute("COMMIT");
17499
17500 let _ = router.execute("BEGIN");
17502 let _ = router.execute("ROLLBACK TRANSACTION");
17503 }
17504
17505 #[test]
17506 fn explain_query() {
17507 let router = QueryRouter::new();
17508 router.execute("CREATE TABLE explained (id int)").unwrap();
17509
17510 let result = router.execute("EXPLAIN SELECT explained");
17511 let _ = result;
17512 }
17513
17514 #[test]
17515 fn with_shared_store_and_engines() {
17516 let store = TensorStore::new();
17517 let router = QueryRouter::with_shared_store(store);
17518
17519 router.execute("CREATE TABLE shared (id int)").unwrap();
17521 router
17522 .execute("INSERT INTO shared (id) VALUES (1)")
17523 .unwrap();
17524
17525 let result = router.execute("SELECT * FROM shared").unwrap();
17526 assert!(matches!(result, QueryResult::Rows(_)));
17527 }
17528
17529 #[test]
17530 fn query_router_accessors() {
17531 let router = QueryRouter::new();
17532
17533 let _relational = router.relational();
17535 let _graph = router.graph();
17536 let _vector = router.vector();
17537
17538 assert!(router.cache().is_none());
17540 assert!(router.vault().is_none());
17541 }
17542
17543 #[test]
17544 fn init_cache_and_vault() {
17545 let mut router = QueryRouter::new();
17546
17547 router.init_cache();
17549 assert!(router.cache().is_some());
17550
17551 router.init_vault(b"test_password_key").unwrap();
17553 assert!(router.vault().is_some());
17554 }
17555
17556 #[test]
17557 fn execute_parsed_direct() {
17558 let router = QueryRouter::new();
17559
17560 router
17562 .execute_parsed("CREATE TABLE parsed (id INT)")
17563 .unwrap();
17564 router
17565 .execute_parsed("INSERT INTO parsed (id) VALUES (1)")
17566 .unwrap();
17567
17568 let result = router.execute_parsed("SELECT * FROM parsed").unwrap();
17569 assert!(matches!(result, QueryResult::Rows(_)));
17570 }
17571
17572 #[test]
17573 fn runtime_accessor() {
17574 let runtime = QueryRouter::create_runtime();
17576 assert!(runtime.is_ok());
17577 }
17578
17579 #[test]
17582 fn test_chain_rollback_via_parsed() {
17583 let mut router = QueryRouter::new();
17584 router.init_chain("test_node").unwrap();
17585 router.set_identity("user:test");
17586
17587 let result = router.execute_parsed("ROLLBACK CHAIN TO 0");
17588 assert!(result.is_ok());
17589 }
17590
17591 #[test]
17592 fn test_chain_similar_via_parsed() {
17593 let mut router = QueryRouter::new();
17594 router.init_chain("test_node").unwrap();
17595 router.set_identity("user:test");
17596
17597 let result = router.execute_parsed("CHAIN SIMILAR [1.0, 2.0, 3.0] LIMIT 10");
17598 assert!(result.is_ok());
17599 }
17600
17601 #[test]
17602 fn test_chain_commit_via_parsed() {
17603 let mut router = QueryRouter::new();
17604 router.init_chain("test_node").unwrap();
17605 router.set_identity("user:test");
17606
17607 router.execute_parsed("BEGIN CHAIN TRANSACTION").unwrap();
17608 let result = router.execute_parsed("COMMIT CHAIN");
17609 assert!(result.is_ok());
17610 }
17611
17612 #[test]
17613 fn test_cluster_disconnect_no_cluster() {
17614 let router = QueryRouter::new();
17615 let result = router.execute_parsed("CLUSTER DISCONNECT");
17616 assert!(result.is_err());
17617 }
17618
17619 #[test]
17620 fn test_cluster_connect_error() {
17621 let router = QueryRouter::new();
17622 let result = router.execute_parsed("CLUSTER CONNECT 'localhost:7000'");
17623 assert!(result.is_err());
17624 }
17625
17626 #[test]
17627 fn test_start_blob_after_init() {
17628 let mut router = QueryRouter::new();
17629 router.init_blob().unwrap();
17630 let result = router.start_blob();
17631 assert!(result.is_ok());
17632 }
17633
17634 #[test]
17635 fn test_entity_get_missing() {
17636 let router = QueryRouter::new();
17637 let result = router.execute_parsed("ENTITY GET 'nonexistent_key'");
17638 let _ = result;
17640 }
17641
17642 #[test]
17643 fn test_describe_missing_table() {
17644 let router = QueryRouter::new();
17645 let result = router.execute("DESCRIBE missing_table");
17646 assert!(result.is_err());
17647 }
17648
17649 #[test]
17650 fn test_select_from_missing_table() {
17651 let router = QueryRouter::new();
17652 let result = router.execute("SELECT * FROM missing_table");
17653 assert!(result.is_err());
17654 }
17655
17656 #[test]
17657 fn test_insert_into_missing_table() {
17658 let router = QueryRouter::new();
17659 let result = router.execute("INSERT INTO missing_table (id) VALUES (1)");
17660 assert!(result.is_err());
17661 }
17662
17663 #[test]
17664 fn test_update_missing_table() {
17665 let router = QueryRouter::new();
17666 let result = router.execute("UPDATE missing_table SET val = 1");
17667 assert!(result.is_err());
17668 }
17669
17670 #[test]
17671 fn test_delete_from_missing_table() {
17672 let router = QueryRouter::new();
17673 let result = router.execute("DELETE FROM missing_table");
17674 assert!(result.is_err());
17675 }
17676
17677 #[test]
17678 fn test_drop_missing_table() {
17679 let router = QueryRouter::new();
17680 let result = router.execute("DROP TABLE missing_table");
17681 assert!(result.is_err());
17682 }
17683
17684 #[test]
17685 fn test_blob_get_missing() {
17686 let mut router = QueryRouter::new();
17687 router.init_blob().unwrap();
17688 router.set_identity("user:test");
17689
17690 let result = router.execute_parsed("BLOB GET 'missing_hash'");
17691 assert!(result.is_err());
17692 }
17693
17694 #[test]
17695 fn test_cache_get_missing() {
17696 let mut router = QueryRouter::new();
17697 router.init_cache();
17698 router.set_identity("user:test");
17699
17700 let result = router.execute_parsed("CACHE GET 'missing_key'");
17701 let _ = result;
17703 }
17704
17705 #[test]
17706 fn test_empty_command_result() {
17707 let router = QueryRouter::new();
17708 let result = router.execute("");
17709 let _ = result;
17711 }
17712
17713 #[test]
17714 fn test_whitespace_command_result() {
17715 let router = QueryRouter::new();
17716 let result = router.execute(" ");
17717 let _ = result;
17719 }
17720
17721 #[test]
17722 fn test_comment_command_result() {
17723 let router = QueryRouter::new();
17724 let result = router.execute("-- this is a comment");
17725 let _ = result;
17727 }
17728
17729 #[test]
17730 fn test_entity_update() {
17731 let router = QueryRouter::new();
17732
17733 let create_result = router.execute_parsed("ENTITY CREATE 'user:1' { name: 'Alice' }");
17735 assert!(create_result.is_ok());
17736
17737 let update_result =
17739 router.execute_parsed("ENTITY UPDATE 'user:1' { name: 'Alicia', age: '30' }");
17740 assert!(update_result.is_ok());
17741
17742 if let Ok(QueryResult::Value(msg)) = update_result {
17743 assert!(msg.contains("updated"));
17744 }
17745 }
17746
17747 #[test]
17748 fn test_entity_update_with_embedding() {
17749 let router = QueryRouter::new();
17750
17751 router
17753 .execute_parsed("ENTITY CREATE 'doc:1' { title: 'Test' } EMBEDDING [0.1, 0.2]")
17754 .unwrap();
17755
17756 let result = router
17758 .execute_parsed("ENTITY UPDATE 'doc:1' { title: 'Updated' } EMBEDDING [0.3, 0.4]");
17759 assert!(result.is_ok());
17760 }
17761
17762 #[test]
17763 fn test_entity_update_nonexistent() {
17764 let router = QueryRouter::new();
17765
17766 let result = router.execute_parsed("ENTITY UPDATE 'nonexistent' { name: 'Test' }");
17768 assert!(result.is_err());
17769 }
17770
17771 #[test]
17772 fn test_entity_delete() {
17773 let router = QueryRouter::new();
17774
17775 router
17777 .execute_parsed("ENTITY CREATE 'user:2' { name: 'Bob' }")
17778 .unwrap();
17779
17780 let delete_result = router.execute_parsed("ENTITY DELETE 'user:2'");
17782 assert!(delete_result.is_ok());
17783
17784 if let Ok(QueryResult::Value(msg)) = delete_result {
17785 assert!(msg.contains("deleted"));
17786 }
17787 }
17788
17789 #[test]
17790 fn test_entity_delete_nonexistent() {
17791 let router = QueryRouter::new();
17792
17793 let result = router.execute_parsed("ENTITY DELETE 'nonexistent'");
17795 assert!(result.is_err());
17796 }
17797
17798 #[test]
17799 fn test_entity_crud_flow() {
17800 let router = QueryRouter::new();
17801
17802 let create = router.execute_parsed("ENTITY CREATE 'item:1' { status: 'new' }");
17804 assert!(create.is_ok());
17805
17806 let get = router.execute_parsed("ENTITY GET 'item:1'");
17808 assert!(get.is_ok());
17809
17810 let update = router.execute_parsed("ENTITY UPDATE 'item:1' { status: 'active' }");
17812 assert!(update.is_ok());
17813
17814 let delete = router.execute_parsed("ENTITY DELETE 'item:1'");
17816 assert!(delete.is_ok());
17817
17818 let get_after = router.execute_parsed("ENTITY GET 'item:1'");
17820 assert!(get_after.is_err());
17821 }
17822
17823 #[test]
17824 fn test_find_rows_from_table() {
17825 let router = QueryRouter::new();
17826
17827 router
17829 .execute("CREATE TABLE products (name string, price int)")
17830 .unwrap();
17831 router
17832 .execute("INSERT INTO products (name, price) VALUES ('Widget', 100)")
17833 .unwrap();
17834 router
17835 .execute("INSERT INTO products (name, price) VALUES ('Gadget', 200)")
17836 .unwrap();
17837
17838 let result = router.execute_parsed("FIND ROWS FROM products");
17840 assert!(result.is_ok());
17841
17842 if let Ok(QueryResult::Unified(unified)) = result {
17843 assert_eq!(unified.items.len(), 2);
17844 }
17845 }
17846
17847 #[test]
17848 fn test_find_rows_with_where() {
17849 let router = QueryRouter::new();
17850
17851 router
17852 .execute("CREATE TABLE items (id int, active bool)")
17853 .unwrap();
17854 router
17855 .execute("INSERT INTO items (id, active) VALUES (1, true)")
17856 .unwrap();
17857 router
17858 .execute("INSERT INTO items (id, active) VALUES (2, false)")
17859 .unwrap();
17860 router
17861 .execute("INSERT INTO items (id, active) VALUES (3, true)")
17862 .unwrap();
17863
17864 let result = router.execute_parsed("FIND ROWS FROM items WHERE active = TRUE");
17865 assert!(result.is_ok());
17866
17867 if let Ok(QueryResult::Unified(unified)) = result {
17868 assert_eq!(unified.items.len(), 2);
17869 }
17870 }
17871
17872 #[test]
17873 fn test_find_rows_with_limit() {
17874 let router = QueryRouter::new();
17875
17876 router.execute("CREATE TABLE numbers (val int)").unwrap();
17877 for i in 1..=10 {
17878 router
17879 .execute(&format!("INSERT INTO numbers (val) VALUES ({i})"))
17880 .unwrap();
17881 }
17882
17883 let result = router.execute_parsed("FIND ROWS FROM numbers LIMIT 3");
17884 assert!(result.is_ok());
17885
17886 if let Ok(QueryResult::Unified(unified)) = result {
17887 assert_eq!(unified.items.len(), 3);
17888 }
17889 }
17890
17891 #[test]
17892 fn test_find_rows_missing_table() {
17893 let router = QueryRouter::new();
17894
17895 let result = router.execute_parsed("FIND ROWS FROM nonexistent");
17896 assert!(result.is_err());
17897 }
17898
17899 #[test]
17902 fn test_find_node_similar_to_basic() {
17903 let router = QueryRouter::new();
17904
17905 router
17907 .execute_parsed(
17908 "ENTITY CREATE 'user:alice' {name: 'Alice', role: 'engineer'} EMBEDDING [1.0, 0.0, 0.0]",
17909 )
17910 .unwrap();
17911 router
17912 .execute_parsed(
17913 "ENTITY CREATE 'user:bob' {name: 'Bob', role: 'engineer'} EMBEDDING [0.9, 0.1, 0.0]",
17914 )
17915 .unwrap();
17916
17917 let result = router
17919 .execute_parsed("FIND NODE SIMILAR TO 'user:alice'")
17920 .unwrap();
17921
17922 match result {
17923 QueryResult::Unified(unified) => {
17924 assert!(!unified.items.is_empty());
17925 },
17926 other => panic!("Expected Unified, got {other:?}"),
17927 }
17928 }
17929
17930 #[test]
17931 fn test_find_node_connected_to_basic() {
17932 let router = QueryRouter::new();
17933
17934 router
17936 .execute_parsed("ENTITY CREATE 'user:alice' {name: 'Alice'}")
17937 .unwrap();
17938 router
17939 .execute_parsed("ENTITY CREATE 'user:bob' {name: 'Bob'}")
17940 .unwrap();
17941 router
17942 .execute_parsed("ENTITY CREATE 'user:carol' {name: 'Carol'}")
17943 .unwrap();
17944
17945 router
17947 .execute_parsed("ENTITY CONNECT 'user:alice' -> 'user:bob' : reports_to")
17948 .unwrap();
17949
17950 let result = router
17952 .execute_parsed("FIND NODE CONNECTED TO 'user:alice'")
17953 .unwrap();
17954
17955 match result {
17956 QueryResult::Unified(unified) => {
17957 assert_eq!(unified.items.len(), 1);
17958 let item = &unified.items[0];
17959 assert!(item
17960 .data
17961 .get("entity_key")
17962 .is_some_and(|ek| ek == "user:bob"));
17963 },
17964 other => panic!("Expected Unified, got {other:?}"),
17965 }
17966 }
17967
17968 #[test]
17969 fn test_find_node_hero_query() {
17970 let router = QueryRouter::new();
17971
17972 router
17974 .execute_parsed(
17975 "ENTITY CREATE 'user:alice' {name: 'Alice', role: 'engineer'} EMBEDDING [1.0, 0.0, 0.0]",
17976 )
17977 .unwrap();
17978 router
17979 .execute_parsed(
17980 "ENTITY CREATE 'user:bob' {name: 'Bob', role: 'engineer'} EMBEDDING [0.9, 0.1, 0.0]",
17981 )
17982 .unwrap();
17983 router
17984 .execute_parsed(
17985 "ENTITY CREATE 'user:carol' {name: 'Carol', role: 'manager'} EMBEDDING [0.0, 1.0, 0.0]",
17986 )
17987 .unwrap();
17988 router
17989 .execute_parsed("ENTITY CREATE 'user:hub' {name: 'Hub', role: 'director'}")
17990 .unwrap();
17991
17992 router
17994 .execute_parsed("ENTITY CONNECT 'user:hub' -> 'user:alice' : manages")
17995 .unwrap();
17996 router
17997 .execute_parsed("ENTITY CONNECT 'user:hub' -> 'user:bob' : manages")
17998 .unwrap();
17999 router
18000 .execute_parsed("ENTITY CONNECT 'user:hub' -> 'user:carol' : manages")
18001 .unwrap();
18002
18003 let result = router
18005 .execute_parsed(
18006 "FIND NODE WHERE role = 'engineer' SIMILAR TO 'user:alice' CONNECTED TO 'user:hub'",
18007 )
18008 .unwrap();
18009
18010 match result {
18011 QueryResult::Unified(unified) => {
18012 assert_eq!(unified.items.len(), 2);
18014 assert!(unified.items[0].score.is_some());
18015 assert!(unified.items[1].score.is_some());
18016 assert!(unified.items[0].score.unwrap() >= unified.items[1].score.unwrap());
18017 },
18018 other => panic!("Expected Unified, got {other:?}"),
18019 }
18020 }
18021
18022 #[test]
18023 fn test_find_edge_similar_to_rejects() {
18024 let router = QueryRouter::new();
18025
18026 let result = router.execute_parsed("FIND EDGE follows SIMILAR TO 'user:alice'");
18027 assert!(result.is_err());
18028 let err_msg = format!("{}", result.unwrap_err());
18029 assert!(err_msg.contains("only supported with FIND NODE"));
18030 }
18031
18032 #[test]
18033 fn test_find_node_similar_connected_with_limit() {
18034 let router = QueryRouter::new();
18035
18036 for i in 0..5 {
18038 router
18039 .execute_parsed(&format!(
18040 "ENTITY CREATE 'user:{i}' {{name: 'User{i}'}} EMBEDDING [{}.0, 0.0, 0.0]",
18041 i + 1
18042 ))
18043 .unwrap();
18044 }
18045
18046 router
18048 .execute_parsed("ENTITY CREATE 'user:hub' {name: 'Hub'}")
18049 .unwrap();
18050 for i in 0..5 {
18051 router
18052 .execute_parsed(&format!(
18053 "ENTITY CONNECT 'user:hub' -> 'user:{i}' : manages"
18054 ))
18055 .unwrap();
18056 }
18057
18058 let result = router
18059 .execute_parsed("FIND NODE SIMILAR TO 'user:0' CONNECTED TO 'user:hub' LIMIT 2")
18060 .unwrap();
18061
18062 match result {
18063 QueryResult::Unified(unified) => {
18064 assert!(unified.items.len() <= 2);
18065 },
18066 other => panic!("Expected Unified, got {other:?}"),
18067 }
18068 }
18069
18070 #[test]
18073 fn test_paginated_query_first_page() {
18074 let router = QueryRouter::new();
18075
18076 router
18078 .execute("CREATE TABLE paged_users (name string, age int)")
18079 .unwrap();
18080 for i in 1..=50 {
18081 router
18082 .execute(&format!(
18083 "INSERT INTO paged_users (name, age) VALUES ('user{i}', {i})"
18084 ))
18085 .unwrap();
18086 }
18087
18088 let options = PaginationOptions::new()
18090 .with_page_size(10)
18091 .with_count_total(true);
18092 let result = router.execute_paginated("SELECT * FROM paged_users", options);
18093
18094 assert!(result.is_ok());
18095 let paged = result.unwrap();
18096
18097 assert_eq!(paged.page_size, 10);
18098 assert_eq!(paged.total_count, Some(50));
18099 assert!(paged.has_more);
18100 assert!(paged.next_cursor.is_some());
18101 assert!(paged.prev_cursor.is_none()); let rows = unwrap_qr_rows(paged.result);
18104 assert_eq!(rows.len(), 10);
18105 }
18106
18107 #[test]
18108 fn test_paginated_query_with_cursor() {
18109 let router = QueryRouter::new();
18110
18111 router
18113 .execute("CREATE TABLE cursor_test (val int)")
18114 .unwrap();
18115 for i in 1..=25 {
18116 router
18117 .execute(&format!("INSERT INTO cursor_test (val) VALUES ({i})"))
18118 .unwrap();
18119 }
18120
18121 let options = PaginationOptions::new()
18123 .with_page_size(10)
18124 .with_count_total(true);
18125 let page1 = router
18126 .execute_paginated("SELECT * FROM cursor_test", options)
18127 .unwrap();
18128
18129 assert!(page1.next_cursor.is_some());
18130 let cursor = page1.next_cursor.unwrap();
18131
18132 let options2 = PaginationOptions::new()
18134 .with_cursor(cursor)
18135 .with_page_size(10)
18136 .with_count_total(true);
18137 let page2 = router
18138 .execute_paginated("SELECT * FROM cursor_test", options2)
18139 .unwrap();
18140
18141 assert!(page2.has_more); assert!(page2.prev_cursor.is_some()); if let QueryResult::Rows(rows) = page2.result {
18145 assert_eq!(rows.len(), 10);
18146 }
18147 }
18148
18149 #[test]
18150 fn test_paginated_query_last_page() {
18151 let router = QueryRouter::new();
18152
18153 router.execute("CREATE TABLE last_page (val int)").unwrap();
18154 for i in 1..=15 {
18155 router
18156 .execute(&format!("INSERT INTO last_page (val) VALUES ({i})"))
18157 .unwrap();
18158 }
18159
18160 let options = PaginationOptions::new()
18162 .with_page_size(20)
18163 .with_count_total(true);
18164 let result = router
18165 .execute_paginated("SELECT * FROM last_page", options)
18166 .unwrap();
18167
18168 assert!(!result.has_more);
18169 assert!(result.next_cursor.is_none());
18170 assert_eq!(result.total_count, Some(15));
18171
18172 if let QueryResult::Rows(rows) = result.result {
18173 assert_eq!(rows.len(), 15);
18174 }
18175 }
18176
18177 #[test]
18178 fn test_paginated_query_nodes() {
18179 let router = QueryRouter::new();
18180
18181 for i in 1..=30 {
18183 router
18184 .execute(&format!("NODE CREATE TestNode {{ id: {i} }}"))
18185 .unwrap();
18186 }
18187
18188 let options = PaginationOptions::new()
18189 .with_page_size(10)
18190 .with_count_total(true);
18191 let result = router.execute_paginated("NODE LIST TestNode", options);
18192
18193 assert!(result.is_ok());
18194 let paged = result.unwrap();
18195
18196 assert!(paged.has_more);
18197 if let QueryResult::Nodes(nodes) = paged.result {
18198 assert_eq!(nodes.len(), 10);
18199 }
18200 }
18201
18202 #[test]
18203 fn test_paginated_query_invalid_cursor() {
18204 let router = QueryRouter::new();
18205
18206 router
18207 .execute("CREATE TABLE invalid_cursor (val int)")
18208 .unwrap();
18209
18210 let options = PaginationOptions::new().with_cursor("invalid-cursor-token".to_string());
18211 let result = router.execute_paginated("SELECT * FROM invalid_cursor", options);
18212
18213 assert!(result.is_err());
18214 }
18215
18216 #[test]
18217 fn test_paginated_query_cursor_mismatch() {
18218 let router = QueryRouter::new();
18219
18220 router.execute("CREATE TABLE mismatch1 (val int)").unwrap();
18221 router.execute("CREATE TABLE mismatch2 (val int)").unwrap();
18222 for i in 1..=10 {
18223 router
18224 .execute(&format!("INSERT INTO mismatch1 (val) VALUES ({i})"))
18225 .unwrap();
18226 router
18227 .execute(&format!("INSERT INTO mismatch2 (val) VALUES ({i})"))
18228 .unwrap();
18229 }
18230
18231 let options = PaginationOptions::new()
18233 .with_page_size(5)
18234 .with_count_total(true);
18235 let page1 = router
18236 .execute_paginated("SELECT * FROM mismatch1", options)
18237 .unwrap();
18238
18239 let cursor = page1.next_cursor.expect("Should have next cursor");
18241
18242 let options2 = PaginationOptions::new().with_cursor(cursor);
18244 let result = router.execute_paginated("SELECT * FROM mismatch2", options2);
18245 assert!(result.is_err());
18246 assert!(result
18247 .unwrap_err()
18248 .to_string()
18249 .contains("Cursor query does not match"));
18250 }
18251
18252 #[test]
18253 fn test_close_cursor() {
18254 let router = QueryRouter::new();
18255
18256 router.execute("CREATE TABLE close_test (val int)").unwrap();
18257 for i in 1..=20 {
18258 router
18259 .execute(&format!("INSERT INTO close_test (val) VALUES ({i})"))
18260 .unwrap();
18261 }
18262
18263 let options = PaginationOptions::new()
18265 .with_page_size(5)
18266 .with_count_total(true);
18267 let page1 = router
18268 .execute_paginated("SELECT * FROM close_test", options)
18269 .unwrap();
18270
18271 let cursor = page1.next_cursor.expect("Should have next cursor");
18273
18274 let closed = router.close_cursor(&cursor).unwrap();
18276 assert!(closed);
18277 }
18278
18279 #[test]
18280 fn test_paginated_non_paginatable_result() {
18281 let router = QueryRouter::new();
18282
18283 router.execute("CREATE TABLE non_page (val int)").unwrap();
18284
18285 let options = PaginationOptions::new().with_page_size(10);
18287 let result = router.execute_paginated("CREATE TABLE another_table (x int)", options);
18288
18289 assert!(result.is_err());
18290 }
18291
18292 #[test]
18293 fn test_pagination_options_builder() {
18294 let options = PaginationOptions::new()
18295 .with_page_size(50)
18296 .with_count_total(true)
18297 .with_cursor_ttl(std::time::Duration::from_secs(60));
18298
18299 assert_eq!(options.page_size, Some(50));
18300 assert!(options.count_total);
18301 assert_eq!(options.cursor_ttl, Some(std::time::Duration::from_secs(60)));
18302 }
18303
18304 #[test]
18305 fn test_paged_query_result_fields() {
18306 let router = QueryRouter::new();
18307
18308 router
18309 .execute("CREATE TABLE fields_test (val int)")
18310 .unwrap();
18311 for i in 1..=5 {
18312 router
18313 .execute(&format!("INSERT INTO fields_test (val) VALUES ({i})"))
18314 .unwrap();
18315 }
18316
18317 let options = PaginationOptions::new()
18318 .with_page_size(3)
18319 .with_count_total(true);
18320 let result = router
18321 .execute_paginated("SELECT * FROM fields_test", options)
18322 .unwrap();
18323
18324 assert_eq!(result.page_size, 3);
18325 assert_eq!(result.total_count, Some(5));
18326 assert!(result.has_more);
18327 assert!(result.next_cursor.is_some());
18328 assert!(result.prev_cursor.is_none());
18329 }
18330
18331 #[test]
18332 fn test_edge_list_parsed() {
18333 let router = QueryRouter::new();
18334
18335 for i in 1..=10 {
18337 router
18338 .execute(&format!("NODE CREATE Person {{ id: {i} }}"))
18339 .unwrap();
18340 }
18341
18342 for i in 1..=25 {
18344 let from = ((i - 1) % 10) + 1;
18345 let to = (i % 10) + 1;
18346 router
18347 .execute(&format!("EDGE CREATE {from} -> {to} : KNOWS"))
18348 .unwrap();
18349 }
18350
18351 let full_result = router.execute_parsed("EDGE LIST KNOWS").unwrap();
18353 let edges = unwrap_qr_edges(full_result);
18354 assert_eq!(edges.len(), 25);
18355 }
18356
18357 #[test]
18358 fn test_paginated_query_similar() {
18359 let router = QueryRouter::new();
18360
18361 for i in 1..=30 {
18363 let vals = (1..=4)
18364 .map(|j| format!("{}", (i * j) as f32 / 100.0))
18365 .collect::<Vec<_>>()
18366 .join(", ");
18367 router.execute(&format!("EMBED key{i} [{vals}]")).unwrap();
18368 }
18369
18370 let options = PaginationOptions::new()
18372 .with_page_size(5)
18373 .with_count_total(true);
18374 let result = router.execute_paginated("SIMILAR key1 TOP 20", options);
18375
18376 assert!(result.is_ok());
18377 let paged = result.unwrap();
18378
18379 let items = unwrap_qr_similar(paged.result);
18380 assert!(items.len() <= 5);
18381 }
18382
18383 #[test]
18384 fn test_paginated_query_unified() {
18385 let router = QueryRouter::new();
18386
18387 router
18389 .execute("CREATE TABLE unified_test (name string, score int)")
18390 .unwrap();
18391 for i in 1..=20 {
18392 router
18393 .execute(&format!(
18394 "INSERT INTO unified_test (name, score) VALUES ('item{i}', {i})"
18395 ))
18396 .unwrap();
18397 }
18398
18399 let options = PaginationOptions::new()
18401 .with_page_size(5)
18402 .with_count_total(true);
18403 let result = router.execute_paginated("FIND ROWS FROM unified_test", options);
18404
18405 assert!(result.is_ok());
18406 let paged = result.unwrap();
18407
18408 assert_eq!(paged.total_count, Some(20));
18409 let unified = unwrap_qr_unified(paged.result);
18410 assert_eq!(unified.items.len(), 5);
18411 }
18412
18413 #[test]
18414 fn test_close_cursor_not_found() {
18415 let router = QueryRouter::new();
18416
18417 router
18418 .execute("CREATE TABLE close_not_found (val int)")
18419 .unwrap();
18420 for i in 1..=20 {
18421 router
18422 .execute(&format!("INSERT INTO close_not_found (val) VALUES ({i})"))
18423 .unwrap();
18424 }
18425
18426 let options = PaginationOptions::new()
18428 .with_page_size(5)
18429 .with_count_total(true);
18430 let page1 = router
18431 .execute_paginated("SELECT * FROM close_not_found", options)
18432 .unwrap();
18433
18434 let cursor = page1.next_cursor.expect("Should have next cursor");
18436
18437 let closed1 = router.close_cursor(&cursor).unwrap();
18439 assert!(closed1);
18440
18441 let closed2 = router.close_cursor(&cursor).unwrap();
18443 assert!(!closed2);
18444 }
18445
18446 #[test]
18447 fn test_cursor_store_accessor() {
18448 let router = QueryRouter::new();
18449 let store = router.cursor_store();
18450
18451 assert!(store.len() <= 1);
18453 }
18454
18455 #[test]
18456 fn test_paginated_with_custom_ttl() {
18457 let router = QueryRouter::new();
18458
18459 router.execute("CREATE TABLE ttl_test (val int)").unwrap();
18460 for i in 1..=20 {
18461 router
18462 .execute(&format!("INSERT INTO ttl_test (val) VALUES ({i})"))
18463 .unwrap();
18464 }
18465
18466 let options = PaginationOptions::new()
18468 .with_page_size(5)
18469 .with_count_total(true)
18470 .with_cursor_ttl(std::time::Duration::from_secs(120));
18471 let result = router
18472 .execute_paginated("SELECT * FROM ttl_test", options)
18473 .unwrap();
18474
18475 assert!(result.has_more);
18476 assert!(result.next_cursor.is_some());
18477 }
18478
18479 #[test]
18480 fn test_paginated_without_count_total() {
18481 let router = QueryRouter::new();
18482
18483 router.execute("CREATE TABLE no_count (val int)").unwrap();
18484 for i in 1..=10 {
18485 router
18486 .execute(&format!("INSERT INTO no_count (val) VALUES ({i})"))
18487 .unwrap();
18488 }
18489
18490 let options = PaginationOptions::new().with_page_size(5);
18492 let result = router
18493 .execute_paginated("SELECT * FROM no_count", options)
18494 .unwrap();
18495
18496 assert_eq!(result.total_count, None);
18498 assert!(!result.has_more);
18500 }
18501
18502 #[test]
18503 fn test_router_error_cursor_display() {
18504 let err = RouterError::CursorError("test cursor error".to_string());
18505 let display = format!("{}", err);
18506 assert!(display.contains("Cursor error"));
18507 assert!(display.contains("test cursor error"));
18508 }
18509
18510 #[test]
18511 fn test_paged_query_result_debug() {
18512 let paged = PagedQueryResult {
18513 result: QueryResult::Empty,
18514 next_cursor: Some("next".to_string()),
18515 prev_cursor: Some("prev".to_string()),
18516 total_count: Some(100),
18517 has_more: true,
18518 page_size: 10,
18519 };
18520
18521 let debug = format!("{:?}", paged);
18522 assert!(debug.contains("PagedQueryResult"));
18523 assert!(debug.contains("next"));
18524 assert!(debug.contains("prev"));
18525 }
18526
18527 #[test]
18528 fn test_pagination_options_default() {
18529 let options = PaginationOptions::default();
18530 assert!(options.cursor.is_none());
18531 assert!(options.page_size.is_none());
18532 assert!(!options.count_total);
18533 assert!(options.cursor_ttl.is_none());
18534 }
18535
18536 #[test]
18537 fn test_paginated_max_ttl_capped() {
18538 let router = QueryRouter::new();
18539
18540 router.execute("CREATE TABLE max_ttl (val int)").unwrap();
18541 for i in 1..=20 {
18542 router
18543 .execute(&format!("INSERT INTO max_ttl (val) VALUES ({i})"))
18544 .unwrap();
18545 }
18546
18547 let options = PaginationOptions::new()
18549 .with_page_size(5)
18550 .with_count_total(true)
18551 .with_cursor_ttl(std::time::Duration::from_secs(7200)); let result = router
18553 .execute_paginated("SELECT * FROM max_ttl", options)
18554 .unwrap();
18555
18556 assert!(result.next_cursor.is_some());
18557 }
18558
18559 #[test]
18560 fn test_paginated_third_page_with_prev() {
18561 let router = QueryRouter::new();
18562
18563 router
18564 .execute("CREATE TABLE three_pages (val int)")
18565 .unwrap();
18566 for i in 1..=30 {
18567 router
18568 .execute(&format!("INSERT INTO three_pages (val) VALUES ({i})"))
18569 .unwrap();
18570 }
18571
18572 let opts1 = PaginationOptions::new()
18574 .with_page_size(10)
18575 .with_count_total(true);
18576 let page1 = router
18577 .execute_paginated("SELECT * FROM three_pages", opts1)
18578 .unwrap();
18579 assert!(page1.prev_cursor.is_none()); let cursor1 = page1.next_cursor.unwrap();
18583 let opts2 = PaginationOptions::new()
18584 .with_cursor(cursor1)
18585 .with_page_size(10)
18586 .with_count_total(true);
18587 let page2 = router
18588 .execute_paginated("SELECT * FROM three_pages", opts2)
18589 .unwrap();
18590 assert!(page2.prev_cursor.is_some()); let cursor2 = page2.next_cursor.unwrap();
18594 let opts3 = PaginationOptions::new()
18595 .with_cursor(cursor2)
18596 .with_page_size(10)
18597 .with_count_total(true);
18598 let page3 = router
18599 .execute_paginated("SELECT * FROM three_pages", opts3)
18600 .unwrap();
18601 assert!(page3.prev_cursor.is_some()); assert!(!page3.has_more); }
18604
18605 #[test]
18606 fn test_cursor_error_from_conversion() {
18607 let cursor_err = CursorError::InvalidToken("bad token".to_string());
18608 let router_err: RouterError = cursor_err.into();
18609 assert!(matches!(router_err, RouterError::CursorError(_)));
18610 }
18611
18612 #[test]
18615 fn test_cluster_status_no_cluster() {
18616 let mut router = QueryRouter::new();
18617 router.set_identity("user:test");
18618 let stmt = parser::parse("CLUSTER STATUS").unwrap();
18619 let result = router.execute_statement(&stmt).unwrap();
18620 let msg = unwrap_qr_value(result);
18621 assert!(msg.contains("single-node"));
18622 }
18623
18624 #[test]
18625 fn test_cluster_nodes_no_cluster() {
18626 let mut router = QueryRouter::new();
18627 router.set_identity("user:test");
18628 let stmt = parser::parse("CLUSTER NODES").unwrap();
18629 let result = router.execute_statement(&stmt).unwrap();
18630 let msg = unwrap_qr_value(result);
18631 assert!(msg.contains("single-node"));
18632 }
18633
18634 #[test]
18635 fn test_cluster_leader_no_cluster() {
18636 let mut router = QueryRouter::new();
18637 router.set_identity("user:test");
18638 let stmt = parser::parse("CLUSTER LEADER").unwrap();
18639 let result = router.execute_statement(&stmt).unwrap();
18640 let msg = unwrap_qr_value(result);
18641 assert!(msg.contains("single-node"));
18642 }
18643
18644 #[test]
18645 fn test_cluster_connect_error_message() {
18646 let mut router = QueryRouter::new();
18647 router.set_identity("user:test");
18648 let stmt = parser::parse("CLUSTER CONNECT '127.0.0.1:9300'").unwrap();
18649 let result = router.execute_statement(&stmt);
18650 assert!(result.is_err());
18651 if let Err(RouterError::InvalidArgument(msg)) = result {
18652 assert!(msg.contains("shell"));
18653 }
18654 }
18655
18656 #[test]
18657 fn test_cluster_disconnect_with_no_cluster() {
18658 let mut router = QueryRouter::new();
18659 router.set_identity("user:test");
18660 let stmt = parser::parse("CLUSTER DISCONNECT").unwrap();
18661 let result = router.execute_statement(&stmt);
18662 assert!(result.is_err());
18663 if let Err(RouterError::InvalidArgument(msg)) = result {
18664 assert!(msg.contains("Not connected"));
18665 }
18666 }
18667
18668 #[test]
18671 fn test_chain_analyze_transitions() {
18672 let mut router = QueryRouter::new();
18673 router.init_chain("test_node").unwrap();
18674 router.set_identity("user:test");
18675
18676 let stmt = parser::parse("ANALYZE CODEBOOK TRANSITIONS").unwrap();
18677 let result = router.execute_statement(&stmt).unwrap();
18678
18679 if let QueryResult::Chain(ChainResult::TransitionAnalysis(analysis)) = result {
18680 assert_eq!(analysis.total_transitions, 0);
18681 assert_eq!(analysis.valid_transitions, 0);
18682 } else {
18683 panic!("expected TransitionAnalysis result");
18684 }
18685 }
18686
18687 #[test]
18688 fn test_chain_show_codebook_global_via_exec() {
18689 let mut router = QueryRouter::new();
18690 router.init_chain("test_node").unwrap();
18691 router.set_identity("user:test");
18692
18693 let stmt = parser::parse("SHOW CODEBOOK GLOBAL").unwrap();
18694 let result = router.execute_statement(&stmt).unwrap();
18695
18696 if let QueryResult::Chain(ChainResult::Codebook(info)) = result {
18697 assert_eq!(info.scope, "global");
18698 assert!(info.domain.is_none());
18699 } else {
18700 panic!("expected Codebook result");
18701 }
18702 }
18703
18704 #[test]
18705 fn test_chain_show_codebook_local_via_exec() {
18706 let mut router = QueryRouter::new();
18707 router.init_chain("test_node").unwrap();
18708 router.set_identity("user:test");
18709
18710 let stmt = parser::parse("SHOW CODEBOOK LOCAL 'my_domain'").unwrap();
18711 let result = router.execute_statement(&stmt).unwrap();
18712
18713 if let QueryResult::Chain(ChainResult::Codebook(info)) = result {
18714 assert_eq!(info.scope, "local");
18715 assert_eq!(info.domain.as_deref(), Some("my_domain"));
18716 } else {
18717 panic!("expected Codebook result");
18718 }
18719 }
18720
18721 #[test]
18722 fn test_chain_similar_empty_result() {
18723 let mut router = QueryRouter::new();
18724 router.init_chain("test_node").unwrap();
18725 router.set_identity("user:test");
18726
18727 let stmt = parser::parse("CHAIN SIMILAR [1.0, 2.0] LIMIT 5").unwrap();
18728 let result = router.execute_statement(&stmt).unwrap();
18729
18730 if let QueryResult::Chain(ChainResult::Similar(items)) = result {
18731 assert!(items.is_empty());
18732 } else {
18733 panic!("expected Similar result");
18734 }
18735 }
18736
18737 #[test]
18738 fn test_chain_commit_via_exec() {
18739 let mut router = QueryRouter::new();
18740 router.init_chain("test_node").unwrap();
18741 router.set_identity("user:test");
18742
18743 let stmt = parser::parse("COMMIT CHAIN").unwrap();
18744 let result = router.execute_statement(&stmt).unwrap();
18745
18746 if let QueryResult::Chain(ChainResult::Committed { height, .. }) = result {
18747 assert_eq!(height, 0);
18748 } else {
18749 panic!("expected Committed result");
18750 }
18751 }
18752
18753 #[test]
18754 fn test_chain_rollback_via_exec() {
18755 let mut router = QueryRouter::new();
18756 router.init_chain("test_node").unwrap();
18757 router.set_identity("user:test");
18758
18759 let stmt = parser::parse("ROLLBACK CHAIN TO 5").unwrap();
18760 let result = router.execute_statement(&stmt).unwrap();
18761
18762 if let QueryResult::Chain(ChainResult::RolledBack { to_height }) = result {
18763 assert_eq!(to_height, 5);
18764 } else {
18765 panic!("expected RolledBack result");
18766 }
18767 }
18768
18769 #[test]
18772 fn test_has_checkpoint_false() {
18773 let router = QueryRouter::new();
18774 assert!(!router.has_checkpoint());
18775 }
18776
18777 #[test]
18778 fn test_has_hnsw_index_false() {
18779 let router = QueryRouter::new();
18780 assert!(!router.has_hnsw_index());
18781 }
18782
18783 #[test]
18784 fn test_hnsw_generation_starts_fresh() {
18785 let router = QueryRouter::new();
18786 assert!(router.hnsw_is_fresh());
18787 }
18788
18789 #[test]
18790 fn test_hnsw_generation_stale_after_embed_store() {
18791 let mut router = QueryRouter::new();
18792 router
18794 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18795 .unwrap();
18796 router.build_vector_index().unwrap();
18798 assert!(router.hnsw_is_fresh());
18799 assert!(router.has_hnsw_index());
18800
18801 router
18803 .execute_parsed("EMBED STORE 'v2' [4.0, 5.0, 6.0]")
18804 .unwrap();
18805 assert!(!router.hnsw_is_fresh());
18806 }
18807
18808 #[test]
18809 fn test_hnsw_generation_fresh_after_rebuild() {
18810 let mut router = QueryRouter::new();
18811 router
18812 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18813 .unwrap();
18814 router.build_vector_index().unwrap();
18815
18816 router
18818 .execute_parsed("EMBED STORE 'v2' [4.0, 5.0, 6.0]")
18819 .unwrap();
18820 assert!(!router.hnsw_is_fresh());
18821
18822 router.build_vector_index().unwrap();
18824 assert!(router.hnsw_is_fresh());
18825 }
18826
18827 #[test]
18828 fn test_hnsw_generation_not_bumped_for_named_collection() {
18829 let mut router = QueryRouter::new();
18830 router
18831 .vector
18832 .create_collection(
18833 "test_coll",
18834 vector_engine::VectorCollectionConfig::default().with_dimension(3),
18835 )
18836 .unwrap();
18837
18838 router
18839 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18840 .unwrap();
18841 router.build_vector_index().unwrap();
18842 assert!(router.hnsw_is_fresh());
18843
18844 router
18846 .execute_parsed("EMBED STORE 'v2' [4.0, 5.0, 6.0] INTO test_coll")
18847 .unwrap();
18848 assert!(router.hnsw_is_fresh());
18849 }
18850
18851 #[test]
18852 fn test_hnsw_stale_after_embed_delete() {
18853 let mut router = QueryRouter::new();
18854 router
18855 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18856 .unwrap();
18857 router.build_vector_index().unwrap();
18858 assert!(router.hnsw_is_fresh());
18859
18860 router.execute_parsed("EMBED DELETE 'v1'").unwrap();
18861 assert!(!router.hnsw_is_fresh());
18862 }
18863
18864 #[test]
18865 fn test_hnsw_stale_after_entity_create_with_embedding() {
18866 let mut router = QueryRouter::new();
18867 router
18868 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18869 .unwrap();
18870 router.build_vector_index().unwrap();
18871 assert!(router.hnsw_is_fresh());
18872
18873 router
18874 .execute_parsed("ENTITY CREATE 'e1' { name: 'test' } EMBEDDING [1.0, 2.0, 3.0]")
18875 .unwrap();
18876 assert!(!router.hnsw_is_fresh());
18877 }
18878
18879 #[test]
18880 fn test_entity_get_via_unified() {
18881 let router = QueryRouter::new();
18882 router
18884 .execute_parsed("ENTITY CREATE 'e1' { name: 'alice' } EMBEDDING [1.0, 2.0, 3.0]")
18885 .unwrap();
18886 let result = router.execute_parsed("ENTITY GET 'e1'").unwrap();
18888 match result {
18889 QueryResult::Unified(u) => {
18890 assert_eq!(u.items.len(), 1);
18891 assert_eq!(u.items[0].id, "e1");
18892 },
18893 other => panic!("Expected Unified result, got {other:?}"),
18894 }
18895 }
18896
18897 #[test]
18898 fn test_entity_get_not_found() {
18899 let router = QueryRouter::new();
18900 let result = router.execute_parsed("ENTITY GET 'nonexistent'");
18901 assert!(result.is_err());
18902 }
18903
18904 #[test]
18905 fn test_hnsw_stale_after_entity_batch_with_embeddings() {
18906 let mut router = QueryRouter::new();
18907 router
18908 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18909 .unwrap();
18910 router.build_vector_index().unwrap();
18911 assert!(router.hnsw_is_fresh());
18912
18913 router
18915 .execute_parsed(
18916 "ENTITY BATCH CREATE [\
18917 {key: 'b1', name: 'one', embedding: [1.0, 2.0, 3.0]}, \
18918 {key: 'b2', name: 'two', embedding: [4.0, 5.0, 6.0]}]",
18919 )
18920 .unwrap();
18921 assert!(!router.hnsw_is_fresh());
18922 }
18923
18924 #[test]
18925 fn test_entity_update_with_embedding_bumps_generation() {
18926 let mut router = QueryRouter::new();
18927 router
18928 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18929 .unwrap();
18930 router
18931 .execute_parsed("ENTITY CREATE 'e1' { name: 'alice' } EMBEDDING [1.0, 2.0, 3.0]")
18932 .unwrap();
18933 router.build_vector_index().unwrap();
18934 assert!(router.hnsw_is_fresh());
18935
18936 router
18938 .execute_parsed("ENTITY UPDATE 'e1' { name: 'bob' } EMBEDDING [4.0, 5.0, 6.0]")
18939 .unwrap();
18940 assert!(!router.hnsw_is_fresh());
18941 }
18942
18943 #[test]
18944 fn test_entity_delete_bumps_generation() {
18945 let mut router = QueryRouter::new();
18946 router
18947 .execute_parsed("EMBED STORE 'v1' [1.0, 2.0, 3.0]")
18948 .unwrap();
18949 router
18950 .execute_parsed("ENTITY CREATE 'e1' { name: 'alice' } EMBEDDING [1.0, 2.0, 3.0]")
18951 .unwrap();
18952 router.build_vector_index().unwrap();
18953 assert!(router.hnsw_is_fresh());
18954
18955 router.execute_parsed("ENTITY DELETE 'e1'").unwrap();
18957 assert!(!router.hnsw_is_fresh());
18958 }
18959
18960 #[test]
18961 fn test_entity_connect_via_parsed() {
18962 let router = QueryRouter::new();
18963 router
18964 .execute_parsed("ENTITY CREATE 'e1' { name: 'alice' }")
18965 .unwrap();
18966 router
18967 .execute_parsed("ENTITY CREATE 'e2' { name: 'bob' }")
18968 .unwrap();
18969 let result = router
18970 .execute_parsed("ENTITY CONNECT 'e1' -> 'e2' : knows")
18971 .unwrap();
18972 match result {
18973 QueryResult::Value(msg) => {
18974 assert!(msg.contains("Connected"), "Expected connect message: {msg}");
18975 },
18976 other => panic!("Expected Value result, got {other:?}"),
18977 }
18978 }
18979
18980 #[test]
18981 fn test_create_and_drop_index_via_parsed() {
18982 let router = QueryRouter::new();
18983 router
18984 .execute_parsed("CREATE TABLE idx_test (id INT, name TEXT)")
18985 .unwrap();
18986 router
18987 .execute_parsed("CREATE INDEX idx_name ON idx_test (name)")
18988 .unwrap();
18989 router
18991 .execute_parsed("DROP INDEX ON idx_test (name)")
18992 .unwrap();
18993 router
18995 .execute_parsed("DROP INDEX IF EXISTS ON idx_test (name)")
18996 .unwrap();
18997 }
18998
18999 #[test]
19000 fn test_drop_table_via_parsed() {
19001 let router = QueryRouter::new();
19002 router
19003 .execute_parsed("CREATE TABLE drop_test (id INT)")
19004 .unwrap();
19005 router.execute_parsed("DROP TABLE drop_test").unwrap();
19006 }
19007
19008 #[test]
19009 fn test_show_tables_and_describe_via_parsed() {
19010 let router = QueryRouter::new();
19011 router
19012 .execute_parsed("CREATE TABLE desc_test (id INT, name TEXT)")
19013 .unwrap();
19014 let result = router.execute_parsed("SHOW TABLES").unwrap();
19015 match &result {
19016 QueryResult::TableList(tables) => assert!(tables.contains(&"desc_test".to_string())),
19017 other => panic!("Expected TableList, got {other:?}"),
19018 }
19019 let result = router.execute_parsed("DESCRIBE TABLE desc_test").unwrap();
19020 assert!(!matches!(result, QueryResult::Empty));
19021 }
19022
19023 #[test]
19024 fn test_legacy_node_list_with_label_filter() {
19025 let store = tensor_store::TensorStore::new();
19026 let router = QueryRouter::with_shared_store(store);
19027 let _id1 = router.graph.create_node("person", HashMap::new()).unwrap();
19029 let id2 = router.graph.create_node("place", HashMap::new()).unwrap();
19030 let _id3 = router.graph.create_node("person", HashMap::new()).unwrap();
19031
19032 let result = router.execute("NODE LIST person").unwrap();
19034 match result {
19035 QueryResult::Nodes(nodes) => {
19036 assert_eq!(nodes.len(), 2);
19037 for n in &nodes {
19038 assert!(n.label.contains("person"));
19039 }
19040 },
19041 other => panic!("Expected Nodes, got {other:?}"),
19042 }
19043
19044 let result = router.execute("NODE LIST").unwrap();
19046 match result {
19047 QueryResult::Nodes(nodes) => assert!(nodes.len() >= 3),
19048 other => panic!("Expected Nodes, got {other:?}"),
19049 }
19050
19051 let result = router.execute("NODE LIST place").unwrap();
19053 match result {
19054 QueryResult::Nodes(nodes) => {
19055 assert_eq!(nodes.len(), 1);
19056 assert_eq!(nodes[0].id, id2);
19057 },
19058 other => panic!("Expected Nodes, got {other:?}"),
19059 }
19060 }
19061
19062 #[test]
19063 fn test_tls_cert_path_none() {
19064 let router = QueryRouter::new();
19065 assert!(router.tls_cert_path().is_none());
19066 }
19067
19068 #[test]
19069 fn test_chain_accessor_none() {
19070 let router = QueryRouter::new();
19071 assert!(router.chain().is_none());
19072 }
19073
19074 #[test]
19075 fn test_chain_accessor_some() {
19076 let mut router = QueryRouter::new();
19077 router.init_chain("test_node").unwrap();
19078 assert!(router.chain().is_some());
19079 }
19080
19081 #[test]
19082 fn test_ensure_chain_auto_init() {
19083 let mut router = QueryRouter::new();
19084 assert!(router.chain().is_none());
19085 let chain = router.ensure_chain();
19086 assert!(chain.is_ok());
19087 assert!(router.chain().is_some());
19088 }
19089
19090 #[test]
19091 fn test_set_confirmation_handler_no_checkpoint() {
19092 struct DummyHandler;
19093 impl ConfirmationHandler for DummyHandler {
19094 fn confirm(&self, _op: &DestructiveOp, _preview: &OperationPreview) -> bool {
19095 true
19096 }
19097 }
19098 let router = QueryRouter::new();
19099 let handler: Arc<dyn ConfirmationHandler> = Arc::new(DummyHandler);
19100 let result = router.set_confirmation_handler(handler);
19101 assert!(result.is_err());
19102 if let Err(RouterError::CheckpointError(msg)) = result {
19103 assert!(msg.contains("not initialized"));
19104 }
19105 }
19106
19107 #[test]
19110 fn test_paginated_query_edges() {
19111 let mut router = QueryRouter::new();
19112 router.set_identity("user:test");
19113
19114 let n1 = match router
19116 .execute("NODE CREATE person { name: 'Alice' }")
19117 .unwrap()
19118 {
19119 QueryResult::Ids(ids) => ids[0],
19120 other => panic!("expected Ids, got {other:?}"),
19121 };
19122 let n2 = match router
19123 .execute("NODE CREATE person { name: 'Bob' }")
19124 .unwrap()
19125 {
19126 QueryResult::Ids(ids) => ids[0],
19127 other => panic!("expected Ids, got {other:?}"),
19128 };
19129 let n3 = match router
19130 .execute("NODE CREATE person { name: 'Carol' }")
19131 .unwrap()
19132 {
19133 QueryResult::Ids(ids) => ids[0],
19134 other => panic!("expected Ids, got {other:?}"),
19135 };
19136
19137 router
19138 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows"))
19139 .unwrap();
19140 router
19141 .execute(&format!("EDGE CREATE {n2} -> {n3} : knows"))
19142 .unwrap();
19143 router
19144 .execute(&format!("EDGE CREATE {n1} -> {n3} : knows"))
19145 .unwrap();
19146
19147 let result = router.execute_parsed("EDGE LIST").unwrap();
19148 let edges = unwrap_qr_edges(result);
19149 assert_eq!(edges.len(), 3);
19150 }
19151
19152 #[test]
19155 fn test_chain_error_conversion() {
19156 let chain_err = tensor_chain::ChainError::ValidationFailed("bad block".to_string());
19157 let router_err: RouterError = chain_err.into();
19158 if let RouterError::ChainError(msg) = router_err {
19159 assert!(msg.contains("bad block"));
19160 } else {
19161 panic!("expected ChainError");
19162 }
19163 }
19164
19165 #[test]
19166 fn test_router_error_display_chain() {
19167 let err = RouterError::ChainError("chain broken".to_string());
19168 let display = err.to_string();
19169 assert!(display.contains("chain broken"));
19170 }
19171
19172 #[test]
19173 fn test_router_error_display_checkpoint() {
19174 let err = RouterError::CheckpointError("cp failed".to_string());
19175 let display = err.to_string();
19176 assert!(display.contains("cp failed"));
19177 }
19178
19179 #[test]
19180 fn test_router_error_display_blob() {
19181 let err = RouterError::BlobError("blob failed".to_string());
19182 let display = err.to_string();
19183 assert!(display.contains("blob failed"));
19184 }
19185
19186 #[test]
19187 fn test_router_error_display_vault() {
19188 let err = RouterError::VaultError("vault failed".to_string());
19189 let display = err.to_string();
19190 assert!(display.contains("vault failed"));
19191 }
19192
19193 #[test]
19194 fn test_router_error_display_cache() {
19195 let err = RouterError::CacheError("cache failed".to_string());
19196 let display = err.to_string();
19197 assert!(display.contains("cache failed"));
19198 }
19199
19200 #[test]
19201 fn test_router_error_display_type_mismatch() {
19202 let err = RouterError::TypeMismatch("expected int".to_string());
19203 let display = err.to_string();
19204 assert!(display.contains("expected int"));
19205 }
19206
19207 #[test]
19208 fn test_router_error_display_not_found() {
19209 let err = RouterError::NotFound("table foo".to_string());
19210 let display = err.to_string();
19211 assert!(display.contains("table foo"));
19212 }
19213
19214 #[test]
19215 fn test_router_error_display_missing_argument() {
19216 let err = RouterError::MissingArgument("table name".to_string());
19217 let display = err.to_string();
19218 assert!(display.contains("table name"));
19219 }
19220
19221 #[test]
19222 fn test_router_error_display_auth_required() {
19223 let err = RouterError::AuthenticationRequired;
19224 let display = err.to_string();
19225 assert!(display.contains("Authentication required"));
19226 }
19227
19228 #[test]
19229 fn test_router_error_display_invalid_argument() {
19230 let err = RouterError::InvalidArgument("bad arg".to_string());
19231 let display = err.to_string();
19232 assert!(display.contains("bad arg"));
19233 }
19234
19235 #[test]
19238 fn test_graph_constraint_create_unique_node() {
19239 let mut router = QueryRouter::new();
19240 router.set_identity("user:test");
19241 let result = router
19242 .execute_parsed("CONSTRAINT CREATE unique_name ON NODE person PROPERTY name UNIQUE")
19243 .unwrap();
19244 assert!(matches!(result, QueryResult::Empty));
19245 }
19246
19247 #[test]
19248 fn test_graph_constraint_create_exists_edge() {
19249 let mut router = QueryRouter::new();
19250 router.set_identity("user:test");
19251 let result = router
19252 .execute_parsed("CONSTRAINT CREATE req_weight ON EDGE knows PROPERTY weight EXISTS")
19253 .unwrap();
19254 assert!(matches!(result, QueryResult::Empty));
19255 }
19256
19257 #[test]
19258 fn test_graph_constraint_create_type_int() {
19259 let mut router = QueryRouter::new();
19260 router.set_identity("user:test");
19261 let result = router
19262 .execute_parsed("CONSTRAINT CREATE age_type ON NODE person PROPERTY age TYPE INT")
19263 .unwrap();
19264 assert!(matches!(result, QueryResult::Empty));
19265 }
19266
19267 #[test]
19268 fn test_graph_constraint_create_type_float() {
19269 let mut router = QueryRouter::new();
19270 router.set_identity("user:test");
19271 router
19272 .execute_parsed("CONSTRAINT CREATE score_type ON NODE person PROPERTY score TYPE FLOAT")
19273 .unwrap();
19274 }
19275
19276 #[test]
19277 fn test_graph_constraint_create_type_bool() {
19278 let mut router = QueryRouter::new();
19279 router.set_identity("user:test");
19280 router
19281 .execute_parsed(
19282 "CONSTRAINT CREATE active_type ON NODE person PROPERTY active TYPE BOOL",
19283 )
19284 .unwrap();
19285 }
19286
19287 #[test]
19288 fn test_graph_constraint_create_type_string() {
19289 let mut router = QueryRouter::new();
19290 router.set_identity("user:test");
19291 router
19292 .execute_parsed("CONSTRAINT CREATE name_type ON NODE person PROPERTY name TYPE STRING")
19293 .unwrap();
19294 }
19295
19296 #[test]
19297 fn test_graph_constraint_list() {
19298 let mut router = QueryRouter::new();
19299 router.set_identity("user:test");
19300 router
19301 .execute_parsed("CONSTRAINT CREATE c1 ON NODE person PROPERTY name UNIQUE")
19302 .unwrap();
19303 let result = router.execute_parsed("CONSTRAINT LIST").unwrap();
19304 let constraints = unwrap_qr_constraints(result);
19305 assert_eq!(constraints.len(), 1);
19306 assert_eq!(constraints[0].name, "c1");
19307 assert!(constraints[0].target.contains("Node"));
19308 assert_eq!(constraints[0].constraint_type, "UNIQUE");
19309 }
19310
19311 #[test]
19312 fn test_graph_constraint_get() {
19313 let mut router = QueryRouter::new();
19314 router.set_identity("user:test");
19315 router
19316 .execute_parsed("CONSTRAINT CREATE c1 ON NODE person PROPERTY name EXISTS")
19317 .unwrap();
19318 let result = router.execute_parsed("CONSTRAINT GET c1").unwrap();
19319 let constraints = unwrap_qr_constraints(result);
19320 assert_eq!(constraints.len(), 1);
19321 assert_eq!(constraints[0].constraint_type, "EXISTS");
19322 }
19323
19324 #[test]
19325 fn test_graph_constraint_get_not_found() {
19326 let mut router = QueryRouter::new();
19327 router.set_identity("user:test");
19328 let result = router.execute_parsed("CONSTRAINT GET nonexistent").unwrap();
19329 let constraints = unwrap_qr_constraints(result);
19330 assert!(constraints.is_empty());
19331 }
19332
19333 #[test]
19334 fn test_graph_constraint_drop() {
19335 let mut router = QueryRouter::new();
19336 router.set_identity("user:test");
19337 router
19338 .execute_parsed("CONSTRAINT CREATE c1 ON NODE person PROPERTY name UNIQUE")
19339 .unwrap();
19340 let result = router.execute_parsed("CONSTRAINT DROP c1").unwrap();
19341 assert!(matches!(result, QueryResult::Empty));
19342 }
19343
19344 #[test]
19345 fn test_graph_constraint_on_all_nodes() {
19346 let mut router = QueryRouter::new();
19347 router.set_identity("user:test");
19348 router
19349 .execute_parsed("CONSTRAINT CREATE c_all ON NODE PROPERTY id UNIQUE")
19350 .unwrap();
19351 let result = router.execute_parsed("CONSTRAINT LIST").unwrap();
19352 let constraints = unwrap_qr_constraints(result);
19353 assert!(constraints[0].target.contains("AllNodes"));
19354 }
19355
19356 #[test]
19357 fn test_graph_constraint_on_all_edges() {
19358 let mut router = QueryRouter::new();
19359 router.set_identity("user:test");
19360 router
19361 .execute_parsed("CONSTRAINT CREATE c_all ON EDGE PROPERTY weight EXISTS")
19362 .unwrap();
19363 let result = router.execute_parsed("CONSTRAINT LIST").unwrap();
19364 let constraints = unwrap_qr_constraints(result);
19365 assert!(constraints[0].target.contains("AllEdges"));
19366 }
19367
19368 #[test]
19371 fn test_graph_aggregate_node_sum() {
19372 let mut router = QueryRouter::new();
19373 router.set_identity("user:test");
19374 router.execute("NODE CREATE person { age: 30 }").unwrap();
19375 router.execute("NODE CREATE person { age: 25 }").unwrap();
19376 let result = router
19377 .execute_parsed("AGGREGATE NODE PROPERTY age SUM")
19378 .unwrap();
19379 if let QueryResult::Aggregate(AggregateResultValue::Sum(sum)) = result {
19380 assert!((sum - 55.0).abs() < 0.01);
19381 } else {
19382 panic!("expected Aggregate Sum result");
19383 }
19384 }
19385
19386 #[test]
19387 fn test_graph_aggregate_node_avg() {
19388 let mut router = QueryRouter::new();
19389 router.set_identity("user:test");
19390 router.execute("NODE CREATE person { age: 30 }").unwrap();
19391 router.execute("NODE CREATE person { age: 20 }").unwrap();
19392 let result = router
19393 .execute_parsed("AGGREGATE NODE PROPERTY age AVG")
19394 .unwrap();
19395 if let QueryResult::Aggregate(AggregateResultValue::Avg(avg)) = result {
19396 assert!((avg - 25.0).abs() < 0.01);
19397 } else {
19398 panic!("expected Aggregate Avg result");
19399 }
19400 }
19401
19402 #[test]
19403 fn test_graph_aggregate_node_min() {
19404 let mut router = QueryRouter::new();
19405 router.set_identity("user:test");
19406 router.execute("NODE CREATE person { age: 30 }").unwrap();
19407 router.execute("NODE CREATE person { age: 20 }").unwrap();
19408 let result = router
19409 .execute_parsed("AGGREGATE NODE PROPERTY age MIN")
19410 .unwrap();
19411 if let QueryResult::Aggregate(AggregateResultValue::Min(min)) = result {
19412 assert!((min - 20.0).abs() < 0.01);
19413 } else {
19414 panic!("expected Aggregate Min result");
19415 }
19416 }
19417
19418 #[test]
19419 fn test_graph_aggregate_node_max() {
19420 let mut router = QueryRouter::new();
19421 router.set_identity("user:test");
19422 router.execute("NODE CREATE person { age: 30 }").unwrap();
19423 router.execute("NODE CREATE person { age: 20 }").unwrap();
19424 let result = router
19425 .execute_parsed("AGGREGATE NODE PROPERTY age MAX")
19426 .unwrap();
19427 if let QueryResult::Aggregate(AggregateResultValue::Max(max)) = result {
19428 assert!((max - 30.0).abs() < 0.01);
19429 } else {
19430 panic!("expected Aggregate Max result");
19431 }
19432 }
19433
19434 #[test]
19435 fn test_graph_aggregate_node_count() {
19436 let mut router = QueryRouter::new();
19437 router.set_identity("user:test");
19438 router.execute("NODE CREATE person { age: 30 }").unwrap();
19439 router.execute("NODE CREATE person { age: 20 }").unwrap();
19440 let result = router
19441 .execute_parsed("AGGREGATE NODE PROPERTY age COUNT")
19442 .unwrap();
19443 if let QueryResult::Aggregate(AggregateResultValue::Count(count)) = result {
19444 assert_eq!(count, 2);
19445 } else {
19446 panic!("expected Aggregate Count result");
19447 }
19448 }
19449
19450 #[test]
19451 fn test_graph_aggregate_node_sum_by_label() {
19452 let mut router = QueryRouter::new();
19453 router.set_identity("user:test");
19454 router.execute("NODE CREATE person { age: 30 }").unwrap();
19455 router.execute("NODE CREATE person { age: 25 }").unwrap();
19456 let result = router
19457 .execute_parsed("AGGREGATE NODE PROPERTY age SUM BY LABEL person")
19458 .unwrap();
19459 if let QueryResult::Aggregate(AggregateResultValue::Sum(sum)) = result {
19460 assert!((sum - 55.0).abs() < 0.01);
19461 } else {
19462 panic!("expected Aggregate Sum result");
19463 }
19464 }
19465
19466 #[test]
19467 fn test_graph_aggregate_edge_sum() {
19468 let mut router = QueryRouter::new();
19469 router.set_identity("user:test");
19470 let n1 = match router.execute("NODE CREATE person { name: 'A' }").unwrap() {
19471 QueryResult::Ids(ids) => ids[0],
19472 other => panic!("expected Ids, got {other:?}"),
19473 };
19474 let n2 = match router.execute("NODE CREATE person { name: 'B' }").unwrap() {
19475 QueryResult::Ids(ids) => ids[0],
19476 other => panic!("expected Ids, got {other:?}"),
19477 };
19478 router
19479 .execute(&format!(
19480 "EDGE CREATE {n1} -> {n2} : knows {{ weight: 1.5 }}"
19481 ))
19482 .unwrap();
19483 let result = router
19484 .execute_parsed("AGGREGATE EDGE PROPERTY weight SUM")
19485 .unwrap();
19486 assert!(matches!(result, QueryResult::Aggregate(_)));
19487 }
19488
19489 #[test]
19490 fn test_graph_aggregate_edge_sum_by_type() {
19491 let mut router = QueryRouter::new();
19492 router.set_identity("user:test");
19493 let n1 = match router.execute("NODE CREATE person { name: 'A' }").unwrap() {
19494 QueryResult::Ids(ids) => ids[0],
19495 other => panic!("expected Ids, got {other:?}"),
19496 };
19497 let n2 = match router.execute("NODE CREATE person { name: 'B' }").unwrap() {
19498 QueryResult::Ids(ids) => ids[0],
19499 other => panic!("expected Ids, got {other:?}"),
19500 };
19501 router
19502 .execute(&format!(
19503 "EDGE CREATE {n1} -> {n2} : knows {{ weight: 1.5 }}"
19504 ))
19505 .unwrap();
19506 let result = router
19507 .execute_parsed("AGGREGATE EDGE PROPERTY weight SUM BY TYPE knows")
19508 .unwrap();
19509 assert!(matches!(result, QueryResult::Aggregate(_)));
19510 }
19511
19512 #[test]
19515 fn test_select_with_offset_clause() {
19516 let mut router = QueryRouter::new();
19517 router.set_identity("user:test");
19518 router
19519 .execute_parsed("CREATE TABLE items (id INT, name TEXT)")
19520 .unwrap();
19521 router
19522 .execute_parsed("INSERT INTO items VALUES (1, 'first')")
19523 .unwrap();
19524 router
19525 .execute_parsed("INSERT INTO items VALUES (2, 'second')")
19526 .unwrap();
19527 router
19528 .execute_parsed("INSERT INTO items VALUES (3, 'third')")
19529 .unwrap();
19530 let result = router
19531 .execute_parsed("SELECT * FROM items ORDER BY id LIMIT 2 OFFSET 1")
19532 .unwrap();
19533 let rows = unwrap_qr_rows(result);
19534 assert_eq!(rows.len(), 2);
19535 }
19536
19537 #[test]
19540 fn test_select_order_by_nulls_first() {
19541 let mut router = QueryRouter::new();
19542 router.set_identity("user:test");
19543 router
19544 .execute_parsed("CREATE TABLE nfirst (id INT, val INT)")
19545 .unwrap();
19546 router
19547 .execute_parsed("INSERT INTO nfirst VALUES (1, 10)")
19548 .unwrap();
19549 router
19550 .execute_parsed("INSERT INTO nfirst VALUES (2, NULL)")
19551 .unwrap();
19552 router
19553 .execute_parsed("INSERT INTO nfirst VALUES (3, 5)")
19554 .unwrap();
19555 let result = router
19556 .execute_parsed("SELECT * FROM nfirst ORDER BY val ASC NULLS FIRST")
19557 .unwrap();
19558 assert!(matches!(result, QueryResult::Rows(_)));
19559 }
19560
19561 #[test]
19562 fn test_select_order_by_nulls_last() {
19563 let mut router = QueryRouter::new();
19564 router.set_identity("user:test");
19565 router
19566 .execute_parsed("CREATE TABLE nlast (id INT, val INT)")
19567 .unwrap();
19568 router
19569 .execute_parsed("INSERT INTO nlast VALUES (1, 10)")
19570 .unwrap();
19571 router
19572 .execute_parsed("INSERT INTO nlast VALUES (2, NULL)")
19573 .unwrap();
19574 router
19575 .execute_parsed("INSERT INTO nlast VALUES (3, 5)")
19576 .unwrap();
19577 let result = router
19578 .execute_parsed("SELECT * FROM nlast ORDER BY val ASC NULLS LAST")
19579 .unwrap();
19580 assert!(matches!(result, QueryResult::Rows(_)));
19581 }
19582
19583 #[test]
19586 fn test_sql_count_with_column() {
19587 let mut router = QueryRouter::new();
19588 router.set_identity("user:test");
19589 router
19590 .execute_parsed("CREATE TABLE ctest (id INT, val INT)")
19591 .unwrap();
19592 router
19593 .execute_parsed("INSERT INTO ctest VALUES (1, 10)")
19594 .unwrap();
19595 router
19596 .execute_parsed("INSERT INTO ctest VALUES (2, NULL)")
19597 .unwrap();
19598 let result = router
19599 .execute_parsed("SELECT COUNT(val) FROM ctest")
19600 .unwrap();
19601 let rows = unwrap_qr_rows(result);
19602 assert_eq!(rows.len(), 1);
19603 }
19604
19605 #[test]
19606 fn test_sql_sum_with_floats() {
19607 let mut router = QueryRouter::new();
19608 router.set_identity("user:test");
19609 router
19610 .execute_parsed("CREATE TABLE ftest (id INT, val FLOAT)")
19611 .unwrap();
19612 router
19613 .execute_parsed("INSERT INTO ftest VALUES (1, 1.5)")
19614 .unwrap();
19615 router
19616 .execute_parsed("INSERT INTO ftest VALUES (2, 2.5)")
19617 .unwrap();
19618 let result = router.execute_parsed("SELECT SUM(val) FROM ftest").unwrap();
19619 assert!(matches!(result, QueryResult::Rows(_)));
19620 }
19621
19622 #[test]
19623 fn test_sql_avg_with_floats() {
19624 let mut router = QueryRouter::new();
19625 router.set_identity("user:test");
19626 router
19627 .execute_parsed("CREATE TABLE favg (id INT, val FLOAT)")
19628 .unwrap();
19629 router
19630 .execute_parsed("INSERT INTO favg VALUES (1, 10.0)")
19631 .unwrap();
19632 router
19633 .execute_parsed("INSERT INTO favg VALUES (2, 20.0)")
19634 .unwrap();
19635 let result = router.execute_parsed("SELECT AVG(val) FROM favg").unwrap();
19636 assert!(matches!(result, QueryResult::Rows(_)));
19637 }
19638
19639 #[test]
19640 fn test_sql_min_max_with_strings() {
19641 let mut router = QueryRouter::new();
19642 router.set_identity("user:test");
19643 router
19644 .execute_parsed("CREATE TABLE stest (id INT, name TEXT)")
19645 .unwrap();
19646 router
19647 .execute_parsed("INSERT INTO stest VALUES (1, 'alpha')")
19648 .unwrap();
19649 router
19650 .execute_parsed("INSERT INTO stest VALUES (2, 'beta')")
19651 .unwrap();
19652 let min_result = router
19653 .execute_parsed("SELECT MIN(name) FROM stest")
19654 .unwrap();
19655 assert!(matches!(min_result, QueryResult::Rows(_)));
19656 let max_result = router
19657 .execute_parsed("SELECT MAX(name) FROM stest")
19658 .unwrap();
19659 assert!(matches!(max_result, QueryResult::Rows(_)));
19660 }
19661
19662 #[test]
19665 fn test_group_by_with_bool() {
19666 let mut router = QueryRouter::new();
19667 router.set_identity("user:test");
19668 router
19669 .execute_parsed("CREATE TABLE gtest (id INT, flag BOOLEAN, val INT)")
19670 .unwrap();
19671 router
19672 .execute_parsed("INSERT INTO gtest VALUES (1, true, 10)")
19673 .unwrap();
19674 router
19675 .execute_parsed("INSERT INTO gtest VALUES (2, false, 20)")
19676 .unwrap();
19677 router
19678 .execute_parsed("INSERT INTO gtest VALUES (3, true, 30)")
19679 .unwrap();
19680 let result = router
19681 .execute_parsed("SELECT flag, SUM(val) FROM gtest GROUP BY flag")
19682 .unwrap();
19683 assert!(matches!(result, QueryResult::Rows(_)));
19684 }
19685
19686 #[test]
19689 fn test_describe_node_label() {
19690 let mut router = QueryRouter::new();
19691 router.set_identity("user:test");
19692 router
19693 .execute("NODE CREATE person { name: 'Alice' }")
19694 .unwrap();
19695 let result = router.execute_parsed("DESCRIBE NODE person").unwrap();
19696 assert!(matches!(result, QueryResult::Value(_)));
19697 }
19698
19699 #[test]
19700 fn test_describe_edge_type() {
19701 let mut router = QueryRouter::new();
19702 router.set_identity("user:test");
19703 let n1 = match router
19704 .execute("NODE CREATE person { name: 'Alice' }")
19705 .unwrap()
19706 {
19707 QueryResult::Ids(ids) => ids[0],
19708 other => panic!("expected Ids, got {other:?}"),
19709 };
19710 let n2 = match router
19711 .execute("NODE CREATE person { name: 'Bob' }")
19712 .unwrap()
19713 {
19714 QueryResult::Ids(ids) => ids[0],
19715 other => panic!("expected Ids, got {other:?}"),
19716 };
19717 router
19718 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows"))
19719 .unwrap();
19720 let result = router.execute_parsed("DESCRIBE EDGE knows").unwrap();
19721 assert!(matches!(result, QueryResult::Value(_)));
19722 }
19723
19724 #[test]
19727 fn test_insert_select() {
19728 let mut router = QueryRouter::new();
19729 router.set_identity("user:test");
19730 router
19731 .execute_parsed("CREATE TABLE src_tbl (id INT, name TEXT)")
19732 .unwrap();
19733 router
19734 .execute_parsed("CREATE TABLE dst_tbl (id INT, name TEXT)")
19735 .unwrap();
19736 router
19737 .execute_parsed("INSERT INTO src_tbl VALUES (1, 'alice')")
19738 .unwrap();
19739 router
19740 .execute_parsed("INSERT INTO src_tbl VALUES (2, 'bob')")
19741 .unwrap();
19742 router
19743 .execute_parsed("INSERT INTO dst_tbl SELECT * FROM src_tbl")
19744 .unwrap();
19745 let result = router.execute_parsed("SELECT * FROM dst_tbl").unwrap();
19746 let rows = unwrap_qr_rows(result);
19747 assert_eq!(rows.len(), 2);
19748 }
19749
19750 #[test]
19753 fn test_qualified_column_in_select() {
19754 let mut router = QueryRouter::new();
19755 router.set_identity("user:test");
19756 router
19757 .execute_parsed("CREATE TABLE qtbl (id INT, name TEXT)")
19758 .unwrap();
19759 router
19760 .execute_parsed("INSERT INTO qtbl VALUES (1, 'alice')")
19761 .unwrap();
19762 let result = router.execute_parsed("SELECT qtbl.name FROM qtbl").unwrap();
19763 let rows = unwrap_qr_rows(result);
19764 assert_eq!(rows.len(), 1);
19765 }
19766
19767 #[test]
19770 fn test_describe_table() {
19771 let mut router = QueryRouter::new();
19772 router.set_identity("user:test");
19773 router
19774 .execute_parsed("CREATE TABLE desc_tbl (id INT, name TEXT)")
19775 .unwrap();
19776 let result = router.execute_parsed("DESCRIBE TABLE desc_tbl").unwrap();
19777 assert!(matches!(result, QueryResult::Value(_)));
19778 }
19779
19780 #[test]
19783 fn test_property_to_string_datetime() {
19784 let router = QueryRouter::new();
19785 router
19786 .graph
19787 .create_node("ts_label", {
19788 let mut props = HashMap::new();
19789 props.insert(
19790 "created".to_string(),
19791 PropertyValue::DateTime(1_700_000_000),
19792 );
19793 props
19794 })
19795 .unwrap();
19796 let result = router.execute_parsed("NODE LIST").unwrap();
19797 assert!(matches!(result, QueryResult::Nodes(_)));
19798 }
19799
19800 #[test]
19801 fn test_property_to_string_list() {
19802 let router = QueryRouter::new();
19803 router
19804 .graph
19805 .create_node("list_label", {
19806 let mut props = HashMap::new();
19807 props.insert(
19808 "tags".to_string(),
19809 PropertyValue::List(vec![
19810 PropertyValue::String("a".to_string()),
19811 PropertyValue::String("b".to_string()),
19812 ]),
19813 );
19814 props
19815 })
19816 .unwrap();
19817 let result = router.execute_parsed("NODE LIST").unwrap();
19818 assert!(matches!(result, QueryResult::Nodes(_)));
19819 }
19820
19821 #[test]
19822 fn test_property_to_string_map() {
19823 let router = QueryRouter::new();
19824 router
19825 .graph
19826 .create_node("map_label", {
19827 let mut props = HashMap::new();
19828 let mut inner = HashMap::new();
19829 inner.insert("x".to_string(), PropertyValue::Int(1));
19830 props.insert("meta".to_string(), PropertyValue::Map(inner));
19831 props
19832 })
19833 .unwrap();
19834 let result = router.execute_parsed("NODE LIST").unwrap();
19835 assert!(matches!(result, QueryResult::Nodes(_)));
19836 }
19837
19838 #[test]
19839 fn test_property_to_string_bytes() {
19840 let router = QueryRouter::new();
19841 router
19842 .graph
19843 .create_node("bytes_label", {
19844 let mut props = HashMap::new();
19845 props.insert("data".to_string(), PropertyValue::Bytes(vec![1, 2, 3]));
19846 props
19847 })
19848 .unwrap();
19849 let result = router.execute_parsed("NODE LIST").unwrap();
19850 assert!(matches!(result, QueryResult::Nodes(_)));
19851 }
19852
19853 #[test]
19854 fn test_property_to_string_point() {
19855 let router = QueryRouter::new();
19856 router
19857 .graph
19858 .create_node("point_label", {
19859 let mut props = HashMap::new();
19860 props.insert(
19861 "location".to_string(),
19862 PropertyValue::Point {
19863 lat: 40.7128,
19864 lon: -74.006,
19865 },
19866 );
19867 props
19868 })
19869 .unwrap();
19870 let result = router.execute_parsed("NODE LIST").unwrap();
19871 assert!(matches!(result, QueryResult::Nodes(_)));
19872 }
19873
19874 #[test]
19875 fn test_select_offset_within_range() {
19876 let router = QueryRouter::new();
19877 router
19878 .execute_parsed("CREATE TABLE off_tbl (id INT, name TEXT)")
19879 .unwrap();
19880 router
19881 .execute_parsed("INSERT INTO off_tbl VALUES (1, 'a')")
19882 .unwrap();
19883 router
19884 .execute_parsed("INSERT INTO off_tbl VALUES (2, 'b')")
19885 .unwrap();
19886 router
19887 .execute_parsed("INSERT INTO off_tbl VALUES (3, 'c')")
19888 .unwrap();
19889 let result = router
19890 .execute_parsed("SELECT * FROM off_tbl OFFSET 1")
19891 .unwrap();
19892 let rows = unwrap_qr_rows(result);
19893 assert_eq!(rows.len(), 2);
19894 }
19895
19896 #[test]
19897 fn test_select_offset_exceeds_rows() {
19898 let router = QueryRouter::new();
19899 router.execute_parsed("CREATE TABLE off2 (id INT)").unwrap();
19900 router
19901 .execute_parsed("INSERT INTO off2 VALUES (1)")
19902 .unwrap();
19903 let result = router
19904 .execute_parsed("SELECT * FROM off2 OFFSET 100")
19905 .unwrap();
19906 let rows = unwrap_qr_rows(result);
19907 assert!(rows.is_empty());
19908 }
19909
19910 #[test]
19911 fn test_unified_result_from_conversion() {
19912 use tensor_unified::UnifiedResult as TensorUnifiedResult;
19913 let tensor_result = TensorUnifiedResult {
19914 description: "test desc".to_string(),
19915 items: vec![],
19916 };
19917 let result: UnifiedResult = tensor_result.into();
19918 assert_eq!(result.description, "test desc");
19919 assert!(result.items.is_empty());
19920 }
19921
19922 #[test]
19923 fn test_graph_aggregate_count_all_edges() {
19924 let router = QueryRouter::new();
19925 let n1 = if let QueryResult::Ids(ids) =
19926 router.execute("NODE CREATE person { name: 'A' }").unwrap()
19927 {
19928 ids[0]
19929 } else {
19930 panic!("expected Ids");
19931 };
19932 let n2 = if let QueryResult::Ids(ids) =
19933 router.execute("NODE CREATE person { name: 'B' }").unwrap()
19934 {
19935 ids[0]
19936 } else {
19937 panic!("expected Ids");
19938 };
19939 router
19940 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows {{ weight: 5 }}"))
19941 .unwrap();
19942 let result = router
19943 .execute_parsed("AGGREGATE EDGE PROPERTY weight SUM")
19944 .unwrap();
19945 assert!(matches!(result, QueryResult::Aggregate(_)));
19946 }
19947
19948 #[test]
19949 fn test_graph_aggregate_edge_by_type() {
19950 let router = QueryRouter::new();
19951 let n1 = if let QueryResult::Ids(ids) =
19952 router.execute("NODE CREATE person { name: 'X' }").unwrap()
19953 {
19954 ids[0]
19955 } else {
19956 panic!("expected Ids");
19957 };
19958 let n2 = if let QueryResult::Ids(ids) =
19959 router.execute("NODE CREATE person { name: 'Y' }").unwrap()
19960 {
19961 ids[0]
19962 } else {
19963 panic!("expected Ids");
19964 };
19965 router
19966 .execute(&format!("EDGE CREATE {n1} -> {n2} : likes {{ score: 3 }}"))
19967 .unwrap();
19968 let result = router
19969 .execute_parsed("AGGREGATE EDGE PROPERTY score AVG BY TYPE likes")
19970 .unwrap();
19971 assert!(matches!(result, QueryResult::Aggregate(_)));
19972 }
19973
19974 #[test]
19975 fn test_graph_index_create_and_show_cov() {
19976 let router = QueryRouter::new();
19977 router
19978 .execute("GRAPH INDEX CREATE ON NODE PROPERTY name")
19979 .unwrap();
19980 let result = router.execute("GRAPH INDEX SHOW ON NODE").unwrap();
19981 assert!(matches!(result, QueryResult::GraphIndexes(_)));
19982 }
19983
19984 #[test]
19985 fn test_graph_index_drop_cov() {
19986 let router = QueryRouter::new();
19987 router
19988 .execute("GRAPH INDEX CREATE ON NODE PROPERTY email")
19989 .unwrap();
19990 let result = router
19991 .execute("GRAPH INDEX DROP ON NODE PROPERTY email")
19992 .unwrap();
19993 assert!(matches!(result, QueryResult::Empty));
19994 }
19995
19996 #[test]
19997 fn test_select_group_by_with_having_count() {
19998 let router = QueryRouter::new();
19999 router
20000 .execute_parsed("CREATE TABLE grp_hv (name TEXT, dept TEXT)")
20001 .unwrap();
20002 router
20003 .execute_parsed("INSERT INTO grp_hv VALUES ('a', 'eng')")
20004 .unwrap();
20005 router
20006 .execute_parsed("INSERT INTO grp_hv VALUES ('b', 'eng')")
20007 .unwrap();
20008 router
20009 .execute_parsed("INSERT INTO grp_hv VALUES ('c', 'sales')")
20010 .unwrap();
20011 let result = router
20012 .execute_parsed("SELECT dept, COUNT(*) FROM grp_hv GROUP BY dept")
20013 .unwrap();
20014 let rows = unwrap_qr_rows(result);
20015 assert_eq!(rows.len(), 2);
20016 }
20017
20018 #[test]
20019 fn test_insert_select_statement() {
20020 let router = QueryRouter::new();
20021 router
20022 .execute_parsed("CREATE TABLE src_t (id INT, val TEXT)")
20023 .unwrap();
20024 router
20025 .execute_parsed("INSERT INTO src_t VALUES (1, 'hello')")
20026 .unwrap();
20027 router
20028 .execute_parsed("CREATE TABLE dst_t (id INT, val TEXT)")
20029 .unwrap();
20030 let result = router
20031 .execute_parsed("INSERT INTO dst_t SELECT * FROM src_t")
20032 .unwrap();
20033 assert!(matches!(result, QueryResult::Ids(ref ids) if !ids.is_empty()));
20035 }
20036
20037 #[test]
20038 fn test_sql_decimal_and_varchar_types() {
20039 let router = QueryRouter::new();
20040 let result = router
20041 .execute_parsed("CREATE TABLE typed (amount DECIMAL(10,2), name VARCHAR(50))")
20042 .unwrap();
20043 assert!(matches!(result, QueryResult::Empty));
20044 }
20045
20046 #[test]
20047 fn test_case_expression_in_select() {
20048 let router = QueryRouter::new();
20049 router
20050 .execute_parsed("CREATE TABLE case_t (val INT)")
20051 .unwrap();
20052 router
20053 .execute_parsed("INSERT INTO case_t VALUES (5)")
20054 .unwrap();
20055 router
20056 .execute_parsed("INSERT INTO case_t VALUES (0)")
20057 .unwrap();
20058 let result = router
20059 .execute_parsed("SELECT CASE WHEN val > 0 THEN 'positive' ELSE 'zero' END FROM case_t")
20060 .unwrap();
20061 let rows = unwrap_qr_rows(result);
20062 assert_eq!(rows.len(), 2);
20063 }
20064
20065 #[test]
20068 #[should_panic(expected = "expected ArtifactInfo")]
20069 fn test_unwrap_qr_artifactinfo_wrong_variant() {
20070 unwrap_qr_artifactinfo(QueryResult::Empty);
20071 }
20072
20073 #[test]
20074 #[should_panic(expected = "expected ArtifactList")]
20075 fn test_unwrap_qr_artifactlist_wrong_variant() {
20076 unwrap_qr_artifactlist(QueryResult::Empty);
20077 }
20078
20079 #[test]
20080 #[should_panic(expected = "expected Blob")]
20081 fn test_unwrap_qr_blob_wrong_variant() {
20082 unwrap_qr_blob(QueryResult::Empty);
20083 }
20084
20085 #[test]
20086 #[should_panic(expected = "expected BlobStats")]
20087 fn test_unwrap_qr_blobstats_wrong_variant() {
20088 unwrap_qr_blobstats(QueryResult::Empty);
20089 }
20090
20091 #[test]
20092 #[should_panic(expected = "expected CheckpointList")]
20093 fn test_unwrap_qr_checkpointlist_wrong_variant() {
20094 unwrap_qr_checkpointlist(QueryResult::Empty);
20095 }
20096
20097 #[test]
20098 #[should_panic(expected = "expected Constraints")]
20099 fn test_unwrap_qr_constraints_wrong_variant() {
20100 unwrap_qr_constraints(QueryResult::Empty);
20101 }
20102
20103 #[test]
20104 #[should_panic(expected = "expected Edges")]
20105 fn test_unwrap_qr_edges_wrong_variant() {
20106 unwrap_qr_edges(QueryResult::Empty);
20107 }
20108
20109 #[test]
20110 #[should_panic(expected = "expected Nodes")]
20111 fn test_unwrap_qr_nodes_wrong_variant() {
20112 unwrap_qr_nodes(QueryResult::Empty);
20113 }
20114
20115 #[test]
20116 #[should_panic(expected = "expected Rows")]
20117 fn test_unwrap_qr_rows_wrong_variant() {
20118 unwrap_qr_rows(QueryResult::Empty);
20119 }
20120
20121 #[test]
20122 #[should_panic(expected = "expected Similar")]
20123 fn test_unwrap_qr_similar_wrong_variant() {
20124 unwrap_qr_similar(QueryResult::Empty);
20125 }
20126
20127 #[test]
20128 #[should_panic(expected = "expected Unified")]
20129 fn test_unwrap_qr_unified_wrong_variant() {
20130 unwrap_qr_unified(QueryResult::Empty);
20131 }
20132
20133 #[test]
20134 #[should_panic(expected = "expected Value")]
20135 fn test_unwrap_qr_value_wrong_variant() {
20136 unwrap_qr_value(QueryResult::Empty);
20137 }
20138
20139 #[test]
20142 fn test_avg_float_column() {
20143 let router = QueryRouter::new();
20144 setup_aggregate_table(&router);
20145
20146 let stmt = parser::parse("SELECT AVG(price) FROM sales").unwrap();
20147 let result = router.execute_statement(&stmt).unwrap();
20148 let rows = unwrap_qr_rows(result);
20149 assert_eq!(rows.len(), 1);
20150 let avg = rows[0]
20151 .values
20152 .iter()
20153 .find(|(k, _)| k == "AVG(price)")
20154 .unwrap()
20155 .1
20156 .clone();
20157 assert!(matches!(avg, Value::Float(f) if (f - 1.4375).abs() < 0.001));
20159 }
20160
20161 #[test]
20162 fn test_min_float_column() {
20163 let router = QueryRouter::new();
20164 setup_aggregate_table(&router);
20165
20166 let stmt = parser::parse("SELECT MIN(price) FROM sales").unwrap();
20167 let result = router.execute_statement(&stmt).unwrap();
20168 let rows = unwrap_qr_rows(result);
20169 assert_eq!(rows.len(), 1);
20170 let min = rows[0]
20171 .values
20172 .iter()
20173 .find(|(k, _)| k == "MIN(price)")
20174 .unwrap()
20175 .1
20176 .clone();
20177 assert_eq!(min, Value::Float(0.75));
20178 }
20179
20180 #[test]
20181 fn test_max_float_column() {
20182 let router = QueryRouter::new();
20183 setup_aggregate_table(&router);
20184
20185 let stmt = parser::parse("SELECT MAX(price) FROM sales").unwrap();
20186 let result = router.execute_statement(&stmt).unwrap();
20187 let rows = unwrap_qr_rows(result);
20188 assert_eq!(rows.len(), 1);
20189 let max = rows[0]
20190 .values
20191 .iter()
20192 .find(|(k, _)| k == "MAX(price)")
20193 .unwrap()
20194 .1
20195 .clone();
20196 assert_eq!(max, Value::Float(2.0));
20197 }
20198
20199 #[test]
20202 fn test_select_offset() {
20203 let router = QueryRouter::new();
20204 setup_aggregate_table(&router);
20205
20206 let stmt = parser::parse("SELECT * FROM sales OFFSET 2").unwrap();
20207 let result = router.execute_statement(&stmt).unwrap();
20208 let rows = unwrap_qr_rows(result);
20209 assert_eq!(rows.len(), 2); }
20211
20212 #[test]
20213 fn test_select_offset_past_end_clears() {
20214 let router = QueryRouter::new();
20215 setup_aggregate_table(&router);
20216
20217 let stmt = parser::parse("SELECT * FROM sales OFFSET 100").unwrap();
20218 let result = router.execute_statement(&stmt).unwrap();
20219 let rows = unwrap_qr_rows(result);
20220 assert!(rows.is_empty()); }
20222
20223 #[test]
20226 fn test_where_and_condition() {
20227 let router = QueryRouter::new();
20228 setup_aggregate_table(&router);
20229
20230 let stmt =
20231 parser::parse("SELECT * FROM sales WHERE amount > 5 AND product = 'Apple'").unwrap();
20232 let result = router.execute_statement(&stmt).unwrap();
20233 let rows = unwrap_qr_rows(result);
20234 assert_eq!(rows.len(), 1); }
20236
20237 #[test]
20238 fn test_where_or_condition() {
20239 let router = QueryRouter::new();
20240 setup_aggregate_table(&router);
20241
20242 let stmt =
20243 parser::parse("SELECT * FROM sales WHERE product = 'Apple' OR product = 'Cherry'")
20244 .unwrap();
20245 let result = router.execute_statement(&stmt).unwrap();
20246 let rows = unwrap_qr_rows(result);
20247 assert_eq!(rows.len(), 3); }
20249
20250 #[test]
20253 fn test_graph_index_create_edge_property() {
20254 let router = QueryRouter::new();
20255 router.execute("NODE CREATE person { name: 'A' }").unwrap();
20256 router.execute("NODE CREATE person { name: 'B' }").unwrap();
20257 let result = router
20258 .execute("GRAPH INDEX CREATE ON EDGE PROPERTY weight")
20259 .unwrap();
20260 assert!(matches!(result, QueryResult::Empty));
20261 }
20262
20263 #[test]
20264 fn test_graph_index_create_on_label() {
20265 let router = QueryRouter::new();
20266 router.execute("NODE CREATE person { name: 'A' }").unwrap();
20267 let result = router.execute("GRAPH INDEX CREATE ON LABEL");
20269 assert!(result.is_ok() || format!("{result:?}").contains("already exists"));
20270 }
20271
20272 #[test]
20273 fn test_graph_index_create_on_edge_type() {
20274 let router = QueryRouter::new();
20275 let result = router.execute("GRAPH INDEX CREATE ON EDGE TYPE").unwrap();
20276 assert!(matches!(result, QueryResult::Empty));
20277 }
20278
20279 #[test]
20280 fn test_graph_index_drop_node_property() {
20281 let router = QueryRouter::new();
20282 router
20283 .execute("GRAPH INDEX CREATE ON NODE PROPERTY name")
20284 .unwrap();
20285 let result = router
20286 .execute("GRAPH INDEX DROP ON NODE PROPERTY name")
20287 .unwrap();
20288 assert!(matches!(result, QueryResult::Empty));
20289 }
20290
20291 #[test]
20292 fn test_graph_index_drop_edge_property() {
20293 let router = QueryRouter::new();
20294 router
20295 .execute("GRAPH INDEX CREATE ON EDGE PROPERTY weight")
20296 .unwrap();
20297 let result = router
20298 .execute("GRAPH INDEX DROP ON EDGE PROPERTY weight")
20299 .unwrap();
20300 assert!(matches!(result, QueryResult::Empty));
20301 }
20302
20303 #[test]
20304 fn test_graph_index_show_on_edge() {
20305 let router = QueryRouter::new();
20306 let result = router.execute("GRAPH INDEX SHOW ON EDGE").unwrap();
20307 assert!(matches!(result, QueryResult::GraphIndexes(_)));
20308 }
20309
20310 #[test]
20313 fn test_graph_aggregate_node_property_by_label() {
20314 let router = QueryRouter::new();
20315 router
20316 .execute("NODE CREATE person { name: 'A', age: 25 }")
20317 .unwrap();
20318 router
20319 .execute("NODE CREATE person { name: 'B', age: 35 }")
20320 .unwrap();
20321 let result = router
20322 .execute_parsed("AGGREGATE NODE PROPERTY age SUM BY LABEL person")
20323 .unwrap();
20324 assert!(matches!(result, QueryResult::Aggregate(_)));
20325 }
20326
20327 #[test]
20328 fn test_graph_aggregate_edge_property_sum() {
20329 let router = QueryRouter::new();
20330 let n1 = if let QueryResult::Ids(ids) =
20331 router.execute("NODE CREATE person { name: 'X' }").unwrap()
20332 {
20333 ids[0]
20334 } else {
20335 panic!("expected Ids");
20336 };
20337 let n2 = if let QueryResult::Ids(ids) =
20338 router.execute("NODE CREATE person { name: 'Y' }").unwrap()
20339 {
20340 ids[0]
20341 } else {
20342 panic!("expected Ids");
20343 };
20344 router
20345 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows {{ weight: 5 }}"))
20346 .unwrap();
20347 let result = router
20348 .execute_parsed("AGGREGATE EDGE PROPERTY weight SUM")
20349 .unwrap();
20350 assert!(matches!(result, QueryResult::Aggregate(_)));
20351 }
20352
20353 #[test]
20356 fn test_entity_create_and_get_cov() {
20357 let router = QueryRouter::new();
20358 let result = router
20359 .execute_parsed("ENTITY CREATE 'test_ent' { name: 'Alice', role: 'admin' }")
20360 .unwrap();
20361 assert!(matches!(result, QueryResult::Value(_)));
20362
20363 let result = router.execute_parsed("ENTITY GET 'test_ent'").unwrap();
20364 assert!(matches!(result, QueryResult::Unified(_)));
20365 }
20366
20367 #[test]
20368 fn test_entity_delete_cov() {
20369 let router = QueryRouter::new();
20370 router
20371 .execute_parsed("ENTITY CREATE 'del_ent' { name: 'Bob' }")
20372 .unwrap();
20373 let result = router.execute_parsed("ENTITY DELETE 'del_ent'").unwrap();
20374 assert!(matches!(result, QueryResult::Value(_) | QueryResult::Empty));
20375 }
20376
20377 #[test]
20380 fn test_similar_in_collection() {
20381 let router = QueryRouter::new();
20382 router
20383 .execute_parsed("EMBED STORE 'c1' [1.0, 0.0] COLLECTION 'grp'")
20384 .unwrap();
20385 router
20386 .execute_parsed("EMBED STORE 'c2' [0.9, 0.1] COLLECTION 'grp'")
20387 .unwrap();
20388 let result = router
20389 .execute_parsed("SIMILAR [1.0, 0.0] TOP 2 COLLECTION 'grp'")
20390 .unwrap();
20391 assert!(matches!(result, QueryResult::Similar(ref s) if !s.is_empty()));
20392 }
20393
20394 fn setup_float_group_table(router: &QueryRouter) {
20397 router
20398 .execute_parsed("CREATE TABLE items (category TEXT, price FLOAT, name TEXT)")
20399 .unwrap();
20400 router
20401 .execute_parsed("INSERT INTO items VALUES ('fruit', 1.50, 'apple')")
20402 .unwrap();
20403 router
20404 .execute_parsed("INSERT INTO items VALUES ('fruit', 0.75, 'banana')")
20405 .unwrap();
20406 router
20407 .execute_parsed("INSERT INTO items VALUES ('fruit', 2.00, 'cherry')")
20408 .unwrap();
20409 router
20410 .execute_parsed("INSERT INTO items VALUES ('veggie', 3.00, 'carrot')")
20411 .unwrap();
20412 router
20413 .execute_parsed("INSERT INTO items VALUES ('veggie', 1.25, 'peas')")
20414 .unwrap();
20415 }
20416
20417 #[test]
20418 fn test_group_by_min_float() {
20419 let router = QueryRouter::new();
20420 setup_float_group_table(&router);
20421
20422 let stmt =
20423 parser::parse("SELECT category, MIN(price) FROM items GROUP BY category").unwrap();
20424 let result = router.execute_statement(&stmt).unwrap();
20425 let rows = unwrap_qr_rows(result);
20426 assert_eq!(rows.len(), 2);
20427 }
20428
20429 #[test]
20430 fn test_group_by_max_float() {
20431 let router = QueryRouter::new();
20432 setup_float_group_table(&router);
20433
20434 let stmt =
20435 parser::parse("SELECT category, MAX(price) FROM items GROUP BY category").unwrap();
20436 let result = router.execute_statement(&stmt).unwrap();
20437 let rows = unwrap_qr_rows(result);
20438 assert_eq!(rows.len(), 2);
20439 }
20440
20441 #[test]
20442 fn test_group_by_avg_float() {
20443 let router = QueryRouter::new();
20444 setup_float_group_table(&router);
20445
20446 let stmt =
20447 parser::parse("SELECT category, AVG(price) FROM items GROUP BY category").unwrap();
20448 let result = router.execute_statement(&stmt).unwrap();
20449 let rows = unwrap_qr_rows(result);
20450 assert_eq!(rows.len(), 2);
20451 }
20452
20453 #[test]
20454 fn test_group_by_min_string() {
20455 let router = QueryRouter::new();
20456 setup_float_group_table(&router);
20457
20458 let stmt =
20459 parser::parse("SELECT category, MIN(name) FROM items GROUP BY category").unwrap();
20460 let result = router.execute_statement(&stmt).unwrap();
20461 let rows = unwrap_qr_rows(result);
20462 assert_eq!(rows.len(), 2);
20463 }
20464
20465 #[test]
20466 fn test_group_by_max_string() {
20467 let router = QueryRouter::new();
20468 setup_float_group_table(&router);
20469
20470 let stmt =
20471 parser::parse("SELECT category, MAX(name) FROM items GROUP BY category").unwrap();
20472 let result = router.execute_statement(&stmt).unwrap();
20473 let rows = unwrap_qr_rows(result);
20474 assert_eq!(rows.len(), 2);
20475 }
20476
20477 #[test]
20478 fn test_group_by_count_column() {
20479 let router = QueryRouter::new();
20480 setup_float_group_table(&router);
20481
20482 let stmt =
20483 parser::parse("SELECT category, COUNT(name) FROM items GROUP BY category").unwrap();
20484 let result = router.execute_statement(&stmt).unwrap();
20485 let rows = unwrap_qr_rows(result);
20486 assert_eq!(rows.len(), 2);
20487 }
20488
20489 #[test]
20492 fn test_cypher_match_basic() {
20493 let router = QueryRouter::new();
20494 router
20495 .execute("NODE CREATE person { name: 'Alice' }")
20496 .unwrap();
20497 let result = router.execute_parsed("MATCH (n:person) RETURN n");
20498 let _ = result;
20500 }
20501
20502 #[test]
20503 fn test_cypher_create_basic() {
20504 let router = QueryRouter::new();
20505 let result = router.execute_parsed("CREATE (n:person {name: 'Bob'})");
20506 let _ = result;
20507 }
20508
20509 #[test]
20512 fn test_where_lt_comparison() {
20513 let router = QueryRouter::new();
20514 setup_aggregate_table(&router);
20515
20516 let stmt = parser::parse("SELECT * FROM sales WHERE amount < 15").unwrap();
20517 let result = router.execute_statement(&stmt).unwrap();
20518 let rows = unwrap_qr_rows(result);
20519 assert_eq!(rows.len(), 2); }
20521
20522 #[test]
20523 fn test_where_le_comparison() {
20524 let router = QueryRouter::new();
20525 setup_aggregate_table(&router);
20526
20527 let stmt = parser::parse("SELECT * FROM sales WHERE amount <= 10").unwrap();
20528 let result = router.execute_statement(&stmt).unwrap();
20529 let rows = unwrap_qr_rows(result);
20530 assert_eq!(rows.len(), 2); }
20532
20533 #[test]
20536 fn test_paginated_select_query() {
20537 let router = QueryRouter::new();
20538 setup_aggregate_table(&router);
20539
20540 let opts = PaginationOptions {
20541 page_size: Some(2),
20542 cursor: None,
20543 cursor_ttl: None,
20544 count_total: true,
20545 };
20546 let result = router
20547 .execute_paginated("SELECT * FROM sales", opts)
20548 .unwrap();
20549 assert!(result.total_count.is_some());
20550 }
20551
20552 #[test]
20555 fn test_select_limit_and_offset() {
20556 let router = QueryRouter::new();
20557 setup_aggregate_table(&router);
20558
20559 let stmt = parser::parse("SELECT * FROM sales LIMIT 2 OFFSET 1").unwrap();
20560 let result = router.execute_statement(&stmt).unwrap();
20561 let rows = unwrap_qr_rows(result);
20562 assert_eq!(rows.len(), 2);
20563 }
20564
20565 #[test]
20568 fn test_entity_update_cov() {
20569 let router = QueryRouter::new();
20570 router
20571 .execute_parsed("ENTITY CREATE 'upd_ent' { name: 'Old' }")
20572 .unwrap();
20573 let result = router.execute_parsed("ENTITY UPDATE 'upd_ent' { name: 'New' }");
20574 let _ = result;
20576 }
20577
20578 #[test]
20581 fn test_entity_connect_cov() {
20582 let router = QueryRouter::new();
20583 router
20584 .execute_parsed("ENTITY CREATE 'e1' { name: 'Alice' }")
20585 .unwrap();
20586 router
20587 .execute_parsed("ENTITY CREATE 'e2' { name: 'Bob' }")
20588 .unwrap();
20589 let result = router.execute_parsed("ENTITY CONNECT 'e1' TO 'e2' AS 'knows'");
20590 let _ = result;
20591 }
20592
20593 #[test]
20596 fn test_chain_height_no_init() {
20597 let router = QueryRouter::new();
20598 let result = router.execute_parsed("CHAIN HEIGHT");
20599 assert!(result.is_err());
20601 }
20602
20603 #[test]
20604 fn test_chain_tip_no_init() {
20605 let router = QueryRouter::new();
20606 let result = router.execute_parsed("CHAIN TIP");
20607 assert!(result.is_err());
20608 }
20609
20610 #[test]
20613 fn test_cluster_status_not_connected() {
20614 let router = QueryRouter::new();
20615 let result = router.execute_parsed("CLUSTER STATUS").unwrap();
20616 assert!(matches!(result, QueryResult::Value(_)));
20618 }
20619
20620 #[test]
20621 fn test_cluster_disconnect_not_connected() {
20622 let router = QueryRouter::new();
20623 let result = router.execute_parsed("CLUSTER DISCONNECT");
20624 let _ = result;
20626 }
20627
20628 #[test]
20631 fn test_group_by_having_sum() {
20632 let router = QueryRouter::new();
20633 setup_float_group_table(&router);
20634
20635 let stmt = parser::parse(
20636 "SELECT category, SUM(price) FROM items GROUP BY category HAVING SUM(price) > 3.0",
20637 )
20638 .unwrap();
20639 let result = router.execute_statement(&stmt).unwrap();
20640 let rows = unwrap_qr_rows(result);
20641 assert_eq!(rows.len(), 2);
20643 }
20644
20645 #[test]
20648 fn test_show_tables_with_data() {
20649 let router = QueryRouter::new();
20650 router
20651 .execute_parsed("CREATE TABLE show_t1 (id INT)")
20652 .unwrap();
20653 router
20654 .execute_parsed("CREATE TABLE show_t2 (name TEXT)")
20655 .unwrap();
20656 let result = router.execute_parsed("SHOW TABLES").unwrap();
20657 assert!(matches!(result, QueryResult::TableList(_)));
20658 }
20659
20660 #[test]
20663 fn test_select_order_by_desc() {
20664 let router = QueryRouter::new();
20665 setup_aggregate_table(&router);
20666
20667 let stmt = parser::parse("SELECT * FROM sales ORDER BY amount DESC").unwrap();
20668 let result = router.execute_statement(&stmt).unwrap();
20669 let rows = unwrap_qr_rows(result);
20670 let first_amount = rows[0]
20672 .values
20673 .iter()
20674 .find(|(k, _)| k == "amount")
20675 .unwrap()
20676 .1
20677 .clone();
20678 assert_eq!(first_amount, Value::Int(20));
20679 }
20680
20681 #[test]
20684 fn test_join_with_offset_cov() {
20685 let router = QueryRouter::new();
20686 setup_join_tables(&router);
20687
20688 let stmt =
20690 parser::parse("SELECT * FROM orders JOIN users ON orders.user_id = users.id OFFSET 1")
20691 .unwrap();
20692 let result = router.execute_statement(&stmt).unwrap();
20693 let rows = unwrap_qr_rows(result);
20694 assert_eq!(rows.len(), 2); }
20696
20697 #[test]
20698 fn test_join_with_offset_exceeds_cov() {
20699 let router = QueryRouter::new();
20700 setup_join_tables(&router);
20701
20702 let stmt = parser::parse(
20703 "SELECT * FROM orders JOIN users ON orders.user_id = users.id OFFSET 100",
20704 )
20705 .unwrap();
20706 let result = router.execute_statement(&stmt).unwrap();
20707 let rows = unwrap_qr_rows(result);
20708 assert!(rows.is_empty());
20709 }
20710
20711 #[test]
20712 fn test_join_with_where_lt_cov() {
20713 let router = QueryRouter::new();
20714 setup_join_tables(&router);
20715
20716 let stmt = parser::parse(
20717 "SELECT * FROM orders JOIN users ON orders.user_id = users.id WHERE amount < 150",
20718 )
20719 .unwrap();
20720 let result = router.execute_statement(&stmt).unwrap();
20721 let rows = unwrap_qr_rows(result);
20722 assert_eq!(rows.len(), 1); }
20724
20725 #[test]
20726 fn test_join_with_where_ne_cov() {
20727 let router = QueryRouter::new();
20728 setup_join_tables(&router);
20729
20730 let stmt = parser::parse(
20731 "SELECT * FROM orders JOIN users ON orders.user_id = users.id WHERE name != 'Alice'",
20732 )
20733 .unwrap();
20734 let result = router.execute_statement(&stmt).unwrap();
20735 let rows = unwrap_qr_rows(result);
20736 assert_eq!(rows.len(), 1); }
20738
20739 fn blob_put(router: &QueryRouter, name: &str, data: &str) -> String {
20742 let result = router
20743 .execute_parsed(&format!("BLOB PUT '{name}' '{data}'"))
20744 .unwrap();
20745 unwrap_qr_value(result)
20746 }
20747
20748 #[test]
20749 fn test_blob_info_and_stats_cov() {
20750 let mut router = QueryRouter::new();
20751 router.init_blob().unwrap();
20752 router.set_identity("user:test");
20753
20754 let id = blob_put(&router, "testblob", "hello world");
20755 let info = router.execute_parsed(&format!("BLOB INFO '{id}'")).unwrap();
20756 assert!(matches!(info, QueryResult::ArtifactInfo(_)));
20757
20758 let stats = router.execute_parsed("BLOB STATS").unwrap();
20759 assert!(matches!(stats, QueryResult::BlobStats(_)));
20760 }
20761
20762 #[test]
20763 fn test_blob_tag_and_untag_cov() {
20764 let mut router = QueryRouter::new();
20765 router.init_blob().unwrap();
20766 router.set_identity("user:test");
20767
20768 let id = blob_put(&router, "tagged_blob2", "data");
20769 let result = router
20770 .execute_parsed(&format!("BLOB TAG '{id}' 'important'"))
20771 .unwrap();
20772 assert!(matches!(result, QueryResult::Empty));
20773
20774 let result = router
20775 .execute_parsed(&format!("BLOB UNTAG '{id}' 'important'"))
20776 .unwrap();
20777 assert!(matches!(result, QueryResult::Empty));
20778 }
20779
20780 #[test]
20781 fn test_blob_link_and_unlink_cov() {
20782 let mut router = QueryRouter::new();
20783 router.init_blob().unwrap();
20784 router.set_identity("user:test");
20785
20786 let id = blob_put(&router, "linked2", "data");
20787 let result = router
20788 .execute_parsed(&format!("BLOB LINK '{id}' TO 'entity:1'"))
20789 .unwrap();
20790 assert!(matches!(result, QueryResult::Empty));
20791
20792 let result = router
20793 .execute_parsed(&format!("BLOB UNLINK '{id}' FROM 'entity:1'"))
20794 .unwrap();
20795 assert!(matches!(result, QueryResult::Empty));
20796 }
20797
20798 #[test]
20799 fn test_blob_verify_cov() {
20800 let mut router = QueryRouter::new();
20801 router.init_blob().unwrap();
20802 router.set_identity("user:test");
20803
20804 let id = blob_put(&router, "verified2", "data");
20805 let result = router.execute_parsed(&format!("BLOB VERIFY '{id}'"));
20806 let _ = result;
20807 }
20808
20809 #[test]
20810 fn test_blob_meta_set_get_cov() {
20811 let mut router = QueryRouter::new();
20812 router.init_blob().unwrap();
20813 router.set_identity("user:test");
20814
20815 let id = blob_put(&router, "metablob2", "data");
20816 let result = router.execute_parsed(&format!("BLOB META SET '{id}' 'key' 'value'"));
20817 let _ = result;
20818
20819 let result = router.execute_parsed(&format!("BLOB META GET '{id}' 'key'"));
20820 let _ = result;
20821 }
20822
20823 #[test]
20824 fn test_blobs_all_and_by_type_cov() {
20825 let mut router = QueryRouter::new();
20826 router.init_blob().unwrap();
20827 router.set_identity("user:test");
20828
20829 blob_put(&router, "a.txt", "data1");
20830 blob_put(&router, "b.txt", "data2");
20831
20832 let result = router.execute_parsed("BLOBS").unwrap();
20833 assert!(matches!(result, QueryResult::ArtifactList(_)));
20834
20835 let result = router.execute_parsed("BLOBS WHERE TYPE 'text/plain'");
20836 let _ = result;
20837 }
20838
20839 #[test]
20842 fn test_similar_with_collection_and_filter() {
20843 let router = QueryRouter::new();
20844 router
20845 .execute_parsed("EMBED STORE 'f1' [1.0, 0.0] COLLECTION 'filtered'")
20846 .unwrap();
20847 router
20848 .execute_parsed("EMBED STORE 'f2' [0.9, 0.1] COLLECTION 'filtered'")
20849 .unwrap();
20850 let result = router
20851 .execute_parsed("SIMILAR [1.0, 0.0] TOP 5 COLLECTION 'filtered' WHERE key = 'f1'");
20852 let _ = result;
20854 }
20855
20856 #[test]
20859 fn test_checkpoint_and_rollback() {
20860 let dir = tempfile::tempdir().unwrap();
20861 let mut router = QueryRouter::new();
20862 router.set_checkpoint_dir(dir.path().to_path_buf());
20863 router.init_checkpoint().unwrap();
20864 router
20865 .execute_parsed("CREATE TABLE ckpt_t (id INT)")
20866 .unwrap();
20867 router
20868 .execute_parsed("INSERT INTO ckpt_t VALUES (1)")
20869 .unwrap();
20870
20871 let result = router.execute_parsed("CHECKPOINT").unwrap();
20872 assert!(matches!(result, QueryResult::Value(_)));
20873
20874 let result = router.execute_parsed("CHECKPOINTS").unwrap();
20875 assert!(matches!(result, QueryResult::CheckpointList(_)));
20876 }
20877
20878 #[test]
20881 fn test_node_get_datetime_property() {
20882 let router = QueryRouter::new();
20883 let id = router
20884 .graph
20885 .create_node("ts_node", {
20886 let mut props = HashMap::new();
20887 props.insert(
20888 "created".to_string(),
20889 PropertyValue::DateTime(1_700_000_000),
20890 );
20891 props
20892 })
20893 .unwrap();
20894 let result = router.execute_parsed(&format!("NODE GET {id}")).unwrap();
20895 let nodes = unwrap_qr_nodes(result);
20896 assert!(nodes[0].properties.get("created").is_some());
20897 }
20898
20899 #[test]
20900 fn test_node_get_list_property() {
20901 let router = QueryRouter::new();
20902 let id = router
20903 .graph
20904 .create_node("list_node", {
20905 let mut props = HashMap::new();
20906 props.insert(
20907 "tags".to_string(),
20908 PropertyValue::List(vec![
20909 PropertyValue::String("a".to_string()),
20910 PropertyValue::String("b".to_string()),
20911 ]),
20912 );
20913 props
20914 })
20915 .unwrap();
20916 let result = router.execute_parsed(&format!("NODE GET {id}")).unwrap();
20917 let nodes = unwrap_qr_nodes(result);
20918 assert!(nodes[0].properties.get("tags").unwrap().contains("["));
20919 }
20920
20921 #[test]
20922 fn test_node_get_map_property() {
20923 let router = QueryRouter::new();
20924 let id = router
20925 .graph
20926 .create_node("map_node", {
20927 let mut props = HashMap::new();
20928 let mut inner = HashMap::new();
20929 inner.insert("x".to_string(), PropertyValue::Int(1));
20930 props.insert("meta".to_string(), PropertyValue::Map(inner));
20931 props
20932 })
20933 .unwrap();
20934 let result = router.execute_parsed(&format!("NODE GET {id}")).unwrap();
20935 let nodes = unwrap_qr_nodes(result);
20936 assert!(nodes[0].properties.get("meta").unwrap().contains("{"));
20937 }
20938
20939 #[test]
20940 fn test_node_get_bytes_property() {
20941 let router = QueryRouter::new();
20942 let id = router
20943 .graph
20944 .create_node("bytes_node", {
20945 let mut props = HashMap::new();
20946 props.insert("data".to_string(), PropertyValue::Bytes(vec![1, 2, 3]));
20947 props
20948 })
20949 .unwrap();
20950 let result = router.execute_parsed(&format!("NODE GET {id}")).unwrap();
20951 let nodes = unwrap_qr_nodes(result);
20952 assert!(nodes[0].properties.get("data").unwrap().contains("bytes"));
20953 }
20954
20955 #[test]
20956 fn test_node_get_point_property() {
20957 let router = QueryRouter::new();
20958 let id = router
20959 .graph
20960 .create_node("point_node", {
20961 let mut props = HashMap::new();
20962 props.insert(
20963 "location".to_string(),
20964 PropertyValue::Point {
20965 lat: 40.7128,
20966 lon: -74.006,
20967 },
20968 );
20969 props
20970 })
20971 .unwrap();
20972 let result = router.execute_parsed(&format!("NODE GET {id}")).unwrap();
20973 let nodes = unwrap_qr_nodes(result);
20974 assert!(nodes[0]
20975 .properties
20976 .get("location")
20977 .unwrap()
20978 .contains("POINT"));
20979 }
20980
20981 #[test]
20984 fn test_join_where_ge_cov() {
20985 let router = QueryRouter::new();
20986 setup_join_tables(&router);
20987
20988 let stmt = parser::parse(
20989 "SELECT * FROM orders JOIN users ON orders.user_id = users.id WHERE amount >= 150",
20990 )
20991 .unwrap();
20992 let result = router.execute_statement(&stmt).unwrap();
20993 let rows = unwrap_qr_rows(result);
20994 assert_eq!(rows.len(), 2); }
20996
20997 #[test]
20998 fn test_join_where_le_cov() {
20999 let router = QueryRouter::new();
21000 setup_join_tables(&router);
21001
21002 let stmt = parser::parse(
21003 "SELECT * FROM orders JOIN users ON orders.user_id = users.id WHERE amount <= 100",
21004 )
21005 .unwrap();
21006 let result = router.execute_statement(&stmt).unwrap();
21007 let rows = unwrap_qr_rows(result);
21008 assert_eq!(rows.len(), 1); }
21010
21011 #[test]
21012 fn test_join_where_gt_cov() {
21013 let router = QueryRouter::new();
21014 setup_join_tables(&router);
21015
21016 let stmt = parser::parse(
21017 "SELECT * FROM orders JOIN users ON orders.user_id = users.id WHERE amount > 150",
21018 )
21019 .unwrap();
21020 let result = router.execute_statement(&stmt).unwrap();
21021 let rows = unwrap_qr_rows(result);
21022 assert_eq!(rows.len(), 1); }
21024
21025 #[test]
21028 fn test_cypher_delete_basic() {
21029 let router = QueryRouter::new();
21030 router
21032 .execute("NODE CREATE person { name: 'ToDelete' }")
21033 .unwrap();
21034 let result = router.execute_parsed("DELETE (n:person)");
21035 let _ = result;
21036 }
21037
21038 #[test]
21039 fn test_cypher_merge_basic() {
21040 let router = QueryRouter::new();
21041 let result = router.execute_parsed("MERGE (n:person {name: 'Charlie'})");
21042 let _ = result;
21043 }
21044
21045 #[test]
21048 fn test_spatial_insert_and_within_radius() {
21049 let router = QueryRouter::new();
21050 router
21052 .execute("SPATIAL INSERT 'a' BOUNDS 0.0 0.0 1.0 1.0")
21053 .unwrap();
21054 router
21055 .execute("SPATIAL INSERT 'b' BOUNDS 5.0 5.0 1.0 1.0")
21056 .unwrap();
21057 router
21058 .execute("SPATIAL INSERT 'c' BOUNDS 100.0 100.0 1.0 1.0")
21059 .unwrap();
21060
21061 let result = router
21063 .execute("SPATIAL WITHIN 3.0 3.0 RADIUS 10.0")
21064 .unwrap();
21065 match result {
21066 QueryResult::Spatial(items) => {
21067 assert_eq!(items.len(), 2);
21068 let keys: Vec<&str> = items.iter().map(|r| r.key.as_str()).collect();
21070 assert!(keys.contains(&"a"));
21071 assert!(keys.contains(&"b"));
21072 for item in &items {
21074 assert!(item.distance >= 0.0);
21075 }
21076 },
21077 other => panic!("Expected Spatial result, got: {other:?}"),
21078 }
21079 }
21080
21081 #[test]
21082 fn test_spatial_within_radius_no_results() {
21083 let router = QueryRouter::new();
21084 router
21085 .execute("SPATIAL INSERT 'far' BOUNDS 100.0 100.0 1.0 1.0")
21086 .unwrap();
21087 let result = router.execute("SPATIAL WITHIN 0.0 0.0 RADIUS 1.0").unwrap();
21088 match result {
21089 QueryResult::Spatial(items) => assert!(items.is_empty()),
21090 other => panic!("Expected Spatial result, got: {other:?}"),
21091 }
21092 }
21093
21094 #[test]
21095 fn test_spatial_within_radius_with_limit() {
21096 let router = QueryRouter::new();
21097 for i in 0..10 {
21098 let x = f64::from(i);
21099 router
21100 .execute(&format!("SPATIAL INSERT 'item{i}' BOUNDS {x} 0.0 1.0 1.0"))
21101 .unwrap();
21102 }
21103 let result = router
21104 .execute("SPATIAL WITHIN 5.0 0.0 RADIUS 100.0 LIMIT 3")
21105 .unwrap();
21106 match result {
21107 QueryResult::Spatial(items) => assert_eq!(items.len(), 3),
21108 other => panic!("Expected Spatial result, got: {other:?}"),
21109 }
21110 }
21111
21112 #[test]
21113 fn test_spatial_delete() {
21114 let router = QueryRouter::new();
21115 router
21116 .execute("SPATIAL INSERT 'del_me' BOUNDS 1.0 2.0 3.0 4.0")
21117 .unwrap();
21118 let result = router.execute("SPATIAL COUNT").unwrap();
21120 assert!(matches!(result, QueryResult::Count(1)));
21121
21122 router
21124 .execute("SPATIAL DELETE 'del_me' BOUNDS 1.0 2.0 3.0 4.0")
21125 .unwrap();
21126 let result = router.execute("SPATIAL COUNT").unwrap();
21127 assert!(matches!(result, QueryResult::Count(0)));
21128 }
21129
21130 #[test]
21131 fn test_spatial_count() {
21132 let router = QueryRouter::new();
21133 let result = router.execute("SPATIAL COUNT").unwrap();
21134 assert!(matches!(result, QueryResult::Count(0)));
21135
21136 router
21137 .execute("SPATIAL INSERT 'x' BOUNDS 0.0 0.0 1.0 1.0")
21138 .unwrap();
21139 router
21140 .execute("SPATIAL INSERT 'y' BOUNDS 5.0 5.0 1.0 1.0")
21141 .unwrap();
21142 let result = router.execute("SPATIAL COUNT").unwrap();
21143 assert!(matches!(result, QueryResult::Count(2)));
21144 }
21145
21146 #[test]
21147 fn test_spatial_invalid_radius() {
21148 let router = QueryRouter::new();
21149 let result = router.execute("SPATIAL WITHIN 0.0 0.0 RADIUS -1.0");
21150 assert!(result.is_err());
21151 }
21152
21153 #[test]
21154 fn test_spatial_invalid_bounds() {
21155 let router = QueryRouter::new();
21156 let result = router.execute("SPATIAL INSERT 'bad' BOUNDS 0.0 0.0 -1.0 1.0");
21158 assert!(result.is_err());
21159 }
21160
21161 #[test]
21162 fn test_spatial_zero_radius() {
21163 let router = QueryRouter::new();
21164 router
21165 .execute("SPATIAL INSERT 'origin' BOUNDS 0.0 0.0 1.0 1.0")
21166 .unwrap();
21167 let result = router.execute("SPATIAL WITHIN 0.5 0.5 RADIUS 0.0").unwrap();
21169 match result {
21170 QueryResult::Spatial(items) => {
21171 assert_eq!(items.len(), 1);
21172 assert_eq!(items[0].key, "origin");
21173 },
21174 other => panic!("Expected Spatial result, got: {other:?}"),
21175 }
21176 }
21177
21178 #[test]
21179 fn test_spatial_delete_nonexistent() {
21180 let router = QueryRouter::new();
21181 let result = router.execute("SPATIAL DELETE 'none' BOUNDS 0.0 0.0 1.0 1.0");
21183 assert!(result.is_err());
21184 }
21185
21186 #[test]
21187 fn test_spatial_end_to_end_parsed() {
21188 let router = QueryRouter::new();
21189 router
21191 .execute_parsed("SPATIAL INSERT 'pt1' BOUNDS 1.0 1.0 2.0 2.0")
21192 .unwrap();
21193 router
21194 .execute_parsed("SPATIAL INSERT 'pt2' BOUNDS 3.0 3.0 1.0 1.0")
21195 .unwrap();
21196 let result = router.execute_parsed("SPATIAL COUNT").unwrap();
21197 assert!(matches!(result, QueryResult::Count(2)));
21198 let result = router
21199 .execute_parsed("SPATIAL WITHIN 2.0 2.0 RADIUS 5.0")
21200 .unwrap();
21201 match result {
21202 QueryResult::Spatial(items) => assert_eq!(items.len(), 2),
21203 other => panic!("Expected Spatial result, got: {other:?}"),
21204 }
21205 }
21206
21207 #[test]
21208 fn test_spatial_nearest_basic() {
21209 let router = QueryRouter::new();
21210 router
21211 .execute("SPATIAL INSERT 'a' BOUNDS 10.0 10.0 5.0 5.0")
21212 .unwrap();
21213 router
21214 .execute("SPATIAL INSERT 'b' BOUNDS 100.0 100.0 5.0 5.0")
21215 .unwrap();
21216 let result = router.execute("SPATIAL NEAREST 12 12").unwrap();
21218 match result {
21219 QueryResult::Spatial(items) => {
21220 assert_eq!(items.len(), 1);
21221 assert_eq!(items[0].key, "a");
21222 },
21223 other => panic!("Expected Spatial result, got: {other:?}"),
21224 }
21225 }
21226
21227 #[test]
21228 fn test_spatial_nearest_with_limit() {
21229 let router = QueryRouter::new();
21230 router
21231 .execute("SPATIAL INSERT 'p1' BOUNDS 0.0 0.0 2.0 2.0")
21232 .unwrap();
21233 router
21234 .execute("SPATIAL INSERT 'p2' BOUNDS 10.0 10.0 2.0 2.0")
21235 .unwrap();
21236 router
21237 .execute("SPATIAL INSERT 'p3' BOUNDS 20.0 20.0 2.0 2.0")
21238 .unwrap();
21239 let result = router.execute("SPATIAL NEAREST 0.0 0.0 LIMIT 2").unwrap();
21241 match result {
21242 QueryResult::Spatial(items) => {
21243 assert_eq!(items.len(), 2);
21244 assert_eq!(items[0].key, "p1");
21246 assert_eq!(items[1].key, "p2");
21247 },
21248 other => panic!("Expected Spatial result, got: {other:?}"),
21249 }
21250 }
21251
21252 #[test]
21253 fn test_spatial_nearest_prefers_small_centroid() {
21254 let router = QueryRouter::new();
21255 router
21257 .execute("SPATIAL INSERT 'big' BOUNDS 0.0 0.0 100.0 100.0")
21258 .unwrap();
21259 router
21261 .execute("SPATIAL INSERT 'small' BOUNDS 4.0 4.0 4.0 4.0")
21262 .unwrap();
21263 let result = router.execute("SPATIAL NEAREST 5 5 LIMIT 2").unwrap();
21265 match result {
21266 QueryResult::Spatial(items) => {
21267 assert_eq!(items.len(), 2);
21268 assert_eq!(items[0].key, "small");
21269 assert_eq!(items[1].key, "big");
21270 },
21271 other => panic!("Expected Spatial result, got: {other:?}"),
21272 }
21273 }
21274
21275 #[test]
21280 fn test_execute_parser_path_select() {
21281 let router = QueryRouter::new();
21283 router
21284 .execute("CREATE TABLE parser_sel (id INT, name TEXT)")
21285 .unwrap();
21286 router
21287 .execute("INSERT INTO parser_sel (id, name) VALUES (1, 'alice')")
21288 .unwrap();
21289 let result = router.execute("SELECT * FROM parser_sel").unwrap();
21290 match result {
21291 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
21292 other => panic!("Expected Rows, got: {other:?}"),
21293 }
21294 }
21295
21296 #[test]
21297 fn test_execute_parser_path_insert() {
21298 let router = QueryRouter::new();
21299 router.execute("CREATE TABLE parser_ins (id INT)").unwrap();
21300 router
21301 .execute("INSERT INTO parser_ins (id) VALUES (42)")
21302 .unwrap();
21303 let result = router.execute("SELECT * FROM parser_ins").unwrap();
21304 match result {
21305 QueryResult::Rows(rows) => {
21306 assert_eq!(rows.len(), 1);
21307 },
21308 other => panic!("Expected Rows, got: {other:?}"),
21309 }
21310 }
21311
21312 #[test]
21313 fn test_execute_parser_path_create_table() {
21314 let router = QueryRouter::new();
21315 router
21316 .execute("CREATE TABLE parser_ct (id INT, val FLOAT)")
21317 .unwrap();
21318 let result = router.execute("SHOW TABLES").unwrap();
21319 match result {
21320 QueryResult::TableList(tables) => {
21321 assert!(tables.contains(&"parser_ct".to_string()));
21322 },
21323 other => panic!("Expected TableList, got: {other:?}"),
21324 }
21325 }
21326
21327 #[test]
21328 fn test_execute_unknown_command_error() {
21329 let router = QueryRouter::new();
21331 let result = router.execute("FOOBAR something");
21332 assert!(result.is_err());
21333 let err = result.unwrap_err();
21334 assert!(
21335 matches!(err, RouterError::UnknownCommand(_)),
21336 "Expected UnknownCommand, got: {err:?}"
21337 );
21338 }
21339
21340 #[test]
21341 fn test_execute_empty_command_error() {
21342 let router = QueryRouter::new();
21343 let result = router.execute("");
21344 assert!(result.is_err());
21345 let err = result.unwrap_err();
21346 assert!(
21347 matches!(err, RouterError::ParseError(_)),
21348 "Expected ParseError for empty, got: {err:?}"
21349 );
21350 }
21351
21352 #[test]
21353 fn test_execute_whitespace_only_error() {
21354 let router = QueryRouter::new();
21355 let result = router.execute(" \t ");
21356 assert!(result.is_err());
21357 }
21358
21359 #[test]
21364 fn test_is_cacheable_statement_select() {
21365 let stmt = parser::parse("SELECT * FROM t").unwrap();
21366 assert!(QueryRouter::is_cacheable_statement(&stmt));
21367 }
21368
21369 #[test]
21370 fn test_is_cacheable_statement_similar() {
21371 let stmt = parser::parse("SIMILAR [1.0, 2.0, 3.0] LIMIT 5").unwrap();
21372 assert!(QueryRouter::is_cacheable_statement(&stmt));
21373 }
21374
21375 #[test]
21376 fn test_is_cacheable_statement_neighbors() {
21377 let stmt = parser::parse("NEIGHBORS 1").unwrap();
21378 assert!(QueryRouter::is_cacheable_statement(&stmt));
21379 }
21380
21381 #[test]
21382 fn test_is_cacheable_statement_path() {
21383 let stmt = parser::parse("PATH 1 -> 5").unwrap();
21384 assert!(QueryRouter::is_cacheable_statement(&stmt));
21385 }
21386
21387 #[test]
21388 fn test_is_cacheable_statement_insert_not_cacheable() {
21389 let stmt = parser::parse("INSERT INTO t (x) VALUES (1)").unwrap();
21390 assert!(!QueryRouter::is_cacheable_statement(&stmt));
21391 }
21392
21393 #[test]
21394 fn test_is_cacheable_statement_create_table_not_cacheable() {
21395 let stmt = parser::parse("CREATE TABLE t (id INT)").unwrap();
21396 assert!(!QueryRouter::is_cacheable_statement(&stmt));
21397 }
21398
21399 #[test]
21400 fn test_is_cacheable_statement_node_create_not_cacheable() {
21401 let stmt = parser::parse("NODE CREATE person").unwrap();
21402 assert!(!QueryRouter::is_cacheable_statement(&stmt));
21403 }
21404
21405 #[test]
21406 fn test_is_cacheable_statement_embed_store_not_cacheable() {
21407 let stmt = parser::parse("EMBED STORE 'k' [1.0, 2.0]").unwrap();
21408 assert!(!QueryRouter::is_cacheable_statement(&stmt));
21409 }
21410
21411 #[test]
21416 fn test_execute_cache_integration_select() {
21417 let mut router = QueryRouter::new();
21420 router.init_cache();
21421 router.execute("CREATE TABLE cache_hit (id INT)").unwrap();
21422 router
21423 .execute("INSERT INTO cache_hit (id) VALUES (1)")
21424 .unwrap();
21425
21426 let r1 = router.execute("SELECT * FROM cache_hit").unwrap();
21428 assert!(matches!(r1, QueryResult::Rows(_)));
21429
21430 let r2 = router.execute("SELECT * FROM cache_hit").unwrap();
21432 assert!(matches!(r2, QueryResult::Rows(_)));
21433
21434 assert!(router.cache.is_some());
21436 }
21437
21438 #[test]
21439 fn test_execute_write_invalidates_cache() {
21440 let mut router = QueryRouter::new();
21442 router.init_cache();
21443 router.execute("CREATE TABLE cache_inv (id INT)").unwrap();
21444
21445 router
21447 .execute("INSERT INTO cache_inv (id) VALUES (1)")
21448 .unwrap();
21449 let _ = router.execute("SELECT * FROM cache_inv").unwrap();
21450
21451 router
21453 .execute("INSERT INTO cache_inv (id) VALUES (2)")
21454 .unwrap();
21455
21456 let result = router.execute("SELECT * FROM cache_inv").unwrap();
21458 match result {
21459 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
21460 other => panic!("Expected Rows, got: {other:?}"),
21461 }
21462 }
21463
21464 #[test]
21469 fn test_execute_drop_table_parser_path() {
21470 let router = QueryRouter::new();
21471 router
21472 .execute("CREATE TABLE drop_ep (id INT, val TEXT)")
21473 .unwrap();
21474 router
21475 .execute("INSERT INTO drop_ep (id, val) VALUES (1, 'a')")
21476 .unwrap();
21477 router.execute("DROP TABLE drop_ep").unwrap();
21479 let result = router.execute("SHOW TABLES").unwrap();
21481 match result {
21482 QueryResult::TableList(tables) => {
21483 assert!(
21484 !tables.contains(&"drop_ep".to_string()),
21485 "Table should have been dropped"
21486 );
21487 },
21488 other => panic!("Expected TableList, got: {other:?}"),
21489 }
21490 }
21491
21492 #[test]
21493 fn test_execute_drop_index_parser_path() {
21494 let router = QueryRouter::new();
21495 router
21496 .execute("CREATE TABLE drop_idx_ep (id INT, name TEXT)")
21497 .unwrap();
21498 router
21499 .execute("CREATE INDEX idx_drop_ep ON drop_idx_ep (name)")
21500 .unwrap();
21501 router.execute("DROP INDEX ON drop_idx_ep (name)").unwrap();
21503 let result = router.execute("DROP INDEX ON drop_idx_ep (name)");
21505 assert!(result.is_err(), "Dropping non-existent index should fail");
21506 }
21507
21508 #[test]
21509 fn test_execute_drop_index_if_exists_parser_path() {
21510 let router = QueryRouter::new();
21511 router
21512 .execute("CREATE TABLE drop_idx_ie (id INT, name TEXT)")
21513 .unwrap();
21514 let result = router
21516 .execute("DROP INDEX IF EXISTS ON drop_idx_ie (name)")
21517 .unwrap();
21518 assert!(matches!(result, QueryResult::Empty));
21519 }
21520
21521 #[test]
21526 fn test_embed_store_negative_values() {
21527 let router = QueryRouter::new();
21528 router
21530 .execute("EMBED STORE 'neg_vec' [-1.0, 2.5, -3.0]")
21531 .unwrap();
21532 let result = router.execute("EMBED GET 'neg_vec'").unwrap();
21534 match result {
21535 QueryResult::Value(v) => {
21536 assert!(
21537 v.contains("-1") || v.contains("neg_vec"),
21538 "Expected value containing embedding info, got: {v}"
21539 );
21540 },
21541 QueryResult::Similar(items) => {
21542 assert!(!items.is_empty());
21543 },
21544 other => panic!("Expected Value or Similar, got: {other:?}"),
21545 }
21546 }
21547
21548 #[test]
21549 fn test_embed_store_mixed_negative_positive() {
21550 let router = QueryRouter::new();
21551 router
21552 .execute("EMBED STORE 'mixed' [-0.5, 0.0, 1.5, -2.0]")
21553 .unwrap();
21554 let result = router.execute("EMBED GET 'mixed'").unwrap();
21556 assert!(
21557 !matches!(result, QueryResult::Empty),
21558 "Embedding should have been stored"
21559 );
21560 }
21561
21562 #[test]
21563 fn test_embed_store_all_negative() {
21564 let router = QueryRouter::new();
21565 router
21566 .execute("EMBED STORE 'all_neg' [-1.0, -2.0, -3.0]")
21567 .unwrap();
21568 let result = router.execute("EMBED GET 'all_neg'").unwrap();
21569 assert!(
21570 !matches!(result, QueryResult::Empty),
21571 "Embedding should have been stored"
21572 );
21573 }
21574
21575 #[test]
21580 fn test_eof_enforcement_trailing_garbage() {
21581 let router = QueryRouter::new();
21582 router.execute("CREATE TABLE eof_t (id INT)").unwrap();
21583 let result = router.execute("SELECT 1 FROM eof_t SELECT");
21585 assert!(result.is_err(), "Trailing garbage should cause an error");
21586 }
21587
21588 #[test]
21589 fn test_eof_enforcement_semicolon_ok() {
21590 let router = QueryRouter::new();
21591 router.execute("CREATE TABLE eof_semi (id INT)").unwrap();
21592 let result = router.execute("SELECT * FROM eof_semi;");
21594 assert!(
21595 result.is_ok(),
21596 "Trailing semicolon should be accepted, got: {:?}",
21597 result.unwrap_err()
21598 );
21599 }
21600
21601 #[test]
21602 fn test_eof_enforcement_trailing_keyword() {
21603 let router = QueryRouter::new();
21604 router.execute("CREATE TABLE eof_kw (id INT)").unwrap();
21605 let result = router.execute("SELECT 1 DROP");
21607 assert!(result.is_err(), "Trailing keyword should cause an error");
21608 }
21609
21610 #[test]
21615 fn test_execute_node_create_via_execute() {
21616 let router = QueryRouter::new();
21617 let result = router.execute("NODE CREATE person {name: 'Alice'}");
21619 assert!(result.is_ok(), "NODE CREATE via execute() should succeed");
21620 }
21621
21622 #[test]
21623 fn test_execute_embed_store_via_execute() {
21624 let router = QueryRouter::new();
21625 let result = router.execute("EMBED STORE 'e1' [1.0, 2.0, 3.0]");
21626 assert!(result.is_ok(), "EMBED STORE via execute() should succeed");
21627 }
21628
21629 #[test]
21630 fn test_execute_show_tables_via_execute() {
21631 let router = QueryRouter::new();
21632 let result = router.execute("SHOW TABLES").unwrap();
21633 assert!(matches!(result, QueryResult::TableList(_)));
21634 }
21635
21636 #[test]
21637 fn test_execute_update_via_execute() {
21638 let router = QueryRouter::new();
21639 router
21640 .execute("CREATE TABLE upd_test (id INT, val TEXT)")
21641 .unwrap();
21642 router
21643 .execute("INSERT INTO upd_test (id, val) VALUES (1, 'old')")
21644 .unwrap();
21645 let result = router.execute("UPDATE upd_test SET val = 'new' WHERE id = 1");
21646 assert!(result.is_ok(), "UPDATE via execute() should succeed");
21647 }
21648
21649 #[test]
21650 fn test_execute_delete_via_execute() {
21651 let router = QueryRouter::new();
21652 router.execute("CREATE TABLE del_test (id INT)").unwrap();
21653 router
21654 .execute("INSERT INTO del_test (id) VALUES (1)")
21655 .unwrap();
21656 let result = router.execute("DELETE FROM del_test WHERE id = 1");
21657 assert!(result.is_ok(), "DELETE via execute() should succeed");
21658 }
21659
21660 #[test]
21665 fn test_is_write_statement_drop_table() {
21666 let stmt = parser::parse("DROP TABLE t").unwrap();
21667 assert!(QueryRouter::is_write_statement(&stmt));
21668 }
21669
21670 #[test]
21671 fn test_is_write_statement_drop_index() {
21672 let stmt = parser::parse("DROP INDEX ON t(x)").unwrap();
21673 assert!(QueryRouter::is_write_statement(&stmt));
21674 }
21675
21676 #[test]
21677 fn test_is_write_statement_node_create() {
21678 let stmt = parser::parse("NODE CREATE person").unwrap();
21679 assert!(QueryRouter::is_write_statement(&stmt));
21680 }
21681
21682 #[test]
21683 fn test_is_write_statement_embed_store() {
21684 let stmt = parser::parse("EMBED STORE 'k' [1.0, 2.0]").unwrap();
21685 assert!(QueryRouter::is_write_statement(&stmt));
21686 }
21687
21688 #[test]
21689 fn test_is_write_statement_select_is_read() {
21690 let stmt = parser::parse("SELECT * FROM t").unwrap();
21691 assert!(!QueryRouter::is_write_statement(&stmt));
21692 }
21693
21694 #[test]
21695 fn test_is_write_statement_neighbors_is_read() {
21696 let stmt = parser::parse("NEIGHBORS 1").unwrap();
21697 assert!(!QueryRouter::is_write_statement(&stmt));
21698 }
21699
21700 #[test]
21701 fn test_is_write_statement_similar_is_read() {
21702 let stmt = parser::parse("SIMILAR [1.0, 2.0] LIMIT 3").unwrap();
21703 assert!(!QueryRouter::is_write_statement(&stmt));
21704 }
21705
21706 #[test]
21707 fn test_is_write_statement_node_get_is_read() {
21708 let stmt = parser::parse("NODE GET 1").unwrap();
21709 assert!(!QueryRouter::is_write_statement(&stmt));
21710 }
21711
21712 #[test]
21713 fn test_is_write_statement_embed_get_is_read() {
21714 let stmt = parser::parse("EMBED GET 'k'").unwrap();
21715 assert!(!QueryRouter::is_write_statement(&stmt));
21716 }
21717
21718 #[test]
21723 fn test_execute_parse_error_non_legacy_keyword() {
21724 let router = QueryRouter::new();
21727 let result = router.execute("SHOW BADSUBCMD");
21728 assert!(result.is_err(), "Invalid SHOW subcommand should error");
21729 }
21730
21731 #[test]
21732 fn test_execute_unknown_single_word() {
21733 let router = QueryRouter::new();
21734 let result = router.execute("XYZZY");
21735 assert!(result.is_err());
21736 assert!(
21737 matches!(result.unwrap_err(), RouterError::UnknownCommand(_)),
21738 "Single unknown word should yield UnknownCommand"
21739 );
21740 }
21741
21742 #[test]
21747 fn test_execute_drop_table_with_data() {
21748 let router = QueryRouter::new();
21750 router
21751 .execute("CREATE TABLE drop_data (id INT, name TEXT)")
21752 .unwrap();
21753 for i in 0..10 {
21754 router
21755 .execute(&format!(
21756 "INSERT INTO drop_data (id, name) VALUES ({i}, 'row{i}')"
21757 ))
21758 .unwrap();
21759 }
21760 router.execute("DROP TABLE drop_data").unwrap();
21762 }
21763
21764 #[test]
21765 fn test_execute_drop_index_named_not_supported() {
21766 let router = QueryRouter::new();
21767 let result = router.execute("DROP INDEX myindex");
21769 assert!(result.is_err(), "Named DROP INDEX should fail");
21770 }
21771
21772 #[test]
21777 fn test_execute_node_get_via_parser() {
21778 let router = QueryRouter::new();
21779 let result = router.execute("NODE CREATE person {name: 'Bob'}").unwrap();
21781 let node_id = match result {
21782 QueryResult::Ids(ids) => ids[0],
21783 other => panic!("Expected Ids, got: {other:?}"),
21784 };
21785 let result = router.execute(&format!("NODE GET {node_id}")).unwrap();
21786 match result {
21787 QueryResult::Nodes(nodes) => {
21788 assert_eq!(nodes.len(), 1);
21789 assert_eq!(nodes[0].id, node_id);
21790 },
21791 other => panic!("Expected Nodes, got: {other:?}"),
21792 }
21793 }
21794
21795 #[test]
21796 fn test_execute_node_delete_via_parser() {
21797 let router = QueryRouter::new();
21798 let result = router.execute("NODE CREATE person {name: 'Del'}").unwrap();
21799 let node_id = match result {
21800 QueryResult::Ids(ids) => ids[0],
21801 other => panic!("Expected Ids, got: {other:?}"),
21802 };
21803 let del_result = router.execute(&format!("NODE DELETE {node_id}")).unwrap();
21804 assert!(matches!(del_result, QueryResult::Count(1)));
21805 }
21806
21807 #[test]
21808 fn test_execute_node_list_via_parser() {
21809 let router = QueryRouter::new();
21810 router
21811 .execute("NODE CREATE animal {species: 'cat'}")
21812 .unwrap();
21813 router
21814 .execute("NODE CREATE animal {species: 'dog'}")
21815 .unwrap();
21816 let result = router.execute("NODE LIST animal").unwrap();
21817 match result {
21818 QueryResult::Nodes(nodes) => assert!(nodes.len() >= 2),
21819 other => panic!("Expected Nodes, got: {other:?}"),
21820 }
21821 }
21822
21823 #[test]
21824 fn test_execute_node_list_with_limit_via_parser() {
21825 let router = QueryRouter::new();
21826 router.execute("NODE CREATE vehicle {type: 'car'}").unwrap();
21827 router
21828 .execute("NODE CREATE vehicle {type: 'bike'}")
21829 .unwrap();
21830 router.execute("NODE CREATE vehicle {type: 'bus'}").unwrap();
21831 let result = router.execute("NODE LIST vehicle LIMIT 2").unwrap();
21832 match result {
21833 QueryResult::Nodes(nodes) => assert!(nodes.len() <= 2),
21834 other => panic!("Expected Nodes, got: {other:?}"),
21835 }
21836 }
21837
21838 #[test]
21839 fn test_execute_edge_create_via_parser() {
21840 let router = QueryRouter::new();
21841 let r1 = router.execute("NODE CREATE person {name: 'A'}").unwrap();
21842 let r2 = router.execute("NODE CREATE person {name: 'B'}").unwrap();
21843 let id1 = match r1 {
21844 QueryResult::Ids(ids) => ids[0],
21845 _ => panic!("Expected Ids"),
21846 };
21847 let id2 = match r2 {
21848 QueryResult::Ids(ids) => ids[0],
21849 _ => panic!("Expected Ids"),
21850 };
21851 let result = router
21852 .execute(&format!("EDGE CREATE {id1} -> {id2} : knows"))
21853 .unwrap();
21854 match result {
21855 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
21856 other => panic!("Expected Ids, got: {other:?}"),
21857 }
21858 }
21859
21860 #[test]
21861 fn test_execute_edge_get_via_parser() {
21862 let router = QueryRouter::new();
21863 let r1 = router.execute("NODE CREATE person {name: 'C'}").unwrap();
21864 let r2 = router.execute("NODE CREATE person {name: 'D'}").unwrap();
21865 let id1 = match r1 {
21866 QueryResult::Ids(ids) => ids[0],
21867 _ => panic!("Expected Ids"),
21868 };
21869 let id2 = match r2 {
21870 QueryResult::Ids(ids) => ids[0],
21871 _ => panic!("Expected Ids"),
21872 };
21873 let edge_result = router
21874 .execute(&format!("EDGE CREATE {id1} -> {id2} : likes"))
21875 .unwrap();
21876 let edge_id = match edge_result {
21877 QueryResult::Ids(ids) => ids[0],
21878 _ => panic!("Expected Ids"),
21879 };
21880 let result = router.execute(&format!("EDGE GET {edge_id}")).unwrap();
21881 match result {
21882 QueryResult::Edges(edges) => {
21883 assert_eq!(edges.len(), 1);
21884 assert_eq!(edges[0].id, edge_id);
21885 },
21886 other => panic!("Expected Edges, got: {other:?}"),
21887 }
21888 }
21889
21890 #[test]
21891 fn test_execute_edge_delete_via_parser() {
21892 let router = QueryRouter::new();
21893 let r1 = router.execute("NODE CREATE person {name: 'E'}").unwrap();
21894 let r2 = router.execute("NODE CREATE person {name: 'F'}").unwrap();
21895 let id1 = match r1 {
21896 QueryResult::Ids(ids) => ids[0],
21897 _ => panic!("Expected Ids"),
21898 };
21899 let id2 = match r2 {
21900 QueryResult::Ids(ids) => ids[0],
21901 _ => panic!("Expected Ids"),
21902 };
21903 let edge_result = router
21904 .execute(&format!("EDGE CREATE {id1} -> {id2} : dislikes"))
21905 .unwrap();
21906 let edge_id = match edge_result {
21907 QueryResult::Ids(ids) => ids[0],
21908 _ => panic!("Expected Ids"),
21909 };
21910 let result = router.execute(&format!("EDGE DELETE {edge_id}")).unwrap();
21911 assert!(matches!(result, QueryResult::Count(1)));
21912 }
21913
21914 #[test]
21915 fn test_execute_edge_list_via_parser() {
21916 let router = QueryRouter::new();
21917 let r1 = router.execute("NODE CREATE person").unwrap();
21918 let r2 = router.execute("NODE CREATE person").unwrap();
21919 let id1 = match r1 {
21920 QueryResult::Ids(ids) => ids[0],
21921 _ => panic!("Expected Ids"),
21922 };
21923 let id2 = match r2 {
21924 QueryResult::Ids(ids) => ids[0],
21925 _ => panic!("Expected Ids"),
21926 };
21927 router
21928 .execute(&format!("EDGE CREATE {id1} -> {id2} : follows"))
21929 .unwrap();
21930 let result = router.execute("EDGE LIST").unwrap();
21931 match result {
21932 QueryResult::Edges(edges) => assert!(!edges.is_empty()),
21933 other => panic!("Expected Edges, got: {other:?}"),
21934 }
21935 }
21936
21937 #[test]
21942 fn test_execute_select_with_order_by() {
21943 let router = QueryRouter::new();
21944 router
21945 .execute("CREATE TABLE sort_test (id INT, name TEXT)")
21946 .unwrap();
21947 router
21948 .execute("INSERT INTO sort_test (id, name) VALUES (3, 'charlie')")
21949 .unwrap();
21950 router
21951 .execute("INSERT INTO sort_test (id, name) VALUES (1, 'alice')")
21952 .unwrap();
21953 router
21954 .execute("INSERT INTO sort_test (id, name) VALUES (2, 'bob')")
21955 .unwrap();
21956 let result = router
21957 .execute("SELECT * FROM sort_test ORDER BY id ASC")
21958 .unwrap();
21959 match result {
21960 QueryResult::Rows(rows) => assert_eq!(rows.len(), 3),
21961 other => panic!("Expected Rows, got: {other:?}"),
21962 }
21963 }
21964
21965 #[test]
21966 fn test_execute_select_with_limit() {
21967 let router = QueryRouter::new();
21968 router.execute("CREATE TABLE limit_test (id INT)").unwrap();
21969 for i in 0..5 {
21970 router
21971 .execute(&format!("INSERT INTO limit_test (id) VALUES ({i})"))
21972 .unwrap();
21973 }
21974 let result = router.execute("SELECT * FROM limit_test LIMIT 2").unwrap();
21975 match result {
21976 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
21977 other => panic!("Expected Rows, got: {other:?}"),
21978 }
21979 }
21980
21981 #[test]
21982 fn test_execute_select_count_aggregate() {
21983 let router = QueryRouter::new();
21984 router
21985 .execute("CREATE TABLE agg_test (id INT, val FLOAT)")
21986 .unwrap();
21987 router
21988 .execute("INSERT INTO agg_test (id, val) VALUES (1, 10.0)")
21989 .unwrap();
21990 router
21991 .execute("INSERT INTO agg_test (id, val) VALUES (2, 20.0)")
21992 .unwrap();
21993 let result = router.execute("SELECT COUNT(*) FROM agg_test").unwrap();
21994 assert!(
21996 !matches!(result, QueryResult::Empty),
21997 "COUNT should return a result"
21998 );
21999 }
22000
22001 #[test]
22002 fn test_execute_describe_table_via_execute() {
22003 let router = QueryRouter::new();
22004 router
22005 .execute("CREATE TABLE desc_exec (id INT, name TEXT, active BOOL)")
22006 .unwrap();
22007 let result = router.execute("DESCRIBE TABLE desc_exec").unwrap();
22008 assert!(
22009 !matches!(result, QueryResult::Empty),
22010 "DESCRIBE should return column info"
22011 );
22012 }
22013
22014 #[test]
22015 fn test_execute_show_embeddings_via_execute() {
22016 let router = QueryRouter::new();
22017 router.execute("EMBED STORE 'show_e1' [1.0, 2.0]").unwrap();
22018 let result = router.execute("SHOW EMBEDDINGS").unwrap();
22019 match result {
22020 QueryResult::Value(v) => {
22021 assert!(v.contains("show_e1"), "Should list stored embedding");
22022 },
22023 other => panic!("Expected Value, got: {other:?}"),
22024 }
22025 }
22026
22027 #[test]
22028 fn test_execute_show_embeddings_with_limit() {
22029 let router = QueryRouter::new();
22030 router.execute("EMBED STORE 'lim_e1' [1.0, 2.0]").unwrap();
22031 router.execute("EMBED STORE 'lim_e2' [3.0, 4.0]").unwrap();
22032 let result = router.execute("SHOW EMBEDDINGS LIMIT 1").unwrap();
22033 assert!(matches!(result, QueryResult::Value(_)));
22034 }
22035
22036 #[test]
22037 fn test_execute_count_embeddings() {
22038 let router = QueryRouter::new();
22039 router.execute("EMBED STORE 'cnt_e1' [1.0, 2.0]").unwrap();
22040 let result = router.execute("COUNT EMBEDDINGS").unwrap();
22041 match result {
22042 QueryResult::Count(c) => assert!(c >= 1),
22043 other => panic!("Expected Count, got: {other:?}"),
22044 }
22045 }
22046
22047 #[test]
22048 fn test_execute_embed_delete_via_execute() {
22049 let router = QueryRouter::new();
22050 router.execute("EMBED STORE 'del_emb' [1.0, 2.0]").unwrap();
22051 let result = router.execute("EMBED DELETE 'del_emb'");
22052 assert!(result.is_ok(), "EMBED DELETE should succeed");
22053 }
22054
22055 #[test]
22056 fn test_execute_neighbors_via_execute() {
22057 let router = QueryRouter::new();
22058 let r1 = router.execute("NODE CREATE person {name: 'N1'}").unwrap();
22059 let r2 = router.execute("NODE CREATE person {name: 'N2'}").unwrap();
22060 let id1 = match r1 {
22061 QueryResult::Ids(ids) => ids[0],
22062 _ => panic!("Expected Ids"),
22063 };
22064 let id2 = match r2 {
22065 QueryResult::Ids(ids) => ids[0],
22066 _ => panic!("Expected Ids"),
22067 };
22068 router
22069 .execute(&format!("EDGE CREATE {id1} -> {id2} : friends"))
22070 .unwrap();
22071 let result = router
22072 .execute(&format!("NEIGHBORS {id1} OUTGOING"))
22073 .unwrap();
22074 assert!(
22076 !matches!(result, QueryResult::Empty),
22077 "NEIGHBORS should return results"
22078 );
22079 }
22080
22081 #[test]
22082 fn test_execute_path_via_execute() {
22083 let router = QueryRouter::new();
22084 let r1 = router.execute("NODE CREATE loc {name: 'X'}").unwrap();
22085 let r2 = router.execute("NODE CREATE loc {name: 'Y'}").unwrap();
22086 let id1 = match r1 {
22087 QueryResult::Ids(ids) => ids[0],
22088 _ => panic!("Expected Ids"),
22089 };
22090 let id2 = match r2 {
22091 QueryResult::Ids(ids) => ids[0],
22092 _ => panic!("Expected Ids"),
22093 };
22094 router
22095 .execute(&format!("EDGE CREATE {id1} -> {id2} : road"))
22096 .unwrap();
22097 let result = router.execute(&format!("PATH {id1} -> {id2}")).unwrap();
22098 match result {
22099 QueryResult::Path(path) => assert!(!path.is_empty()),
22100 other => panic!("Expected Path, got: {other:?}"),
22101 }
22102 }
22103
22104 #[test]
22105 fn test_execute_similar_via_execute() {
22106 let router = QueryRouter::new();
22107 router
22108 .execute("EMBED STORE 'sim1' [1.0, 0.0, 0.0]")
22109 .unwrap();
22110 router
22111 .execute("EMBED STORE 'sim2' [0.9, 0.1, 0.0]")
22112 .unwrap();
22113 let result = router.execute("SIMILAR [1.0, 0.0, 0.0] LIMIT 2").unwrap();
22114 match result {
22115 QueryResult::Similar(items) => assert!(!items.is_empty()),
22116 other => panic!("Expected Similar, got: {other:?}"),
22117 }
22118 }
22119
22120 #[test]
22125 fn test_is_write_statement_embed_delete() {
22126 let stmt = parser::parse("EMBED DELETE 'k'").unwrap();
22127 assert!(QueryRouter::is_write_statement(&stmt));
22128 }
22129
22130 #[test]
22131 fn test_is_write_statement_node_delete() {
22132 let stmt = parser::parse("NODE DELETE 1").unwrap();
22133 assert!(QueryRouter::is_write_statement(&stmt));
22134 }
22135
22136 #[test]
22137 fn test_is_write_statement_edge_create() {
22138 let stmt = parser::parse("EDGE CREATE 1 -> 2 : knows").unwrap();
22139 assert!(QueryRouter::is_write_statement(&stmt));
22140 }
22141
22142 #[test]
22143 fn test_is_write_statement_edge_delete() {
22144 let stmt = parser::parse("EDGE DELETE 1").unwrap();
22145 assert!(QueryRouter::is_write_statement(&stmt));
22146 }
22147
22148 #[test]
22149 fn test_is_write_statement_edge_list_is_read() {
22150 let stmt = parser::parse("EDGE LIST").unwrap();
22151 assert!(!QueryRouter::is_write_statement(&stmt));
22152 }
22153
22154 #[test]
22155 fn test_is_write_statement_node_list_is_read() {
22156 let stmt = parser::parse("NODE LIST").unwrap();
22157 assert!(!QueryRouter::is_write_statement(&stmt));
22158 }
22159
22160 #[test]
22161 fn test_is_write_statement_path_is_read() {
22162 let stmt = parser::parse("PATH 1 -> 2").unwrap();
22163 assert!(!QueryRouter::is_write_statement(&stmt));
22164 }
22165
22166 #[test]
22167 fn test_is_write_statement_show_tables_is_read() {
22168 let stmt = parser::parse("SHOW TABLES").unwrap();
22169 assert!(!QueryRouter::is_write_statement(&stmt));
22170 }
22171
22172 #[test]
22173 fn test_is_write_statement_describe_is_read() {
22174 let stmt = parser::parse("DESCRIBE TABLE t").unwrap();
22175 assert!(!QueryRouter::is_write_statement(&stmt));
22176 }
22177
22178 #[test]
22183 fn test_execute_select_with_where_and() {
22184 let router = QueryRouter::new();
22185 router
22186 .execute("CREATE TABLE where_and (id INT, a INT, b INT)")
22187 .unwrap();
22188 router
22189 .execute("INSERT INTO where_and (id, a, b) VALUES (1, 10, 20)")
22190 .unwrap();
22191 router
22192 .execute("INSERT INTO where_and (id, a, b) VALUES (2, 30, 40)")
22193 .unwrap();
22194 let result = router
22195 .execute("SELECT * FROM where_and WHERE a = 10 AND b = 20")
22196 .unwrap();
22197 match result {
22198 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
22199 other => panic!("Expected Rows, got: {other:?}"),
22200 }
22201 }
22202
22203 #[test]
22204 fn test_execute_select_with_where_or() {
22205 let router = QueryRouter::new();
22206 router
22207 .execute("CREATE TABLE where_or (id INT, val INT)")
22208 .unwrap();
22209 router
22210 .execute("INSERT INTO where_or (id, val) VALUES (1, 10)")
22211 .unwrap();
22212 router
22213 .execute("INSERT INTO where_or (id, val) VALUES (2, 20)")
22214 .unwrap();
22215 router
22216 .execute("INSERT INTO where_or (id, val) VALUES (3, 30)")
22217 .unwrap();
22218 let result = router
22219 .execute("SELECT * FROM where_or WHERE val = 10 OR val = 30")
22220 .unwrap();
22221 match result {
22222 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
22223 other => panic!("Expected Rows, got: {other:?}"),
22224 }
22225 }
22226
22227 #[test]
22228 fn test_execute_select_with_comparison_operators() {
22229 let router = QueryRouter::new();
22230 router
22231 .execute("CREATE TABLE cmp_test (id INT, val INT)")
22232 .unwrap();
22233 for i in 1..=5 {
22234 router
22235 .execute(&format!(
22236 "INSERT INTO cmp_test (id, val) VALUES ({i}, {})",
22237 i * 10
22238 ))
22239 .unwrap();
22240 }
22241 let result = router
22243 .execute("SELECT * FROM cmp_test WHERE val > 30")
22244 .unwrap();
22245 match result {
22246 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
22247 other => panic!("Expected Rows, got: {other:?}"),
22248 }
22249 let result = router
22251 .execute("SELECT * FROM cmp_test WHERE val <= 20")
22252 .unwrap();
22253 match result {
22254 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
22255 other => panic!("Expected Rows, got: {other:?}"),
22256 }
22257 }
22258
22259 #[test]
22260 fn test_execute_insert_multiple_rows() {
22261 let router = QueryRouter::new();
22262 router
22263 .execute("CREATE TABLE multi_ins (id INT, name TEXT)")
22264 .unwrap();
22265 router
22267 .execute("INSERT INTO multi_ins (id, name) VALUES (1, 'a'), (2, 'b'), (3, 'c')")
22268 .unwrap();
22269 let result = router.execute("SELECT * FROM multi_ins").unwrap();
22270 match result {
22271 QueryResult::Rows(rows) => assert_eq!(rows.len(), 3),
22272 other => panic!("Expected Rows, got: {other:?}"),
22273 }
22274 }
22275
22276 #[test]
22277 fn test_execute_show_vector_index() {
22278 let router = QueryRouter::new();
22279 let result = router.execute("SHOW VECTOR INDEX").unwrap();
22280 match result {
22282 QueryResult::Value(v) => assert!(v.contains("No HNSW") || v.contains("HNSW")),
22283 other => panic!("Expected Value, got: {other:?}"),
22284 }
22285 }
22286
22287 #[test]
22288 fn test_execute_embed_integer_vector() {
22289 let router = QueryRouter::new();
22291 router.execute("EMBED STORE 'int_vec' [1, 2, 3]").unwrap();
22292 let result = router.execute("EMBED GET 'int_vec'").unwrap();
22293 assert!(
22294 !matches!(result, QueryResult::Empty),
22295 "Integer vector should be stored"
22296 );
22297 }
22298
22299 #[test]
22300 fn test_execute_select_with_offset() {
22301 let router = QueryRouter::new();
22302 router.execute("CREATE TABLE offset_test (id INT)").unwrap();
22303 for i in 0..5 {
22304 router
22305 .execute(&format!("INSERT INTO offset_test (id) VALUES ({i})"))
22306 .unwrap();
22307 }
22308 let result = router
22309 .execute("SELECT * FROM offset_test LIMIT 2 OFFSET 2")
22310 .unwrap();
22311 match result {
22312 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
22313 other => panic!("Expected Rows, got: {other:?}"),
22314 }
22315 }
22316
22317 #[test]
22318 fn test_execute_eof_enforcement_garbage_after_semicolon() {
22319 let router = QueryRouter::new();
22320 let result = router.execute("SELECT 1; GARBAGE");
22322 assert!(result.is_err(), "Garbage after semicolons should fail");
22323 }
22324
22325 #[test]
22326 fn test_execute_embed_store_negative_integer() {
22327 let router = QueryRouter::new();
22329 router.execute("EMBED STORE 'neg_int' [-1, -2, 3]").unwrap();
22330 let result = router.execute("EMBED GET 'neg_int'").unwrap();
22331 assert!(
22332 !matches!(result, QueryResult::Empty),
22333 "Negative integer vector should be stored"
22334 );
22335 }
22336
22337 #[test]
22342 fn test_execute_entity_create_via_execute() {
22343 let router = QueryRouter::new();
22344 let result = router.execute("ENTITY CREATE 'ent1' {name: 'test'}");
22345 assert!(result.is_ok(), "ENTITY CREATE should succeed");
22346 }
22347
22348 #[test]
22349 fn test_execute_entity_get_with_embedding() {
22350 let router = QueryRouter::new();
22352 router
22354 .execute("EMBED STORE 'ent_emb' [1.0, 2.0, 3.0]")
22355 .unwrap();
22356 let result = router.execute("ENTITY GET 'ent_emb'");
22358 let _ = result;
22361 }
22362
22363 #[test]
22364 fn test_execute_entity_get_not_found() {
22365 let router = QueryRouter::new();
22366 let result = router.execute("ENTITY GET 'nonexistent_ent'");
22367 assert!(result.is_err(), "ENTITY GET of missing entity should fail");
22368 }
22369
22370 #[test]
22371 fn test_execute_entity_delete_via_execute() {
22372 let router = QueryRouter::new();
22373 router.execute("ENTITY CREATE 'ent_del' {x: 'y'}").unwrap();
22374 let result = router.execute("ENTITY DELETE 'ent_del'");
22375 let _ = result;
22377 }
22378
22379 #[test]
22380 fn test_execute_entity_create_with_embedding() {
22381 let router = QueryRouter::new();
22382 let result =
22383 router.execute("ENTITY CREATE 'ent_vec' {name: 'vec_test'} EMBEDDING [1.0, 2.0]");
22384 assert!(
22385 result.is_ok(),
22386 "ENTITY CREATE with embedding should succeed"
22387 );
22388 }
22389
22390 #[test]
22395 fn test_execute_embed_store_with_collection() {
22396 let router = QueryRouter::new();
22397 let result = router.execute("EMBED STORE 'coll_e1' [1.0, 2.0] INTO 'my_collection'");
22398 assert!(result.is_ok(), "EMBED STORE with collection should succeed");
22399 }
22400
22401 #[test]
22402 fn test_execute_similar_with_limit() {
22403 let router = QueryRouter::new();
22404 router.execute("EMBED STORE 'sl1' [1.0, 0.0, 0.0]").unwrap();
22405 router.execute("EMBED STORE 'sl2' [0.0, 1.0, 0.0]").unwrap();
22406 router.execute("EMBED STORE 'sl3' [0.0, 0.0, 1.0]").unwrap();
22407 let result = router.execute("SIMILAR [1.0, 0.0, 0.0] LIMIT 1").unwrap();
22408 match result {
22409 QueryResult::Similar(items) => {
22410 assert!(items.len() <= 1);
22411 },
22412 other => panic!("Expected Similar, got: {other:?}"),
22413 }
22414 }
22415
22416 #[test]
22421 fn test_execute_graph_count_nodes() {
22422 let router = QueryRouter::new();
22423 router.execute("NODE CREATE counter").unwrap();
22424 router.execute("NODE CREATE counter").unwrap();
22425 let result = router.execute("GRAPH COUNT NODES");
22427 let _ = result;
22429 }
22430
22431 #[test]
22432 fn test_execute_graph_count_edges() {
22433 let router = QueryRouter::new();
22434 let r1 = router.execute("NODE CREATE counter").unwrap();
22435 let r2 = router.execute("NODE CREATE counter").unwrap();
22436 let id1 = match r1 {
22437 QueryResult::Ids(ids) => ids[0],
22438 _ => panic!("Expected Ids"),
22439 };
22440 let id2 = match r2 {
22441 QueryResult::Ids(ids) => ids[0],
22442 _ => panic!("Expected Ids"),
22443 };
22444 router
22445 .execute(&format!("EDGE CREATE {id1} -> {id2} : counted"))
22446 .unwrap();
22447 let result = router.execute("GRAPH COUNT EDGES");
22448 let _ = result;
22449 }
22450
22451 #[test]
22456 fn test_execute_select_join() {
22457 let router = QueryRouter::new();
22458 router
22459 .execute("CREATE TABLE j_users (id INT, name TEXT)")
22460 .unwrap();
22461 router
22462 .execute("CREATE TABLE j_orders (id INT, user_id INT, amount FLOAT)")
22463 .unwrap();
22464 router
22465 .execute("INSERT INTO j_users (id, name) VALUES (1, 'alice')")
22466 .unwrap();
22467 router
22468 .execute("INSERT INTO j_users (id, name) VALUES (2, 'bob')")
22469 .unwrap();
22470 router
22471 .execute("INSERT INTO j_orders (id, user_id, amount) VALUES (1, 1, 100.0)")
22472 .unwrap();
22473 router
22474 .execute("INSERT INTO j_orders (id, user_id, amount) VALUES (2, 1, 200.0)")
22475 .unwrap();
22476 let result =
22477 router.execute("SELECT * FROM j_users JOIN j_orders ON j_users.id = j_orders.user_id");
22478 let _ = result;
22480 }
22481
22482 #[test]
22487 fn test_execute_graph_batch_create_nodes() {
22488 let router = QueryRouter::new();
22489 let result = router.execute("GRAPH BATCH CREATE NODES [{label: 'item'}, {label: 'item'}]");
22490 let _ = result;
22491 }
22492
22493 #[test]
22498 fn test_execute_similar_by_key() {
22499 let router = QueryRouter::new();
22500 router.execute("EMBED STORE 'simkey1' [1.0, 0.0]").unwrap();
22501 router.execute("EMBED STORE 'simkey2' [0.9, 0.1]").unwrap();
22502 let result = router.execute("SIMILAR 'simkey1' LIMIT 5").unwrap();
22503 match result {
22504 QueryResult::Similar(items) => assert!(!items.is_empty()),
22505 other => panic!("Expected Similar, got: {other:?}"),
22506 }
22507 }
22508
22509 #[test]
22514 fn test_execute_embed_build_index() {
22515 let router = QueryRouter::new();
22516 router
22517 .execute("EMBED STORE 'idx1' [1.0, 0.0, 0.0]")
22518 .unwrap();
22519 router
22520 .execute("EMBED STORE 'idx2' [0.0, 1.0, 0.0]")
22521 .unwrap();
22522 let result = router.execute("EMBED BUILD INDEX");
22523 let _ = result;
22524 }
22525
22526 #[test]
22531 fn test_execute_select_distinct() {
22532 let router = QueryRouter::new();
22533 router
22534 .execute("CREATE TABLE dist_test (id INT, cat TEXT)")
22535 .unwrap();
22536 router
22537 .execute("INSERT INTO dist_test (id, cat) VALUES (1, 'a')")
22538 .unwrap();
22539 router
22540 .execute("INSERT INTO dist_test (id, cat) VALUES (2, 'a')")
22541 .unwrap();
22542 router
22543 .execute("INSERT INTO dist_test (id, cat) VALUES (3, 'b')")
22544 .unwrap();
22545 let result = router.execute("SELECT DISTINCT cat FROM dist_test");
22546 let _ = result;
22547 }
22548
22549 #[test]
22554 fn test_execute_begin_commit() {
22555 let router = QueryRouter::new();
22556 let _ = router.execute("BEGIN");
22558 let _ = router.execute("COMMIT");
22559 }
22560
22561 #[test]
22566 fn test_execute_entity_connect() {
22567 let router = QueryRouter::new();
22568 router.execute("ENTITY CREATE 'ec1' {name: 'a'}").unwrap();
22569 router.execute("ENTITY CREATE 'ec2' {name: 'b'}").unwrap();
22570 let result = router.execute("ENTITY CONNECT 'ec1' -> 'ec2' : related");
22571 let _ = result;
22572 }
22573
22574 #[test]
22579 fn test_execute_node_list_all() {
22580 let router = QueryRouter::new();
22581 router.execute("NODE CREATE typeA").unwrap();
22582 router.execute("NODE CREATE typeB").unwrap();
22583 let result = router.execute("NODE LIST").unwrap();
22584 match result {
22585 QueryResult::Nodes(nodes) => assert!(nodes.len() >= 2),
22586 other => panic!("Expected Nodes, got: {other:?}"),
22587 }
22588 }
22589
22590 #[test]
22595 fn test_execute_edge_list_with_type() {
22596 let router = QueryRouter::new();
22597 let r1 = router.execute("NODE CREATE person").unwrap();
22598 let r2 = router.execute("NODE CREATE person").unwrap();
22599 let id1 = match r1 {
22600 QueryResult::Ids(ids) => ids[0],
22601 _ => panic!("Expected Ids"),
22602 };
22603 let id2 = match r2 {
22604 QueryResult::Ids(ids) => ids[0],
22605 _ => panic!("Expected Ids"),
22606 };
22607 router
22608 .execute(&format!("EDGE CREATE {id1} -> {id2} : typed_edge"))
22609 .unwrap();
22610 let result = router.execute("EDGE LIST typed_edge").unwrap();
22611 match result {
22612 QueryResult::Edges(edges) => assert!(!edges.is_empty()),
22613 other => panic!("Expected Edges, got: {other:?}"),
22614 }
22615 }
22616
22617 #[test]
22622 fn test_execute_update_no_where() {
22623 let router = QueryRouter::new();
22624 router
22625 .execute("CREATE TABLE upd_all (id INT, val TEXT)")
22626 .unwrap();
22627 router
22628 .execute("INSERT INTO upd_all (id, val) VALUES (1, 'old')")
22629 .unwrap();
22630 router
22631 .execute("INSERT INTO upd_all (id, val) VALUES (2, 'old')")
22632 .unwrap();
22633 let result = router.execute("UPDATE upd_all SET val = 'new'").unwrap();
22635 match result {
22636 QueryResult::Count(c) => assert_eq!(c, 2),
22637 other => panic!("Expected Count, got: {other:?}"),
22638 }
22639 }
22640
22641 #[test]
22642 fn test_execute_delete_all() {
22643 let router = QueryRouter::new();
22644 router.execute("CREATE TABLE del_all (id INT)").unwrap();
22645 router
22646 .execute("INSERT INTO del_all (id) VALUES (1)")
22647 .unwrap();
22648 router
22649 .execute("INSERT INTO del_all (id) VALUES (2)")
22650 .unwrap();
22651 let result = router.execute("DELETE FROM del_all").unwrap();
22653 match result {
22654 QueryResult::Count(c) => assert_eq!(c, 2),
22655 other => panic!("Expected Count, got: {other:?}"),
22656 }
22657 }
22658
22659 #[test]
22664 fn test_execute_select_specific_columns() {
22665 let router = QueryRouter::new();
22666 router
22667 .execute("CREATE TABLE proj_test (id INT, name TEXT, age INT)")
22668 .unwrap();
22669 router
22670 .execute("INSERT INTO proj_test (id, name, age) VALUES (1, 'alice', 30)")
22671 .unwrap();
22672 let result = router.execute("SELECT name, age FROM proj_test").unwrap();
22673 match result {
22674 QueryResult::Rows(rows) => {
22675 assert_eq!(rows.len(), 1);
22676 },
22677 other => panic!("Expected Rows, got: {other:?}"),
22678 }
22679 }
22680
22681 #[test]
22682 fn test_execute_select_where_not_equal() {
22683 let router = QueryRouter::new();
22684 router
22685 .execute("CREATE TABLE neq_test (id INT, val TEXT)")
22686 .unwrap();
22687 router
22688 .execute("INSERT INTO neq_test (id, val) VALUES (1, 'a')")
22689 .unwrap();
22690 router
22691 .execute("INSERT INTO neq_test (id, val) VALUES (2, 'b')")
22692 .unwrap();
22693 let result = router
22694 .execute("SELECT * FROM neq_test WHERE val != 'a'")
22695 .unwrap();
22696 match result {
22697 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
22698 other => panic!("Expected Rows, got: {other:?}"),
22699 }
22700 }
22701
22702 #[test]
22703 fn test_execute_select_where_between() {
22704 let router = QueryRouter::new();
22705 router
22706 .execute("CREATE TABLE betw_test (id INT, val INT)")
22707 .unwrap();
22708 for i in 1..=10 {
22709 router
22710 .execute(&format!(
22711 "INSERT INTO betw_test (id, val) VALUES ({i}, {i})"
22712 ))
22713 .unwrap();
22714 }
22715 let result = router
22716 .execute("SELECT * FROM betw_test WHERE val >= 3 AND val <= 7")
22717 .unwrap();
22718 match result {
22719 QueryResult::Rows(rows) => assert_eq!(rows.len(), 5),
22720 other => panic!("Expected Rows, got: {other:?}"),
22721 }
22722 }
22723
22724 #[test]
22727 fn test_parser_first_empty_command_error() {
22728 let router = QueryRouter::new();
22729 assert!(router.execute("").is_err());
22730 assert!(router.execute(" ").is_err());
22731 }
22732
22733 #[test]
22734 fn test_parser_first_unknown_command_error() {
22735 let router = QueryRouter::new();
22736 let err = router.execute("FROBNICATE something").unwrap_err();
22737 assert!(
22738 matches!(err, RouterError::UnknownCommand(_))
22739 || matches!(err, RouterError::ParseError(_))
22740 );
22741 }
22742
22743 #[test]
22744 fn test_parser_first_create_table_and_insert() {
22745 let router = QueryRouter::new();
22746 router.execute("CREATE TABLE pf (x INT, y TEXT)").unwrap();
22748 router
22749 .execute("INSERT INTO pf (x, y) VALUES (1, 'hello')")
22750 .unwrap();
22751 router
22752 .execute("INSERT INTO pf (x, y) VALUES (2, 'world')")
22753 .unwrap();
22754 let result = router.execute("SELECT * FROM pf").unwrap();
22755 match result {
22756 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
22757 other => panic!("Expected Rows, got: {other:?}"),
22758 }
22759 }
22760
22761 #[test]
22762 fn test_parser_first_update() {
22763 let router = QueryRouter::new();
22764 router
22765 .execute("CREATE TABLE pfu (id INT, val TEXT)")
22766 .unwrap();
22767 router
22768 .execute("INSERT INTO pfu (id, val) VALUES (1, 'old')")
22769 .unwrap();
22770 router
22771 .execute("UPDATE pfu SET val = 'new' WHERE id = 1")
22772 .unwrap();
22773 let result = router
22774 .execute("SELECT * FROM pfu WHERE val = 'new'")
22775 .unwrap();
22776 match result {
22777 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
22778 other => panic!("Expected Rows, got: {other:?}"),
22779 }
22780 }
22781
22782 #[test]
22783 fn test_parser_first_delete_without_from() {
22784 let router = QueryRouter::new();
22785 router.execute("CREATE TABLE pfd (id INT)").unwrap();
22786 router.execute("INSERT INTO pfd (id) VALUES (1)").unwrap();
22787 router.execute("INSERT INTO pfd (id) VALUES (2)").unwrap();
22788 router.execute("DELETE pfd WHERE id = 1").unwrap();
22790 let result = router.execute("SELECT * FROM pfd").unwrap();
22791 match result {
22792 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
22793 other => panic!("Expected Rows, got: {other:?}"),
22794 }
22795 }
22796
22797 #[test]
22798 fn test_parser_first_delete_with_from() {
22799 let router = QueryRouter::new();
22800 router.execute("CREATE TABLE pfd2 (id INT)").unwrap();
22801 router.execute("INSERT INTO pfd2 (id) VALUES (1)").unwrap();
22802 router.execute("DELETE FROM pfd2 WHERE id = 1").unwrap();
22803 let result = router.execute("SELECT * FROM pfd2").unwrap();
22804 match result {
22805 QueryResult::Rows(rows) => assert!(rows.is_empty()),
22806 other => panic!("Expected Rows, got: {other:?}"),
22807 }
22808 }
22809
22810 #[test]
22811 fn test_parser_first_node_create_with_brace_props() {
22812 let router = QueryRouter::new();
22813 let result = router
22814 .execute("NODE CREATE person { name: 'Alice', age: 30 }")
22815 .unwrap();
22816 match result {
22817 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
22818 other => panic!("Expected Ids, got: {other:?}"),
22819 }
22820 }
22821
22822 #[test]
22823 fn test_parser_first_edge_create_default_label() {
22824 let router = QueryRouter::new();
22825 let n1 = match router.execute("NODE CREATE person { name: 'A' }").unwrap() {
22826 QueryResult::Ids(ids) => ids[0],
22827 other => panic!("Expected Ids, got: {other:?}"),
22828 };
22829 let n2 = match router.execute("NODE CREATE person { name: 'B' }").unwrap() {
22830 QueryResult::Ids(ids) => ids[0],
22831 other => panic!("Expected Ids, got: {other:?}"),
22832 };
22833 let result = router
22835 .execute(&format!("EDGE CREATE {n1} -> {n2}"))
22836 .unwrap();
22837 match result {
22838 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
22839 other => panic!("Expected Ids, got: {other:?}"),
22840 }
22841 }
22842
22843 #[test]
22844 fn test_parser_first_edge_create_explicit_label() {
22845 let router = QueryRouter::new();
22846 let n1 = match router.execute("NODE CREATE person { name: 'X' }").unwrap() {
22847 QueryResult::Ids(ids) => ids[0],
22848 other => panic!("Expected Ids, got: {other:?}"),
22849 };
22850 let n2 = match router.execute("NODE CREATE person { name: 'Y' }").unwrap() {
22851 QueryResult::Ids(ids) => ids[0],
22852 other => panic!("Expected Ids, got: {other:?}"),
22853 };
22854 let result = router
22855 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows"))
22856 .unwrap();
22857 match result {
22858 QueryResult::Ids(ids) => assert_eq!(ids.len(), 1),
22859 other => panic!("Expected Ids, got: {other:?}"),
22860 }
22861 }
22862
22863 #[test]
22864 fn test_parser_first_neighbors_default_direction() {
22865 let router = QueryRouter::new();
22866 let n1 = match router.execute("NODE CREATE user { name: 'Hub' }").unwrap() {
22867 QueryResult::Ids(ids) => ids[0],
22868 other => panic!("Expected Ids, got: {other:?}"),
22869 };
22870 let n2 = match router
22871 .execute("NODE CREATE user { name: 'Spoke' }")
22872 .unwrap()
22873 {
22874 QueryResult::Ids(ids) => ids[0],
22875 other => panic!("Expected Ids, got: {other:?}"),
22876 };
22877 router
22878 .execute(&format!("EDGE CREATE {n1} -> {n2} : friend"))
22879 .unwrap();
22880 let result = router.execute(&format!("NEIGHBORS {n1}")).unwrap();
22882 match result {
22883 QueryResult::Ids(ids) => assert!(!ids.is_empty()),
22884 other => panic!("Expected Ids, got: {other:?}"),
22885 }
22886 }
22887
22888 #[test]
22889 fn test_parser_first_embed_shorthand() {
22890 let router = QueryRouter::new();
22891 router.execute("EMBED 'short1' [1.0, 0.0, 0.0]").unwrap();
22893 let result = router.execute("SIMILAR 'short1' LIMIT 1").unwrap();
22894 match result {
22895 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
22896 other => panic!("Expected Similar, got: {other:?}"),
22897 }
22898 }
22899
22900 #[test]
22901 fn test_parser_first_embed_store_with_negative_values() {
22902 let router = QueryRouter::new();
22903 router
22905 .execute("EMBED STORE 'neg1' [-0.5, 0.3, -0.8]")
22906 .unwrap();
22907 router
22908 .execute("EMBED STORE 'neg2' [0.5, -0.3, 0.8]")
22909 .unwrap();
22910 let result = router.execute("SIMILAR 'neg1' LIMIT 2").unwrap();
22911 match result {
22912 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
22913 other => panic!("Expected Similar, got: {other:?}"),
22914 }
22915 }
22916
22917 #[test]
22918 fn test_parser_first_similar_top_keyword() {
22919 let router = QueryRouter::new();
22920 router.execute("EMBED STORE 'top1' [1.0, 0.0]").unwrap();
22921 router.execute("EMBED STORE 'top2' [0.9, 0.1]").unwrap();
22922 let result = router.execute("SIMILAR 'top1' TOP 2").unwrap();
22924 match result {
22925 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
22926 other => panic!("Expected Similar, got: {other:?}"),
22927 }
22928 }
22929
22930 #[test]
22931 fn test_parser_first_similar_metric_before_limit() {
22932 let router = QueryRouter::new();
22933 router.execute("EMBED STORE 'mb1' [1.0, 0.0]").unwrap();
22934 router.execute("EMBED STORE 'mb2' [0.8, 0.2]").unwrap();
22935 let result = router.execute("SIMILAR 'mb1' COSINE LIMIT 2").unwrap();
22937 match result {
22938 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
22939 other => panic!("Expected Similar, got: {other:?}"),
22940 }
22941 }
22942
22943 #[test]
22944 fn test_parser_first_similar_limit_before_metric() {
22945 let router = QueryRouter::new();
22946 router.execute("EMBED STORE 'lm1' [1.0, 0.0]").unwrap();
22947 router.execute("EMBED STORE 'lm2' [0.8, 0.2]").unwrap();
22948 let result = router.execute("SIMILAR 'lm1' LIMIT 2 EUCLIDEAN").unwrap();
22950 match result {
22951 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
22952 other => panic!("Expected Similar, got: {other:?}"),
22953 }
22954 }
22955
22956 #[test]
22957 fn test_parser_first_find_nodes_plural_parsed() {
22958 let router = QueryRouter::new();
22959 router
22960 .execute("NODE CREATE animal { species: 'cat' }")
22961 .unwrap();
22962 let result = router.execute("FIND NODES animal");
22964 assert!(result.is_ok(), "FIND NODES failed: {result:?}");
22965 }
22966
22967 #[test]
22968 fn test_parser_first_find_edges_plural_parsed() {
22969 let router = QueryRouter::new();
22970 let n1 = match router.execute("NODE CREATE p { v: 1 }").unwrap() {
22971 QueryResult::Ids(ids) => ids[0],
22972 other => panic!("Expected Ids, got: {other:?}"),
22973 };
22974 let n2 = match router.execute("NODE CREATE p { v: 2 }").unwrap() {
22975 QueryResult::Ids(ids) => ids[0],
22976 other => panic!("Expected Ids, got: {other:?}"),
22977 };
22978 router
22979 .execute(&format!("EDGE CREATE {n1} -> {n2} : knows"))
22980 .unwrap();
22981 let result = router.execute("FIND EDGES knows");
22983 assert!(result.is_ok(), "FIND EDGES failed: {result:?}");
22984 }
22985
22986 #[test]
22987 fn test_parser_first_create_index() {
22988 let router = QueryRouter::new();
22989 router
22990 .execute("CREATE TABLE idx_t (id INT, name TEXT)")
22991 .unwrap();
22992 let result = router.execute("CREATE INDEX idx_name ON idx_t(name)");
22993 assert!(result.is_ok(), "CREATE INDEX failed: {result:?}");
22994 }
22995
22996 #[test]
22997 fn test_parser_first_keyword_column_names_insert() {
22998 let router = QueryRouter::new();
22999 router
23000 .execute("CREATE TABLE kc (id INT, status TEXT, type TEXT)")
23001 .unwrap();
23002 router
23003 .execute("INSERT INTO kc (id, status, type) VALUES (1, 'active', 'user')")
23004 .unwrap();
23005 let result = router.execute("SELECT * FROM kc").unwrap();
23006 match result {
23007 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
23008 other => panic!("Expected Rows, got: {other:?}"),
23009 }
23010 }
23011
23012 #[test]
23013 fn test_parser_first_keyword_column_names_update() {
23014 let router = QueryRouter::new();
23015 router
23016 .execute("CREATE TABLE ku (id INT, status TEXT)")
23017 .unwrap();
23018 router
23019 .execute("INSERT INTO ku (id, status) VALUES (1, 'old')")
23020 .unwrap();
23021 router
23022 .execute("UPDATE ku SET status = 'new' WHERE id = 1")
23023 .unwrap();
23024 let result = router
23025 .execute("SELECT * FROM ku WHERE status = 'new'")
23026 .unwrap();
23027 match result {
23028 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
23029 other => panic!("Expected Rows, got: {other:?}"),
23030 }
23031 }
23032
23033 #[test]
23034 fn test_parser_first_cache_invalidation_on_write() {
23035 let mut router = QueryRouter::new();
23036 router.init_cache();
23037 router.execute("CREATE TABLE ci (id INT)").unwrap();
23038 router.execute("INSERT INTO ci (id) VALUES (1)").unwrap();
23039 let r1 = router.execute("SELECT * FROM ci").unwrap();
23041 match &r1 {
23042 QueryResult::Rows(rows) => assert_eq!(rows.len(), 1),
23043 other => panic!("Expected Rows, got: {other:?}"),
23044 }
23045 router.execute("INSERT INTO ci (id) VALUES (2)").unwrap();
23047 let r2 = router.execute("SELECT * FROM ci").unwrap();
23049 match r2 {
23050 QueryResult::Rows(rows) => assert_eq!(rows.len(), 2),
23051 other => panic!("Expected Rows, got: {other:?}"),
23052 }
23053 }
23054
23055 #[test]
23056 fn test_parser_first_node_get() {
23057 let router = QueryRouter::new();
23058 let node_id = match router
23059 .execute("NODE CREATE city { name: 'Berlin' }")
23060 .unwrap()
23061 {
23062 QueryResult::Ids(ids) => ids[0],
23063 other => panic!("Expected Ids, got: {other:?}"),
23064 };
23065 let result = router.execute(&format!("NODE GET {node_id}")).unwrap();
23066 match result {
23067 QueryResult::Nodes(nodes) => {
23068 assert_eq!(nodes.len(), 1);
23069 },
23070 other => panic!("Expected Nodes, got: {other:?}"),
23071 }
23072 }
23073
23074 #[test]
23075 fn test_parser_first_node_delete() {
23076 let router = QueryRouter::new();
23077 let node_id = match router.execute("NODE CREATE temp { val: 1 }").unwrap() {
23078 QueryResult::Ids(ids) => ids[0],
23079 other => panic!("Expected Ids, got: {other:?}"),
23080 };
23081 router.execute(&format!("NODE DELETE {node_id}")).unwrap();
23082 let result = router.execute(&format!("NODE GET {node_id}"));
23083 assert!(result.is_err());
23084 }
23085
23086 #[test]
23087 fn test_parser_first_edge_delete() {
23088 let router = QueryRouter::new();
23089 let n1 = match router.execute("NODE CREATE t { v: 1 }").unwrap() {
23090 QueryResult::Ids(ids) => ids[0],
23091 other => panic!("Expected Ids, got: {other:?}"),
23092 };
23093 let n2 = match router.execute("NODE CREATE t { v: 2 }").unwrap() {
23094 QueryResult::Ids(ids) => ids[0],
23095 other => panic!("Expected Ids, got: {other:?}"),
23096 };
23097 let edge_id = match router
23098 .execute(&format!("EDGE CREATE {n1} -> {n2} : linked"))
23099 .unwrap()
23100 {
23101 QueryResult::Ids(ids) => ids[0],
23102 other => panic!("Expected Ids, got: {other:?}"),
23103 };
23104 router.execute(&format!("EDGE DELETE {edge_id}")).unwrap();
23105 }
23106
23107 #[test]
23108 fn test_parser_first_embed_get() {
23109 let router = QueryRouter::new();
23110 router
23111 .execute("EMBED STORE 'eg_key' [1.0, 2.0, 3.0]")
23112 .unwrap();
23113 let result = router.execute("EMBED GET 'eg_key'").unwrap();
23114 match result {
23115 QueryResult::Value(s) => {
23116 assert!(s.contains("1.0"));
23117 assert!(s.contains("2.0"));
23118 assert!(s.contains("3.0"));
23119 },
23120 other => panic!("Expected Value, got: {other:?}"),
23121 }
23122 }
23123
23124 #[test]
23125 fn test_parser_first_embed_delete() {
23126 let router = QueryRouter::new();
23127 router.execute("EMBED STORE 'ed_key' [1.0, 0.0]").unwrap();
23128 router.execute("EMBED DELETE 'ed_key'").unwrap();
23129 let result = router.execute("EMBED GET 'ed_key'");
23130 assert!(result.is_err());
23131 }
23132
23133 #[test]
23134 fn test_parser_first_embed_batch() {
23135 let router = QueryRouter::new();
23136 router
23137 .execute("EMBED BATCH [('b1', [1.0, 0.0]), ('b2', [0.0, 1.0])]")
23138 .unwrap();
23139 let r1 = router.execute("EMBED GET 'b1'").unwrap();
23140 match r1 {
23141 QueryResult::Value(s) => assert!(s.contains("1.0")),
23142 other => panic!("Expected Embedding, got: {other:?}"),
23143 }
23144 }
23145
23146 #[test]
23147 fn test_parser_first_graph_aggregate_on_node() {
23148 let router = QueryRouter::new();
23149 router
23150 .execute("NODE CREATE worker { salary: 50000 }")
23151 .unwrap();
23152 router
23153 .execute("NODE CREATE worker { salary: 70000 }")
23154 .unwrap();
23155 let result = router
23156 .execute("AGGREGATE NODE PROPERTY salary SUM ON worker")
23157 .unwrap();
23158 match result {
23159 QueryResult::Aggregate(AggregateResultValue::Sum(v)) => {
23160 assert!((v - 120_000.0).abs() < 0.01);
23161 },
23162 other => panic!("Expected Aggregate Sum, got: {other:?}"),
23163 }
23164 }
23165
23166 #[test]
23167 fn test_parser_first_describe_table() {
23168 let router = QueryRouter::new();
23169 router
23170 .execute("CREATE TABLE desc_t (id INT, name TEXT)")
23171 .unwrap();
23172 let result = router.execute("DESCRIBE TABLE desc_t").unwrap();
23173 match result {
23174 QueryResult::Value(s) => {
23175 assert!(s.contains("desc_t"));
23176 assert!(s.contains("id"));
23177 assert!(s.contains("name"));
23178 },
23179 other => panic!("Expected Value, got: {other:?}"),
23180 }
23181 }
23182
23183 #[test]
23184 fn test_parser_first_show_tables() {
23185 let router = QueryRouter::new();
23186 router.execute("CREATE TABLE show1 (id INT)").unwrap();
23187 let result = router.execute("SHOW TABLES").unwrap();
23188 match result {
23189 QueryResult::TableList(tables) => {
23190 assert!(tables.iter().any(|t| t == "show1"));
23191 },
23192 other => panic!("Expected TableList, got: {other:?}"),
23193 }
23194 }
23195
23196 #[test]
23197 fn test_parser_first_path_shortest() {
23198 let router = QueryRouter::new();
23199 let n1 = match router.execute("NODE CREATE city { name: 'A' }").unwrap() {
23200 QueryResult::Ids(ids) => ids[0],
23201 other => panic!("Expected Ids, got: {other:?}"),
23202 };
23203 let n2 = match router.execute("NODE CREATE city { name: 'B' }").unwrap() {
23204 QueryResult::Ids(ids) => ids[0],
23205 other => panic!("Expected Ids, got: {other:?}"),
23206 };
23207 router
23208 .execute(&format!("EDGE CREATE {n1} -> {n2} : road"))
23209 .unwrap();
23210 let result = router.execute(&format!("PATH {n1} -> {n2}")).unwrap();
23211 match result {
23212 QueryResult::Path(path) => assert!(!path.is_empty()),
23213 other => panic!("Expected Path, got: {other:?}"),
23214 }
23215 }
23216
23217 #[test]
23218 fn test_parser_first_node_list() {
23219 let router = QueryRouter::new();
23220 router
23221 .execute("NODE CREATE fruit { name: 'apple' }")
23222 .unwrap();
23223 router
23224 .execute("NODE CREATE fruit { name: 'banana' }")
23225 .unwrap();
23226 let result = router.execute("NODE LIST").unwrap();
23227 match result {
23228 QueryResult::Nodes(nodes) => assert!(nodes.len() >= 2),
23229 other => panic!("Expected Nodes, got: {other:?}"),
23230 }
23231 }
23232
23233 #[test]
23234 fn test_parser_first_edge_list() {
23235 let router = QueryRouter::new();
23236 let n1 = match router.execute("NODE CREATE thing { v: 1 }").unwrap() {
23237 QueryResult::Ids(ids) => ids[0],
23238 other => panic!("Expected Ids, got: {other:?}"),
23239 };
23240 let n2 = match router.execute("NODE CREATE thing { v: 2 }").unwrap() {
23241 QueryResult::Ids(ids) => ids[0],
23242 other => panic!("Expected Ids, got: {other:?}"),
23243 };
23244 router
23245 .execute(&format!("EDGE CREATE {n1} -> {n2} : conn"))
23246 .unwrap();
23247 let result = router.execute("EDGE LIST").unwrap();
23248 match result {
23249 QueryResult::Edges(edges) => assert!(!edges.is_empty()),
23250 other => panic!("Expected Edges, got: {other:?}"),
23251 }
23252 }
23253
23254 #[test]
23255 fn test_parser_first_embed_negative_float_vector() {
23256 let router = QueryRouter::new();
23257 router
23259 .execute("EMBED STORE 'nf1' [-1.5, 2.0, -0.5, 0.0]")
23260 .unwrap();
23261 let result = router.execute("EMBED GET 'nf1'").unwrap();
23262 match result {
23263 QueryResult::Value(s) => {
23264 assert!(s.contains("-1.5"));
23265 assert!(s.contains("-0.5"));
23266 },
23267 other => panic!("Expected Value, got: {other:?}"),
23268 }
23269 }
23270
23271 #[test]
23272 fn test_parser_first_embed_negative_integer_in_vector() {
23273 let router = QueryRouter::new();
23274 router.execute("EMBED STORE 'ni1' [-1, 2, -3, 0]").unwrap();
23275 let result = router.execute("EMBED GET 'ni1'").unwrap();
23276 match result {
23277 QueryResult::Value(s) => {
23278 assert!(s.contains("-1."));
23279 assert!(s.contains("-3."));
23280 },
23281 other => panic!("Expected Value, got: {other:?}"),
23282 }
23283 }
23284
23285 #[test]
23286 fn test_parser_first_similar_with_collection() {
23287 let router = QueryRouter::new();
23288 router
23289 .execute("EMBED STORE 'sc1' [1.0, 0.0] INTO grp")
23290 .unwrap();
23291 router
23292 .execute("EMBED STORE 'sc2' [0.9, 0.1] INTO grp")
23293 .unwrap();
23294 let result = router.execute("SIMILAR 'sc1' LIMIT 2 INTO grp").unwrap();
23295 match result {
23296 QueryResult::Similar(sims) => assert!(!sims.is_empty()),
23297 other => panic!("Expected Similar, got: {other:?}"),
23298 }
23299 }
23300
23301 #[test]
23302 fn test_parser_first_create_table_if_not_exists() {
23303 let router = QueryRouter::new();
23304 router
23305 .execute("CREATE TABLE IF NOT EXISTS ine (id INT)")
23306 .unwrap();
23307 router
23309 .execute("CREATE TABLE IF NOT EXISTS ine (id INT)")
23310 .unwrap();
23311 }
23312
23313 #[test]
23314 fn test_parser_first_drop_table() {
23315 let router = QueryRouter::new();
23316 router.execute("CREATE TABLE dt (id INT)").unwrap();
23317 router.execute("DROP TABLE dt").unwrap();
23318 let result = router.execute("SELECT * FROM dt");
23320 assert!(result.is_err());
23321 }
23322
23323 #[test]
23328 fn test_spatial_accessor_returns_spatial_index() {
23329 let router = QueryRouter::new();
23330 let spatial = router.spatial();
23331 let guard = spatial.read();
23332 assert_eq!(
23333 guard.len(),
23334 0,
23335 "New router should have an empty spatial index"
23336 );
23337 }
23338
23339 #[test]
23344 fn test_execute_empty_statement_semicolon() {
23345 let router = QueryRouter::new();
23346 let result = router.execute(";").unwrap();
23347 assert!(matches!(result, QueryResult::Empty));
23348 }
23349
23350 #[test]
23355 fn test_execute_describe_node() {
23356 let router = QueryRouter::new();
23357 router.execute("NODE CREATE person").unwrap();
23358 let result = router.execute("DESCRIBE NODE person").unwrap();
23359 match result {
23360 QueryResult::Value(s) => {
23361 assert!(s.contains("person"), "Should mention the label");
23362 assert!(s.contains("NODE LIST"), "Should reference NODE LIST");
23363 },
23364 other => panic!("Expected Value, got: {other:?}"),
23365 }
23366 }
23367
23368 #[test]
23373 fn test_execute_describe_edge() {
23374 let router = QueryRouter::new();
23375 let result = router.execute("DESCRIBE EDGE follows").unwrap();
23376 match result {
23377 QueryResult::Value(s) => {
23378 assert!(s.contains("follows"), "Should mention the edge type");
23379 assert!(s.contains("EDGE LIST"), "Should reference EDGE LIST");
23380 },
23381 other => panic!("Expected Value, got: {other:?}"),
23382 }
23383 }
23384
23385 #[test]
23390 fn test_cypher_merge_via_execute_statement() {
23391 use neumann_parser::cypher::{CypherElement, CypherMergeStmt, CypherNode, CypherPattern};
23392 use neumann_parser::{Ident, Span};
23393
23394 let router = QueryRouter::new();
23395 let stmt = Statement::new(
23396 StatementKind::CypherMerge(CypherMergeStmt {
23397 pattern: CypherPattern {
23398 variable: None,
23399 elements: vec![CypherElement::Node(CypherNode {
23400 variable: Some(Ident::new("n", Span::from_offsets(0, 1))),
23401 labels: vec![Ident::new("TestLabel", Span::from_offsets(0, 9))],
23402 properties: vec![],
23403 })],
23404 },
23405 on_create: vec![],
23406 on_match: vec![],
23407 }),
23408 Span::from_offsets(0, 1),
23409 );
23410 let result = router.execute_statement(&stmt).unwrap();
23411 assert!(matches!(result, QueryResult::Ids(_)));
23413 }
23414
23415 #[test]
23420 fn test_cypher_create_via_execute_statement() {
23421 use neumann_parser::cypher::{CypherCreateStmt, CypherElement, CypherNode, CypherPattern};
23422 use neumann_parser::{Ident, Span};
23423
23424 let router = QueryRouter::new();
23425 let stmt = Statement::new(
23426 StatementKind::CypherCreate(CypherCreateStmt {
23427 patterns: vec![CypherPattern {
23428 variable: None,
23429 elements: vec![CypherElement::Node(CypherNode {
23430 variable: Some(Ident::new("n", Span::from_offsets(0, 1))),
23431 labels: vec![Ident::new("CypherNode", Span::from_offsets(0, 10))],
23432 properties: vec![],
23433 })],
23434 }],
23435 }),
23436 Span::from_offsets(0, 1),
23437 );
23438 let result = router.execute_statement(&stmt).unwrap();
23439 assert!(matches!(result, QueryResult::Ids(_)));
23440 }
23441
23442 #[test]
23447 fn test_cypher_delete_via_execute_statement() {
23448 use neumann_parser::cypher::CypherDeleteStmt;
23449 use neumann_parser::{Ident, Span};
23450
23451 let router = QueryRouter::new();
23452 router.execute("NODE CREATE testdel").unwrap();
23454 let stmt = Statement::new(
23457 StatementKind::CypherDelete(CypherDeleteStmt {
23458 detach: false,
23459 variables: vec![Expr::new(
23460 ExprKind::Ident(Ident::new("n", Span::from_offsets(0, 1))),
23461 Span::from_offsets(0, 1),
23462 )],
23463 }),
23464 Span::from_offsets(0, 1),
23465 );
23466 let _ = router.execute_statement(&stmt);
23468 }
23469
23470 #[test]
23475 fn test_cypher_match_via_execute_statement() {
23476 use neumann_parser::cypher::{
23477 CypherElement, CypherMatchStmt, CypherNode, CypherPattern, CypherReturn,
23478 CypherReturnItem,
23479 };
23480 use neumann_parser::{Ident, Span};
23481
23482 let router = QueryRouter::new();
23483 router.execute("NODE CREATE matchlabel").unwrap();
23484 let stmt = Statement::new(
23485 StatementKind::CypherMatch(CypherMatchStmt {
23486 optional: false,
23487 patterns: vec![CypherPattern {
23488 variable: None,
23489 elements: vec![CypherElement::Node(CypherNode {
23490 variable: Some(Ident::new("n", Span::from_offsets(0, 1))),
23491 labels: vec![Ident::new("matchlabel", Span::from_offsets(0, 10))],
23492 properties: vec![],
23493 })],
23494 }],
23495 where_clause: None,
23496 return_clause: CypherReturn {
23497 distinct: false,
23498 items: vec![CypherReturnItem {
23499 expr: Expr::new(
23500 ExprKind::Ident(Ident::new("n", Span::from_offsets(0, 1))),
23501 Span::from_offsets(0, 1),
23502 ),
23503 alias: None,
23504 }],
23505 },
23506 order_by: vec![],
23507 skip: None,
23508 limit: None,
23509 }),
23510 Span::from_offsets(0, 1),
23511 );
23512 let result = router.execute_statement(&stmt);
23513 let _ = result;
23515 }
23516
23517 #[test]
23522 fn test_drop_index_invalid_syntax_via_execute_statement() {
23523 use neumann_parser::{DropIndexStmt, Span};
23524
23525 let router = QueryRouter::new();
23526 let stmt = Statement::new(
23528 StatementKind::DropIndex(DropIndexStmt {
23529 if_exists: false,
23530 name: None,
23531 table: None,
23532 column: None,
23533 }),
23534 Span::from_offsets(0, 1),
23535 );
23536 let result = router.execute_statement(&stmt);
23537 assert!(result.is_err());
23538 let err_msg = result.unwrap_err().to_string();
23539 assert!(
23540 err_msg.contains("Invalid DROP INDEX syntax"),
23541 "Error should mention invalid syntax, got: {err_msg}"
23542 );
23543 }
23544
23545 #[test]
23550 fn test_paginated_edge_list() {
23551 let router = QueryRouter::new();
23552 let n1 = router.graph.create_node("a", HashMap::new()).unwrap();
23553 let n2 = router.graph.create_node("b", HashMap::new()).unwrap();
23554 router
23555 .graph
23556 .create_edge(n1, n2, "knows", HashMap::new(), true)
23557 .unwrap();
23558 let options = PaginationOptions::new()
23559 .with_page_size(10)
23560 .with_count_total(true);
23561 let result = router.execute_paginated("EDGE LIST", options);
23562 assert!(result.is_ok());
23563 let paged = result.unwrap();
23564 assert!(paged.total_count.is_some());
23565 assert!(matches!(paged.result, QueryResult::Edges(_)));
23566 }
23567
23568 #[test]
23573 fn test_create_index_no_columns_via_execute_statement() {
23574 use neumann_parser::{CreateIndexStmt, Ident, Span};
23575
23576 let router = QueryRouter::new();
23577 router
23578 .execute("CREATE TABLE cidx (id INT, name TEXT)")
23579 .unwrap();
23580 let stmt = Statement::new(
23582 StatementKind::CreateIndex(CreateIndexStmt {
23583 unique: false,
23584 if_not_exists: false,
23585 name: Ident::new("idx_empty", Span::from_offsets(0, 9)),
23586 table: Ident::new("cidx", Span::from_offsets(0, 4)),
23587 columns: vec![],
23588 }),
23589 Span::from_offsets(0, 1),
23590 );
23591 let result = router.execute_statement(&stmt).unwrap();
23592 assert!(matches!(result, QueryResult::Empty));
23593 }
23594
23595 #[test]
23600 fn test_graph_pattern_dispatch() {
23601 use neumann_parser::{PatternSpec, Span};
23602
23603 let router = QueryRouter::new();
23604 router.execute("NODE CREATE gp_person").unwrap();
23605 let stmt = Statement::new(
23606 StatementKind::GraphPattern(GraphPatternStmt {
23607 operation: GraphPatternOp::Match {
23608 pattern: PatternSpec {
23609 nodes: vec![],
23610 edges: vec![],
23611 },
23612 limit: None,
23613 },
23614 }),
23615 Span::from_offsets(0, 1),
23616 );
23617 let result = router.execute_statement(&stmt);
23618 let _ = result;
23620 }
23621}