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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Cargo.toml +8 -8
  3. data/ext/db-index/Cargo.toml +2 -2
  4. data/ext/gqlitedb/Cargo.toml +0 -2
  5. data/ext/gqlitedb/src/aggregators.rs +28 -0
  6. data/ext/gqlitedb/src/compiler/expression_analyser.rs +33 -0
  7. data/ext/gqlitedb/src/compiler/variables_manager.rs +111 -11
  8. data/ext/gqlitedb/src/compiler.rs +506 -48
  9. data/ext/gqlitedb/src/connection.rs +14 -2
  10. data/ext/gqlitedb/src/consts.rs +1 -1
  11. data/ext/gqlitedb/src/error.rs +31 -0
  12. data/ext/gqlitedb/src/interpreter/evaluators.rs +153 -331
  13. data/ext/gqlitedb/src/interpreter/instructions.rs +77 -0
  14. data/ext/gqlitedb/src/interpreter/mod.rs +1 -0
  15. data/ext/gqlitedb/src/interpreter/path/bfs.rs +494 -0
  16. data/ext/gqlitedb/src/interpreter/path/dfs.rs +267 -0
  17. data/ext/gqlitedb/src/interpreter/path/group.rs +418 -0
  18. data/ext/gqlitedb/src/interpreter/path.rs +542 -0
  19. data/ext/gqlitedb/src/planner.rs +231 -6
  20. data/ext/gqlitedb/src/query_result.rs +19 -4
  21. data/ext/gqlitedb/src/store/redb.rs +1 -1
  22. data/ext/gqlitedb/src/tests/compiler.rs +2454 -1
  23. data/ext/gqlitedb/src/tests/connection/postgres.rs +22 -0
  24. data/ext/gqlitedb/src/tests/connection/redb.rs +24 -0
  25. data/ext/gqlitedb/src/tests/connection/sqlite.rs +16 -0
  26. data/ext/gqlitedb/src/tests/connection.rs +101 -0
  27. data/ext/gqlitedb/src/tests/evaluators.rs +2206 -1
  28. data/ext/gqlitedb/src/tests/parser.rs +1225 -0
  29. data/ext/gqlitedb/src/tests/planner.rs +433 -2
  30. data/ext/gqlitedb/src/tests/store/vector_index/postgres.rs +5 -5
  31. data/ext/gqlitedb/src/tests/store/vector_index/sqlite.rs +3 -1
  32. data/ext/gqlitedb/src/tests/templates/ast.rs +294 -1
  33. data/ext/gqlitedb/src/tests/templates/programs.rs +318 -0
  34. data/ext/gqliterb/Cargo.toml +0 -1
  35. data/ext/gqlparser/src/oc/ast.rs +72 -3
  36. data/ext/gqlparser/src/oc/error.rs +18 -0
  37. data/ext/gqlparser/src/oc/lexer.rs +157 -2
  38. data/ext/gqlparser/src/oc/parser/tests.rs +735 -29
  39. data/ext/gqlparser/src/oc/parser.rs +1312 -182
  40. data/ext/gqlparser/src/oc/string.rs +172 -0
  41. data/ext/gqlparser/src/oc.rs +1 -0
  42. data/ext/graphcore/src/value/value_map.rs +2 -2
  43. data/ext/graphcore/src/value.rs +13 -1
  44. metadata +6 -2
  45. data/ext/gqlitedb/src/parser/gql.pest +0 -198
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69d9620b19310b76b69a9bb2373cde64cfd94133e73b4197c51d59b0b5463c3d
4
- data.tar.gz: 80af6abeddd183326704ae95cb5b00f2e41317e5a104d2a0c4d77c95cc4cdd5b
3
+ metadata.gz: c6ebd6bd26d6614d8d3edd02d592c4bfd125a5dc3bf8170cb69c3f7e22dd48a7
4
+ data.tar.gz: 1c9f08c8d98976a104166ddc321e23a81e3a66d36fa16c97dcff0aa81adc735f
5
5
  SHA512:
