use crate::model::{BlankNode, Literal, NamedNode, Term, Triple}; use crate::sparql::Variable; use crate::storage::numeric_encoder::EncodedTerm; use regex::Regex; use spargebra::algebra::GraphPattern; use spargebra::term::GroundTerm; use std::cmp::max; use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; use std::fmt; use std::rc::Rc; #[derive(Debug, Clone)] pub enum PlanNode { StaticBindings { encoded_tuples: Vec, variables: Vec, plain_bindings: Vec>>, }, Service { service_name: PatternValue, variables: Rc<[Variable]>, child: Rc, graph_pattern: Rc, silent: bool, }, QuadPattern { subject: PatternValue, predicate: PatternValue, object: PatternValue, graph_name: PatternValue, }, PathPattern { subject: PatternValue, path: Rc, object: PatternValue, graph_name: PatternValue, }, /// Streams left and materializes right join HashJoin { probe_child: Rc, build_child: Rc, }, /// Right nested in left loop ForLoopJoin { left: Rc, right: Rc, }, /// Streams left and materializes right anti join AntiJoin { left: Rc, right: Rc, }, Filter { child: Rc, expression: Box, }, Union { children: Vec>, }, /// hash left join HashLeftJoin { left: Rc, right: Rc, expression: Box, }, /// right nested in left loop ForLoopLeftJoin { left: Rc, right: Rc, }, Extend { child: Rc, variable: PlanVariable, expression: Box, }, Sort { child: Rc, by: Vec, }, HashDeduplicate { child: Rc, }, /// Removes duplicated consecutive elements Reduced { child: Rc, }, Skip { child: Rc, count: usize, }, Limit { child: Rc, count: usize, }, Project { child: Rc, mapping: Rc<[(PlanVariable, PlanVariable)]>, // pairs of (variable key in child, variable key in output) }, Aggregate { // By definition the group by key are the range 0..key_mapping.len() child: Rc, key_variables: Rc<[PlanVariable]>, aggregates: Rc<[(PlanAggregation, PlanVariable)]>, }, } impl PlanNode { /// Returns variables that might be bound in the result set pub fn used_variables(&self) -> BTreeSet { let mut set = BTreeSet::default(); self.lookup_used_variables(&mut |v| { set.insert(v); }); set } pub fn lookup_used_variables(&self, callback: &mut impl FnMut(usize)) { match self { Self::StaticBindings { encoded_tuples, .. } => { for tuple in encoded_tuples { for (key, value) in tuple.iter().enumerate() { if value.is_some() { callback(key); } } } } Self::QuadPattern { subject, predicate, object, graph_name, } => { subject.lookup_variables(callback); predicate.lookup_variables(callback); object.lookup_variables(callback); graph_name.lookup_variables(callback); } Self::PathPattern { subject, object, graph_name, .. } => { subject.lookup_variables(callback); object.lookup_variables(callback); graph_name.lookup_variables(callback); } Self::Filter { child, expression } => { expression.lookup_used_variables(callback); child.lookup_used_variables(callback); } Self::Union { children } => { for child in children { child.lookup_used_variables(callback); } } Self::HashJoin { probe_child: left, build_child: right, } | Self::ForLoopJoin { left, right, .. } | Self::AntiJoin { left, right } | Self::ForLoopLeftJoin { left, right, .. } => { left.lookup_used_variables(callback); right.lookup_used_variables(callback); } Self::HashLeftJoin { left, right, expression, } => { left.lookup_used_variables(callback); right.lookup_used_variables(callback); expression.lookup_used_variables(callback); } Self::Extend { child, variable, expression, } => { callback(variable.encoded); expression.lookup_used_variables(callback); child.lookup_used_variables(callback); } Self::Sort { child, .. } | Self::HashDeduplicate { child } | Self::Reduced { child } | Self::Skip { child, .. } | Self::Limit { child, .. } => child.lookup_used_variables(callback), Self::Service { child, service_name, .. } => { service_name.lookup_variables(callback); child.lookup_used_variables(callback); } Self::Project { mapping, child } => { let child_bound = child.used_variables(); for (child_i, output_i) in mapping.iter() { if child_bound.contains(&child_i.encoded) { callback(output_i.encoded); } } } Self::Aggregate { key_variables, aggregates, .. } => { for var in key_variables.iter() { callback(var.encoded); } for (_, var) in aggregates.iter() { callback(var.encoded); } } } } /// Returns subset of the set of variables that are always bound in the result set /// /// (subset because this function is not perfect yet) pub fn always_bound_variables(&self) -> BTreeSet { let mut set = BTreeSet::default(); self.lookup_always_bound_variables(&mut |v| { set.insert(v); }); set } pub fn lookup_always_bound_variables(&self, callback: &mut impl FnMut(usize)) { match self { Self::StaticBindings { encoded_tuples, .. } => { let mut variables = BTreeMap::default(); // value true iff always bound let max_tuple_length = encoded_tuples .iter() .map(EncodedTuple::capacity) .fold(0, max); for tuple in encoded_tuples { for key in 0..max_tuple_length { match variables.entry(key) { Entry::Vacant(e) => { e.insert(tuple.contains(key)); } Entry::Occupied(mut e) => { if !tuple.contains(key) { e.insert(false); } } } } } for (k, v) in variables { if v { callback(k); } } } Self::QuadPattern { subject, predicate, object, graph_name, } => { subject.lookup_variables(callback); predicate.lookup_variables(callback); object.lookup_variables(callback); graph_name.lookup_variables(callback); } Self::PathPattern { subject, object, graph_name, .. } => { subject.lookup_variables(callback); object.lookup_variables(callback); graph_name.lookup_variables(callback); } Self::Filter { child, .. } => { //TODO: have a look at the expression to know if it filters out unbound variables child.lookup_always_bound_variables(callback); } Self::Union { children } => { if let Some(vars) = children .iter() .map(|c| c.always_bound_variables()) .reduce(|a, b| a.intersection(&b).copied().collect()) { for v in vars { callback(v); } } } Self::HashJoin { probe_child: left, build_child: right, } | Self::ForLoopJoin { left, right, .. } => { left.lookup_always_bound_variables(callback); right.lookup_always_bound_variables(callback); } Self::AntiJoin { left, .. } | Self::HashLeftJoin { left, .. } | Self::ForLoopLeftJoin { left, .. } => { left.lookup_always_bound_variables(callback); } Self::Extend { child, variable, expression, } => { if matches!( expression.as_ref(), PlanExpression::NamedNode(_) | PlanExpression::Literal(_) ) { // TODO: more cases? callback(variable.encoded); } child.lookup_always_bound_variables(callback); } Self::Sort { child, .. } | Self::HashDeduplicate { child } | Self::Reduced { child } | Self::Skip { child, .. } | Self::Limit { child, .. } => child.lookup_always_bound_variables(callback), Self::Service { child, silent, .. } => { if *silent { // none, might return a null tuple } else { child.lookup_always_bound_variables(callback) } } Self::Project { mapping, child } => { let child_bound = child.always_bound_variables(); for (child_i, output_i) in mapping.iter() { if child_bound.contains(&child_i.encoded) { callback(output_i.encoded); } } } Self::Aggregate { .. } => { //TODO } } } } #[derive(Debug, Clone)] pub struct PlanTerm { pub encoded: EncodedTerm, pub plain: T, } impl fmt::Display for PlanTerm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.plain) } } #[derive(Debug, Clone)] pub enum PatternValue { Constant(PlanTerm), Variable(PlanVariable), TriplePattern(Box), } impl PatternValue { pub fn lookup_variables(&self, callback: &mut impl FnMut(usize)) { if let Self::Variable(v) = self { callback(v.encoded) } else if let Self::TriplePattern(p) = self { p.subject.lookup_variables(callback); p.predicate.lookup_variables(callback); p.object.lookup_variables(callback); } } } impl fmt::Display for PatternValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Constant(c) => write!(f, "{c}"), Self::Variable(v) => write!(f, "{v}"), Self::TriplePattern(p) => write!(f, "{p}"), } } } #[derive(Debug, Clone)] pub enum PatternValueConstant { NamedNode(NamedNode), Literal(Literal), Triple(Box), DefaultGraph, } impl fmt::Display for PatternValueConstant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NamedNode(n) => write!(f, "{n}"), Self::Literal(l) => write!(f, "{l}"), Self::Triple(t) => write!(f, "<< {t} >>"), Self::DefaultGraph => f.write_str("DEFAULT"), } } } #[derive(Debug, Clone)] pub struct TriplePatternValue { pub subject: PatternValue, pub predicate: PatternValue, pub object: PatternValue, } impl fmt::Display for TriplePatternValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} {} {}", self.subject, self.predicate, self.object) } } #[derive(Debug, Clone)] pub struct PlanVariable

