Skip to content

Commit 24ab5bd

Browse files
authored
Implement Graph evaluation for matches without alternatives (#547)
1 parent 7782bb2 commit 24ab5bd

File tree

31 files changed

+1607
-54
lines changed

31 files changed

+1607
-54
lines changed

extension/partiql-extension-ddl/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ thiserror = "1.0"
2727
miette = { version = "7", features = ["fancy"] }
2828
time = { version = "0.3", features = ["formatting", "parsing", "serde"] }
2929

30-
indexmap = "2.5"
30+
indexmap = "2"
3131

3232
[dev-dependencies]
3333

extension/partiql-extension-ion/tests/test.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ fn flatten_dump_owned(prefix: &str, value: Value, indent: usize) -> String {
180180
assert_eq!(datum_lowered_value_stats, datum_owned_value_stats);
181181
result += &datum_owned_value_stats;
182182
}
183+
_ => {
184+
unreachable!("unhandled category")
185+
}
183186
}
184187
result
185188
}
@@ -279,6 +282,9 @@ fn flatten_dump_ref(prefix: &str, value: Value, indent: usize) -> String {
279282
assert_eq!(datum_lowered_value_stats, datum_owned_value_stats);
280283
result += &datum_owned_value_stats;
281284
}
285+
_ => {
286+
unreachable!("unhandled category")
287+
}
282288
}
283289
result
284290
}

partiql-ast-passes/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ partiql-common = { path = "../partiql-common", version = "0.11.*" }
2626
partiql-types = { path = "../partiql-types", version = "0.11.*" }
2727

2828
fnv = "1"
29-
indexmap = "2.5"
29+
indexmap = "2"
3030
thiserror = "1.0"
3131

3232
[dev-dependencies]

partiql-ast/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ bench = false
2121

2222
[dependencies]
2323
partiql-common = { path = "../partiql-common", version = "0.11.*" }
24-
indexmap = "2.5"
24+
indexmap = "2"
2525
rust_decimal = { version = "1.36.0", default-features = false, features = ["std"] }
2626
serde = { version = "1", features = ["derive"], optional = true }
2727
pretty = "0.12"

partiql-common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "src/lib.rs"
2020
bench = false
2121

2222
[dependencies]
23-
indexmap = "2.5"
23+
indexmap = "2"
2424
pretty = "0.12"
2525
serde = { version = "1", features = ["derive"], optional = true }
2626
rust_decimal = { version = "1.36", default-features = false, features = ["std"] }

partiql-conformance-tests/src/bin/generate_comparison_report.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct CTSReport {
2020
/// - number of tests passing in both reports
2121
/// - number of tests failing in both reports
2222
/// - number of tests passing in the first report but now fail in the second report (i.e. tests with
23-
/// regressed behavior)
23+
/// regressed behavior)
2424
/// - also lists out these tests and gives a warning
2525
/// - number of tests failing in the first report but now pass in the second report
2626
/// - also lists out these tests

partiql-eval/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ partiql-value = { path = "../partiql-value", version = "0.11.*" }
2727
partiql-catalog = { path = "../partiql-catalog", version = "0.11.*" }
2828
partiql-types = { path = "../partiql-types", version = "0.11.*" }
2929
partiql-extension-ion = { path = "../extension/partiql-extension-ion", version = "0.11.*" }
30+
ion-rs_old = { version = "0.18", package = "ion-rs" }
31+
lasso = "0.7"
3032
petgraph = "0.7"
33+
fxhash = "0.2"
34+
indexmap = "2"
3135
ordered-float = "4"
3236
itertools = "0.13"
3337
unicase = "2"
@@ -44,6 +48,8 @@ serde = { version = "1", features = ["derive"], optional = true }
4448

4549
[dev-dependencies]
4650
criterion = "0.5"
51+
partiql-parser = { path = "../partiql-parser", version = "0.11.*" }
52+
partiql-logical-planner = { path = "../partiql-logical-planner", version = "0.11.*" }
4753

4854

4955
[features]

partiql-eval/src/eval/eval_expr_wrapper.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ impl TypeSatisfier for Static {
6262
(StaticCategory::Tuple(), DatumCategoryRef::Tuple(_)) => {
6363
true // TODO when Static typing knows how to type a tuple
6464
}
65+
(StaticCategory::Graph(), DatumCategoryRef::Graph(_)) => {
66+
true // TODO when Static typing knows how to type a graph
67+
}
6568
_ => false,
6669
}
6770
}