6
- metadata.gz: 1f14ae08b820f1e5e48960220912e01f74a41b83610571eec99e46647de2ebd48eed5925e280bf45f728973a7826b1da63cff2e526382bf5d0399b347043a247
7
- data.tar.gz: 9de4b0738781537c2657c90bad7e1831064f8e783387973503971de741c2b861f651efc64733ecc406b0741dba2ba19cfb5f32fab83692b24f2be6a62b6057a2
6
+ metadata.gz: b524bcae9534be01d0d7b5ba78430058c736314c2f57770ca5774edb888b9192653fa23f1664bd62833c27745b29004dc1d78d280c769def4819e54ba5ff8a41
7
+ data.tar.gz: 73f5e717c9e6dc74c19389c939a2c5933c16b4a47da74756fab937e4ece53922e8fe25c8b4979a1d60e6be0d5a7c2701b4747bc5ff803789fa54b3b56132874e
data/ext/Cargo.toml CHANGED
@@ -3,26 +3,26 @@ resolver = "3"
3
3
  members = ["gqliterb", "gqlitedb", "graphcore"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.11.0"
6
+ version = "0.13.0"
7
7
  edition = "2024"
8
8
  license = "MIT"
9
9
  homepage = "https://gqlite.org"
10
10
  repository = "https://gitlab.com/gqlite/gqlite"
11
11
 
12
12
  [workspace.dependencies]
13
- graphcore = { version = "0.11.0", path = "graphcore" }
14
- gqlitedb = { version = "0.11.0", path = "gqlitedb" }
15
- gqlparser = { version = "0.11.0", path = "gqlparser" }
16
- db-index = { version = "0.11.0", path = "db-index" }
13
+ graphcore = { version = "0.13.0", path = "graphcore" }
14
+ gqlitedb = { version = "0.13.0", path = "gqlitedb" }
15
+ gqlparser = { version = "0.13.0", path = "gqlparser" }
16
+ db-index = { version = "0.13.0", path = "db-index" }
17
17
 
18
- askama = { version = "0.15" }
18
+ askama = { version = "0.16" }
19
19
  ccutils = { version = "0.4" }
20
20
  ciborium = "0.2"
21
21
  divan = "0.1"
22
22
  indexmap = "2"
23
- itertools = "0.14"
23
+ itertools = "0.15"
24
24
  log = "0.4"
25
- rusqlite = { version = "0.38", features = ["load_extension"] }
25
+ rusqlite = { version = "0.40", features = ["load_extension"] }
26
26
  serde = "1"
27
27
  thiserror = "2"
28
28
  uuid = { version = "1", features = ["v4"] }
@@ -2,10 +2,10 @@
2
2
  name = "db-index"
3
3
  description = "Reusable indexing algorithms for key-value storage engines"
4
4
  readme = "README.md"
5
- license = "MIT"
5
+ license.workspace = true
6
6
  homepage.workspace = true
7
7
  repository.workspace = true
8
- version = "0.11.0"
8
+ version.workspace = true
9
9
  edition = "2021"
10
10
 
11
11
  [dependencies]
@@ -38,8 +38,6 @@ db-index = { workspace = true, optional = true }
38
38
  indexmap = { version = "2", features = ["serde"] }
39
39
  itertools = { workspace = true }
40
40
  log = { workspace = true }
41
- pest = "2"
42
- pest_derive = "2"
43
41
  pgrx = { version = "0.18", optional = true }
44
42
  postgres = { version = "0.19", optional = true, features = [
45
43
  "with-uuid-1",
@@ -22,6 +22,34 @@ pub(crate) trait AggregatorTrait: Debug + Sync + Send
22
22
 
23
23
  pub(crate) type Aggregator = Arc<Box<dyn AggregatorTrait>>;
24
24
 
25
+ #[derive(Debug)]
26
+ pub(crate) struct DistinctAggregatorState
27
+ {
28
+ pub(crate) inner: Box<dyn AggregatorState>,
29
+ pub(crate) seen: Vec<value::Value>,
30
+ }
31
+
32
+ impl AggregatorState for DistinctAggregatorState
33
+ {
34
+ fn next(&mut self, expression: value::Value) -> Result<()>
35
+ {
36
+ if self.seen.iter().any(|v| v == &expression)
37
+ {
38
+ Ok(())
39
+ }
40
+ else
41
+ {
42
+ self.seen.push(expression.clone());
43
+ self.inner.next(expression)
44
+ }
45
+ }
46
+
47
+ fn finalise(self: Box<Self>) -> Result<value::Value>
48
+ {
49
+ self.inner.finalise()
50
+ }
51
+ }
52
+
25
53
  macro_rules! declare_aggregator {
26
54
  ($function_name: ident, $type_name: ident, $state_type_name: tt, ( $( $arg_type: ty $(,)? )* ) -> $ret_type: ty) => {
27
55
  #[derive(Debug)]
@@ -256,6 +256,15 @@ impl<'b> Analyser<'b>
256
256
  )),
257
257
  ast::Expression::FunctionCall(call) =>
258
258
  {
259
+ if call.distinct && !self.functions_manager.is_aggregate(&call.name)?
260
+ {
261
+ return Err(
262
+ error::CompileTimeError::DistinctOnNonAggregateFunction {
263
+ name: call.name.clone(),
264
+ }
265
+ .into(),
266
+ );
267
+ }
259
268
  let arguments = (call.arguments.iter(), validators::any).analyse(self)?;
260
269
  Ok(ExpressionInfo::new(
261
270
  self.functions_manager.validate_arguments(
@@ -438,6 +447,30 @@ impl<'b> Analyser<'b>
438
447
  false,
439
448
  false,
440
449
  )),
450
+ ast::Expression::Contains(contains) => Ok(ExpressionInfo::new_type(
451
+ ExpressionType::Boolean,
452
+ (
453
+ [&contains.left, &contains.right].into_iter(),
454
+ validators::string_or_null,
455
+ )
456
+ .analyse(self)?,
457
+ )),
458
+ ast::Expression::StartsWith(starts_with) => Ok(ExpressionInfo::new_type(
459
+ ExpressionType::Boolean,
460
+ (
461
+ [&starts_with.left, &starts_with.right].into_iter(),
462
+ validators::string_or_null,
463
+ )
464
+ .analyse(self)?,
465
+ )),
466
+ ast::Expression::EndsWith(ends_with) => Ok(ExpressionInfo::new_type(
467
+ ExpressionType::Boolean,
468
+ (
469
+ [&ends_with.left, &ends_with.right].into_iter(),
470
+ validators::string_or_null,
471
+ )
472
+ .analyse(self)?,
473
+ )),
441
474
  }
442
475
  }
443
476
  }
@@ -2,7 +2,10 @@ use std::collections::HashMap;
2
2
 
3
3
  use gqlparser::oc::ast;
4
4
 
5
- use crate::{planner::LogicalPlan, prelude::*};
5
+ use crate::{
6
+ planner::{self, LogicalPlan},
7
+ prelude::*,
8
+ };
6
9
 
7
10
  use compiler::expression_analyser::{self, ExpressionType};
8
11
 
@@ -72,14 +75,17 @@ impl Variable
72
75
  variable: None,
73
76
  labels: ast::LabelExpression::None,
74
77
  properties: None,
78
+ predicate: None,
75
79
  },
76
80
  destination: ast::NodePattern {
77
81
  variable: None,
78
82
  labels: ast::LabelExpression::None,
79
83
  properties: None,
84
+ predicate: None,
80
85
  },
81
86
  directivity: graphcore::EdgeDirectivity::Directed,
82
- recursive_range: None,
87
+
88
+ predicate: None,
83
89
  }),