{ pub encoded: usize, pub plain: P, } impl fmt::Display for PlanVariable

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.plain) } } #[derive(Debug, Clone)] pub enum PlanExpression { NamedNode(PlanTerm), Literal(PlanTerm), Variable(PlanVariable), Exists(Rc), Or(Vec), And(Vec), Equal(Box, Box), Greater(Box, Box), GreaterOrEqual(Box, Box), Less(Box, Box), LessOrEqual(Box, Box), Add(Box, Box), Subtract(Box, Box), Multiply(Box, Box), Divide(Box, Box), UnaryPlus(Box), UnaryMinus(Box), Not(Box), Str(Box), Lang(Box), LangMatches(Box, Box), Datatype(Box), Bound(PlanVariable), Iri(Box), BNode(Option>), Rand, Abs(Box), Ceil(Box), Floor(Box), Round(Box), Concat(Vec), SubStr(Box, Box, Option>), StrLen(Box), StaticReplace(Box, Regex, Box), DynamicReplace(Box, Box, Box, Option>), UCase(Box), LCase(Box), EncodeForUri(Box), Contains(Box, Box), StrStarts(Box, Box), StrEnds(Box, Box), StrBefore(Box, Box), StrAfter(Box, Box), Year(Box), Month(Box), Day(Box), Hours(Box), Minutes(Box), Seconds(Box), Timezone(Box), Tz(Box), Now, Uuid, StrUuid, Md5(Box), Sha1(Box), Sha256(Box), Sha384(Box), Sha512(Box), Coalesce(Vec), If(Box, Box, Box), StrLang(Box, Box), StrDt(Box, Box), SameTerm(Box, Box), IsIri(Box), IsBlank(Box), IsLiteral(Box), IsNumeric(Box), StaticRegex(Box, Regex), DynamicRegex(Box, Box, Option>), Triple(Box, Box, Box), Subject(Box), Predicate(Box), Object(Box), IsTriple(Box), Adjust(Box, Box), BooleanCast(Box), DoubleCast(Box), FloatCast(Box), DecimalCast(Box), IntegerCast(Box), DateCast(Box), TimeCast(Box), DateTimeCast(Box), DurationCast(Box), YearMonthDurationCast(Box), DayTimeDurationCast(Box), StringCast(Box), CustomFunction(NamedNode, Vec), } impl PlanExpression { pub fn lookup_used_variables(&self, callback: &mut impl FnMut(usize)) { match self { Self::Variable(v) | Self::Bound(v) => { callback(v.encoded); } Self::NamedNode(_) | Self::Literal(_) | Self::Rand | Self::Now | Self::Uuid | Self::StrUuid | Self::BNode(None) => (), Self::UnaryPlus(e) | Self::UnaryMinus(e) | Self::Not(e) | Self::BNode(Some(e)) | Self::Str(e) | Self::Lang(e) | Self::Datatype(e) | Self::Iri(e) | Self::Abs(e) | Self::Ceil(e) | Self::Floor(e) | Self::Round(e) | Self::UCase(e) | Self::LCase(e) | Self::StrLen(e) | Self::EncodeForUri(e) | Self::StaticRegex(e, _) | Self::Year(e) | Self::Month(e) | Self::Day(e) | Self::Hours(e) | Self::Minutes(e) | Self::Seconds(e) | Self::Timezone(e) | Self::Tz(e) | Self::Md5(e) | Self::Sha1(e) | Self::Sha256(e) | Self::Sha384(e) | Self::Sha512(e) | Self::IsIri(e) | Self::IsBlank(e) | Self::IsLiteral(e) | Self::IsNumeric(e) | Self::IsTriple(e) | Self::Subject(e) | Self::Predicate(e) | Self::Object(e) | Self::BooleanCast(e) | Self::DoubleCast(e) | Self::FloatCast(e) | Self::DecimalCast(e) | Self::IntegerCast(e) | Self::DateCast(e) | Self::TimeCast(e) | Self::DateTimeCast(e) | Self::DurationCast(e) | Self::YearMonthDurationCast(e) | Self::DayTimeDurationCast(e) | Self::StringCast(e) => e.lookup_used_variables(callback), Self::Equal(a, b) | Self::Greater(a, b) | Self::GreaterOrEqual(a, b) | Self::Less(a, b) | Self::LessOrEqual(a, b) | Self::Add(a, b) | Self::Subtract(a, b) | Self::Multiply(a, b) | Self::Divide(a, b) | Self::LangMatches(a, b) | Self::Contains(a, b) | Self::StaticReplace(a, _, b) | Self::StrStarts(a, b) | Self::StrEnds(a, b) | Self::StrBefore(a, b) | Self::StrAfter(a, b) | Self::StrLang(a, b) | Self::StrDt(a, b) | Self::SameTerm(a, b) | Self::SubStr(a, b, None) | Self::DynamicRegex(a, b, None) | Self::Adjust(a, b) => { a.lookup_used_variables(callback); b.lookup_used_variables(callback); } Self::If(a, b, c) | Self::SubStr(a, b, Some(c)) | Self::DynamicRegex(a, b, Some(c)) | Self::DynamicReplace(a, b, c, None) | Self::Triple(a, b, c) => { a.lookup_used_variables(callback); b.lookup_used_variables(callback); c.lookup_used_variables(callback); } Self::DynamicReplace(a, b, c, Some(d)) => { a.lookup_used_variables(callback); b.lookup_used_variables(callback); c.lookup_used_variables(callback); d.lookup_used_variables(callback); } Self::Or(es) | Self::And(es) | Self::Concat(es) | Self::Coalesce(es) | Self::CustomFunction(_, es) => { for e in es { e.lookup_used_variables(callback); } } Self::Exists(e) => { e.lookup_used_variables(callback); } } } } impl fmt::Display for PlanExpression { #[allow(clippy::many_single_char_names)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Variable(v) => { write!(f, "{v}") } Self::Bound(v) => { write!(f, "Bound({v})") } Self::NamedNode(n) => write!(f, "{n}"), Self::Literal(l) => write!(f, "{l}"), Self::Rand => write!(f, "Rand()"), Self::Now => write!(f, "Now()"), Self::Uuid => write!(f, "Uuid()"), Self::StrUuid => write!(f, "StrUuid()"), Self::UnaryPlus(e) => write!(f, "UnaryPlus({e})"), Self::UnaryMinus(e) => write!(f, "UnaryMinus({e})"), Self::Not(e) => write!(f, "Not({e})"), Self::BNode(e) => { if let Some(e) = e { write!(f, "BNode({e})") } else { write!(f, "BNode()") } } Self::Str(e) => write!(f, "Str({e})"), Self::Lang(e) => write!(f, "Lang({e})"), Self::Datatype(e) => write!(f, "Datatype({e})"), Self::Iri(e) => write!(f, "Iri({e})"), Self::Abs(e) => write!(f, "Abs({e})"), Self::Ceil(e) => write!(f, "Ceil({e})"), Self::Floor(e) => write!(f, "Floor({e})"), Self::Round(e) => write!(f, "Round({e})"), Self::UCase(e) => write!(f, "UCase({e})"), Self::LCase(e) => write!(f, "LCase({e})"), Self::StrLen(e) => write!(f, "StrLen({e})"), Self::EncodeForUri(e) => write!(f, "EncodeForUri({e})"), Self::StaticRegex(e, r) => write!(f, "StaticRegex({e}, {r})"), Self::Year(e) => write!(f, "Year({e})"), Self::Month(e) => write!(f, "Month({e})"), Self::Day(e) => write!(f, "Day({e})"), Self::Hours(e) => write!(f, "Hours({e})"), Self::Minutes(e) => write!(f, "Minutes({e})"), Self::Seconds(e) => write!(f, "Seconds({e})"), Self::Timezone(e) => write!(f, "Timezone({e})"), Self::Tz(e) => write!(f, "Tz({e})"), Self::Md5(e) => write!(f, "Md5({e})"), Self::Sha1(e) => write!(f, "Sha1({e})"), Self::Sha256(e) => write!(f, "Sha256({e})"), Self::Sha384(e) => write!(f, "Sha384({e})"), Self::Sha512(e) => write!(f, "Sha512({e})"), Self::IsIri(e) => write!(f, "IsIri({e})"), Self::IsBlank(e) => write!(f, "IsBlank({e})"), Self::IsLiteral(e) => write!(f, "IsLiteral({e})"), Self::IsNumeric(e) => write!(f, "IsNumeric({e})"), Self::IsTriple(e) => write!(f, "IsTriple({e})"), Self::Subject(e) => write!(f, "Subject({e})"), Self::Predicate(e) => write!(f, "Predicate({e})"), Self::Object(e) => write!(f, "Object({e})"), Self::BooleanCast(e) => write!(f, "BooleanCast({e})"), Self::DoubleCast(e) => write!(f, "DoubleCast({e})"), Self::FloatCast(e) => write!(f, "FloatCast({e})"), Self::DecimalCast(e) => write!(f, "DecimalCast({e})"), Self::IntegerCast(e) => write!(f, "IntegerCast({e})"), Self::DateCast(e) => write!(f, "DateCast({e})"), Self::TimeCast(e) => write!(f, "TimeCast({e})"), Self::DateTimeCast(e) => write!(f, "DateTimeCast({e})"), Self::DurationCast(e) => write!(f, "DurationCast({e})"), Self::YearMonthDurationCast(e) => write!(f, "YearMonthDurationCast({e})"), Self::DayTimeDurationCast(e) => write!(f, "DayTimeDurationCast({e})"), Self::StringCast(e) => write!(f, "StringCast({e})"), Self::Or(es) => { write!(f, "Or(")?; for (i, e) in es.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{e}")?; } write!(f, ")") } Self::And(es) => { write!(f, "And(")?; for (i, e) in es.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{e}")?; } write!(f, ")") } Self::Equal(a, b) => write!(f, "Equal({a}, {b})"), Self::Greater(a, b) => write!(f, "Greater({a}, {b})"), Self::GreaterOrEqual(a, b) => write!(f, "GreaterOrEqual({a}, {b})"), Self::Less(a, b) => write!(f, "Less({a}, {b})"), Self::LessOrEqual(a, b) => write!(f, "LessOrEqual({a}, {b})"), Self::Add(a, b) => write!(f, "Add({a}, {b})"), Self::Subtract(a, b) => write!(f, "Subtract({a}, {b})"), Self::Multiply(a, b) => write!(f, "Multiply({a}, {b})"), Self::Divide(a, b) => write!(f, "Divide({a}, {b})"), Self::LangMatches(a, b) => write!(f, "LangMatches({a}, {b})"), Self::Contains(a, b) => write!(f, "Contains({a}, {b})"), Self::StaticReplace(a, b, c) => write!(f, "StaticReplace({a}, {b}, {c})"), Self::StrStarts(a, b) => write!(f, "StrStarts({a}, {b})"), Self::StrEnds(a, b) => write!(f, "StrEnds({a}, {b})"), Self::StrBefore(a, b) => write!(f, "StrBefore({a}, {b})"), Self::StrAfter(a, b) => write!(f, "StrAfter({a}, {b})"), Self::StrLang(a, b) => write!(f, "StrLang({a}, {b})"), Self::StrDt(a, b) => write!(f, "StrDt({a}, {b})"), Self::SameTerm(a, b) => write!(f, "SameTerm({a}, {b})"), Self::SubStr(a, b, None) => write!(f, "SubStr({a}, {b})"), Self::DynamicRegex(a, b, None) => write!(f, "DynamicRegex({a}, {b})"), Self::Adjust(a, b) => write!(f, "Adjust({a}, {b})"), Self::If(a, b, c) => write!(f, "If({a}, {b}, {c})"), Self::SubStr(a, b, Some(c)) => write!(f, "SubStr({a}, {b}, {c})"), Self::DynamicRegex(a, b, Some(c)) => write!(f, "DynamicRegex({a}, {b}, {c})"), Self::DynamicReplace(a, b, c, None) => write!(f, "DynamicReplace({a}, {b}, {c})"), Self::Triple(a, b, c) => write!(f, "Triple({a}, {b}, {c})"), Self::DynamicReplace(a, b, c, Some(d)) => { write!(f, "DynamicReplace({a}, {b}, {c}, {d})") } Self::Concat(es) => { write!(f, "Concat(")?; for (i, e) in es.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{e}")?; } write!(f, ")") } Self::Coalesce(es) => { write!(f, "Coalesce(")?; for (i, e) in es.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{e}")?; } write!(f, ")") } Self::CustomFunction(name, es) => { write!(f, "{name}(")?; for (i, e) in es.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{e}")?; } write!(f, ")") } Self::Exists(_) => write!(f, "Exists()"), //TODO } } } #[derive(Debug, Clone)] pub struct PlanAggregation { pub function: PlanAggregationFunction, pub parameter: Option, pub distinct: bool, } impl fmt::Display for PlanAggregation { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.function { PlanAggregationFunction::Count => { write!(f, "Count") } PlanAggregationFunction::Sum => { write!(f, "Sum") } PlanAggregationFunction::Min => { write!(f, "Min") } PlanAggregationFunction::Max => { write!(f, "Max") } PlanAggregationFunction::Avg => { write!(f, "Avg") } PlanAggregationFunction::GroupConcat { .. } => { write!(f, "GroupConcat") } PlanAggregationFunction::Sample => write!(f, "Sample"), }?; if self.distinct { write!(f, "Distinct")?; } write!(f, "(")?; if let Some(expr) = &self.parameter { write!(f, "{expr}")?; } if let PlanAggregationFunction::GroupConcat { separator } = &self.function { write!(f, "; separator={separator}")?; } write!(f, ")") } } #[derive(Debug, Clone)] pub enum PlanAggregationFunction { Count, Sum, Min, Max, Avg, Sample, GroupConcat { separator: Rc }, } #[derive(Debug, Clone)] pub enum PlanPropertyPath { Path(PlanTerm), Reverse(Rc), Sequence(Rc, Rc), Alternative(Rc, Rc), ZeroOrMore(Rc), OneOrMore(Rc), ZeroOrOne(Rc), NegatedPropertySet(Rc<[PlanTerm]>), } impl fmt::Display for PlanPropertyPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Path(p) => write!(f, "{p}"), Self::Reverse(p) => write!(f, "Reverse({p})"), Self::Sequence(a, b) => write!(f, "Sequence{a}, {b}"), Self::Alternative(a, b) => write!(f, "Alternative{a}, {b}"), Self::ZeroOrMore(p) => write!(f, "ZeroOrMore({p})"), Self::OneOrMore(p) => write!(f, "OneOrMore({p})"), Self::ZeroOrOne(p) => write!(f, "ZeroOrOne({p})"), Self::NegatedPropertySet(ps) => { write!(f, "NegatedPropertySet(")?; for (i, p) in ps.iter().enumerate() { if i > 0 { write!(f, ", ")?; } write!(f, "{p}")?; } write!(f, ")") } } } } #[derive(Debug, Clone)] pub enum Comparator { Asc(PlanExpression), Desc(PlanExpression), } impl fmt::Display for Comparator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Asc(c) => write!(f, "Asc({c})"), Self::Desc(c) => write!(f, "Desc({c})"), } } } #[derive(Debug, Clone)] pub struct TripleTemplate { pub subject: TripleTemplateValue, pub predicate: TripleTemplateValue, pub object: TripleTemplateValue, } impl fmt::Display for TripleTemplate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} {} {}", self.subject, self.predicate, self.object) } } #[derive(Debug, Clone)] pub enum TripleTemplateValue { Constant(PlanTerm), BlankNode(PlanVariable), Variable(PlanVariable), Triple(Box), } impl fmt::Display for TripleTemplateValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Constant(c) => write!(f, "{c}"), Self::BlankNode(bn) => write!(f, "{bn}"), Self::Variable(v) => write!(f, "{v}"), Self::Triple(t) => write!(f, "<< {t} >>"), } } } #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct EncodedTuple { inner: Vec>, } impl EncodedTuple { pub fn with_capacity(capacity: usize) -> Self { Self { inner: Vec::with_capacity(capacity), } } pub fn capacity(&self) -> usize { self.inner.capacity() } pub fn contains(&self, index: usize) -> bool { self.inner.get(index).map_or(false, Option::is_some) } pub fn get(&self, index: usize) -> Option<&EncodedTerm> { self.inner.get(index).unwrap_or(&None).as_ref() } pub fn iter(&self) -> impl Iterator> + '_ { self.inner.iter().cloned() } pub fn set(&mut self, index: usize, value: EncodedTerm) { if self.inner.len() <= index { self.inner.resize(index + 1, None); } self.inner[index] = Some(value); } pub fn combine_with(&self, other: &Self) -> Option { if self.inner.len() < other.inner.len() { let mut result = other.inner.clone(); for (key, self_value) in self.inner.iter().enumerate() { if let Some(self_value) = self_value { match &other.inner[key] { Some(other_value) => { if self_value != other_value { return None; } } None => result[key] = Some(self_value.clone()), } } } Some(Self { inner: result }) } else { let mut result = self.inner.clone(); for (key, other_value) in other.inner.iter().enumerate() { if let Some(other_value) = other_value { match &self.inner[key] { Some(self_value) => { if self_value != other_value { return None; } } None => result[key] = Some(other_value.clone()), } } } Some(Self { inner: result }) } } } impl IntoIterator for EncodedTuple { type Item = Option; type IntoIter = std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() } }