From 18fe57b5f277782593325b5e919a946babd20e92 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Sun, 18 Feb 2024 14:05:59 +0100 Subject: [PATCH] fix(cognitarium): avoir error on not found ns at plan building --- .../okp4-cognitarium/src/querier/engine.rs | 1 + .../okp4-cognitarium/src/querier/plan.rs | 8 +++ .../src/querier/plan_builder.rs | 60 ++++++++++++++++--- .../okp4-cognitarium/src/state/namespaces.rs | 4 ++ 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index c9989167..515e68b8 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -77,6 +77,7 @@ impl<'a> QueryEngine<'a> { object.clone(), )) }), + QueryNode::Noop { .. } => Rc::new(|_| Box::new(iter::empty())), QueryNode::CartesianProductJoin { left, right } => { let left = self.eval_node(*left); let right = self.eval_node(*right); diff --git a/contracts/okp4-cognitarium/src/querier/plan.rs b/contracts/okp4-cognitarium/src/querier/plan.rs index e2d86fc5..4882770b 100644 --- a/contracts/okp4-cognitarium/src/querier/plan.rs +++ b/contracts/okp4-cognitarium/src/querier/plan.rs @@ -37,6 +37,11 @@ pub enum QueryNode { object: PatternValue, }, + /// Results in no solutions, this special node is used when we know before plan execution that a node + /// will end up with no possible solutions. For example, using a triple pattern filtering with a constant + /// named node containing a non-existing namespace. + Noop { bound_variables: Vec }, + /// Join two nodes by applying the cartesian product of the nodes variables. /// /// This should be used when the nodes doesn't have variables in common, and can be seen as a @@ -77,6 +82,9 @@ impl QueryNode { predicate.lookup_bound_variable(callback); object.lookup_bound_variable(callback); } + QueryNode::Noop { bound_variables } => { + bound_variables.iter().for_each(|v| callback(*v)); + } QueryNode::CartesianProductJoin { left, right } | QueryNode::ForLoopJoin { left, right } => { left.lookup_bound_variables(callback); diff --git a/contracts/okp4-cognitarium/src/querier/plan_builder.rs b/contracts/okp4-cognitarium/src/querier/plan_builder.rs index a85145d8..b4d42476 100644 --- a/contracts/okp4-cognitarium/src/querier/plan_builder.rs +++ b/contracts/okp4-cognitarium/src/querier/plan_builder.rs @@ -105,10 +105,43 @@ impl<'a> PlanBuilder<'a> { } fn build_triple_pattern(&mut self, pattern: &TriplePattern) -> StdResult { - Ok(QueryNode::TriplePattern { - subject: self.build_subject_pattern(pattern.subject.clone())?, - predicate: self.build_predicate_pattern(pattern.predicate.clone())?, - object: self.build_object_pattern(pattern.object.clone())?, + let subject_res = self.build_subject_pattern(pattern.subject.clone()); + let predicate_res = self.build_predicate_pattern(pattern.predicate.clone()); + let object_res = self.build_object_pattern(pattern.object.clone()); + + let mut bound_variables: Vec = vec![]; + let maybe_subject = match subject_res { + Ok(value) => { + value.lookup_bound_variable(&mut |v| bound_variables.push(v)); + Some(value) + } + Err(err) if NamespaceResolver::is_ns_not_found_error(&err) => None, + _ => Some(subject_res?), + }; + let maybe_predicate = match predicate_res { + Ok(value) => { + value.lookup_bound_variable(&mut |v| bound_variables.push(v)); + Some(value) + } + Err(err) if NamespaceResolver::is_ns_not_found_error(&err) => None, + _ => Some(predicate_res?), + }; + let maybe_object = match object_res { + Ok(value) => { + value.lookup_bound_variable(&mut |v| bound_variables.push(v)); + Some(value) + } + Err(err) if NamespaceResolver::is_ns_not_found_error(&err) => None, + _ => Some(object_res?), + }; + + Ok(match (maybe_subject, maybe_predicate, maybe_object) { + (Some(subject), Some(predicate), Some(object)) => QueryNode::TriplePattern { + subject, + predicate, + object, + }, + _ => QueryNode::Noop { bound_variables }, }) } @@ -376,7 +409,9 @@ mod test { predicate: VarOrNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, - Err(StdError::not_found("Namespace")), + Ok(QueryNode::Noop { + bound_variables: vec![1usize, 2usize], + }), ), ( TriplePattern { @@ -386,7 +421,9 @@ mod test { ))), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, - Err(StdError::not_found("Namespace")), + Ok(QueryNode::Noop { + bound_variables: vec![0usize, 2usize], + }), ), ( TriplePattern { @@ -396,7 +433,9 @@ mod test { "notexisting#outch".to_string(), ))), }, - Err(StdError::not_found("Namespace")), + Ok(QueryNode::Noop { + bound_variables: vec![0usize, 1usize], + }), ), ]; @@ -451,7 +490,12 @@ mod test { predicate: VarOrNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], - Err(StdError::not_found("Namespace")), + Ok(QueryPlan { + entrypoint: QueryNode::Noop { + bound_variables: vec![0usize, 1usize], + }, + variables: vec!["predicate".to_string(), "object".to_string()], + }), ), ( None, diff --git a/contracts/okp4-cognitarium/src/state/namespaces.rs b/contracts/okp4-cognitarium/src/state/namespaces.rs index a0ff6570..f4c0a2dd 100644 --- a/contracts/okp4-cognitarium/src/state/namespaces.rs +++ b/contracts/okp4-cognitarium/src/state/namespaces.rs @@ -132,6 +132,10 @@ impl NamespaceResolver { None => Err(StdError::not_found("Namespace")), } } + + pub fn is_ns_not_found_error(err: &StdError) -> bool { + matches!(err, StdError::NotFound { kind, .. } if kind == "Namespace") + } } impl Default for NamespaceResolver {