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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6ebd6bd26d6614d8d3edd02d592c4bfd125a5dc3bf8170cb69c3f7e22dd48a7
|
|
4
|
+
data.tar.gz: 1c9f08c8d98976a104166ddc321e23a81e3a66d36fa16c97dcff0aa81adc735f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
14
|
-
gqlitedb = { version = "0.
|
|
15
|
-
gqlparser = { version = "0.
|
|
16
|
-
db-index = { version = "0.
|
|
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.
|
|
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.
|
|
23
|
+
itertools = "0.15"
|
|
24
24
|
log = "0.4"
|
|
25
|
-
rusqlite = { version = "0.
|
|
25
|
+
rusqlite = { version = "0.40", features = ["load_extension"] }
|
|
26
26
|
serde = "1"
|
|
27
27
|
thiserror = "2"
|
|
28
28
|
uuid = { version = "1", features = ["v4"] }
|
data/ext/db-index/Cargo.toml
CHANGED
|
@@ -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 =
|
|
5
|
+
license.workspace = true
|
|
6
6
|
homepage.workspace = true
|
|
7
7
|
repository.workspace = true
|
|
8
|
-
version =
|
|
8
|
+
version.workspace = true
|
|
9
9
|
edition = "2021"
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
data/ext/gqlitedb/Cargo.toml
CHANGED
|
@@ -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::{
|
|
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
|
-
|
|
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: &
|
|
495
|
+
fn analyse_pattern(&mut self, pattern: &planner::PlannedPattern, is_create: bool) -> Result<()>
|
|
488
496
|
{
|
|
489
497
|
match pattern
|
|
490
498
|
{
|
|
491
|
-
|
|
499
|
+
planner::PlannedPattern::Node(node) =>
|
|
492
500
|
{
|
|
493
501
|
self.validate_node(node)?;
|
|
494
502
|
}
|
|
495
|
-
|
|
503
|
+
planner::PlannedPattern::Edge(edge) =>
|
|
496
504
|
{
|
|
497
505
|
self.analyse_edge_path(None, edge, is_create)?;
|
|
498
506
|
}
|
|
499
|
-
|
|
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
|
|
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, .. }
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|