gqlite 1.8.1 → 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/gqlitedb/Cargo.toml +0 -2
- data/ext/gqlitedb/src/compiler/variables_manager.rs +109 -10
- data/ext/gqlitedb/src/compiler.rs +481 -47
- data/ext/gqlitedb/src/connection.rs +12 -0
- data/ext/gqlitedb/src/consts.rs +1 -1
- data/ext/gqlitedb/src/error.rs +24 -0
- data/ext/gqlitedb/src/interpreter/evaluators.rs +91 -325
- data/ext/gqlitedb/src/interpreter/instructions.rs +72 -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 +218 -6
- data/ext/gqlitedb/src/query_result.rs +19 -4
- data/ext/gqlitedb/src/tests/compiler.rs +2453 -1
- data/ext/gqlitedb/src/tests/connection/redb.rs +8 -0
- data/ext/gqlitedb/src/tests/connection.rs +37 -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 +432 -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 +284 -1
- data/ext/gqlitedb/src/tests/templates/programs.rs +306 -0
- data/ext/gqliterb/Cargo.toml +0 -1
- data/ext/gqlparser/src/oc/ast.rs +52 -3
- data/ext/gqlparser/src/oc/error.rs +18 -0
- data/ext/gqlparser/src/oc/lexer.rs +146 -0
- data/ext/gqlparser/src/oc/parser/tests.rs +365 -20
- data/ext/gqlparser/src/oc/parser.rs +1196 -143
- data/ext/graphcore/src/value.rs +12 -1
- metadata +5 -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"
|
|
@@ -221,6 +223,8 @@ fn test_plan_maps_direct_statements()
|
|
|
221
223
|
variable: Some(node_identifier.clone()),
|
|
222
224
|
labels: LabelExpression::None,
|
|
223
225
|
properties: None,
|
|
226
|
+
|
|
227
|
+
predicate: None,
|
|
224
228
|
})],
|
|
225
229
|
}
|
|
226
230
|
.into(),
|
|
@@ -340,6 +344,8 @@ fn test_plan_match_with_search_emits_vector_search()
|
|
|
340
344
|
variable: Some(node_identifier.clone()),
|
|
341
345
|
labels: LabelExpression::String("Doc".into()),
|
|
342
346
|
properties: None,
|
|
347
|
+
|
|
348
|
+
predicate: None,
|
|
343
349
|
})],
|
|
344
350
|
where_expression: None,
|
|
345
351
|
optional: false,
|
|
@@ -391,6 +397,8 @@ fn test_plan_match_with_search_and_score()
|
|
|
391
397
|
variable: Some(node_identifier.clone()),
|
|
392
398
|
labels: LabelExpression::String("Doc".into()),
|
|
393
399
|
properties: None,
|
|
400
|
+
|
|
401
|
+
predicate: None,
|
|
394
402
|
})],
|
|
395
403
|
where_expression: None,
|
|
396
404
|
optional: false,
|
|
@@ -440,6 +448,8 @@ fn test_plan_match_without_search_still_emits_node_scan()
|
|
|
440
448
|
variable: Some(node_identifier.clone()),
|
|
441
449
|
labels: LabelExpression::String("Person".into()),
|
|
442
450
|
properties: None,
|
|
451
|
+
|
|
452
|
+
predicate: None,
|
|
443
453
|
})],
|
|
444
454
|
where_expression: None,
|
|
445
455
|
optional: false,
|
|
@@ -457,7 +467,7 @@ fn test_plan_match_without_search_still_emits_node_scan()
|
|
|
457
467
|
}
|
|
458
468
|
if matches!(
|
|
459
469
|
&patterns[0],
|
|
460
|
-
|
|
470
|
+
planner::PlannedPattern::Node(NodePattern {
|
|
461
471
|
labels: LabelExpression::String(label),
|
|
462
472
|
..
|
|
463
473
|
}) if label == "Person"
|
|
@@ -477,6 +487,8 @@ fn test_plan_match_with_search_and_filter()
|
|
|
477
487
|
variable: Some(node_identifier.clone()),
|
|
478
488
|
labels: LabelExpression::String("Doc".into()),
|
|
479
489
|
properties: None,
|
|
490
|
+
|
|
491
|
+
predicate: None,
|
|
480
492
|
})],
|
|
481
493
|
where_expression: Some(Value { value: true.into() }.into()),
|
|
482
494
|
optional: false,
|
|
@@ -510,3 +522,421 @@ fn test_plan_match_with_search_and_filter()
|
|
|
510
522
|
&& *k == 3
|
|
511
523
|
));
|
|
512
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
|
|