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
data/ext/gqlparser/src/oc/ast.rs
CHANGED
|
@@ -450,12 +450,13 @@ pub struct OrderByExpression
|
|
|
450
450
|
|
|
451
451
|
// Values: CreatePatterns
|
|
452
452
|
|
|
453
|
-
#[derive(Debug)]
|
|
453
|
+
#[derive(Debug, Clone)]
|
|
454
454
|
pub enum Pattern
|
|
455
455
|
{
|
|
456
456
|
Node(NodePattern),
|
|
457
457
|
Edge(EdgePattern),
|
|
458
458
|
Path(PathPattern),
|
|
459
|
+
QuantifiedPath(QuantifiedPathPattern),
|
|
459
460
|
}
|
|
460
461
|
|
|
461
462
|
macro_rules! create_from_pattern {
|
|
@@ -470,6 +471,7 @@ pub struct NodePattern
|
|
|
470
471
|
pub variable: Option<VariableIdentifier>,
|
|
471
472
|
pub labels: LabelExpression,
|
|
472
473
|
pub properties: Option<Expression>,
|
|
474
|
+
pub predicate: Option<Expression>,
|
|
473
475
|
}
|
|
474
476
|
|
|
475
477
|
create_from_pattern! {NodePattern, Node}
|
|
@@ -483,8 +485,7 @@ pub struct EdgePattern
|
|
|
483
485
|
pub directivity: graphcore::EdgeDirectivity,
|
|
484
486
|
pub labels: LabelExpression,
|
|
485
487
|
pub properties: Option<Expression>,
|
|
486
|
-
|
|
487
|
-
pub recursive_range: Option<std::ops::Range<usize>>,
|
|
488
|
+
pub predicate: Option<Expression>,
|
|
488
489
|
}
|
|
489
490
|
|
|
490
491
|
create_from_pattern! {EdgePattern, Edge}
|
|
@@ -498,6 +499,54 @@ pub struct PathPattern
|
|
|
498
499
|
|
|
499
500
|
create_from_pattern! {PathPattern, Path}
|
|
500
501
|
|
|
502
|
+
#[derive(Debug, Clone)]
|
|
503
|
+
pub struct QuantifiedPathPattern
|
|
504
|
+
{
|
|
505
|
+
/// Path variable, if bound via `p = ...`. `None` for a bare quantified
|
|
506
|
+
/// relationship/group with no path variable.
|
|
507
|
+
pub variable: Option<VariableIdentifier>,
|
|
508
|
+
/// The fixed source node anchor outside the repeated group. For
|
|
509
|
+
/// `(a) ((n)-[r]->(m)){1,5} (b)`, this is `a` (the left outer anchor).
|
|
510
|
+
pub source: NodePattern,
|
|
511
|
+
/// The repeated unit. For the single-edge case (legacy `[*min..max]` or
|
|
512
|
+
/// new `{min,max}`/`*`/`+` syntax — the only case Milestone 2 produces),
|
|
513
|
+
/// this is always exactly one `Pattern::Edge`. Multi-element groups
|
|
514
|
+
/// (Milestone 3) populate more than one element here.
|
|
515
|
+
pub group: Vec<Pattern>,
|
|
516
|
+
/// Optional inline `WHERE` scoped to the group (Milestone 3). Always
|
|
517
|
+
/// `None` for anything Milestone 2 produces. Applied per repetition during
|
|
518
|
+
/// group expansion (genuine groups only, never present on wrapper-promoted patterns).
|
|
519
|
+
pub group_where: Option<Expression>,
|
|
520
|
+
/// Optional `WHERE` for the whole assembled path, from a selector-scoped wrapper.
|
|
521
|
+
/// Evaluated once when a candidate reaches `destination`, distinct from
|
|
522
|
+
/// `group_where`'s per-repetition timing. Only populated by wrapper resolution
|
|
523
|
+
/// in `planner::plan_pattern`; genuine groups never have `path_where`.
|
|
524
|
+
pub path_where: Option<Expression>,
|
|
525
|
+
/// `Some(min..max)` for a repeatable group (everything Milestone 2
|
|
526
|
+
/// produces). `None` is reserved for the pre-filter scoping-wrapper use
|
|
527
|
+
/// (Milestone 5, design §1d) — not constructible yet.
|
|
528
|
+
pub quantifier: Option<std::ops::Range<usize>>,
|
|
529
|
+
/// The fixed destination node anchor outside the repeated group. For
|
|
530
|
+
/// `(a) ((n)-[r]->(m)){1,5} (b)`, this is `b` (the right outer anchor).
|
|
531
|
+
pub destination: NodePattern,
|
|
532
|
+
/// Path selector (`SHORTEST`/`ALL SHORTEST`/etc., design's `PathSelector`
|
|
533
|
+
/// below). Always `PathSelector::All` for anything Milestone 2 produces
|
|
534
|
+
/// — no grammar exists yet to populate the other variants.
|
|
535
|
+
pub selector: PathSelector,
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
create_from_pattern! {QuantifiedPathPattern, QuantifiedPath}
|
|
539
|
+
|
|
540
|
+
#[derive(Debug, Clone)]
|
|
541
|
+
pub enum PathSelector
|
|
542
|
+
{
|
|
543
|
+
All,
|
|
544
|
+
Any,
|
|
545
|
+
AllShortest,
|
|
546
|
+
Shortest(Expression),
|
|
547
|
+
ShortestGroups(Expression),
|
|
548
|
+
}
|
|
549
|
+
|
|
501
550
|
// Label Expression
|
|
502
551
|
#[derive(Debug, Clone, PartialEq)]
|
|
503
552
|
pub enum LabelExpression
|
|
@@ -27,6 +27,9 @@ pub enum ErrorKind
|
|
|
27
27
|
NoSingleRelationshipType,
|
|
28
28
|
MixedUnionOperators,
|
|
29
29
|
VariableNotFound,
|
|
30
|
+
CreatingWithPredicate,
|
|
31
|
+
ConflictingQuantifiers,
|
|
32
|
+
EmptyQuantifiedGroup,
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
pub(crate) fn parse_int_error_to_error_kind(e: std::num::ParseIntError) -> ErrorKind
|
|
@@ -157,6 +160,21 @@ impl std::fmt::Display for Error
|
|
|
157
160
|
"NoSingleRelationshipType: edge patterns must have exactly one relationship type at {}..{}.",
|
|
158
161
|
self.span.start, self.span.end
|
|
159
162
|
),
|
|
163
|
+
ErrorKind::CreatingWithPredicate => write!(
|
|
164
|
+
f,
|
|
165
|
+
"CreatingWithPredicate: CREATE patterns cannot use an inline WHERE predicate at {}..{}.",
|
|
166
|
+
self.span.start, self.span.end
|
|
167
|
+
),
|
|
168
|
+
ErrorKind::ConflictingQuantifiers => write!(
|
|
169
|
+
f,
|
|
170
|
+
"ConflictingQuantifiers: cannot specify both a legacy `[*min..max]` range and a `{{min,max}}`/`*`/`+` quantifier on the same relationship at {}..{}.",
|
|
171
|
+
self.span.start, self.span.end
|
|
172
|
+
),
|
|
173
|
+
ErrorKind::EmptyQuantifiedGroup => write!(
|
|
174
|
+
f,
|
|
175
|
+
"EmptyQuantifiedGroup: a quantified group must contain at least one relationship at {}..{}.",
|
|
176
|
+
self.span.start, self.span.end
|
|
177
|
+
),
|
|
160
178
|
ErrorKind::MixedUnionOperators => write!(
|
|
161
179
|
f,
|
|
162
180
|
"MixedUnionOperators: cannot mix UNION and UNION ALL in the same query at {}..{}.",
|
|
@@ -13,6 +13,8 @@ pub(crate) enum Token
|
|
|
13
13
|
And,
|
|
14
14
|
#[token("all", ignore(case))]
|
|
15
15
|
All,
|
|
16
|
+
#[token("any", ignore(case))]
|
|
17
|
+
Any,
|
|
16
18
|
#[token("as", ignore(case))]
|
|
17
19
|
As,
|
|
18
20
|
#[token("asc", ignore(case))]
|
|
@@ -47,6 +49,10 @@ pub(crate) enum Token
|
|
|
47
49
|
False,
|
|
48
50
|
#[token("graph", ignore(case))]
|
|
49
51
|
Graph,
|
|
52
|
+
#[token("group", ignore(case))]
|
|
53
|
+
Group,
|
|
54
|
+
#[token("groups", ignore(case))]
|
|
55
|
+
Groups,
|
|
50
56
|
#[token("if", ignore(case))]
|
|
51
57
|
If,
|
|
52
58
|
#[token("index", ignore(case))]
|
|
@@ -71,6 +77,8 @@ pub(crate) enum Token
|
|
|
71
77
|
Search,
|
|
72
78
|
#[token("set", ignore(case))]
|
|
73
79
|
Set,
|
|
80
|
+
#[token("shortest", ignore(case))]
|
|
81
|
+
Shortest,
|
|
74
82
|
#[token("starts", ignore(case))]
|
|
75
83
|
Starts,
|
|
76
84
|
#[token("not", ignore(case))]
|
|
@@ -191,6 +199,7 @@ impl Token
|
|
|
191
199
|
matches!(
|
|
192
200
|
self,
|
|
193
201
|
Token::And
|
|
202
|
+
| Token::Any
|
|
194
203
|
| Token::As
|
|
195
204
|
| Token::Asc
|
|
196
205
|
| Token::By
|
|
@@ -205,6 +214,8 @@ impl Token
|
|
|
205
214
|
| Token::For
|
|
206
215
|
| Token::False
|
|
207
216
|
| Token::Graph
|
|
217
|
+
| Token::Group
|
|
218
|
+
| Token::Groups
|
|
208
219
|
| Token::If
|
|
209
220
|
| Token::Index
|
|
210
221
|
| Token::In
|
|
@@ -223,6 +234,7 @@ impl Token
|
|
|
223
234
|
| Token::Score
|
|
224
235
|
| Token::Search
|
|
225
236
|
| Token::Set
|
|
237
|
+
| Token::Shortest
|
|
226
238
|
| Token::Skip
|
|
227
239
|
| Token::True
|
|
228
240
|
| Token::Union
|
|
@@ -435,4 +447,138 @@ mod tests
|
|
|
435
447
|
]
|
|
436
448
|
);
|
|
437
449
|
}
|
|
450
|
+
|
|
451
|
+
#[test]
|
|
452
|
+
fn test_oc_lexer_new_keywords()
|
|
453
|
+
{
|
|
454
|
+
// Test 'any' keyword in different cases
|
|
455
|
+
assert_eq!(
|
|
456
|
+
tokenize("any").unwrap(),
|
|
457
|
+
vec![Spanned {
|
|
458
|
+
value: Token::Any,
|
|
459
|
+
span: 0..3
|
|
460
|
+
}]
|
|
461
|
+
);
|
|
462
|
+
assert_eq!(
|
|
463
|
+
tokenize("ANY").unwrap(),
|
|
464
|
+
vec![Spanned {
|
|
465
|
+
value: Token::Any,
|
|
466
|
+
span: 0..3
|
|
467
|
+
}]
|
|
468
|
+
);
|
|
469
|
+
assert_eq!(
|
|
470
|
+
tokenize("Any").unwrap(),
|
|
471
|
+
vec![Spanned {
|
|
472
|
+
value: Token::Any,
|
|
473
|
+
span: 0..3
|
|
474
|
+
}]
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
// Test 'group' keyword in different cases
|
|
478
|
+
assert_eq!(
|
|
479
|
+
tokenize("group").unwrap(),
|
|
480
|
+
vec![Spanned {
|
|
481
|
+
value: Token::Group,
|
|
482
|
+
span: 0..5
|
|
483
|
+
}]
|
|
484
|
+
);
|
|
485
|
+
assert_eq!(
|
|
486
|
+
tokenize("GROUP").unwrap(),
|
|
487
|
+
vec![Spanned {
|
|
488
|
+
value: Token::Group,
|
|
489
|
+
span: 0..5
|
|
490
|
+
}]
|
|
491
|
+
);
|
|
492
|
+
assert_eq!(
|
|
493
|
+
tokenize("Group").unwrap(),
|
|
494
|
+
vec![Spanned {
|
|
495
|
+
value: Token::Group,
|
|
496
|
+
span: 0..5
|
|
497
|
+
}]
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Test 'groups' keyword in different cases
|
|
501
|
+
assert_eq!(
|
|
502
|
+
tokenize("groups").unwrap(),
|
|
503
|
+
vec![Spanned {
|
|
504
|
+
value: Token::Groups,
|
|
505
|
+
span: 0..6
|
|
506
|
+
}]
|
|
507
|
+
);
|
|
508
|
+
assert_eq!(
|
|
509
|
+
tokenize("GROUPS").unwrap(),
|
|
510
|
+
vec![Spanned {
|
|
511
|
+
value: Token::Groups,
|
|
512
|
+
span: 0..6
|
|
513
|
+
}]
|
|
514
|
+
);
|
|
515
|
+
assert_eq!(
|
|
516
|
+
tokenize("Groups").unwrap(),
|
|
517
|
+
vec![Spanned {
|
|
518
|
+
value: Token::Groups,
|
|
519
|
+
span: 0..6
|
|
520
|
+
}]
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// Test 'shortest' keyword in different cases
|
|
524
|
+
assert_eq!(
|
|
525
|
+
tokenize("shortest").unwrap(),
|
|
526
|
+
vec![Spanned {
|
|
527
|
+
value: Token::Shortest,
|
|
528
|
+
span: 0..8
|
|
529
|
+
}]
|
|
530
|
+
);
|
|
531
|
+
assert_eq!(
|
|
532
|
+
tokenize("SHORTEST").unwrap(),
|
|
533
|
+
vec![Spanned {
|
|
534
|
+
value: Token::Shortest,
|
|
535
|
+
span: 0..8
|
|
536
|
+
}]
|
|
537
|
+
);
|
|
538
|
+
assert_eq!(
|
|
539
|
+
tokenize("Shortest").unwrap(),
|
|
540
|
+
vec![Spanned {
|
|
541
|
+
value: Token::Shortest,
|
|
542
|
+
span: 0..8
|
|
543
|
+
}]
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
#[test]
|
|
548
|
+
fn test_oc_lexer_reserved_keywords_compatibility()
|
|
549
|
+
{
|
|
550
|
+
// These keywords are now reserved and cannot be used as identifiers.
|
|
551
|
+
// This test demonstrates the compatibility cost: queries that previously
|
|
552
|
+
// worked with these as variable names will now fail.
|
|
553
|
+
|
|
554
|
+
// Before fix: 'any' would be tokenized as Identifier
|
|
555
|
+
// After fix: 'any' is tokenized as Token::Any (keyword)
|
|
556
|
+
let tokens = tokenize("any").unwrap();
|
|
557
|
+
assert_eq!(
|
|
558
|
+
tokens[0].value,
|
|
559
|
+
Token::Any,
|
|
560
|
+
"Expected 'any' to be reserved keyword, not identifier"
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
let tokens = tokenize("group").unwrap();
|
|
564
|
+
assert_eq!(
|
|
565
|
+
tokens[0].value,
|
|
566
|
+
Token::Group,
|
|
567
|
+
"Expected 'group' to be reserved keyword, not identifier"
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
let tokens = tokenize("groups").unwrap();
|
|
571
|
+
assert_eq!(
|
|
572
|
+
tokens[0].value,
|
|
573
|
+
Token::Groups,
|
|
574
|
+
"Expected 'groups' to be reserved keyword, not identifier"
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
let tokens = tokenize("shortest").unwrap();
|
|
578
|
+
assert_eq!(
|
|
579
|
+
tokens[0].value,
|
|
580
|
+
Token::Shortest,
|
|
581
|
+
"Expected 'shortest' to be reserved keyword, not identifier"
|
|
582
|
+
);
|
|
583
|
+
}
|
|
438
584
|
}
|