//! [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery) representation use crate::term::print_quoted_str; use crate::term::*; use std::collections::BTreeSet; use std::fmt; /// A [property path expression](https://www.w3.org/TR/sparql11-query/#defn_PropertyPathExpr) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum PropertyPathExpression { NamedNode(NamedNode), Reverse(Box), Sequence(Box, Box), Alternative(Box, Box), ZeroOrMore(Box), OneOrMore(Box), ZeroOrOne(Box), NegatedPropertySet(Vec), } impl fmt::Display for PropertyPathExpression { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PropertyPathExpression::NamedNode(p) => p.fmt(f), PropertyPathExpression::Reverse(p) => write!(f, "(reverse {})", p), PropertyPathExpression::Alternative(a, b) => write!(f, "(alt {} {})", a, b), PropertyPathExpression::Sequence(a, b) => write!(f, "(seq {} {})", a, b), PropertyPathExpression::ZeroOrMore(p) => write!(f, "(path* {})", p), PropertyPathExpression::OneOrMore(p) => write!(f, "(path+ {})", p), PropertyPathExpression::ZeroOrOne(p) => write!(f, "(path? {})", p), PropertyPathExpression::NegatedPropertySet(p) => { write!(f, "(notoneof ")?; for p in p { write!(f, " {}", p)?; } write!(f, ")") } } } } struct SparqlPropertyPath<'a>(&'a PropertyPathExpression); impl<'a> fmt::Display for SparqlPropertyPath<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { PropertyPathExpression::NamedNode(p) => p.fmt(f), PropertyPathExpression::Reverse(p) => write!(f, "^{}", SparqlPropertyPath(&*p)), PropertyPathExpression::Sequence(a, b) => write!( f, "({} / {})", SparqlPropertyPath(&*a), SparqlPropertyPath(&*b) ), PropertyPathExpression::Alternative(a, b) => write!( f, "({} | {})", SparqlPropertyPath(&*a), SparqlPropertyPath(&*b) ), PropertyPathExpression::ZeroOrMore(p) => write!(f, "{}*", SparqlPropertyPath(&*p)), PropertyPathExpression::OneOrMore(p) => write!(f, "{}+", SparqlPropertyPath(&*p)), PropertyPathExpression::ZeroOrOne(p) => write!(f, "{}?", SparqlPropertyPath(&*p)), PropertyPathExpression::NegatedPropertySet(p) => write!( f, "!({})", p.iter() .map(|v| v.to_string()) .collect::>() .join(" | ") ), } } } impl From for PropertyPathExpression { fn from(p: NamedNode) -> Self { PropertyPathExpression::NamedNode(p) } } /// An [expression](https://www.w3.org/TR/sparql11-query/#expressions) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum Expression { NamedNode(NamedNode), Literal(Literal), Variable(Variable), /// [Logical-or](https://www.w3.org/TR/sparql11-query/#func-logical-or) Or(Box, Box), /// [Logical-and](https://www.w3.org/TR/sparql11-query/#func-logical-and) And(Box, Box), /// [RDFterm-equal](https://www.w3.org/TR/sparql11-query/#func-RDFterm-equal) and all the XSD equalities Equal(Box, Box), /// [sameTerm](https://www.w3.org/TR/sparql11-query/#func-sameTerm) SameTerm(Box, Box), /// [op:numeric-greater-than](https://www.w3.org/TR/xpath-functions/#func-numeric-greater-than) and other XSD greater than operators Greater(Box, Box), GreaterOrEqual(Box, Box), /// [op:numeric-less-than](https://www.w3.org/TR/xpath-functions/#func-numeric-less-than) and other XSD greater than operators Less(Box, Box), LessOrEqual(Box, Box), /// [IN](https://www.w3.org/TR/sparql11-query/#func-in) In(Box, Vec), /// [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add) and other XSD additions Add(Box, Box), /// [op:numeric-subtract](https://www.w3.org/TR/xpath-functions/#func-numeric-subtract) and other XSD subtractions Subtract(Box, Box), /// [op:numeric-multiply](https://www.w3.org/TR/xpath-functions/#func-numeric-multiply) and other XSD multiplications Multiply(Box, Box), /// [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide) and other XSD divides Divide(Box, Box), /// [op:numeric-unary-plus](https://www.w3.org/TR/xpath-functions/#func-numeric-unary-plus) and other XSD unary plus UnaryPlus(Box), /// [op:numeric-unary-minus](https://www.w3.org/TR/xpath-functions/#func-numeric-unary-minus) and other XSD unary minus UnaryMinus(Box), /// [fn:not](https://www.w3.org/TR/xpath-functions/#func-not) Not(Box), /// [EXISTS](https://www.w3.org/TR/sparql11-query/#func-filter-exists) Exists(Box), /// [BOUND](https://www.w3.org/TR/sparql11-query/#func-bound) Bound(Variable), /// [IF](https://www.w3.org/TR/sparql11-query/#func-if) If(Box, Box, Box), /// [COALESCE](https://www.w3.org/TR/sparql11-query/#func-coalesce) Coalesce(Vec), /// A regular function call FunctionCall(Function, Vec), } impl fmt::Display for Expression { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Expression::NamedNode(node) => node.fmt(f), Expression::Literal(l) => l.fmt(f), Expression::Variable(var) => var.fmt(f), Expression::Or(a, b) => write!(f, "(|| {} {})", a, b), Expression::And(a, b) => write!(f, "(&& {} {})", a, b), Expression::Equal(a, b) => write!(f, "(= {} {})", a, b), Expression::SameTerm(a, b) => write!(f, "(sameTerm {} {})", a, b), Expression::Greater(a, b) => write!(f, "(> {} {})", a, b), Expression::GreaterOrEqual(a, b) => write!(f, "(>= {} {})", a, b), Expression::Less(a, b) => write!(f, "(< {} {})", a, b), Expression::LessOrEqual(a, b) => write!(f, "(<= {} {})", a, b), Expression::In(a, b) => { write!(f, "(in {}", a)?; for p in b { write!(f, " {}", p)?; } write!(f, ")") } Expression::Add(a, b) => write!(f, "(+ {} {})", a, b), Expression::Subtract(a, b) => write!(f, "(- {} {})", a, b), Expression::Multiply(a, b) => write!(f, "(* {} {})", a, b), Expression::Divide(a, b) => write!(f, "(/ {} {})", a, b), Expression::UnaryPlus(e) => write!(f, "(+ {})", e), Expression::UnaryMinus(e) => write!(f, "(- {})", e), Expression::Not(e) => write!(f, "(! {})", e), Expression::FunctionCall(function, parameters) => { write!(f, "({}", function)?; for p in parameters { write!(f, " {}", p)?; } write!(f, ")") } Expression::Exists(p) => write!(f, "(exists {})", p), Expression::Bound(v) => write!(f, "(bound {})", v), Expression::If(a, b, c) => write!(f, "(if {} {} {})", a, b, c), Expression::Coalesce(parameters) => { write!(f, "(coalesce")?; for p in parameters { write!(f, " {}", p)?; } write!(f, ")") } } } } impl From for Expression { fn from(p: NamedNode) -> Self { Expression::NamedNode(p) } } impl From for Expression { fn from(p: Literal) -> Self { Expression::Literal(p) } } impl From for Expression { fn from(v: Variable) -> Self { Expression::Variable(v) } } impl From for Expression { fn from(p: NamedNodePattern) -> Self { match p { NamedNodePattern::NamedNode(p) => p.into(), NamedNodePattern::Variable(p) => p.into(), } } } struct SparqlExpression<'a>(&'a Expression); impl<'a> fmt::Display for SparqlExpression<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { Expression::NamedNode(node) => node.fmt(f), Expression::Literal(l) => l.fmt(f), Expression::Variable(var) => var.fmt(f), Expression::Or(a, b) => write!( f, "({} || {})", SparqlExpression(&*a), SparqlExpression(&*b) ), Expression::And(a, b) => write!( f, "({} && {})", SparqlExpression(&*a), SparqlExpression(&*b) ), Expression::Equal(a, b) => { write!(f, "({} = {})", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::SameTerm(a, b) => { write!( f, "sameTerm({}, {})", SparqlExpression(&*a), SparqlExpression(&*b) ) } Expression::Greater(a, b) => { write!(f, "({} > {})", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::GreaterOrEqual(a, b) => write!( f, "({} >= {})", SparqlExpression(&*a), SparqlExpression(&*b) ), Expression::Less(a, b) => { write!(f, "({} < {})", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::LessOrEqual(a, b) => write!( f, "({} <= {})", SparqlExpression(&*a), SparqlExpression(&*b) ), Expression::In(a, b) => { write!(f, "({} IN ", SparqlExpression(&*a))?; write_arg_list(b.iter().map(|p| SparqlExpression(&*p)), f)?; write!(f, ")") } Expression::Add(a, b) => { write!(f, "{} + {}", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::Subtract(a, b) => { write!(f, "{} - {}", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::Multiply(a, b) => { write!(f, "{} * {}", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::Divide(a, b) => { write!(f, "{} / {}", SparqlExpression(&*a), SparqlExpression(&*b)) } Expression::UnaryPlus(e) => write!(f, "+{}", SparqlExpression(&*e)), Expression::UnaryMinus(e) => write!(f, "-{}", SparqlExpression(&*e)), Expression::Not(e) => match e.as_ref() { Expression::Exists(p) => write!(f, "NOT EXISTS {{ {} }}", SparqlGraphPattern(&*p)), e => write!(f, "!{}", SparqlExpression(&*e)), }, Expression::FunctionCall(function, parameters) => { write!(f, "{}", function)?; write_arg_list(parameters.iter().map(|p| SparqlExpression(&*p)), f) } Expression::Bound(v) => write!(f, "BOUND({})", v), Expression::Exists(p) => write!(f, "EXISTS {{ {} }}", SparqlGraphPattern(&*p)), Expression::If(a, b, c) => write!( f, "IF({}, {}, {})", SparqlExpression(&*a), SparqlExpression(&*b), SparqlExpression(&*c) ), Expression::Coalesce(parameters) => { write!(f, "COALESCE")?; write_arg_list(parameters.iter().map(|p| SparqlExpression(&*p)), f) } } } } fn write_arg_list( params: impl IntoIterator, f: &mut fmt::Formatter<'_>, ) -> fmt::Result { write!(f, "(")?; let mut cont = false; for p in params { if cont { write!(f, ", ")?; } p.fmt(f)?; cont = true; } write!(f, ")") } /// A function name #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum Function { Str, Lang, LangMatches, Datatype, Iri, BNode, Rand, Abs, Ceil, Floor, Round, Concat, SubStr, StrLen, Replace, UCase, LCase, EncodeForUri, Contains, StrStarts, StrEnds, StrBefore, StrAfter, Year, Month, Day, Hours, Minutes, Seconds, Timezone, Tz, Now, Uuid, StrUuid, Md5, Sha1, Sha256, Sha384, Sha512, StrLang, StrDt, IsIri, IsBlank, IsLiteral, IsNumeric, Regex, Triple, Subject, Predicate, Object, IsTriple, Custom(NamedNode), } impl fmt::Display for Function { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Function::Str => write!(f, "STR"), Function::Lang => write!(f, "LANG"), Function::LangMatches => write!(f, "LANGMATCHES"), Function::Datatype => write!(f, "DATATYPE"), Function::Iri => write!(f, "IRI"), Function::BNode => write!(f, "BNODE"), Function::Rand => write!(f, "RAND"), Function::Abs => write!(f, "ABS"), Function::Ceil => write!(f, "CEIL"), Function::Floor => write!(f, "FLOOR"), Function::Round => write!(f, "ROUND"), Function::Concat => write!(f, "CONCAT"), Function::SubStr => write!(f, "SUBSTR"), Function::StrLen => write!(f, "STRLEN"), Function::Replace => write!(f, "REPLACE"), Function::UCase => write!(f, "UCASE"), Function::LCase => write!(f, "LCASE"), Function::EncodeForUri => write!(f, "ENCODE_FOR_URI"), Function::Contains => write!(f, "CONTAINS"), Function::StrStarts => write!(f, "STRSTATS"), Function::StrEnds => write!(f, "STRENDS"), Function::StrBefore => write!(f, "STRBEFORE"), Function::StrAfter => write!(f, "STRAFTER"), Function::Year => write!(f, "YEAR"), Function::Month => write!(f, "MONTH"), Function::Day => write!(f, "DAY"), Function::Hours => write!(f, "HOURS"), Function::Minutes => write!(f, "MINUTES"), Function::Seconds => write!(f, "SECONDS"), Function::Timezone => write!(f, "TIMEZONE"), Function::Tz => write!(f, "TZ"), Function::Now => write!(f, "NOW"), Function::Uuid => write!(f, "UUID"), Function::StrUuid => write!(f, "STRUUID"), Function::Md5 => write!(f, "MD5"), Function::Sha1 => write!(f, "SHA1"), Function::Sha256 => write!(f, "SHA256"), Function::Sha384 => write!(f, "SHA384"), Function::Sha512 => write!(f, "SHA512"), Function::StrLang => write!(f, "STRLANG"), Function::StrDt => write!(f, "STRDT"), Function::IsIri => write!(f, "isIRI"), Function::IsBlank => write!(f, "isBLANK"), Function::IsLiteral => write!(f, "isLITERAL"), Function::IsNumeric => write!(f, "isNUMERIC"), Function::Regex => write!(f, "REGEX"), Function::Triple => write!(f, "TRIPLE"), Function::Subject => write!(f, "SUBJECT"), Function::Predicate => write!(f, "PREDICATE"), Function::Object => write!(f, "OBJECT"), Function::IsTriple => write!(f, "isTRIPLE"), Function::Custom(iri) => iri.fmt(f), } } } /// A SPARQL query [graph pattern](https://www.w3.org/TR/sparql11-query/#sparqlQuery) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphPattern { /// A [basic graph pattern](https://www.w3.org/TR/sparql11-query/#defn_BasicGraphPattern) Bgp(Vec), /// A [property path pattern](https://www.w3.org/TR/sparql11-query/#defn_evalPP_predicate) Path { subject: TermPattern, path: PropertyPathExpression, object: TermPattern, }, /// [Join](https://www.w3.org/TR/sparql11-query/#defn_algJoin) Join { left: Box, right: Box, }, /// [LeftJoin](https://www.w3.org/TR/sparql11-query/#defn_algLeftJoin) LeftJoin { left: Box, right: Box, expr: Option, }, /// [Filter](https://www.w3.org/TR/sparql11-query/#defn_algFilter) Filter { expr: Expression, inner: Box, }, /// [Union](https://www.w3.org/TR/sparql11-query/#defn_algUnion) Union { left: Box, right: Box, }, Graph { graph_name: NamedNodePattern, inner: Box, }, /// [Extend](https://www.w3.org/TR/sparql11-query/#defn_extend) Extend { inner: Box, var: Variable, expr: Expression, }, /// [Minus](https://www.w3.org/TR/sparql11-query/#defn_algMinus) Minus { left: Box, right: Box, }, /// A table used to provide inline values Table { variables: Vec, rows: Vec>>, }, /// [OrderBy](https://www.w3.org/TR/sparql11-query/#defn_algOrdered) OrderBy { inner: Box, condition: Vec, }, /// [Project](https://www.w3.org/TR/sparql11-query/#defn_algProjection) Project { inner: Box, projection: Vec, }, /// [Distinct](https://www.w3.org/TR/sparql11-query/#defn_algDistinct) Distinct { inner: Box }, /// [Reduced](https://www.w3.org/TR/sparql11-query/#defn_algReduced) Reduced { inner: Box }, /// [Slice](https://www.w3.org/TR/sparql11-query/#defn_algSlice) Slice { inner: Box, start: usize, length: Option, }, /// [Group](https://www.w3.org/TR/sparql11-federated-query/#aggregateAlgebra) Group { inner: Box, by: Vec, aggregates: Vec<(Variable, AggregationFunction)>, }, /// [Service](https://www.w3.org/TR/sparql11-federated-query/#defn_evalService) Service { name: NamedNodePattern, pattern: Box, silent: bool, }, } impl fmt::Display for GraphPattern { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GraphPattern::Bgp(p) => { write!(f, "(bgp")?; for pattern in p { write!(f, " {} .", pattern)?; } write!(f, ")") } GraphPattern::Path { subject, path, object, } => write!(f, "(path {} {} {})", subject, path, object), GraphPattern::Join { left, right } => write!(f, "(join {} {})", left, right), GraphPattern::LeftJoin { left, right, expr } => { if let Some(expr) = expr { write!(f, "(leftjoin {} {} {})", left, right, expr) } else { write!(f, "(leftjoin {} {})", left, right) } } GraphPattern::Filter { expr, inner } => write!(f, "(filter {} {})", expr, inner), GraphPattern::Union { left, right } => write!(f, "(union {} {})", left, right), GraphPattern::Graph { graph_name, inner } => { write!(f, "(graph {} {})", graph_name, inner) } GraphPattern::Extend { inner, var, expr } => { write!(f, "(extend ({} {}) {})", var, expr, inner) } GraphPattern::Minus { left, right } => write!(f, "(minus {} {})", left, right), GraphPattern::Service { name, pattern, silent, } => { if *silent { write!(f, "(service silent {} {})", name, pattern) } else { write!(f, "(service {} {})", name, pattern) } } GraphPattern::Group { inner, by, aggregates, } => write!( f, "(group ({}) ({}) {})", by.iter() .map(|v| v.name.as_str()) .collect::>() .join(" "), aggregates .iter() .map(|(a, v)| format!("({} {})", v, a)) .collect::>() .join(" "), inner ), GraphPattern::Table { variables, rows } => { write!(f, "(table (vars")?; for var in variables { write!(f, " {}", var)?; } write!(f, ")")?; for row in rows { write!(f, " (row")?; for (value, var) in row.iter().zip(variables) { if let Some(value) = value { write!(f, " ({} {})", var, value)?; } } write!(f, ")")?; } write!(f, ")") } GraphPattern::OrderBy { inner, condition } => write!( f, "(order ({}) {})", condition .iter() .map(|c| c.to_string()) .collect::>() .join(" "), inner ), GraphPattern::Project { inner, projection } => write!( f, "(project ({}) {})", projection .iter() .map(|v| v.to_string()) .collect::>() .join(" "), inner ), GraphPattern::Distinct { inner } => write!(f, "(distinct {})", inner), GraphPattern::Reduced { inner } => write!(f, "(reduced {})", inner), GraphPattern::Slice { inner, start, length, } => write!( f, "(slice {} {} {})", start, length .map(|l| l.to_string()) .unwrap_or_else(|| '_'.to_string()), inner ), } } } impl Default for GraphPattern { fn default() -> Self { GraphPattern::Bgp(Vec::default()) } } impl GraphPattern { pub fn visible_variables(&self) -> BTreeSet<&Variable> { let mut vars = BTreeSet::default(); self.add_visible_variables(&mut vars); vars } fn add_visible_variables<'a>(&'a self, vars: &mut BTreeSet<&'a Variable>) { match self { GraphPattern::Bgp(p) => { for pattern in p { if let TermPattern::Variable(s) = &pattern.subject { vars.insert(s); } if let NamedNodePattern::Variable(p) = &pattern.predicate { vars.insert(p); } if let TermPattern::Variable(o) = &pattern.object { vars.insert(o); } } } GraphPattern::Path { subject, object, .. } => { if let TermPattern::Variable(s) = subject { vars.insert(s); } if let TermPattern::Variable(o) = object { vars.insert(o); } } GraphPattern::Join { left, right } | GraphPattern::LeftJoin { left, right, .. } | GraphPattern::Union { left, right } => { left.add_visible_variables(vars); right.add_visible_variables(vars); } GraphPattern::Filter { inner, .. } => inner.add_visible_variables(vars), GraphPattern::Graph { graph_name, inner } => { if let NamedNodePattern::Variable(ref g) = graph_name { vars.insert(g); } inner.add_visible_variables(vars); } GraphPattern::Extend { inner, var, .. } => { vars.insert(var); inner.add_visible_variables(vars); } GraphPattern::Minus { left, .. } => left.add_visible_variables(vars), GraphPattern::Service { pattern, .. } => pattern.add_visible_variables(vars), GraphPattern::Group { by, aggregates, .. } => { vars.extend(by); for (v, _) in aggregates { vars.insert(v); } } GraphPattern::Table { variables, .. } => vars.extend(variables), GraphPattern::Project { projection, .. } => vars.extend(projection.iter()), GraphPattern::OrderBy { inner, .. } | GraphPattern::Distinct { inner } | GraphPattern::Reduced { inner } | GraphPattern::Slice { inner, .. } => inner.add_visible_variables(vars), } } } struct SparqlGraphPattern<'a>(&'a GraphPattern); impl<'a> fmt::Display for SparqlGraphPattern<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { GraphPattern::Bgp(p) => { for pattern in p { write!(f, "{} .", pattern)? } Ok(()) } GraphPattern::Path { subject, path, object, } => write!(f, "{} {} {} .", subject, SparqlPropertyPath(path), object), GraphPattern::Join { left, right } => write!( f, "{} {}", SparqlGraphPattern(&*left), SparqlGraphPattern(&*right) ), GraphPattern::LeftJoin { left, right, expr } => { if let Some(expr) = expr { write!( f, "{} OPTIONAL {{ {} FILTER({}) }}", SparqlGraphPattern(&*left), SparqlGraphPattern(&*right), SparqlExpression(expr) ) } else { write!( f, "{} OPTIONAL {{ {} }}", SparqlGraphPattern(&*left), SparqlGraphPattern(&*right) ) } } GraphPattern::Filter { expr, inner } => write!( f, "{} FILTER({})", SparqlGraphPattern(&*inner), SparqlExpression(expr) ), GraphPattern::Union { left, right } => write!( f, "{{ {} }} UNION {{ {} }}", SparqlGraphPattern(&*left), SparqlGraphPattern(&*right), ), GraphPattern::Graph { graph_name, inner } => { write!( f, "GRAPH {} {{ {} }}", graph_name, SparqlGraphPattern(&*inner) ) } GraphPattern::Extend { inner, var, expr } => write!( f, "{} BIND({} AS {})", SparqlGraphPattern(&*inner), SparqlExpression(expr), var ), GraphPattern::Minus { left, right } => write!( f, "{} MINUS {{ {} }}", SparqlGraphPattern(&*left), SparqlGraphPattern(&*right) ), GraphPattern::Service { name, pattern, silent, } => { if *silent { write!( f, "SERVICE SILENT {} {{ {} }}", name, SparqlGraphPattern(&*pattern) ) } else { write!( f, "SERVICE {} {{ {} }}", name, SparqlGraphPattern(&*pattern) ) } } GraphPattern::Table { variables, rows } => { write!(f, "VALUES ( ")?; for var in variables { write!(f, "{} ", var)?; } write!(f, ") {{ ")?; for row in rows { write!(f, "( ")?; for val in row { match val { Some(val) => write!(f, "{} ", val), None => write!(f, "UNDEF "), }?; } write!(f, ") ")?; } write!(f, " }}") } GraphPattern::Group { inner, by, aggregates, } => write!( f, "{{ SELECT {} WHERE {{ {} }} GROUP BY {} }}", aggregates .iter() .map(|(v, a)| format!("({} AS {})", SparqlAggregationFunction(a), v)) .chain(by.iter().map(|e| e.to_string())) .collect::>() .join(" "), SparqlGraphPattern(&*inner), by.iter() .map(|e| format!("({})", e.to_string())) .collect::>() .join(" ") ), p => write!( f, "{{ {} }}", SparqlGraphRootPattern { pattern: p, dataset: None } ), } } } pub(crate) struct SparqlGraphRootPattern<'a> { pub(crate) pattern: &'a GraphPattern, pub(crate) dataset: Option<&'a QueryDataset>, } impl<'a> fmt::Display for SparqlGraphRootPattern<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut distinct = false; let mut reduced = false; let mut order = None; let mut start = 0; let mut length = None; let mut project: &[Variable] = &[]; let mut child = self.pattern; loop { match child { GraphPattern::OrderBy { inner, condition } => { order = Some(condition); child = &*inner; } GraphPattern::Project { inner, projection } if project.is_empty() => { project = projection; child = &*inner; } GraphPattern::Distinct { inner } => { distinct = true; child = &*inner; } GraphPattern::Reduced { inner } => { reduced = true; child = &*inner; } GraphPattern::Slice { inner, start: s, length: l, } => { start = *s; length = *l; child = inner; } p => { write!(f, "SELECT ")?; if distinct { write!(f, "DISTINCT ")?; } if reduced { write!(f, "REDUCED ")?; } build_sparql_select_arguments(project).fmt(f)?; if let Some(dataset) = self.dataset { dataset.fmt(f)?; } write!(f, " WHERE {{ {} }}", SparqlGraphPattern(p))?; if let Some(order) = order { write!( f, " ORDER BY {}", order .iter() .map(|c| SparqlOrderComparator(c).to_string()) .collect::>() .join(" ") )?; } if start > 0 { write!(f, " OFFSET {}", start)?; } if let Some(length) = length { write!(f, " LIMIT {}", length)?; } return Ok(()); } } } } } fn build_sparql_select_arguments(args: &[Variable]) -> String { if args.is_empty() { "*".to_owned() } else { args.iter() .map(|v| v.to_string()) .collect::>() .join(" ") } } /// A set function used in aggregates (c.f. [`GraphPattern::Group`]) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum AggregationFunction { /// [Count](https://www.w3.org/TR/sparql11-query/#defn_aggCount) Count { expr: Option>, distinct: bool, }, /// [Sum](https://www.w3.org/TR/sparql11-query/#defn_aggSum) Sum { expr: Box, distinct: bool, }, /// [Avg](https://www.w3.org/TR/sparql11-query/#defn_aggAvg) Avg { expr: Box, distinct: bool, }, /// [Min](https://www.w3.org/TR/sparql11-query/#defn_aggMin) Min { expr: Box, distinct: bool, }, /// [Max](https://www.w3.org/TR/sparql11-query/#defn_aggMax) Max { expr: Box, distinct: bool, }, /// [GroupConcat](https://www.w3.org/TR/sparql11-query/#defn_aggGroupConcat) GroupConcat { expr: Box, distinct: bool, separator: Option, }, /// [Sample](https://www.w3.org/TR/sparql11-query/#defn_aggSample) Sample { expr: Box, distinct: bool, }, /// Custom function Custom { name: NamedNode, expr: Box, distinct: bool, }, } impl fmt::Display for AggregationFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { AggregationFunction::Count { expr, distinct } => { if *distinct { if let Some(expr) = expr { write!(f, "(count distinct {})", expr) } else { write!(f, "(count distinct)") } } else if let Some(expr) = expr { write!(f, "(count {})", expr) } else { write!(f, "(count)") } } AggregationFunction::Sum { expr, distinct } => { if *distinct { write!(f, "(sum distinct {})", expr) } else { write!(f, "(sum {})", expr) } } AggregationFunction::Avg { expr, distinct } => { if *distinct { write!(f, "(avg distinct {})", expr) } else { write!(f, "(avg {})", expr) } } AggregationFunction::Min { expr, distinct } => { if *distinct { write!(f, "(min distinct {})", expr) } else { write!(f, "(min {})", expr) } } AggregationFunction::Max { expr, distinct } => { if *distinct { write!(f, "(max distinct {})", expr) } else { write!(f, "(max {})", expr) } } AggregationFunction::Sample { expr, distinct } => { if *distinct { write!(f, "(sample distinct {})", expr) } else { write!(f, "(sample {})", expr) } } AggregationFunction::GroupConcat { expr, distinct, separator, } => { if *distinct { if let Some(separator) = separator { write!(f, "(group_concat distinct {} ", expr)?; print_quoted_str(separator, f)?; write!(f, ")") } else { write!(f, "(group_concat distinct {})", expr) } } else if let Some(separator) = separator { write!(f, "(group_concat {} ", expr)?; print_quoted_str(separator, f)?; write!(f, ")") } else { write!(f, "(group_concat {})", expr) } } AggregationFunction::Custom { name, expr, distinct, } => { if *distinct { write!(f, "({} distinct {})", name, expr) } else { write!(f, "({} {})", name, expr) } } } } } struct SparqlAggregationFunction<'a>(&'a AggregationFunction); impl<'a> fmt::Display for SparqlAggregationFunction<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { AggregationFunction::Count { expr, distinct } => { if *distinct { if let Some(expr) = expr { write!(f, "COUNT(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "COUNT(DISTINCT *)") } } else if let Some(expr) = expr { write!(f, "COUNT({})", SparqlExpression(expr)) } else { write!(f, "COUNT(*)") } } AggregationFunction::Sum { expr, distinct } => { if *distinct { write!(f, "SUM(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "SUM({})", SparqlExpression(expr)) } } AggregationFunction::Min { expr, distinct } => { if *distinct { write!(f, "MIN(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "MIN({})", SparqlExpression(expr)) } } AggregationFunction::Max { expr, distinct } => { if *distinct { write!(f, "MAX(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "MAX({})", SparqlExpression(expr)) } } AggregationFunction::Avg { expr, distinct } => { if *distinct { write!(f, "AVG(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "AVG({})", SparqlExpression(expr)) } } AggregationFunction::Sample { expr, distinct } => { if *distinct { write!(f, "SAMPLE(DISTINCT {})", SparqlExpression(expr)) } else { write!(f, "SAMPLE({})", SparqlExpression(expr)) } } AggregationFunction::GroupConcat { expr, distinct, separator, } => { if *distinct { if let Some(separator) = separator { write!( f, "GROUP_CONCAT(DISTINCT {}; SEPARATOR = ", SparqlExpression(expr) )?; print_quoted_str(separator, f)?; write!(f, ")") } else { write!(f, "GROUP_CONCAT(DISTINCT {})", SparqlExpression(expr)) } } else if let Some(separator) = separator { write!(f, "GROUP_CONCAT({}; SEPARATOR = ", SparqlExpression(expr))?; print_quoted_str(separator, f)?; write!(f, ")") } else { write!(f, "GROUP_CONCAT({})", SparqlExpression(expr)) } } AggregationFunction::Custom { name, expr, distinct, } => { if *distinct { write!(f, "{}(DISTINCT {})", name, SparqlExpression(expr)) } else { write!(f, "{}({})", name, SparqlExpression(expr)) } } } } } /// An ordering comparator used by [`GraphPattern::OrderBy`] #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum OrderComparator { /// Ascending order Asc(Expression), /// Descending order Desc(Expression), } impl fmt::Display for OrderComparator { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { OrderComparator::Asc(e) => write!(f, "(asc {})", e), OrderComparator::Desc(e) => write!(f, "(desc {})", e), } } } struct SparqlOrderComparator<'a>(&'a OrderComparator); impl<'a> fmt::Display for SparqlOrderComparator<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { OrderComparator::Asc(e) => write!(f, "ASC({})", SparqlExpression(e)), OrderComparator::Desc(e) => write!(f, "DESC({})", SparqlExpression(e)), } } } /// A SPARQL query [dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub struct QueryDataset { pub default: Vec, pub named: Option>, } impl fmt::Display for QueryDataset { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for g in &self.default { write!(f, " FROM {}", g)?; } if let Some(named) = &self.named { for g in named { write!(f, " FROM NAMED {}", g)?; } } Ok(()) } } /// A target RDF graph for update operations /// /// Could be a specific graph, all named graphs or the complete dataset. #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub enum GraphTarget { NamedNode(NamedNode), DefaultGraph, NamedGraphs, AllGraphs, } impl fmt::Display for GraphTarget { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NamedNode(node) => write!(f, "GRAPH {}", node), Self::DefaultGraph => write!(f, "DEFAULT"), Self::NamedGraphs => write!(f, "NAMED"), Self::AllGraphs => write!(f, "ALL"), } } } impl From for GraphTarget { fn from(node: NamedNode) -> Self { Self::NamedNode(node) } } impl From for GraphTarget { fn from(graph_name: GraphName) -> Self { match graph_name { GraphName::NamedNode(node) => Self::NamedNode(node), GraphName::DefaultGraph => Self::DefaultGraph, } } }