84
90
  variable_type: ExpressionType::Edge,
85
91
  col_id,
@@ -90,6 +96,8 @@ impl Variable
90
96
  variable: None,
91
97
  labels: ast::LabelExpression::None,
92
98
  properties: None,
99
+
100
+ predicate: None,
93
101
  }),
94
102
  variable_type: ExpressionType::Node,
95
103
  col_id,
@@ -484,23 +492,101 @@ impl VariablesManager
484
492
  Ok(())
485
493
  }
486
494
 
487
- fn analyse_pattern(&mut self, pattern: &ast::Pattern, is_create: bool) -> Result<()>
495
+ fn analyse_pattern(&mut self, pattern: &planner::PlannedPattern, is_create: bool) -> Result<()>
488
496
  {
489
497
  match pattern
490
498
  {
491
- ast::Pattern::Node(node) =>
499
+ planner::PlannedPattern::Node(node) =>
492
500
  {
493
501
  self.validate_node(node)?;
494
502
  }
495
- ast::Pattern::Edge(edge) =>
503
+ planner::PlannedPattern::Edge(edge) =>
496
504
  {
497
505
  self.analyse_edge_path(None, edge, is_create)?;
498
506
  }
499
- ast::Pattern::Path(path) =>
507
+ planner::PlannedPattern::Path(path) =>
500
508
  {
501
509
  self.analyse_edge_path(None, &path.edge, is_create)?;
502
510
  self.declare_variable(&path.variable, ExpressionType::Path)?;
503
511
  }
512
+ planner::PlannedPattern::QuantifiedPath { pattern: qp, .. } =>
513
+ {
514
+ if qp.group.len() != 1
515
+ {
516
+ // Multi-element groups: declare path variable and outer anchors
517
+ if let Some(path_variable) = &qp.variable
518
+ {
519
+ self.declare_variable(path_variable, ExpressionType::Path)?;
520
+ }
521
+ if !self.is_valid_existing_node(&qp.source)?
522
+ {
523
+ self.validate_node(&qp.source)?;
524
+ }
525
+ if !self.is_valid_existing_node(&qp.destination)?
526
+ {
527
+ self.validate_node(&qp.destination)?;
528
+ }
529
+ // Multi-element groups: analyze all patterns in the group
530
+ for pattern in &qp.group
531
+ {
532
+ match pattern
533
+ {
534
+ ast::Pattern::Node(node) =>
535
+ {
536
+ self.validate_node(node)?;
537
+ }
538
+ ast::Pattern::Edge(edge) =>
539
+ {
540
+ self.validate_edge(edge)?;
541
+ }
542
+ ast::Pattern::Path(path) =>
543
+ {
544
+ self.declare_variable(&path.variable, ExpressionType::Path)?;
545
+ self.validate_edge(&path.edge)?;
546
+ }
547
+ ast::Pattern::QuantifiedPath(_) =>
548
+ {
549
+ return Err(InternalError::Unimplemented("nested quantified path patterns").into());
550
+ }
551
+ }
552
+ }
553
+ }
554
+ else
555
+ {
556
+ if let Some(edge) = super::as_plain_quantifier(qp)
557
+ {
558
+ self.analyse_edge_path(qp.variable.clone(), edge, is_create)?;
559
+ }
560
+ else
561
+ {
562
+ // Explicit group: outer anchors differ from inner nodes
563
+ // Need to declare the outer anchors
564
+ if let Some(path_variable) = &qp.variable
565
+ {
566
+ self.declare_variable(path_variable, ExpressionType::Path)?;
567
+ }
568
+ if !self.is_valid_existing_node(&qp.source)?
569
+ {
570
+ self.validate_node(&qp.source)?;
571
+ }
572
+ if !self.is_valid_existing_node(&qp.destination)?
573
+ {
574
+ self.validate_node(&qp.destination)?;
575
+ }
576
+ // Validate every inner edge, not just the first - group.len() may be
577
+ // > 1 for multi-element groups (e.g. ((n)-[r1]->(x)-[r2]->(m))), and
578
+ // only checking group[0] would silently skip every subsequent edge's
579
+ // label/property/predicate constraints.
580
+ for pattern in &qp.group
581
+ {
582
+ if let ast::Pattern::Edge(edge) = pattern
583
+ {
584
+ self.validate_edge(edge)?;
585
+ }
586
+ }
587
+ }
588
+ }
589
+ }
504
590
  }
505
591
  Ok(())
506
592
  }
