gqlite 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/Cargo.toml +8 -8
- data/ext/db-index/Cargo.toml +2 -2
- data/ext/gqlitedb/Cargo.toml +0 -2
- data/ext/gqlitedb/src/aggregators.rs +28 -0
- data/ext/gqlitedb/src/compiler/expression_analyser.rs +33 -0
- data/ext/gqlitedb/src/compiler/variables_manager.rs +111 -11
- data/ext/gqlitedb/src/compiler.rs +506 -48
- data/ext/gqlitedb/src/connection.rs +14 -2
- data/ext/gqlitedb/src/consts.rs +1 -1
- data/ext/gqlitedb/src/error.rs +31 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +153 -331
- data/ext/gqlitedb/src/interpreter/instructions.rs +77 -0
- data/ext/gqlitedb/src/interpreter/mod.rs +1 -0
- data/ext/gqlitedb/src/interpreter/path/bfs.rs +494 -0
- data/ext/gqlitedb/src/interpreter/path/dfs.rs +267 -0
- data/ext/gqlitedb/src/interpreter/path/group.rs +418 -0
- data/ext/gqlitedb/src/interpreter/path.rs +542 -0
- data/ext/gqlitedb/src/planner.rs +231 -6
- data/ext/gqlitedb/src/query_result.rs +19 -4
- data/ext/gqlitedb/src/store/redb.rs +1 -1
- data/ext/gqlitedb/src/tests/compiler.rs +2454 -1
- data/ext/gqlitedb/src/tests/connection/postgres.rs +22 -0
- data/ext/gqlitedb/src/tests/connection/redb.rs +24 -0
- data/ext/gqlitedb/src/tests/connection/sqlite.rs +16 -0
- data/ext/gqlitedb/src/tests/connection.rs +101 -0
- data/ext/gqlitedb/src/tests/evaluators.rs +2206 -1
- data/ext/gqlitedb/src/tests/parser.rs +1225 -0
- data/ext/gqlitedb/src/tests/planner.rs +433 -2
- data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +5 -5
- data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +3 -1
- data/ext/gqlitedb/src/tests/templates/ast.rs +294 -1
- data/ext/gqlitedb/src/tests/templates/programs.rs +318 -0
- data/ext/gqliterb/Cargo.toml +0 -1
- data/ext/gqlparser/src/oc/ast.rs +72 -3
- data/ext/gqlparser/src/oc/error.rs +18 -0
- data/ext/gqlparser/src/oc/lexer.rs +157 -2
- data/ext/gqlparser/src/oc/parser/tests.rs +735 -29
- data/ext/gqlparser/src/oc/parser.rs +1312 -182
- data/ext/gqlparser/src/oc/string.rs +172 -0
- data/ext/gqlparser/src/oc.rs +1 -0
- data/ext/graphcore/src/value/value_map.rs +2 -2
- data/ext/graphcore/src/value.rs +13 -1
- metadata +6 -2
- data/ext/gqlitedb/src/parser/gql.pest +0 -198
|
@@ -53,6 +53,8 @@ fn test_plan_match_creates_scan_and_filter()
|
|
|
53
53
|
variable: Some(node_identifier.clone()),
|
|
54
54
|
labels: LabelExpression::String("Person".into()),
|
|
55
55
|
properties: None,
|
|
56
|
+
|
|
57
|
+
predicate: None,
|
|
56
58
|
})],
|
|
57
59
|
where_expression: Some(
|
|
58
60
|
Variable {
|
|
@@ -74,7 +76,7 @@ fn test_plan_match_creates_scan_and_filter()
|
|
|
74
76
|
LogicalPlan::NodeScan { patterns, optional: true }
|
|
75
77
|
if matches!(
|
|
76
78
|
&patterns[0],
|
|
77
|
-
|
|
79
|
+
planner::PlannedPattern::Node(NodePattern {
|
|
78
80
|
labels: LabelExpression::String(label),
|
|
79
81
|
..
|
|
80
82
|
}) if label == "Person"
|
|
@@ -99,6 +101,7 @@ fn test_plan_return_creates_projection_modifier_chain()
|
|
|
99
101
|
.into(),
|
|
100
102
|
}],
|
|
101
103
|
modifiers: Modifiers {
|
|
104
|
+
distinct: false,
|
|
102
105
|
skip: Some(Value { value: 2.into() }.into()),
|
|
103
106
|
limit: Some(Value { value: 4.into() }.into()),
|
|
104
107
|
order_by: Some(OrderBy {
|
|
@@ -220,6 +223,8 @@ fn test_plan_maps_direct_statements()
|
|
|
220
223
|
variable: Some(node_identifier.clone()),
|
|
221
224
|
labels: LabelExpression::None,
|
|
222
225
|
properties: None,
|
|
226
|
+
|
|
227
|
+
predicate: None,
|
|
223
228
|
})],
|
|
224
229
|
}
|
|
225
230
|
.into(),
|
|
@@ -339,6 +344,8 @@ fn test_plan_match_with_search_emits_vector_search()
|
|
|
339
344
|
variable: Some(node_identifier.clone()),
|
|
340
345
|
labels: LabelExpression::String("Doc".into()),
|
|
341
346
|
properties: None,
|
|
347
|
+
|
|
348
|
+
predicate: None,
|
|
342
349
|
})],
|
|
343
350
|
where_expression: None,
|
|
344
351
|
optional: false,
|
|
@@ -390,6 +397,8 @@ fn test_plan_match_with_search_and_score()
|
|
|
390
397
|
variable: Some(node_identifier.clone()),
|
|
391
398
|
labels: LabelExpression::String("Doc".into()),
|
|
392
399
|
properties: None,
|
|
400
|
+
|
|
401
|
+
predicate: None,
|
|
393
402
|
})],
|
|
394
403
|
where_expression: None,
|
|
395
404
|
optional: false,
|
|
@@ -439,6 +448,8 @@ fn test_plan_match_without_search_still_emits_node_scan()
|
|
|
439
448
|
variable: Some(node_identifier.clone()),
|
|
440
449
|
labels: LabelExpression::String("Person".into()),
|
|
441
450
|
properties: None,
|
|
451
|
+
|
|
452
|
+
predicate: None,
|
|
442
453
|
})],
|
|
443
454
|
where_expression: None,
|
|
444
455
|
optional: false,
|
|
@@ -456,7 +467,7 @@ fn test_plan_match_without_search_still_emits_node_scan()
|
|
|
456
467
|
}
|
|
457
468
|
if matches!(
|
|
458
469
|
&patterns[0],
|
|
459
|
-
|
|
470
|
+
planner::PlannedPattern::Node(NodePattern {
|
|
460
471
|
labels: LabelExpression::String(label),
|
|
461
472
|
..
|
|
462
473
|
}) if label == "Person"
|
|
@@ -476,6 +487,8 @@ fn test_plan_match_with_search_and_filter()
|
|
|
476
487
|
variable: Some(node_identifier.clone()),
|
|
477
488
|
labels: LabelExpression::String("Doc".into()),
|
|
478
489
|
properties: None,
|
|
490
|
+
|
|
491
|
+
predicate: None,
|
|
479
492
|
})],
|
|
480
493
|
where_expression: Some(Value { value: true.into() }.into()),
|
|
481
494
|
optional: false,
|
|
@@ -509,3 +522,421 @@ fn test_plan_match_with_search_and_filter()
|
|
|
509
522
|
&& *k == 3
|
|
510
523
|
));
|
|
511
524
|
}
|
|
525
|
+
|
|
526
|
+
#[test]
|
|
527
|
+
fn test_choose_path_strategy_all_to_dfs()
|
|
528
|
+
{
|
|
529
|
+
let selector = PathSelector::All;
|
|
530
|
+
assert_eq!(
|
|
531
|
+
planner::choose_path_strategy(&selector),
|
|
532
|
+
planner::PathStrategy::Dfs
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
#[test]
|
|
537
|
+
fn test_choose_path_strategy_any_to_bfs()
|
|
538
|
+
{
|
|
539
|
+
let selector = PathSelector::Any;
|
|
540
|
+
assert_eq!(
|
|
541
|
+
planner::choose_path_strategy(&selector),
|
|
542
|
+
planner::PathStrategy::Bfs {
|
|
543
|
+
stop_condition: planner::StopCondition::Shortest(Value { value: 1i64.into() }.into()),
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#[test]
|
|
549
|
+
fn test_choose_path_strategy_all_shortest_to_bfs()
|
|
550
|
+
{
|
|
551
|
+
let selector = PathSelector::AllShortest;
|
|
552
|
+
assert_eq!(
|
|
553
|
+
planner::choose_path_strategy(&selector),
|
|
554
|
+
planner::PathStrategy::Bfs {
|
|
555
|
+
stop_condition: planner::StopCondition::ShortestGroups(Value { value: 1i64.into() }.into()),
|
|
556
|
+
}
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
#[test]
|
|
561
|
+
fn test_choose_path_strategy_shortest_to_bfs()
|
|
562
|
+
{
|
|
563
|
+
let selector = PathSelector::Shortest(Value { value: 10.into() }.into());
|
|
564
|
+
assert_eq!(
|
|
565
|
+
planner::choose_path_strategy(&selector),
|
|
566
|
+
planner::PathStrategy::Bfs {
|
|
567
|
+
stop_condition: planner::StopCondition::Shortest(Value { value: 10.into() }.into()),
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
#[test]
|
|
573
|
+
fn test_choose_path_strategy_shortest_groups_to_bfs()
|
|
574
|
+
{
|
|
575
|
+
let selector = PathSelector::ShortestGroups(Value { value: 5.into() }.into());
|
|
576
|
+
assert_eq!(
|
|
577
|
+
planner::choose_path_strategy(&selector),
|
|
578
|
+
planner::PathStrategy::Bfs {
|
|
579
|
+
stop_condition: planner::StopCondition::ShortestGroups(Value { value: 5.into() }.into()),
|
|
580
|
+
}
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
#[test]
|
|
585
|
+
fn test_plan_pattern_node_passes_through()
|
|
586
|
+
{
|
|
587
|
+
let identifiers = VariableIdentifiers::default();
|
|
588
|
+
let n_identifier = identifiers.create_variable_from_name("n");
|
|
589
|
+
|
|
590
|
+
let node_pattern = Pattern::Node(NodePattern {
|
|
591
|
+
variable: Some(n_identifier.clone()),
|
|
592
|
+
labels: LabelExpression::String("Person".into()),
|
|
593
|
+
properties: None,
|
|
594
|
+
predicate: None,
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
let planned = planner::plan_pattern(node_pattern);
|
|
598
|
+
assert!(matches!(planned, planner::PlannedPattern::Node(_)));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
#[test]
|
|
602
|
+
fn test_plan_pattern_edge_passes_through()
|
|
603
|
+
{
|
|
604
|
+
let node_a = NodePattern {
|
|
605
|
+
variable: None,
|
|
606
|
+
labels: LabelExpression::None,
|
|
607
|
+
properties: None,
|
|
608
|
+
predicate: None,
|
|
609
|
+
};
|
|
610
|
+
let node_b = NodePattern {
|
|
611
|
+
variable: None,
|
|
612
|
+
labels: LabelExpression::None,
|
|
613
|
+
properties: None,
|
|
614
|
+
predicate: None,
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
let edge_pattern = Pattern::Edge(EdgePattern {
|
|
618
|
+
variable: None,
|
|
619
|
+
source: node_a,
|
|
620
|
+
destination: node_b,
|
|
621
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
622
|
+
labels: LabelExpression::String("R".into()),
|
|
623
|
+
properties: None,
|
|
624
|
+
predicate: None,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
let planned = planner::plan_pattern(edge_pattern);
|
|
628
|
+
assert!(matches!(planned, planner::PlannedPattern::Edge(_)));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
#[test]
|
|
632
|
+
fn test_plan_pattern_quantified_path_uses_dfs_for_all()
|
|
633
|
+
{
|
|
634
|
+
let identifiers = VariableIdentifiers::default();
|
|
635
|
+
let node_a = NodePattern {
|
|
636
|
+
variable: Some(identifiers.create_variable_from_name("a")),
|
|
637
|
+
labels: LabelExpression::None,
|
|
638
|
+
properties: None,
|
|
639
|
+
predicate: None,
|
|
640
|
+
};
|
|
641
|
+
let node_b = NodePattern {
|
|
642
|
+
variable: None,
|
|
643
|
+
labels: LabelExpression::None,
|
|
644
|
+
properties: None,
|
|
645
|
+
predicate: None,
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
let qp_source = node_a.clone();
|
|
649
|
+
let qp_destination = node_b.clone();
|
|
650
|
+
let edge_pattern = EdgePattern {
|
|
651
|
+
variable: None,
|
|
652
|
+
source: node_a,
|
|
653
|
+
destination: node_b,
|
|
654
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
655
|
+
labels: LabelExpression::String("R".into()),
|
|
656
|
+
properties: None,
|
|
657
|
+
predicate: None,
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
let quantified_pattern = Pattern::QuantifiedPath(QuantifiedPathPattern {
|
|
661
|
+
variable: None,
|
|
662
|
+
source: qp_source,
|
|
663
|
+
group: vec![Pattern::Edge(edge_pattern)],
|
|
664
|
+
group_where: None,
|
|
665
|
+
path_where: None,
|
|
666
|
+
quantifier: Some(1..3),
|
|
667
|
+
destination: qp_destination,
|
|
668
|
+
selector: PathSelector::All,
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
let planned = planner::plan_pattern(quantified_pattern);
|
|
672
|
+
assert!(matches!(
|
|
673
|
+
planned,
|
|
674
|
+
planner::PlannedPattern::QuantifiedPath { strategy, .. }
|
|
675
|
+
if strategy == planner::PathStrategy::Dfs
|
|
676
|
+
));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
#[test]
|
|
680
|
+
fn test_plan_pattern_quantified_path_uses_bfs_for_shortest()
|
|
681
|
+
{
|
|
682
|
+
let identifiers = VariableIdentifiers::default();
|
|
683
|
+
let node_a = NodePattern {
|
|
684
|
+
variable: Some(identifiers.create_variable_from_name("a")),
|
|
685
|
+
labels: LabelExpression::None,
|
|
686
|
+
properties: None,
|
|
687
|
+
predicate: None,
|
|
688
|
+
};
|
|
689
|
+
let node_b = NodePattern {
|
|
690
|
+
variable: None,
|
|
691
|
+
labels: LabelExpression::None,
|
|
692
|
+
properties: None,
|
|
693
|
+
predicate: None,
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
let qp_source = node_a.clone();
|
|
697
|
+
let qp_destination = node_b.clone();
|
|
698
|
+
let edge_pattern = EdgePattern {
|
|
699
|
+
variable: None,
|
|
700
|
+
source: node_a,
|
|
701
|
+
destination: node_b,
|
|
702
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
703
|
+
labels: LabelExpression::String("R".into()),
|
|
704
|
+
properties: None,
|
|
705
|
+
predicate: None,
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
let quantified_pattern = Pattern::QuantifiedPath(QuantifiedPathPattern {
|
|
709
|
+
variable: None,
|
|
710
|
+
source: qp_source,
|
|
711
|
+
group: vec![Pattern::Edge(edge_pattern)],
|
|
712
|
+
group_where: None,
|
|
713
|
+
path_where: None,
|
|
714
|
+
quantifier: Some(1..3),
|
|
715
|
+
destination: qp_destination,
|
|
716
|
+
selector: PathSelector::Shortest(Value { value: 1.into() }.into()),
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
let planned = planner::plan_pattern(quantified_pattern);
|
|
720
|
+
assert!(matches!(
|
|
721
|
+
planned,
|
|
722
|
+
planner::PlannedPattern::QuantifiedPath { strategy, .. }
|
|
723
|
+
if matches!(strategy, planner::PathStrategy::Bfs { stop_condition: planner::StopCondition::Shortest(_) })
|
|
724
|
+
));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
#[test]
|
|
728
|
+
fn test_wrapper_around_plain_edge_promotes_to_degenerate_quantifier()
|
|
729
|
+
{
|
|
730
|
+
let identifiers = VariableIdentifiers::default();
|
|
731
|
+
let node_a = NodePattern {
|
|
732
|
+
variable: Some(identifiers.create_variable_from_name("a")),
|
|
733
|
+
labels: LabelExpression::None,
|
|
734
|
+
properties: None,
|
|
735
|
+
predicate: None,
|
|
736
|
+
};
|
|
737
|
+
let node_b = NodePattern {
|
|
738
|
+
variable: None,
|
|
739
|
+
labels: LabelExpression::None,
|
|
740
|
+
properties: None,
|
|
741
|
+
predicate: None,
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
let qp_source = node_a.clone();
|
|
745
|
+
let qp_destination = node_b.clone();
|
|
746
|
+
let edge_pattern = EdgePattern {
|
|
747
|
+
variable: None,
|
|
748
|
+
source: node_a,
|
|
749
|
+
destination: node_b,
|
|
750
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
751
|
+
labels: LabelExpression::String("R".into()),
|
|
752
|
+
properties: None,
|
|
753
|
+
predicate: None,
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Wrapper pattern with quantifier: None (the scoping wrapper)
|
|
757
|
+
let wrapper = QuantifiedPathPattern {
|
|
758
|
+
variable: None,
|
|
759
|
+
source: qp_source,
|
|
760
|
+
group: vec![Pattern::Edge(edge_pattern)],
|
|
761
|
+
group_where: None,
|
|
762
|
+
path_where: None,
|
|
763
|
+
quantifier: None,
|
|
764
|
+
destination: qp_destination,
|
|
765
|
+
selector: PathSelector::Shortest(Value { value: 1.into() }.into()),
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
let planned = planner::plan_pattern(Pattern::QuantifiedPath(wrapper));
|
|
769
|
+
|
|
770
|
+
// Assert: the result should be a QuantifiedPath with promoted quantifier and outer strategy
|
|
771
|
+
match planned
|
|
772
|
+
{
|
|
773
|
+
planner::PlannedPattern::QuantifiedPath { pattern, strategy } =>
|
|
774
|
+
{
|
|
775
|
+
// Check quantifier was promoted to exactly-one repetition (1..1)
|
|
776
|
+
assert_eq!(
|
|
777
|
+
pattern.quantifier,
|
|
778
|
+
Some(1..1),
|
|
779
|
+
"quantifier should be promoted to Some(1..1)"
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
// Check strategy matches the outer selector (Shortest with literal 1)
|
|
783
|
+
assert!(
|
|
784
|
+
matches!(
|
|
785
|
+
strategy,
|
|
786
|
+
planner::PathStrategy::Bfs {
|
|
787
|
+
stop_condition: planner::StopCondition::Shortest(_)
|
|
788
|
+
}
|
|
789
|
+
),
|
|
790
|
+
"strategy should be Bfs with Shortest stop condition"
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
_ => panic!("expected PlannedPattern::QuantifiedPath"),
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
#[test]
|
|
798
|
+
fn test_wrapper_around_quantified_path_preserves_inner_quantifier_and_applies_outer_strategy()
|
|
799
|
+
{
|
|
800
|
+
let identifiers = VariableIdentifiers::default();
|
|
801
|
+
let node_a = NodePattern {
|
|
802
|
+
variable: Some(identifiers.create_variable_from_name("a")),
|
|
803
|
+
labels: LabelExpression::None,
|
|
804
|
+
properties: None,
|
|
805
|
+
predicate: None,
|
|
806
|
+
};
|
|
807
|
+
let node_b = NodePattern {
|
|
808
|
+
variable: None,
|
|
809
|
+
labels: LabelExpression::None,
|
|
810
|
+
properties: None,
|
|
811
|
+
predicate: None,
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
let qp_source = node_a.clone();
|
|
815
|
+
let qp_destination = node_b.clone();
|
|
816
|
+
let edge_pattern = EdgePattern {
|
|
817
|
+
variable: None,
|
|
818
|
+
source: node_a,
|
|
819
|
+
destination: node_b,
|
|
820
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
821
|
+
labels: LabelExpression::String("R".into()),
|
|
822
|
+
properties: None,
|
|
823
|
+
predicate: None,
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
// Inner already-quantified pattern (simulating `--+`)
|
|
827
|
+
let inner_quantified = QuantifiedPathPattern {
|
|
828
|
+
variable: None,
|
|
829
|
+
source: qp_source.clone(),
|
|
830
|
+
group: vec![Pattern::Edge(edge_pattern)],
|
|
831
|
+
group_where: None,
|
|
832
|
+
path_where: None,
|
|
833
|
+
quantifier: Some(1..usize::MAX),
|
|
834
|
+
destination: qp_destination.clone(),
|
|
835
|
+
selector: PathSelector::All,
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
// Outer wrapper with quantifier: None (scoping wrapper)
|
|
839
|
+
let wrapper = QuantifiedPathPattern {
|
|
840
|
+
variable: None,
|
|
841
|
+
source: qp_source,
|
|
842
|
+
group: vec![Pattern::QuantifiedPath(inner_quantified)],
|
|
843
|
+
group_where: None,
|
|
844
|
+
path_where: None,
|
|
845
|
+
quantifier: None,
|
|
846
|
+
destination: qp_destination,
|
|
847
|
+
selector: PathSelector::Shortest(Value { value: 1.into() }.into()),
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
let planned = planner::plan_pattern(Pattern::QuantifiedPath(wrapper));
|
|
851
|
+
|
|
852
|
+
// Assert: inner quantifier is preserved, but strategy is from outer selector
|
|
853
|
+
match planned
|
|
854
|
+
{
|
|
855
|
+
planner::PlannedPattern::QuantifiedPath { pattern, strategy } =>
|
|
856
|
+
{
|
|
857
|
+
// Check inner quantifier was preserved (1..usize::MAX)
|
|
858
|
+
assert_eq!(
|
|
859
|
+
pattern.quantifier,
|
|
860
|
+
Some(1..usize::MAX),
|
|
861
|
+
"inner quantifier should be preserved"
|
|
862
|
+
);
|
|
863
|
+
|
|
864
|
+
// Check strategy is from outer selector, not inner's All/Dfs
|
|
865
|
+
assert!(
|
|
866
|
+
matches!(
|
|
867
|
+
strategy,
|
|
868
|
+
planner::PathStrategy::Bfs {
|
|
869
|
+
stop_condition: planner::StopCondition::Shortest(_)
|
|
870
|
+
}
|
|
871
|
+
),
|
|
872
|
+
"strategy should be Bfs (from outer), not Dfs (from inner All)"
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
_ => panic!("expected PlannedPattern::QuantifiedPath"),
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
#[test]
|
|
880
|
+
fn test_wrapper_with_group_where_is_preserved()
|
|
881
|
+
{
|
|
882
|
+
let identifiers = VariableIdentifiers::default();
|
|
883
|
+
let node_a = NodePattern {
|
|
884
|
+
variable: Some(identifiers.create_variable_from_name("a")),
|
|
885
|
+
labels: LabelExpression::None,
|
|
886
|
+
properties: None,
|
|
887
|
+
predicate: None,
|
|
888
|
+
};
|
|
889
|
+
let node_b = NodePattern {
|
|
890
|
+
variable: None,
|
|
891
|
+
labels: LabelExpression::None,
|
|
892
|
+
properties: None,
|
|
893
|
+
predicate: None,
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
let qp_source = node_a.clone();
|
|
897
|
+
let qp_destination = node_b.clone();
|
|
898
|
+
let edge_pattern = EdgePattern {
|
|
899
|
+
variable: None,
|
|
900
|
+
source: node_a,
|
|
901
|
+
destination: node_b,
|
|
902
|
+
directivity: graphcore::EdgeDirectivity::Directed,
|
|
903
|
+
labels: LabelExpression::String("R".into()),
|
|
904
|
+
properties: None,
|
|
905
|
+
predicate: None,
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
// Group where condition - using a simple Value expression
|
|
909
|
+
let group_where = Some(Value { value: true.into() }.into());
|
|
910
|
+
|
|
911
|
+
// Wrapper pattern with quantifier: None and group_where
|
|
912
|
+
let wrapper = QuantifiedPathPattern {
|
|
913
|
+
variable: None,
|
|
914
|
+
source: qp_source,
|
|
915
|
+
group: vec![Pattern::Edge(edge_pattern)],
|
|
916
|
+
group_where: group_where.clone(),
|
|
917
|
+
path_where: None,
|
|
918
|
+
quantifier: None,
|
|
919
|
+
destination: qp_destination,
|
|
920
|
+
selector: PathSelector::Shortest(Value { value: 1.into() }.into()),
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
let planned = planner::plan_pattern(Pattern::QuantifiedPath(wrapper));
|
|
924
|
+
|
|
925
|
+
// Assert: the result should have path_where set (wrapper predicate routes to path_where)
|
|
926
|
+
// and group_where should be None (wrapper-promoted patterns never have group_where)
|
|
927
|
+
match planned
|
|
928
|
+
{
|
|
929
|
+
planner::PlannedPattern::QuantifiedPath { pattern, .. } =>
|
|
930
|
+
{
|
|
931
|
+
assert!(
|
|
932
|
+
pattern.path_where.is_some(),
|
|
933
|
+
"path_where should contain the wrapper's predicate in the promoted pattern"
|
|
934
|
+
);
|
|
935
|
+
assert!(
|
|
936
|
+
pattern.group_where.is_none(),
|
|
937
|
+
"group_where should be None in wrapper-promoted patterns (predicates route to path_where)"
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
_ => panic!("expected PlannedPattern::QuantifiedPath"),
|
|
941
|
+
}
|
|
942
|
+
}
|
|
@@ -34,7 +34,7 @@ mod reopen
|
|
|
34
34
|
#[test]
|
|
35
35
|
fn test_postgres_node_update_syncs_to_vector_index()
|
|
36
36
|
{
|
|
37
|
-
let db = create_tmp_db(
|
|
37
|
+
let db = create_tmp_db(290);
|
|
38
38
|
let config: postgres::Config = db.connection_uri().parse().unwrap();
|
|
39
39
|
let store = crate::store::postgres::Store::connect(config).unwrap();
|
|
40
40
|
super::test_vector_updated_on_node_property_update(store);
|
|
@@ -43,7 +43,7 @@ fn test_postgres_node_update_syncs_to_vector_index()
|
|
|
43
43
|
#[test]
|
|
44
44
|
fn test_postgres_upsert_rejects_nan_component()
|
|
45
45
|
{
|
|
46
|
-
let db = create_tmp_db(
|
|
46
|
+
let db = create_tmp_db(291);
|
|
47
47
|
let config: postgres::Config = db.connection_uri().parse().unwrap();
|
|
48
48
|
let store = crate::store::postgres::Store::connect(config).unwrap();
|
|
49
49
|
|
|
@@ -79,7 +79,7 @@ fn test_postgres_upsert_rejects_nan_component()
|
|
|
79
79
|
#[test]
|
|
80
80
|
fn test_postgres_upsert_rejects_inf_component()
|
|
81
81
|
{
|
|
82
|
-
let db = create_tmp_db(
|
|
82
|
+
let db = create_tmp_db(292);
|
|
83
83
|
let config: postgres::Config = db.connection_uri().parse().unwrap();
|
|
84
84
|
let store = crate::store::postgres::Store::connect(config).unwrap();
|
|
85
85
|
|
|
@@ -115,7 +115,7 @@ fn test_postgres_upsert_rejects_inf_component()
|
|
|
115
115
|
#[test]
|
|
116
116
|
fn test_postgres_search_rejects_nan_query()
|
|
117
117
|
{
|
|
118
|
-
let db = create_tmp_db(
|
|
118
|
+
let db = create_tmp_db(293);
|
|
119
119
|
let config: postgres::Config = db.connection_uri().parse().unwrap();
|
|
120
120
|
let store = crate::store::postgres::Store::connect(config).unwrap();
|
|
121
121
|
|
|
@@ -153,7 +153,7 @@ fn test_postgres_search_rejects_nan_query()
|
|
|
153
153
|
#[test]
|
|
154
154
|
fn test_postgres_delete_vectors_rejects_malformed_node_id()
|
|
155
155
|
{
|
|
156
|
-
let db = create_tmp_db(
|
|
156
|
+
let db = create_tmp_db(294);
|
|
157
157
|
let config: postgres::Config = db.connection_uri().parse().unwrap();
|
|
158
158
|
let store = crate::store::postgres::Store::connect(config).unwrap();
|
|
159
159
|
|
|
@@ -33,7 +33,9 @@ fn test_sqlite_vector_binary_roundtrip()
|
|
|
33
33
|
let blob: Vec<u8> = original.iter().flat_map(|f| f.to_le_bytes()).collect();
|
|
34
34
|
|
|
35
35
|
let decoded: Vec<f32> = blob
|
|
36
|
-
.
|
|
36
|
+
.as_chunks::<4>()
|
|
37
|
+
.0
|
|
38
|
+
.iter()
|
|
37
39
|
.map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
|
|
38
40
|
.collect();
|
|
39
41
|
|