partiql-eval/src/eval/evaluable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ macro_rules! take_input {
2323
($expr:expr, $ctx:expr) => {
2424
match $expr {
2525
None => {
26-
$ctx.add_error(EvaluationError::IllegalState(
26+
$ctx.add_error($crate::error::EvaluationError::IllegalState(
2727
"Error in retrieving input value".to_string(),
2828
));
29-
return Missing;
29+
return partiql_value::Value::Missing;
3030
}
3131
Some(val) => val,
3232
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::sync::atomic::AtomicU32;
2+
use std::sync::atomic::Ordering::Relaxed;
3+
4+
/// A unicode non-character prefixed onto 'anonymous' bind names
5+
const ANON_PREFIX: char = '\u{FDD0}';
6+
7+
pub trait BindNameExt {
8+
fn is_anon(&self) -> bool;
9+
}
10+
11+
impl<S: AsRef<str>> BindNameExt for S {
12+
fn is_anon(&self) -> bool {
13+
self.as_ref().starts_with(ANON_PREFIX)
14+
}
15+
}
16+
17+
/// Creates 'fresh' bind names
18+
pub struct FreshBinder {
19+
#[allow(dead_code)] // TODO remove once graph planning is implemented
20+
node: AtomicU32,
21+
22+
#[allow(dead_code)] // TODO remove once graph planning is implemented
23+
edge: AtomicU32,
24+
}
25+
26+
impl Default for FreshBinder {
27+
fn default() -> Self {
28+
Self {
29+
node: AtomicU32::new(1),
30+
edge: AtomicU32::new(1),
31+
}
32+
}
33+
}
34+
35+
impl FreshBinder {
36+
#[allow(dead_code)] // TODO remove once graph planning is implemented
37+
pub fn node(&self) -> String {
38+
format!("{ANON_PREFIX}🞎{}", self.node.fetch_add(1, Relaxed))
39+
}
40+
41+
#[allow(dead_code)] // TODO remove once graph planning is implemented
42+
pub fn edge(&self) -> String {
43+
format!("{ANON_PREFIX}⁃{}", self.edge.fetch_add(1, Relaxed))
44+
}
45+
}

partiql-eval/src/eval/graph/engine.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use crate::eval::graph::plan::{
2+
DirectionFilter, GraphPlanConvert, NodeFilter, StepFilter, TripleFilter,
3+
};
4+
use crate::eval::graph::result::Triple;
5+
use crate::eval::graph::string_graph::types::StringGraphTypes;
6+
use crate::eval::graph::types::GraphTypes;
7+
use partiql_value::Value;
8+
9+
/// A graph 'engine'; Exposes scanning, node access, and plan conversion to a target graph.
10+
pub trait GraphEngine<GT: GraphTypes>:
11+
GraphScan<GT>
12+
+ GraphPlanConvert<StringGraphTypes, GT>
13+
+ GraphPlanConvert<GT, StringGraphTypes>
14+
+ GraphAccess<GT>
15+
{
16+
}
17+
18+
/// A trait to scan paths and nodes for a graph.
19+
pub trait GraphScan<GT: GraphTypes> {
20+
fn scan(&self, spec: &StepFilter<GT>) -> Vec<Triple<GT>>;
21+
fn get(&self, spec: &NodeFilter<GT>) -> Vec<GT::NodeId>;
22+
}
23+
24+
/// A trait to retrieve named nodes and edges for a graph.
25+
pub trait GraphAccess<GT: GraphTypes> {
26+
fn node(&self, id: &GT::NodeId) -> &Option<Value>;
27+
fn edge(&self, id: &GT::EdgeId) -> &Option<Value>;
28+
}
29+
30+
/// A train to scan paths and nodes for a triple-based graph.
31+
pub trait TripleScan<GT: GraphTypes> {
32+
fn scan_directed_from_to(&self, spec: &TripleFilter<GT>) -> impl Iterator<Item = Triple<GT>>;
33+
34+
fn scan_directed_to_from(&self, spec: &TripleFilter<GT>) -> impl Iterator<Item = Triple<GT>>;
35+
36+
fn scan_directed_both(&self, spec: &TripleFilter<GT>) -> impl Iterator<Item = Triple<GT>>;
37+
38+
fn scan_undirected(&self, spec: &TripleFilter<GT>) -> impl Iterator<Item = Triple<GT>>;
39+
fn get(&self, spec: &NodeFilter<GT>) -> Vec<GT::NodeId>;
40+
}
41+
42+
impl<T, GT> GraphScan<GT> for T
43+
where
44+
GT: GraphTypes,
45+
T: TripleScan<GT>,
46+
{
47+
fn scan(&self, spec: &StepFilter<GT>) -> Vec<Triple<GT>> {
48+
let StepFilter { dir, triple } = spec;
49+
let (to_from, undirected, from_to) = match dir {
50+
DirectionFilter::L => (true, false, false),
51+
DirectionFilter::U => (false, true, false),
52+
DirectionFilter::R => (false, false, true),
53+
DirectionFilter::LU => (true, true, false),
54+
DirectionFilter::UR => (false, true, true),
55+
DirectionFilter::LR => (true, false, true),
56+
DirectionFilter::LUR => (true, true, true),
57+
};
58+
59+
let mut result = vec![];
60+
if undirected {
61+
result.extend(self.scan_undirected(triple));
62+
}
63+
match (from_to, to_from) {
64+
(true, true) => {
65+
result.extend(self.scan_directed_both(triple));
66+
}
67+
(true, false) => {
68+
result.extend(self.scan_directed_from_to(triple));
69+
}
70+
(false, true) => {
71+
result.extend(self.scan_directed_to_from(triple));
72+
}
73+
(false, false) => {}
74+
}
75+
76+
result
77+
}
78+
79+
fn get(&self, spec: &NodeFilter<GT>) -> Vec<GT::NodeId> {
80+
TripleScan::get(self, spec)
81+
}
82+
}

0 commit comments

Comments
 (0)