@@ -573,7 +659,7 @@ impl VariablesManager
573
659
  {
574
660
  for pattern in patterns.iter()
575
661
  {
576
- if let ast::Pattern::Node(n) = &pattern
662
+ if let planner::PlannedPattern::Node(n) = pattern
577
663
  && self.has_variable(&n.variable)
578
664
  {
579
665
  return Err(
@@ -596,7 +682,8 @@ impl VariablesManager
596
682
  LogicalPlan::Filter { source, .. }
597
683
  | LogicalPlan::Sort { source, .. }
598
684
  | LogicalPlan::Skip { source, .. }
599
- | LogicalPlan::Limit { source, .. } => self.analyse(source.as_ref())?,
685
+ | LogicalPlan::Limit { source, .. }
686
+ | LogicalPlan::Distinct { source } => self.analyse(source.as_ref())?,
600
687
  LogicalPlan::Projection { .. } =>
601
688
  {}
602
689
  LogicalPlan::Call { .. } =>
@@ -616,7 +703,7 @@ impl VariablesManager
616
703
  {
617
704
  match pattern
618
705
  {
619
- ast::Pattern::Node(n) =>
706
+ planner::PlannedPattern::Node(n) =>
620
707
  {
621
708
  if self.has_variable(&n.variable)
622
709
  {
@@ -628,7 +715,7 @@ impl VariablesManager
628
715
  );
629
716
  }
630
717
  }
631
- ast::Pattern::Edge(e) =>
718
+ planner::PlannedPattern::Edge(e) =>
632
719
  {
633
720
  if self.has_variable(&e.variable)
634
721
  {
@@ -640,7 +727,7 @@ impl VariablesManager
640
727
  );
641
728
  }
642
729
  }
643
- ast::Pattern::Path(p) =>
730
+ planner::PlannedPattern::Path(p) =>
644
731
  {
645
732
  if self.has_variable(&Some(p.variable.clone()))
646
733
  {
@@ -652,6 +739,19 @@ impl VariablesManager
652
739
  );
653
740
  }
654
741
  }
742
+ planner::PlannedPattern::QuantifiedPath { pattern: qp, .. } =>
743
+ {
744
+ if let Some(var) = &qp.variable
745
+ && self.has_variable(&Some(var.clone()))
746
+ {
747
+ return Err(
748
+ CompileTimeError::VariableAlreadyBound {
749
+ name: var.name().clone(),
750
+ }
751
+ .into(),
752
+ );
753
+ }
754
+ }
655
755
  }
656
756
  self.analyse_pattern(pattern, false)?;
657
757
  }