Moves SPARQL parser and algebra to an independent crate

pull/171/head
Tpt 4 years ago
parent e4f97bafb3
commit be4a5b0b6b
  1. 1
      Cargo.toml
  2. 3
      lib/Cargo.toml
  3. 1795
      lib/src/sparql/algebra.rs
  4. 10
      lib/src/sparql/eval.rs
  5. 47
      lib/src/sparql/mod.rs
  6. 6
      lib/src/sparql/model.rs
  7. 2
      lib/src/sparql/plan.rs
  8. 101
      lib/src/sparql/plan_builder.rs
  9. 276
      lib/src/sparql/update.rs
  10. 19
      spargebra/Cargo.toml
  11. 40
      spargebra/README.md
  12. 1331
      spargebra/src/algebra.rs
  13. 36
      spargebra/src/lib.rs
  14. 270
      spargebra/src/parser.rs
  15. 181
      spargebra/src/query.rs
  16. 426
      spargebra/src/term.rs
  17. 199
      spargebra/src/update.rs
  18. 7
      testsuite/tests/sparql.rs

@ -3,6 +3,7 @@ members = [
"lib", "lib",
"python", "python",
"server", "server",
"spargebra",
"testsuite", "testsuite",
"wikibase" "wikibase"
] ]

@ -37,13 +37,14 @@ rio_turtle = "0.5"
rio_xml = "0.5" rio_xml = "0.5"
hex = "0.4" hex = "0.4"
nom = "6" nom = "6"
peg = "0.7"
siphasher = "0.3" siphasher = "0.3"
lasso = {version="0.5", features=["multi-threaded", "inline-more"]} lasso = {version="0.5", features=["multi-threaded", "inline-more"]}
sophia_api = { version = "0.6.2", optional = true } sophia_api = { version = "0.6.2", optional = true }
http = "0.2" http = "0.2"
httparse = { version = "1", optional = true } httparse = { version = "1", optional = true }
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
spargebra = { version = "0.1", path="../spargebra" }
[dev-dependencies] [dev-dependencies]
rayon = "1" rayon = "1"

File diff suppressed because it is too large Load Diff

@ -2,7 +2,7 @@ use crate::model::vocab::{rdf, xsd};
use crate::model::xsd::*; use crate::model::xsd::*;
use crate::model::Triple; use crate::model::Triple;
use crate::model::{BlankNode, LiteralRef, NamedNodeRef}; use crate::model::{BlankNode, LiteralRef, NamedNodeRef};
use crate::sparql::algebra::{GraphPattern, Query, QueryDataset}; use crate::sparql::algebra::{Query, QueryDataset};
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::sparql::plan::*; use crate::sparql::plan::*;
@ -18,6 +18,7 @@ use rand::random;
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
use sha1::Sha1; use sha1::Sha1;
use sha2::{Sha256, Sha384, Sha512}; use sha2::{Sha256, Sha384, Sha512};
use spargebra::algebra::GraphPattern;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
@ -510,11 +511,14 @@ where
get_pattern_value(service_name, from) get_pattern_value(service_name, from)
.ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?, .ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?,
)?, )?,
Query::Select { Query {
dataset: QueryDataset::default(), inner: spargebra::Query::Select {
dataset: None,
pattern: graph_pattern.clone(), pattern: graph_pattern.clone(),
base_iri: self.base_iri.as_ref().map(|iri| iri.as_ref().clone()), base_iri: self.base_iri.as_ref().map(|iri| iri.as_ref().clone()),
}, },
dataset: QueryDataset::new(),
},
)? { )? {
Ok(self.encode_bindings(variables, iter)) Ok(self.encode_bindings(variables, iter))
} else { } else {

@ -10,7 +10,6 @@ mod eval;
mod http; mod http;
mod json_results; mod json_results;
mod model; mod model;
mod parser;
mod plan; mod plan;
mod plan_builder; mod plan_builder;
mod service; mod service;
@ -27,13 +26,13 @@ pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionIter; pub use crate::sparql::model::QuerySolutionIter;
pub use crate::sparql::model::QueryTripleIter; pub use crate::sparql::model::QueryTripleIter;
pub use crate::sparql::model::{Variable, VariableNameParseError}; pub use crate::sparql::model::{Variable, VariableNameParseError};
pub use crate::sparql::parser::ParseError;
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
pub use crate::sparql::service::ServiceHandler; pub use crate::sparql::service::ServiceHandler;
use crate::sparql::service::{EmptyServiceHandler, ErrorConversionServiceHandler}; use crate::sparql::service::{EmptyServiceHandler, ErrorConversionServiceHandler};
use crate::sparql::update::SimpleUpdateEvaluator; use crate::sparql::update::SimpleUpdateEvaluator;
use crate::store::numeric_encoder::StrContainer; use crate::store::numeric_encoder::StrContainer;
use crate::store::{ReadableEncodedStore, StoreOrParseError, WritableEncodedStore}; use crate::store::{ReadableEncodedStore, StoreOrParseError, WritableEncodedStore};
pub use spargebra::ParseError;
use std::convert::TryInto; use std::convert::TryInto;
use std::io; use std::io;
use std::rc::Rc; use std::rc::Rc;
@ -43,27 +42,31 @@ pub(crate) fn evaluate_query<R: ReadableEncodedStore + 'static>(
query: impl TryInto<Query, Error = impl Into<EvaluationError>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResults, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
match query.try_into().map_err(|e| e.into())? { let query = query.try_into().map_err(|e| e.into())?;
Query::Select { let dataset = DatasetView::new(store, &query.dataset)?;
pattern, match query.inner {
base_iri, spargebra::Query::Select {
dataset, pattern, base_iri, ..
} => { } => {
let dataset = DatasetView::new(store, &dataset)?;
let (plan, variables) = PlanBuilder::build(&dataset, &pattern)?; let (plan, variables) = PlanBuilder::build(&dataset, &pattern)?;
SimpleEvaluator::new( SimpleEvaluator::new(
Rc::new(dataset), Rc::new(dataset),
base_iri.map(Rc::new), base_iri.map(Rc::new),
options.service_handler, options.service_handler,
) )
.evaluate_select_plan(&plan, Rc::new(variables)) .evaluate_select_plan(
&plan,
Rc::new(
variables
.into_iter()
.map(|v| Variable::new_unchecked(v.name))
.collect(),
),
)
} }
Query::Ask { spargebra::Query::Ask {
pattern, pattern, base_iri, ..
base_iri,
dataset,
} => { } => {
let dataset = DatasetView::new(store, &dataset)?;
let (plan, _) = PlanBuilder::build(&dataset, &pattern)?; let (plan, _) = PlanBuilder::build(&dataset, &pattern)?;
SimpleEvaluator::new( SimpleEvaluator::new(
Rc::new(dataset), Rc::new(dataset),
@ -72,13 +75,12 @@ pub(crate) fn evaluate_query<R: ReadableEncodedStore + 'static>(
) )
.evaluate_ask_plan(&plan) .evaluate_ask_plan(&plan)
} }
Query::Construct { spargebra::Query::Construct {
template, template,
pattern, pattern,
base_iri, base_iri,
dataset, ..
} => { } => {
let dataset = DatasetView::new(store, &dataset)?;
let (plan, variables) = PlanBuilder::build(&dataset, &pattern)?; let (plan, variables) = PlanBuilder::build(&dataset, &pattern)?;
let construct = PlanBuilder::build_graph_template(&dataset, &template, variables)?; let construct = PlanBuilder::build_graph_template(&dataset, &template, variables)?;
SimpleEvaluator::new( SimpleEvaluator::new(
@ -88,12 +90,9 @@ pub(crate) fn evaluate_query<R: ReadableEncodedStore + 'static>(
) )
.evaluate_construct_plan(&plan, construct) .evaluate_construct_plan(&plan, construct)
} }
Query::Describe { spargebra::Query::Describe {
pattern, pattern, base_iri, ..
base_iri,
dataset,
} => { } => {
let dataset = DatasetView::new(store, &dataset)?;
let (plan, _) = PlanBuilder::build(&dataset, &pattern)?; let (plan, _) = PlanBuilder::build(&dataset, &pattern)?;
SimpleEvaluator::new( SimpleEvaluator::new(
Rc::new(dataset), Rc::new(dataset),
@ -192,6 +191,6 @@ pub(crate) fn evaluate_update<
where where
io::Error: From<StoreOrParseError<W::Error>>, io::Error: From<StoreOrParseError<W::Error>>,
{ {
SimpleUpdateEvaluator::new(read, write, update.base_iri.map(Rc::new), options) SimpleUpdateEvaluator::new(read, write, update.inner.base_iri.map(Rc::new), options)
.eval_all(&update.operations) .eval_all(&update.inner.operations, &update.using_datasets)
} }

@ -6,7 +6,6 @@ use crate::sparql::csv_results::{read_tsv_results, write_csv_results, write_tsv_
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::json_results::write_json_results; use crate::sparql::json_results::write_json_results;
use crate::sparql::xml_results::{read_xml_results, write_xml_results}; use crate::sparql::xml_results::{read_xml_results, write_xml_results};
use rand::random;
use std::error::Error; use std::error::Error;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::rc::Rc; use std::rc::Rc;
@ -443,11 +442,6 @@ impl Variable {
pub fn into_string(self) -> String { pub fn into_string(self) -> String {
self.name self.name
} }
#[inline]
pub(crate) fn new_random() -> Self {
Self::new_unchecked(format!("{:x}", random::<u128>()))
}
} }
impl fmt::Display for Variable { impl fmt::Display for Variable {

@ -1,6 +1,6 @@
use crate::sparql::algebra::GraphPattern;
use crate::sparql::model::Variable; use crate::sparql::model::Variable;
use crate::store::numeric_encoder::EncodedTerm; use crate::store::numeric_encoder::EncodedTerm;
use spargebra::algebra::GraphPattern;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::rc::Rc; use std::rc::Rc;

@ -1,9 +1,11 @@
use crate::model::{BlankNode, Literal, NamedNode, Term}; use crate::model::{LiteralRef, NamedNodeRef};
use crate::sparql::algebra::*;
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::Variable as OxVariable;
use crate::sparql::plan::*; use crate::sparql::plan::*;
use crate::store::numeric_encoder::{EncodedTerm, WriteEncoder}; use crate::store::numeric_encoder::{EncodedTerm, WriteEncoder};
use rand::random;
use spargebra::algebra::*;
use spargebra::term::*;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
use std::rc::Rc; use std::rc::Rc;
@ -40,7 +42,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<PlanNode, EvaluationError> { ) -> Result<PlanNode, EvaluationError> {
Ok(match pattern { Ok(match pattern {
GraphPattern::BGP(p) => self.build_for_bgp(p, variables, graph_name)?, GraphPattern::Bgp(p) => self.build_for_bgp(p, variables, graph_name)?,
GraphPattern::Path { GraphPattern::Path {
subject, subject,
path, path,
@ -147,7 +149,12 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
self.pattern_value_from_named_node_or_variable(name, variables)?; self.pattern_value_from_named_node_or_variable(name, variables)?;
PlanNode::Service { PlanNode::Service {
service_name, service_name,
variables: Rc::new(variables.clone()), variables: Rc::new(
variables
.iter()
.map(|v| OxVariable::new_unchecked(v.name.clone()))
.collect(),
),
child: Rc::new(child), child: Rc::new(child),
graph_pattern: Rc::new(*pattern.clone()), graph_pattern: Rc::new(*pattern.clone()),
silent: *silent, silent: *silent,
@ -411,7 +418,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
Function::Datatype => PlanExpression::Datatype(Box::new( Function::Datatype => PlanExpression::Datatype(Box::new(
self.build_for_expression(&parameters[0], variables, graph_name)?, self.build_for_expression(&parameters[0], variables, graph_name)?,
)), )),
Function::IRI => PlanExpression::Iri(Box::new(self.build_for_expression( Function::Iri => PlanExpression::Iri(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
@ -482,7 +489,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
variables, variables,
graph_name, graph_name,
)?)), )?)),
Function::EncodeForURI => PlanExpression::EncodeForUri(Box::new( Function::EncodeForUri => PlanExpression::EncodeForUri(Box::new(
self.build_for_expression(&parameters[0], variables, graph_name)?, self.build_for_expression(&parameters[0], variables, graph_name)?,
)), )),
Function::Contains => PlanExpression::Contains( Function::Contains => PlanExpression::Contains(
@ -544,29 +551,29 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
)?)), )?)),
Function::Now => PlanExpression::Now, Function::Now => PlanExpression::Now,
Function::UUID => PlanExpression::Uuid, Function::Uuid => PlanExpression::Uuid,
Function::StrUUID => PlanExpression::StrUuid, Function::StrUuid => PlanExpression::StrUuid,
Function::MD5 => PlanExpression::Md5(Box::new(self.build_for_expression( Function::Md5 => PlanExpression::Md5(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
)?)), )?)),
Function::SHA1 => PlanExpression::Sha1(Box::new(self.build_for_expression( Function::Sha1 => PlanExpression::Sha1(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
)?)), )?)),
Function::SHA256 => PlanExpression::Sha256(Box::new(self.build_for_expression( Function::Sha256 => PlanExpression::Sha256(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
)?)), )?)),
Function::SHA384 => PlanExpression::Sha384(Box::new(self.build_for_expression( Function::Sha384 => PlanExpression::Sha384(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
)?)), )?)),
Function::SHA512 => PlanExpression::Sha512(Box::new(self.build_for_expression( Function::Sha512 => PlanExpression::Sha512(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
@ -575,11 +582,11 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
Box::new(self.build_for_expression(&parameters[0], variables, graph_name)?), Box::new(self.build_for_expression(&parameters[0], variables, graph_name)?),
Box::new(self.build_for_expression(&parameters[1], variables, graph_name)?), Box::new(self.build_for_expression(&parameters[1], variables, graph_name)?),
), ),
Function::StrDT => PlanExpression::StrDt( Function::StrDt => PlanExpression::StrDt(
Box::new(self.build_for_expression(&parameters[0], variables, graph_name)?), Box::new(self.build_for_expression(&parameters[0], variables, graph_name)?),
Box::new(self.build_for_expression(&parameters[1], variables, graph_name)?), Box::new(self.build_for_expression(&parameters[1], variables, graph_name)?),
), ),
Function::IsIRI => PlanExpression::IsIri(Box::new(self.build_for_expression( Function::IsIri => PlanExpression::IsIri(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
variables, variables,
graph_name, graph_name,
@ -606,7 +613,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
}, },
), ),
Function::Custom(name) => { Function::Custom(name) => {
if name == "http://www.w3.org/2001/XMLSchema#boolean" { if name.iri == "http://www.w3.org/2001/XMLSchema#boolean" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::BooleanCast, PlanExpression::BooleanCast,
@ -614,7 +621,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"boolean", "boolean",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#double" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#double" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DoubleCast, PlanExpression::DoubleCast,
@ -622,7 +629,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"double", "double",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#float" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#float" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::FloatCast, PlanExpression::FloatCast,
@ -630,7 +637,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"float", "float",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#decimal" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#decimal" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DecimalCast, PlanExpression::DecimalCast,
@ -638,7 +645,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"decimal", "decimal",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#integer" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#integer" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::IntegerCast, PlanExpression::IntegerCast,
@ -646,7 +653,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"integer", "integer",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#date" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#date" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DateCast, PlanExpression::DateCast,
@ -654,7 +661,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"date", "date",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#time" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#time" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::TimeCast, PlanExpression::TimeCast,
@ -662,7 +669,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"time", "time",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#dateTime" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#dateTime" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DateTimeCast, PlanExpression::DateTimeCast,
@ -670,7 +677,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"dateTime", "dateTime",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#duration" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#duration" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DurationCast, PlanExpression::DurationCast,
@ -678,7 +685,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"duration", "duration",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#yearMonthDuration" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#yearMonthDuration" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::YearMonthDurationCast, PlanExpression::YearMonthDurationCast,
@ -686,7 +693,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"yearMonthDuration", "yearMonthDuration",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#dayTimeDuration" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#dayTimeDuration" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::DayTimeDurationCast, PlanExpression::DayTimeDurationCast,
@ -694,7 +701,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
graph_name, graph_name,
"dayTimeDuration", "dayTimeDuration",
)? )?
} else if name == "http://www.w3.org/2001/XMLSchema#string" { } else if name.iri == "http://www.w3.org/2001/XMLSchema#string" {
self.build_cast( self.build_cast(
parameters, parameters,
PlanExpression::StringCast, PlanExpression::StringCast,
@ -770,7 +777,9 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
TermOrVariable::Term(Term::BlankNode(bnode)) => { TermOrVariable::Term(Term::BlankNode(bnode)) => {
PatternValue::Variable(variable_key( PatternValue::Variable(variable_key(
variables, variables,
&Variable::new_unchecked(bnode.as_str()), &Variable {
name: bnode.id.clone(),
},
)) ))
//TODO: very bad hack to convert bnode to variable //TODO: very bad hack to convert bnode to variable
} }
@ -796,7 +805,7 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
fn encode_bindings( fn encode_bindings(
&mut self, &mut self,
table_variables: &[Variable], table_variables: &[Variable],
rows: &[Vec<Option<Term>>], rows: &[Vec<Option<NamedNodeOrLiteral>>],
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<Vec<EncodedTuple>, EvaluationError> { ) -> Result<Vec<EncodedTuple>, EvaluationError> {
let bindings_variables_keys = table_variables let bindings_variables_keys = table_variables
@ -808,7 +817,13 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
let mut result = EncodedTuple::with_capacity(variables.len()); let mut result = EncodedTuple::with_capacity(variables.len());
for (key, value) in row.iter().enumerate() { for (key, value) in row.iter().enumerate() {
if let Some(term) = value { if let Some(term) = value {
result.set(bindings_variables_keys[key], self.build_term(term)?); result.set(
bindings_variables_keys[key],
match term {
NamedNodeOrLiteral::NamedNode(node) => self.build_named_node(node),
NamedNodeOrLiteral::Literal(literal) => self.build_literal(literal),
}?,
);
} }
} }
Ok(result) Ok(result)
@ -961,7 +976,9 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
}) { }) {
to_id to_id
} else { } else {
to.push(Variable::new_random()); to.push(Variable {
name: format!("{:x}", random::<u128>()),
});
to.len() - 1 to.len() - 1
} }
} }
@ -1030,15 +1047,29 @@ impl<E: WriteEncoder<Error = EvaluationError>> PlanBuilder<E> {
} }
fn build_named_node(&mut self, node: &NamedNode) -> Result<EncodedTerm, EvaluationError> { fn build_named_node(&mut self, node: &NamedNode) -> Result<EncodedTerm, EvaluationError> {
self.encoder.encode_named_node(node.as_ref()) self.encoder
.encode_named_node(NamedNodeRef::new_unchecked(node.iri.as_str()))
} }
fn build_literal(&mut self, literal: &Literal) -> Result<EncodedTerm, EvaluationError> { fn build_literal(&mut self, literal: &Literal) -> Result<EncodedTerm, EvaluationError> {
self.encoder.encode_literal(literal.as_ref()) self.encoder.encode_literal(match literal {
Literal::Simple { value } => LiteralRef::new_simple_literal(value),
Literal::LanguageTaggedString { value, language } => {
LiteralRef::new_language_tagged_literal_unchecked(value, language.as_str())
}
Literal::Typed { value, datatype } => LiteralRef::new_typed_literal(
value,
NamedNodeRef::new_unchecked(datatype.iri.as_str()),
),
})
} }
fn build_term(&mut self, term: &Term) -> Result<EncodedTerm, EvaluationError> { fn build_term(&mut self, term: &Term) -> Result<EncodedTerm, EvaluationError> {
self.encoder.encode_term(term.as_ref()) match term {
Term::NamedNode(node) => self.build_named_node(node),
Term::BlankNode(_) => Err(EvaluationError::msg("Unexpected blank node")),
Term::Literal(literal) => self.build_literal(literal),
}
} }
} }

@ -1,16 +1,13 @@
use crate::error::{invalid_data_error, invalid_input_error}; use crate::error::{invalid_data_error, invalid_input_error};
use crate::io::GraphFormat; use crate::io::GraphFormat;
use crate::model::{BlankNode, GraphNameRef, NamedNode, NamedOrBlankNode, Quad, Term}; use crate::model::{BlankNode as OxBlankNode, GraphNameRef, LiteralRef, NamedNodeRef};
use crate::sparql::algebra::{ use crate::sparql::algebra::QueryDataset;
GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern,
QueryDataset, TermOrVariable,
};
use crate::sparql::dataset::DatasetView; use crate::sparql::dataset::DatasetView;
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
use crate::sparql::http::Client; use crate::sparql::http::Client;
use crate::sparql::plan::EncodedTuple; use crate::sparql::plan::EncodedTuple;
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
use crate::sparql::{EvaluationError, UpdateOptions, Variable}; use crate::sparql::{EvaluationError, UpdateOptions};
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrLookup, WriteEncoder, EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrLookup, WriteEncoder,
}; };
@ -18,6 +15,12 @@ use crate::store::{load_graph, ReadableEncodedStore, StoreOrParseError, Writable
use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT};
use http::{Method, Request, StatusCode}; use http::{Method, Request, StatusCode};
use oxiri::Iri; use oxiri::Iri;
use spargebra::algebra::{GraphPattern, GraphTarget, QuadPattern};
use spargebra::term::{
BlankNode, GraphName, Literal, NamedNode, NamedNodeOrVariable, NamedOrBlankNode, Quad, Term,
TermOrVariable, Variable,
};
use spargebra::GraphUpdateOperation;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::rc::Rc; use std::rc::Rc;
@ -53,23 +56,31 @@ where
} }
} }
pub fn eval_all(&mut self, updates: &[GraphUpdateOperation]) -> Result<(), EvaluationError> { pub fn eval_all(
for update in updates { &mut self,
self.eval(update)?; updates: &[GraphUpdateOperation],
using_datasets: &[Option<QueryDataset>],
) -> Result<(), EvaluationError> {
for (update, using_dataset) in updates.iter().zip(using_datasets) {
self.eval(update, using_dataset)?;
} }
Ok(()) Ok(())
} }
fn eval(&mut self, update: &GraphUpdateOperation) -> Result<(), EvaluationError> { fn eval(
&mut self,
update: &GraphUpdateOperation,
using_dataset: &Option<QueryDataset>,
) -> Result<(), EvaluationError> {
match update { match update {
GraphUpdateOperation::InsertData { data } => self.eval_insert_data(data), GraphUpdateOperation::InsertData { data } => self.eval_insert_data(data),
GraphUpdateOperation::DeleteData { data } => self.eval_delete_data(data), GraphUpdateOperation::DeleteData { data } => self.eval_delete_data(data),
GraphUpdateOperation::DeleteInsert { GraphUpdateOperation::DeleteInsert {
delete, delete,
insert, insert,
using,
pattern, pattern,
} => self.eval_delete_insert(delete, insert, using, pattern), ..
} => self.eval_delete_insert(delete, insert, using_dataset.as_ref().unwrap(), pattern),
GraphUpdateOperation::Load { silent, from, to } => { GraphUpdateOperation::Load { silent, from, to } => {
if let Err(error) = self.eval_load(from, to) { if let Err(error) = self.eval_load(from, to) {
if *silent { if *silent {
@ -176,7 +187,7 @@ where
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
let request = Request::builder() let request = Request::builder()
.method(Method::GET) .method(Method::GET)
.uri(from.as_str()) .uri(&from.iri)
.header( .header(
ACCEPT, ACCEPT,
"application/n-triples, text/turtle, application/rdf+xml", "application/n-triples, text/turtle, application/rdf+xml",
@ -207,7 +218,7 @@ where
)) ))
})?; })?;
let to_graph_name = if let Some(graph_name) = to { let to_graph_name = if let Some(graph_name) = to {
graph_name.as_ref().into() NamedNodeRef::new_unchecked(&graph_name.iri).into()
} else { } else {
GraphNameRef::DefaultGraph GraphNameRef::DefaultGraph
}; };
@ -216,17 +227,14 @@ where
response.into_body(), response.into_body(),
format, format,
to_graph_name, to_graph_name,
Some(from.as_str()), Some(&from.iri),
) )
.map_err(io::Error::from)?; .map_err(io::Error::from)?;
Ok(()) Ok(())
} }
fn eval_create(&mut self, graph: &NamedNode, silent: bool) -> Result<(), EvaluationError> { fn eval_create(&mut self, graph: &NamedNode, silent: bool) -> Result<(), EvaluationError> {
let encoded_graph_name = self let encoded_graph_name = self.encode_named_node_for_insertion(graph)?;
.write
.encode_named_node(graph.as_ref())
.map_err(to_eval_error)?;
if self if self
.read .read
.contains_encoded_named_graph(encoded_graph_name) .contains_encoded_named_graph(encoded_graph_name)
@ -250,11 +258,7 @@ where
fn eval_clear(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> { fn eval_clear(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> {
match graph { match graph {
GraphTarget::NamedNode(graph_name) => { GraphTarget::NamedNode(graph_name) => {
if let Some(graph_name) = self if let Some(graph_name) = self.encode_named_node_for_deletion(graph_name)? {
.read
.get_encoded_named_node(graph_name.as_ref())
.map_err(to_eval_error)?
{
if self if self
.read .read
.contains_encoded_named_graph(graph_name) .contains_encoded_named_graph(graph_name)
@ -305,11 +309,7 @@ where
fn eval_drop(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> { fn eval_drop(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> {
match graph { match graph {
GraphTarget::NamedNode(graph_name) => { GraphTarget::NamedNode(graph_name) => {
if let Some(graph_name) = self if let Some(graph_name) = self.encode_named_node_for_deletion(graph_name)? {
.read
.get_encoded_named_node(graph_name.as_ref())
.map_err(to_eval_error)?
{
if self if self
.read .read
.contains_encoded_named_graph(graph_name) .contains_encoded_named_graph(graph_name)
@ -350,34 +350,36 @@ where
fn encode_quad_for_insertion( fn encode_quad_for_insertion(
&mut self, &mut self,
quad: &Quad, quad: &Quad,
bnodes: &mut HashMap<BlankNode, BlankNode>, bnodes: &mut HashMap<BlankNode, OxBlankNode>,
) -> Result<Option<EncodedQuad>, EvaluationError> { ) -> Result<Option<EncodedQuad>, EvaluationError> {
Ok(Some(EncodedQuad { Ok(Some(EncodedQuad {
subject: match &quad.subject { subject: match &quad.subject {
NamedOrBlankNode::NamedNode(subject) => { NamedOrBlankNode::NamedNode(subject) => {
self.write.encode_named_node(subject.as_ref()) self.encode_named_node_for_insertion(subject)?
} }
NamedOrBlankNode::BlankNode(subject) => self NamedOrBlankNode::BlankNode(subject) => self
.write .write
.encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref()), .encode_blank_node(bnodes.entry(subject.clone()).or_default().as_ref())
}
.map_err(to_eval_error)?, .map_err(to_eval_error)?,
},
predicate: self predicate: self
.write .write
.encode_named_node(quad.predicate.as_ref()) .encode_named_node(NamedNodeRef::new_unchecked(&quad.predicate.iri))
.map_err(to_eval_error)?, .map_err(to_eval_error)?,
object: match &quad.object { object: match &quad.object {
Term::NamedNode(object) => self.write.encode_named_node(object.as_ref()), Term::NamedNode(object) => self.encode_named_node_for_insertion(object)?,
Term::BlankNode(object) => self Term::BlankNode(object) => self
.write .write
.encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref()), .encode_blank_node(bnodes.entry(object.clone()).or_default().as_ref())
Term::Literal(object) => self.write.encode_literal(object.as_ref()),
}
.map_err(to_eval_error)?,
graph_name: self
.write
.encode_graph_name(quad.graph_name.as_ref())
.map_err(to_eval_error)?, .map_err(to_eval_error)?,
Term::Literal(object) => self.encode_literal_for_insertion(object)?,
},
graph_name: match &quad.graph_name {
GraphName::NamedNode(graph_name) => {
self.encode_named_node_for_insertion(graph_name)?
}
GraphName::DefaultGraph => EncodedTerm::DefaultGraph,
},
})) }))
} }
@ -386,35 +388,41 @@ where
quad: &QuadPattern, quad: &QuadPattern,
variables: &[Variable], variables: &[Variable],
values: &[Option<EncodedTerm>], values: &[Option<EncodedTerm>],
bnodes: &mut HashMap<BlankNode, BlankNode>, bnodes: &mut HashMap<BlankNode, OxBlankNode>,
) -> Result<Option<EncodedQuad>, EvaluationError> { ) -> Result<Option<EncodedQuad>, EvaluationError> {
Ok(Some(EncodedQuad { Ok(Some(EncodedQuad {
subject: if let Some(subject) = subject: if let Some(subject) = self.encode_term_or_var_for_insertion(
self.encode_term_for_insertion(&quad.subject, variables, values, bnodes, |t| { &quad.subject,
t.is_named_node() || t.is_blank_node() variables,
})? { values,
bnodes,
|t| t.is_named_node() || t.is_blank_node(),
)? {
subject subject
} else { } else {
return Ok(None); return Ok(None);
}, },
predicate: if let Some(predicate) = predicate: if let Some(predicate) =
self.encode_named_node_for_insertion(&quad.predicate, variables, values)? self.encode_named_node_or_var_for_insertion(&quad.predicate, variables, values)?
{ {
predicate predicate
} else { } else {
return Ok(None); return Ok(None);
}, },
object: if let Some(object) = object: if let Some(object) = self.encode_term_or_var_for_insertion(
self.encode_term_for_insertion(&quad.object, variables, values, bnodes, |t| { &quad.object,
!t.is_default_graph() variables,
})? { values,
bnodes,
|t| !t.is_default_graph(),
)? {
object object
} else { } else {
return Ok(None); return Ok(None);
}, },
graph_name: if let Some(graph_name) = &quad.graph_name { graph_name: if let Some(graph_name) = &quad.graph_name {
if let Some(graph_name) = if let Some(graph_name) =
self.encode_named_node_for_insertion(graph_name, variables, values)? self.encode_named_node_or_var_for_insertion(graph_name, variables, values)?
{ {
graph_name graph_name
} else { } else {
@ -426,24 +434,23 @@ where
})) }))
} }
fn encode_term_for_insertion( fn encode_term_or_var_for_insertion(
&mut self, &mut self,
term: &TermOrVariable, term: &TermOrVariable,
variables: &[Variable], variables: &[Variable],
values: &[Option<EncodedTerm>], values: &[Option<EncodedTerm>],
bnodes: &mut HashMap<BlankNode, BlankNode>, bnodes: &mut HashMap<BlankNode, OxBlankNode>,
validate: impl FnOnce(&EncodedTerm) -> bool, validate: impl FnOnce(&EncodedTerm) -> bool,
) -> Result<Option<EncodedTerm>, EvaluationError> { ) -> Result<Option<EncodedTerm>, EvaluationError> {
Ok(match term { Ok(match term {
TermOrVariable::Term(term) => Some( TermOrVariable::Term(term) => Some(match term {
self.write Term::NamedNode(term) => self.encode_named_node_for_insertion(term)?,
.encode_term(if let Term::BlankNode(bnode) = term { Term::BlankNode(bnode) => self
bnodes.entry(bnode.clone()).or_default().as_ref().into() .write
} else { .encode_blank_node(bnodes.entry(bnode.clone()).or_default().as_ref())
term.as_ref()
})
.map_err(to_eval_error)?, .map_err(to_eval_error)?,
), Term::Literal(term) => self.encode_literal_for_insertion(term)?,
}),
TermOrVariable::Variable(v) => { TermOrVariable::Variable(v) => {
if let Some(Some(term)) = variables if let Some(Some(term)) = variables
.iter() .iter()
@ -462,18 +469,16 @@ where
}) })
} }
fn encode_named_node_for_insertion( fn encode_named_node_or_var_for_insertion(
&mut self, &mut self,
term: &NamedNodeOrVariable, term: &NamedNodeOrVariable,
variables: &[Variable], variables: &[Variable],
values: &[Option<EncodedTerm>], values: &[Option<EncodedTerm>],
) -> Result<Option<EncodedTerm>, EvaluationError> { ) -> Result<Option<EncodedTerm>, EvaluationError> {
Ok(match term { Ok(match term {
NamedNodeOrVariable::NamedNode(term) => Some( NamedNodeOrVariable::NamedNode(term) => {
self.write Some(self.encode_named_node_for_insertion(term)?)
.encode_named_node(term.into()) }
.map_err(to_eval_error)?,
),
NamedNodeOrVariable::Variable(v) => { NamedNodeOrVariable::Variable(v) => {
if let Some(Some(term)) = variables if let Some(Some(term)) = variables
.iter() .iter()
@ -492,43 +497,77 @@ where
}) })
} }
fn encode_named_node_for_insertion(
&mut self,
term: &NamedNode,
) -> Result<EncodedTerm, EvaluationError> {
self.write
.encode_named_node(NamedNodeRef::new_unchecked(&term.iri))
.map_err(to_eval_error)
}
fn encode_literal_for_insertion(
&mut self,
term: &Literal,
) -> Result<EncodedTerm, EvaluationError> {
self.write
.encode_literal(match term {
Literal::Simple { value } => LiteralRef::new_simple_literal(value),
Literal::LanguageTaggedString { value, language } => {
LiteralRef::new_language_tagged_literal_unchecked(value, language)
}
Literal::Typed { value, datatype } => {
LiteralRef::new_typed_literal(value, NamedNodeRef::new_unchecked(&datatype.iri))
}
})
.map_err(to_eval_error)
}
fn encode_quad_for_deletion( fn encode_quad_for_deletion(
&mut self, &mut self,
quad: &Quad, quad: &Quad,
) -> Result<Option<EncodedQuad>, EvaluationError> { ) -> Result<Option<EncodedQuad>, EvaluationError> {
Ok(Some(EncodedQuad { Ok(Some(EncodedQuad {
subject: if let Some(subject) = self subject: if let Some(subject) = match &quad.subject {
.read NamedOrBlankNode::NamedNode(subject) => {
.get_encoded_named_or_blank_node(quad.subject.as_ref()) self.encode_named_node_for_deletion(subject)?
.map_err(to_eval_error)? }
{ NamedOrBlankNode::BlankNode(_) => {
return Err(EvaluationError::msg(
"Blank nodes are not allowed in DELETE DATA",
))
}
} {
subject subject
} else { } else {
return Ok(None); return Ok(None);
}, },
predicate: if let Some(predicate) = self predicate: if let Some(predicate) =
.read self.encode_named_node_for_deletion(&quad.predicate)?
.get_encoded_named_node(quad.predicate.as_ref())
.map_err(to_eval_error)?
{ {
predicate predicate
} else { } else {
return Ok(None); return Ok(None);
}, },
object: if let Some(object) = self object: if let Some(object) = match &quad.object {
.read Term::NamedNode(object) => self.encode_named_node_for_deletion(object)?,
.get_encoded_term(quad.object.as_ref()) Term::BlankNode(_) => {
.map_err(to_eval_error)? return Err(EvaluationError::msg(
{ "Blank nodes are not allowed in DELETE DATA",
))
}
Term::Literal(object) => self.encode_literal_for_deletion(object)?,
} {
object object
} else { } else {
return Ok(None); return Ok(None);
}, },
graph_name: if let Some(graph_name) = self graph_name: if let Some(graph_name) = match &quad.graph_name {
.read GraphName::NamedNode(graph_name) => {
.get_encoded_graph_name(quad.graph_name.as_ref()) self.encode_named_node_for_deletion(graph_name)?
.map_err(to_eval_error)? }
{ GraphName::DefaultGraph => Some(EncodedTerm::DefaultGraph),
} {
graph_name graph_name
} else { } else {
return Ok(None); return Ok(None);
@ -544,21 +583,21 @@ where
) -> Result<Option<EncodedQuad>, EvaluationError> { ) -> Result<Option<EncodedQuad>, EvaluationError> {
Ok(Some(EncodedQuad { Ok(Some(EncodedQuad {
subject: if let Some(subject) = subject: if let Some(subject) =
self.encode_term_for_deletion(&quad.subject, variables, values)? self.encode_term_or_var_for_deletion(&quad.subject, variables, values)?
{ {
subject subject
} else { } else {
return Ok(None); return Ok(None);
}, },
predicate: if let Some(predicate) = predicate: if let Some(predicate) =
self.encode_named_node_for_deletion(&quad.predicate, variables, values)? self.encode_named_node_or_var_for_deletion(&quad.predicate, variables, values)?
{ {
predicate predicate
} else { } else {
return Ok(None); return Ok(None);
}, },
object: if let Some(object) = object: if let Some(object) =
self.encode_term_for_deletion(&quad.object, variables, values)? self.encode_term_or_var_for_deletion(&quad.object, variables, values)?
{ {
object object
} else { } else {
@ -566,7 +605,7 @@ where
}, },
graph_name: if let Some(graph_name) = &quad.graph_name { graph_name: if let Some(graph_name) = &quad.graph_name {
if let Some(graph_name) = if let Some(graph_name) =
self.encode_named_node_for_deletion(graph_name, variables, values)? self.encode_named_node_or_var_for_deletion(graph_name, variables, values)?
{ {
graph_name graph_name
} else { } else {
@ -578,24 +617,20 @@ where
})) }))
} }
fn encode_term_for_deletion( fn encode_term_or_var_for_deletion(
&self, &self,
term: &TermOrVariable, term: &TermOrVariable,
variables: &[Variable], variables: &[Variable],
values: &[Option<EncodedTerm>], values: &[Option<EncodedTerm>],
) -> Result<Option<EncodedTerm>, EvaluationError> { ) -> Result<Option<EncodedTerm>, EvaluationError> {
match term { match term {
TermOrVariable::Term(term) => { TermOrVariable::Term(term) => match term {
if term.is_blank_node() { Term::NamedNode(term) => self.encode_named_node_for_deletion(term),
Err(EvaluationError::msg( Term::BlankNode(_) => Err(EvaluationError::msg(
"Blank node are not allowed in deletion patterns", "Blank nodes are not allowed in DELETE patterns",
)) )),
} else { Term::Literal(term) => self.encode_literal_for_deletion(term),
self.read },
.get_encoded_term(term.into())
.map_err(to_eval_error)
}
}
TermOrVariable::Variable(v) => Ok( TermOrVariable::Variable(v) => Ok(
if let Some(Some(term)) = variables if let Some(Some(term)) = variables
.iter() .iter()
@ -610,17 +645,14 @@ where
} }
} }
fn encode_named_node_for_deletion( fn encode_named_node_or_var_for_deletion(
&self, &self,
term: &NamedNodeOrVariable, term: &NamedNodeOrVariable,
variables: &[Variable], variables: &[Variable],
values: &[Option<EncodedTerm>], values: &[Option<EncodedTerm>],
) -> Result<Option<EncodedTerm>, EvaluationError> { ) -> Result<Option<EncodedTerm>, EvaluationError> {
Ok(match term { Ok(match term {
NamedNodeOrVariable::NamedNode(term) => self NamedNodeOrVariable::NamedNode(term) => self.encode_named_node_for_deletion(term)?,
.read
.get_encoded_named_node(term.into())
.map_err(to_eval_error)?,
NamedNodeOrVariable::Variable(v) => { NamedNodeOrVariable::Variable(v) => {
if let Some(Some(term)) = variables if let Some(Some(term)) = variables
.iter() .iter()
@ -638,6 +670,32 @@ where
} }
}) })
} }
fn encode_named_node_for_deletion(
&self,
term: &NamedNode,
) -> Result<Option<EncodedTerm>, EvaluationError> {
self.read
.get_encoded_named_node(NamedNodeRef::new_unchecked(&term.iri))
.map_err(to_eval_error)
}
fn encode_literal_for_deletion(
&self,
term: &Literal,
) -> Result<Option<EncodedTerm>, EvaluationError> {
self.read
.get_encoded_literal(match term {
Literal::Simple { value } => LiteralRef::new_simple_literal(value),
Literal::LanguageTaggedString { value, language } => {
LiteralRef::new_language_tagged_literal_unchecked(value, language)
}
Literal::Typed { value, datatype } => {
LiteralRef::new_typed_literal(value, NamedNodeRef::new_unchecked(&datatype.iri))
}
})
.map_err(to_eval_error)
}
} }
fn to_eval_error(e: impl Into<EvaluationError>) -> EvaluationError { fn to_eval_error(e: impl Into<EvaluationError>) -> EvaluationError {

@ -0,0 +1,19 @@
[package]
name = "spargebra"
version = "0.1.0"
authors = ["Tpt <thomas@pellissier-tanon.fr>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
keywords = ["SPARQL"]
repository = "https://github.com/oxigraph/oxigraph/tree/master/spargebra"
homepage = "https://oxigraph.org/"
description = """
A SPARQL parser
"""
edition = "2018"
[dependencies]
peg = "0.7"
rand = "0.8"
oxiri = "0.1"
oxilangtag = "0.1"

@ -0,0 +1,40 @@
Spargebra
========
[![Latest Version](https://img.shields.io/crates/v/spargebra.svg)](https://crates.io/crates/spargebra)
[![Released API docs](https://docs.rs/spargebra/badge.svg)](https://docs.rs/spargebra)
[![Crates.io downloads](https://img.shields.io/crates/d/spargebra)](https://crates.io/crates/spargebra)
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions)
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Spargebra is a [SPARQL](https://www.w3.org/TR/sparql11-overview/) parser.
It supports both [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) and [SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/).
This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org).
Usage example:
```rust
use spargebra::Query;
let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
let mut query = Query::parse(query_str, None)?;
assert_eq!(query.to_string(), query_str);
```
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Futures by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,36 @@
//! This crate provides [SPARQL 1.1](http://www.w3.org/TR/sparql11-overview/) query and update parsers.
//! The emitted tree is based on [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery) objects.
//!
//! The API entry point for SPARQL queries is [`Query`] and the API entry point for SPARQL updates is [`Update`].
//!
//! This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org).
//!
//! Usage example:
//! ```
//! use spargebra::Query;
//!
//! let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
//! let mut query = Query::parse(query_str, None)?;
//! assert_eq!(query.to_string(), query_str);
//! # Result::Ok::<_, spargebra::ParseError>(())
//! ```
#![deny(
future_incompatible,
nonstandard_style,
rust_2018_idioms,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unused_qualifications
)]
pub mod algebra;
mod parser;
mod query;
pub mod term;
mod update;
pub use parser::ParseError;
pub use query::*;
pub use update::*;

@ -1,15 +1,15 @@
use crate::model::vocab::rdf; use crate::algebra::*;
use crate::model::vocab::xsd; use crate::query::*;
use crate::model::*; use crate::term::*;
use crate::sparql::algebra::*; use crate::update::*;
use crate::sparql::model::*; use oxilangtag::LanguageTag;
use oxiri::{Iri, IriParseError}; use oxiri::{Iri, IriParseError};
use peg::parser; use peg::parser;
use peg::str::LineCol; use peg::str::LineCol;
use rand::random;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::error::Error; use std::error::Error;
use std::rc::Rc;
use std::str::Chars; use std::str::Chars;
use std::str::FromStr; use std::str::FromStr;
use std::{char, fmt}; use std::{char, fmt};
@ -156,7 +156,7 @@ fn add_to_triple_or_path_patterns(
add_to_triple_or_path_patterns(object, *p, subject, patterns) add_to_triple_or_path_patterns(object, *p, subject, patterns)
} }
PropertyPathExpression::Sequence(a, b) => { PropertyPathExpression::Sequence(a, b) => {
let middle = BlankNode::default(); let middle = bnode();
add_to_triple_or_path_patterns(subject, *a, middle.clone().into(), patterns); add_to_triple_or_path_patterns(subject, *a, middle.clone().into(), patterns);
add_to_triple_or_path_patterns(middle.into(), *b, object, patterns); add_to_triple_or_path_patterns(middle.into(), *b, object, patterns);
} }
@ -182,7 +182,7 @@ fn build_bgp(patterns: Vec<TripleOrPathPattern>) -> GraphPattern {
} => paths.push((subject, path, object)), } => paths.push((subject, path, object)),
} }
} }
let mut graph_pattern = GraphPattern::BGP(bgp); let mut graph_pattern = GraphPattern::Bgp(bgp);
for (subject, path, object) in paths { for (subject, path, object) in paths {
graph_pattern = new_join( graph_pattern = new_join(
graph_pattern, graph_pattern,
@ -263,12 +263,12 @@ enum PartialGraphPattern {
fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern { fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern {
//Avoid to output empty BGPs //Avoid to output empty BGPs
if let GraphPattern::BGP(pl) = &l { if let GraphPattern::Bgp(pl) = &l {
if pl.is_empty() { if pl.is_empty() {
return r; return r;
} }
} }
if let GraphPattern::BGP(pr) = &r { if let GraphPattern::Bgp(pr) = &r {
if pr.is_empty() { if pr.is_empty() {
return l; return l;
} }
@ -276,9 +276,9 @@ fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern {
//Merge BGPs //Merge BGPs
match (l, r) { match (l, r) {
(GraphPattern::BGP(mut pl), GraphPattern::BGP(pr)) => { (GraphPattern::Bgp(mut pl), GraphPattern::Bgp(pr)) => {
pl.extend(pr); pl.extend(pr);
GraphPattern::BGP(pl) GraphPattern::Bgp(pl)
} }
( (
GraphPattern::Graph { GraphPattern::Graph {
@ -354,10 +354,17 @@ fn build_select(
//GROUP BY //GROUP BY
let aggregates = state.aggregates.pop().unwrap_or_else(Vec::default); let aggregates = state.aggregates.pop().unwrap_or_else(Vec::default);
if group.is_none() && !aggregates.is_empty() { if group.is_none() && !aggregates.is_empty() {
let const_variable = Variable::new_random(); let const_variable = variable();
group = Some(( group = Some((
vec![const_variable.clone()], vec![const_variable.clone()],
vec![(Literal::from(1).into(), const_variable)], vec![(
Literal::Typed {
value: "1".into(),
datatype: iri("http://www.w3.org/2001/XMLSchema#integer"),
}
.into(),
const_variable,
)],
)); ));
} }
@ -448,20 +455,20 @@ fn build_select(
} }
fn copy_graph(from: Option<NamedNode>, to: Option<NamedNodeOrVariable>) -> GraphUpdateOperation { fn copy_graph(from: Option<NamedNode>, to: Option<NamedNodeOrVariable>) -> GraphUpdateOperation {
let bgp = GraphPattern::BGP(vec![TriplePattern::new( let bgp = GraphPattern::Bgp(vec![TriplePattern::new(
Variable::new_unchecked("s"), Variable { name: "s".into() },
Variable::new_unchecked("p"), Variable { name: "p".into() },
Variable::new_unchecked("o"), Variable { name: "o".into() },
)]); )]);
GraphUpdateOperation::DeleteInsert { GraphUpdateOperation::DeleteInsert {
delete: Vec::new(), delete: Vec::new(),
insert: vec![QuadPattern::new( insert: vec![QuadPattern::new(
Variable::new_unchecked("s"), Variable { name: "s".into() },
Variable::new_unchecked("p"), Variable { name: "p".into() },
Variable::new_unchecked("o"), Variable { name: "o".into() },
to, to,
)], )],
using: QueryDataset::default(), using: None,
pattern: Box::new(if let Some(from) = from { pattern: Box::new(if let Some(from) = from {
GraphPattern::Graph { GraphPattern::Graph {
graph_name: from.into(), graph_name: from.into(),
@ -502,7 +509,7 @@ impl ParserState {
.find_map(|(v, a)| if a == &agg { Some(v) } else { None }) .find_map(|(v, a)| if a == &agg { Some(v) } else { None })
.cloned() .cloned()
.unwrap_or_else(|| { .unwrap_or_else(|| {
let new_var = Variable::new_random(); let new_var = variable();
aggregates.push((new_var.clone(), agg)); aggregates.push((new_var.clone(), agg));
new_var new_var
})) }))
@ -713,6 +720,22 @@ pub fn unescape_pn_local(input: &str) -> Cow<'_, str> {
unescape_characters(input, &UNESCAPE_PN_CHARACTERS, &UNESCAPE_PN_REPLACEMENT) unescape_characters(input, &UNESCAPE_PN_CHARACTERS, &UNESCAPE_PN_REPLACEMENT)
} }
fn iri(value: impl Into<String>) -> NamedNode {
NamedNode { iri: value.into() }
}
fn bnode() -> BlankNode {
BlankNode {
id: format!("{:x}", random::<u128>()),
}
}
fn variable() -> Variable {
Variable {
name: format!("{:x}", random::<u128>()),
}
}
parser! { parser! {
//See https://www.w3.org/TR/turtle/#sec-grammar //See https://www.w3.org/TR/turtle/#sec-grammar
grammar parser(state: &mut ParserState) for str { grammar parser(state: &mut ParserState) for str {
@ -791,7 +814,7 @@ parser! {
dataset: d, dataset: d,
pattern: build_select( pattern: build_select(
Selection::default(), Selection::default(),
GraphPattern::BGP(c), GraphPattern::Bgp(c),
g, h, o, l, v, state g, h, o, l, v, state
), ),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
@ -815,7 +838,7 @@ parser! {
pattern: build_select(Selection { pattern: build_select(Selection {
option: SelectionOption::Default, option: SelectionOption::Default,
variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri { variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri {
NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::new_random()), NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), variable()),
NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v) NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v)
}).collect()) }).collect())
}, w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state), }, w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state),
@ -828,16 +851,17 @@ parser! {
rule AskQuery() -> Query = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { rule AskQuery() -> Query = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
Query::Ask { Query::Ask {
dataset: d, dataset: d,
pattern: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)), pattern: build_select(Selection::default(), w, g, h, o, l, v, state),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }
//[13] //[13]
rule DatasetClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d } rule DatasetClause() -> (Option<NamedNode>, Option<NamedNode>) = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d }
rule DatasetClauses() -> QueryDataset = d:DatasetClause() ** (_) { rule DatasetClauses() -> Option<QueryDataset> = d:DatasetClause() ** (_) {
let mut dataset = QueryDataset::default(); if d.is_empty() {
if !d.is_empty() { return None;
}
let mut default = Vec::new(); let mut default = Vec::new();
let mut named = Vec::new(); let mut named = Vec::new();
for (d, n) in d { for (d, n) in d {
@ -848,20 +872,19 @@ parser! {
named.push(n); named.push(n);
} }
} }
dataset.set_default_graph(default); Some(QueryDataset {
dataset.set_available_named_graphs(named); default, named: Some(named)
} })
dataset
} }
//[14] //[14]
rule DefaultGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = s:SourceSelector() { rule DefaultGraphClause() -> (Option<NamedNode>, Option<NamedNode>) = s:SourceSelector() {
(Some(s.into()), None) (Some(s), None)
} }
//[15] //[15]
rule NamedGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ s:SourceSelector() { rule NamedGraphClause() -> (Option<NamedNode>, Option<NamedNode>) = i("NAMED") _ s:SourceSelector() {
(None, Some(s.into())) (None, Some(s))
} }
//[16] //[16]
@ -879,7 +902,7 @@ parser! {
if let Expression::Variable(v) = e { if let Expression::Variable(v) = e {
v v
} else { } else {
let v = vo.unwrap_or_else(Variable::new_random); let v = vo.unwrap_or_else(variable);
projections.push((e, v.clone())); projections.push((e, v.clone()));
v v
} }
@ -970,7 +993,7 @@ parser! {
if from == to { if from == to {
Vec::new() // identity case Vec::new() // identity case
} else { } else {
let bgp = GraphPattern::BGP(vec![TriplePattern::new(Variable::new_unchecked("s"), Variable::new_unchecked("p"), Variable::new_unchecked("o"))]); let bgp = GraphPattern::Bgp(vec![TriplePattern::new(Variable { name: "s".into() }, Variable { name: "p".into() }, Variable { name: "o".into() })]);
vec![copy_graph(from, to.map(NamedNodeOrVariable::NamedNode))] vec![copy_graph(from, to.map(NamedNodeOrVariable::NamedNode))]
} }
} }
@ -981,7 +1004,7 @@ parser! {
if from == to { if from == to {
Vec::new() // identity case Vec::new() // identity case
} else { } else {
let bgp = GraphPattern::BGP(vec![TriplePattern::new(Variable::new_unchecked("s"), Variable::new_unchecked("p"), Variable::new_unchecked("o"))]); let bgp = GraphPattern::Bgp(vec![TriplePattern::new(Variable { name: "s".into() }, Variable { name: "p".into() }, Variable { name: "o".into() })]);
vec![GraphUpdateOperation::Drop { silent: true, graph: to.clone().map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }, copy_graph(from.clone(), to.map(NamedNodeOrVariable::NamedNode)), GraphUpdateOperation::Drop { silent, graph: from.map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }] vec![GraphUpdateOperation::Drop { silent: true, graph: to.clone().map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }, copy_graph(from.clone(), to.map(NamedNodeOrVariable::NamedNode)), GraphUpdateOperation::Drop { silent, graph: from.map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }]
} }
} }
@ -992,7 +1015,7 @@ parser! {
if from == to { if from == to {
Vec::new() // identity case Vec::new() // identity case
} else { } else {
let bgp = GraphPattern::BGP(vec![TriplePattern::new(Variable::new_unchecked("s"), Variable::new_unchecked("p"), Variable::new_unchecked("o"))]); let bgp = GraphPattern::Bgp(vec![TriplePattern::new(Variable { name: "s".into() }, Variable { name: "p".into() }, Variable{ name: "o".into() })]);
vec![GraphUpdateOperation::Drop { silent: true, graph: to.clone().map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }, copy_graph(from, to.map(NamedNodeOrVariable::NamedNode))] vec![GraphUpdateOperation::Drop { silent: true, graph: to.clone().map_or(GraphTarget::DefaultGraph, GraphTarget::NamedNode) }, copy_graph(from, to.map(NamedNodeOrVariable::NamedNode))]
} }
} }
@ -1004,7 +1027,7 @@ parser! {
//[39] //[39]
rule DeleteData() -> Vec<GraphUpdateOperation> = i("DELETE") _ i("DATA") _ data:QuadData() {? rule DeleteData() -> Vec<GraphUpdateOperation> = i("DELETE") _ i("DATA") _ data:QuadData() {?
if data.iter().any(|quad| quad.subject.is_blank_node() || quad.object.is_blank_node() || quad.graph_name.is_blank_node()) { if data.iter().any(|quad| matches!(quad.subject, NamedOrBlankNode::BlankNode(_)) || matches!(quad.object, Term::BlankNode(_))) {
Err("Blank nodes are not allowed in DELETE DATA") Err("Blank nodes are not allowed in DELETE DATA")
} else { } else {
Ok(vec![GraphUpdateOperation::DeleteData { data }]) Ok(vec![GraphUpdateOperation::DeleteData { data }])
@ -1017,17 +1040,17 @@ parser! {
Err("Blank nodes are not allowed in DELETE WHERE") Err("Blank nodes are not allowed in DELETE WHERE")
} else { } else {
let pattern = d.iter().map(|q| { let pattern = d.iter().map(|q| {
let bgp = GraphPattern::BGP(vec![TriplePattern::new(q.subject.clone(), q.predicate.clone(), q.object.clone())]); let bgp = GraphPattern::Bgp(vec![TriplePattern::new(q.subject.clone(), q.predicate.clone(), q.object.clone())]);
if let Some(graph_name) = &q.graph_name { if let Some(graph_name) = &q.graph_name {
GraphPattern::Graph { graph_name: graph_name.clone(), inner: Box::new(bgp) } GraphPattern::Graph { graph_name: graph_name.clone(), inner: Box::new(bgp) }
} else { } else {
bgp bgp
} }
}).fold(GraphPattern::BGP(Vec::new()), new_join); }).fold(GraphPattern::Bgp(Vec::new()), new_join);
Ok(vec![GraphUpdateOperation::DeleteInsert { Ok(vec![GraphUpdateOperation::DeleteInsert {
delete: d, delete: d,
insert: Vec::new(), insert: Vec::new(),
using: QueryDataset::default(), using: None,
pattern: Box::new(pattern) pattern: Box::new(pattern)
}]) }])
} }
@ -1040,21 +1063,21 @@ parser! {
let mut insert = insert.unwrap_or_else(Vec::new); let mut insert = insert.unwrap_or_else(Vec::new);
let mut pattern = pattern; let mut pattern = pattern;
let mut using = QueryDataset::default(); let mut using = if u.is_empty() {
if !u.is_empty() { None
let mut using_default = Vec::new(); } else {
let mut using_named = Vec::new(); let mut default = Vec::new();
let mut named = Vec::new();
for (d, n) in u { for (d, n) in u {
if let Some(d) = d { if let Some(d) = d {
using_default.push(d) default.push(d)
} }
if let Some(n) = n { if let Some(n) = n {
using_named.push(n) named.push(n)
} }
} }
using.set_default_graph(using_default); Some(QueryDataset { default, named: Some(named) })
using.set_available_named_graphs(using_named); };
}
if let Some(with) = with { if let Some(with) = with {
// We inject WITH everywhere // We inject WITH everywhere
@ -1068,8 +1091,8 @@ parser! {
} else { } else {
q q
}).collect(); }).collect();
if using.is_default_dataset() { if using.is_none() {
using.set_default_graph(vec![with.into()]); using = Some(QueryDataset { default: vec![with], named: None });
} }
} }
@ -1104,12 +1127,12 @@ parser! {
rule InsertClause() -> Vec<QuadPattern> = i("INSERT") _ q:QuadPattern() { q } rule InsertClause() -> Vec<QuadPattern> = i("INSERT") _ q:QuadPattern() { q }
//[44] //[44]
rule UsingClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d } rule UsingClause() -> (Option<NamedNode>, Option<NamedNode>) = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d }
rule UsingClause_default() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i:iri() { rule UsingClause_default() -> (Option<NamedNode>, Option<NamedNode>) = i:iri() {
(Some(i.into()), None) (Some(i), None)
} }
rule UsingClause_named() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ i:iri() { rule UsingClause_named() -> (Option<NamedNode>, Option<NamedNode>) = i("NAMED") _ i:iri() {
(None, Some(i.into())) (None, Some(i))
} }
//[45] //[45]
@ -1282,21 +1305,21 @@ parser! {
} }
//[63] //[63]
rule InlineDataOneVar() -> (Vec<Variable>, Vec<Vec<Option<Term>>>) = var:Var() _ "{" _ d:InlineDataOneVar_value()* "}" { rule InlineDataOneVar() -> (Vec<Variable>, Vec<Vec<Option<NamedNodeOrLiteral>>>) = var:Var() _ "{" _ d:InlineDataOneVar_value()* "}" {
(vec![var], d) (vec![var], d)
} }
rule InlineDataOneVar_value() -> Vec<Option<Term>> = t:DataBlockValue() _ { vec![t] } rule InlineDataOneVar_value() -> Vec<Option<NamedNodeOrLiteral>> = t:DataBlockValue() _ { vec![t] }
//[64] //[64]
rule InlineDataFull() -> (Vec<Variable>, Vec<Vec<Option<Term>>>) = "(" _ vars:InlineDataFull_var()* _ ")" _ "{" _ val:InlineDataFull_values()* "}" { rule InlineDataFull() -> (Vec<Variable>, Vec<Vec<Option<NamedNodeOrLiteral>>>) = "(" _ vars:InlineDataFull_var()* _ ")" _ "{" _ val:InlineDataFull_values()* "}" {
(vars, val) (vars, val)
} }
rule InlineDataFull_var() -> Variable = v:Var() _ { v } rule InlineDataFull_var() -> Variable = v:Var() _ { v }
rule InlineDataFull_values() -> Vec<Option<Term>> = "(" _ v:InlineDataFull_value()* _ ")" _ { v } rule InlineDataFull_values() -> Vec<Option<NamedNodeOrLiteral>> = "(" _ v:InlineDataFull_value()* _ ")" _ { v }
rule InlineDataFull_value() -> Option<Term> = v:DataBlockValue() _ { v } rule InlineDataFull_value() -> Option<NamedNodeOrLiteral> = v:DataBlockValue() _ { v }
//[65] //[65]
rule DataBlockValue() -> Option<Term> = rule DataBlockValue() -> Option<NamedNodeOrLiteral> =
i:iri() { Some(i.into()) } / i:iri() { Some(i.into()) } /
l:RDFLiteral() { Some(l.into()) } / l:RDFLiteral() { Some(l.into()) } /
l:NumericLiteral() { Some(l.into()) } / l:NumericLiteral() { Some(l.into()) } /
@ -1331,7 +1354,7 @@ parser! {
//[71] //[71]
rule ArgList() -> Vec<Expression> = rule ArgList() -> Vec<Expression> =
"(" _ i("DISTINCT")? _ e:ArgList_item() **<1,> ("," _) _ ")" { e } / "(" _ e:ArgList_item() **<1,> ("," _) _ ")" { e } /
NIL() { Vec::new() } NIL() { Vec::new() }
rule ArgList_item() -> Expression = e:Expression() _ { e } rule ArgList_item() -> Expression = e:Expression() _ { e }
@ -1393,7 +1416,7 @@ parser! {
} }
//[78] //[78]
rule Verb() -> NamedNodeOrVariable = VarOrIri() / "a" { rdf::TYPE.into_owned().into() } rule Verb() -> NamedNodeOrVariable = VarOrIri() / "a" { iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").into() }
//[79] //[79]
rule ObjectList() -> FocusedTriplePattern<Vec<TermOrVariable>> = o:ObjectList_item() **<1,> ("," _) { rule ObjectList() -> FocusedTriplePattern<Vec<TermOrVariable>> = o:ObjectList_item() **<1,> ("," _) {
@ -1513,7 +1536,7 @@ parser! {
//[94] //[94]
rule PathPrimary() -> PropertyPathExpression = rule PathPrimary() -> PropertyPathExpression =
v:iri() { v.into() } / v:iri() { v.into() } /
"a" { rdf::TYPE.into_owned().into() } / "a" { iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type").into() } /
"!" _ p:PathNegatedPropertySet() { p } / "!" _ p:PathNegatedPropertySet() { p } /
"(" _ p:Path() _ ")" { p } "(" _ p:Path() _ ")" { p }
@ -1550,9 +1573,9 @@ parser! {
//[96] //[96]
rule PathOneInPropertySet() -> Either<NamedNode,NamedNode> = rule PathOneInPropertySet() -> Either<NamedNode,NamedNode> =
"^" _ v:iri() { Either::Right(v) } / "^" _ v:iri() { Either::Right(v) } /
"^" _ "a" { Either::Right(rdf::TYPE.into_owned()) } / "^" _ "a" { Either::Right(iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")) } /
v:iri() { Either::Left(v) } / v:iri() { Either::Left(v) } /
"a" { Either::Left(rdf::TYPE.into_owned()) } "a" { Either::Left(iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")) }
//[98] //[98]
rule TriplesNode() -> FocusedTriplePattern<TermOrVariable> = Collection() / BlankNodePropertyList() rule TriplesNode() -> FocusedTriplePattern<TermOrVariable> = Collection() / BlankNodePropertyList()
@ -1560,7 +1583,7 @@ parser! {
//[99] //[99]
rule BlankNodePropertyList() -> FocusedTriplePattern<TermOrVariable> = "[" _ po:PropertyListNotEmpty() _ "]" { rule BlankNodePropertyList() -> FocusedTriplePattern<TermOrVariable> = "[" _ po:PropertyListNotEmpty() _ "]" {
let mut patterns: Vec<TriplePattern> = Vec::default(); let mut patterns: Vec<TriplePattern> = Vec::default();
let mut bnode = TermOrVariable::from(BlankNode::default()); let mut bnode = TermOrVariable::from(bnode());
for (p, os) in po.focus { for (p, os) in po.focus {
for o in os { for o in os {
patterns.push(TriplePattern::new(bnode.clone(), p.clone(), o)); patterns.push(TriplePattern::new(bnode.clone(), p.clone(), o));
@ -1578,7 +1601,7 @@ parser! {
//[101] //[101]
rule BlankNodePropertyListPath() -> FocusedTripleOrPathPattern<TermOrVariable> = "[" _ po:PropertyListPathNotEmpty() _ "]" { rule BlankNodePropertyListPath() -> FocusedTripleOrPathPattern<TermOrVariable> = "[" _ po:PropertyListPathNotEmpty() _ "]" {
let mut patterns: Vec<TripleOrPathPattern> = Vec::default(); let mut patterns: Vec<TripleOrPathPattern> = Vec::default();
let mut bnode = TermOrVariable::from(BlankNode::default()); let mut bnode = TermOrVariable::from(bnode());
for (p, os) in po.focus { for (p, os) in po.focus {
for o in os { for o in os {
add_to_triple_or_path_patterns(bnode.clone(), p.clone(), o, &mut patterns); add_to_triple_or_path_patterns(bnode.clone(), p.clone(), o, &mut patterns);
@ -1593,11 +1616,11 @@ parser! {
//[102] //[102]
rule Collection() -> FocusedTriplePattern<TermOrVariable> = "(" _ o:Collection_item()+ ")" { rule Collection() -> FocusedTriplePattern<TermOrVariable> = "(" _ o:Collection_item()+ ")" {
let mut patterns: Vec<TriplePattern> = Vec::default(); let mut patterns: Vec<TriplePattern> = Vec::default();
let mut current_list_node = TermOrVariable::from(rdf::NIL.into_owned()); let mut current_list_node = TermOrVariable::from(iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"));
for objWithPatterns in o.into_iter().rev() { for objWithPatterns in o.into_iter().rev() {
let new_blank_node = TermOrVariable::from(BlankNode::default()); let new_blank_node = TermOrVariable::from(bnode());
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::FIRST.into_owned(), objWithPatterns.focus.clone())); patterns.push(TriplePattern::new(new_blank_node.clone(), iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#first"), objWithPatterns.focus.clone()));
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::REST.into_owned(), current_list_node)); patterns.push(TriplePattern::new(new_blank_node.clone(), iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"), current_list_node));
current_list_node = new_blank_node; current_list_node = new_blank_node;
patterns.extend_from_slice(&objWithPatterns.patterns); patterns.extend_from_slice(&objWithPatterns.patterns);
} }
@ -1611,11 +1634,11 @@ parser! {
//[103] //[103]
rule CollectionPath() -> FocusedTripleOrPathPattern<TermOrVariable> = "(" _ o:CollectionPath_item()+ _ ")" { rule CollectionPath() -> FocusedTripleOrPathPattern<TermOrVariable> = "(" _ o:CollectionPath_item()+ _ ")" {
let mut patterns: Vec<TripleOrPathPattern> = Vec::default(); let mut patterns: Vec<TripleOrPathPattern> = Vec::default();
let mut current_list_node = TermOrVariable::from(rdf::NIL.into_owned()); let mut current_list_node = TermOrVariable::from(iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"));
for objWithPatterns in o.into_iter().rev() { for objWithPatterns in o.into_iter().rev() {
let new_blank_node = TermOrVariable::from(BlankNode::default()); let new_blank_node = TermOrVariable::from(bnode());
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::FIRST.into_owned(), objWithPatterns.focus.clone()).into()); patterns.push(TriplePattern::new(new_blank_node.clone(), iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#first"), objWithPatterns.focus.clone()).into());
patterns.push(TriplePattern::new(new_blank_node.clone(), rdf::REST.into_owned(), current_list_node).into()); patterns.push(TriplePattern::new(new_blank_node.clone(), iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"), current_list_node).into());
current_list_node = new_blank_node; current_list_node = new_blank_node;
patterns.extend(objWithPatterns.patterns); patterns.extend(objWithPatterns.patterns);
} }
@ -1647,7 +1670,7 @@ parser! {
i:iri() { i.into() } i:iri() { i.into() }
//[108] //[108]
rule Var() -> Variable = v:(VAR1() / VAR2()) { Variable::new_unchecked(v) } rule Var() -> Variable = name:(VAR1() / VAR2()) { Variable { name: name.into() } }
//[109] //[109]
rule GraphTerm() -> Term = rule GraphTerm() -> Term =
@ -1656,7 +1679,7 @@ parser! {
l:NumericLiteral() { l.into() } / l:NumericLiteral() { l.into() } /
l:BooleanLiteral() { l.into() } / l:BooleanLiteral() { l.into() } /
b:BlankNode() { b.into() } / b:BlankNode() { b.into() } /
NIL() { rdf::NIL.into() } NIL() { iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil").into() }
//[110] //[110]
rule Expression() -> Expression = e:ConditionalOrExpression() {e} rule Expression() -> Expression = e:ConditionalOrExpression() {e}
@ -1749,7 +1772,7 @@ parser! {
i("LANGMATCHES") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::LangMatches, vec![a, b]) } / i("LANGMATCHES") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::LangMatches, vec![a, b]) } /
i("DATATYPE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Datatype, vec![e]) } / i("DATATYPE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Datatype, vec![e]) } /
i("BOUND") _ "(" _ v:Var() _ ")" { Expression::Bound(v) } / i("BOUND") _ "(" _ v:Var() _ ")" { Expression::Bound(v) } /
(i("IRI") / i("URI")) _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IRI, vec![e]) } / (i("IRI") / i("URI")) _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Iri, vec![e]) } /
i("BNODE") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::BNode, vec![e]) } / i("BNODE") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::BNode, vec![e]) } /
i("BNODE") NIL() { Expression::FunctionCall(Function::BNode, vec![]) } / i("BNODE") NIL() { Expression::FunctionCall(Function::BNode, vec![]) } /
i("RAND") _ NIL() { Expression::FunctionCall(Function::Rand, vec![]) } / i("RAND") _ NIL() { Expression::FunctionCall(Function::Rand, vec![]) } /
@ -1763,7 +1786,7 @@ parser! {
StrReplaceExpression() / StrReplaceExpression() /
i("UCASE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::UCase, vec![e]) } / i("UCASE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::UCase, vec![e]) } /
i("LCASE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::LCase, vec![e]) } / i("LCASE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::LCase, vec![e]) } /
i("ENCODE_FOR_URI") "(" _ e: Expression() _ ")" { Expression::FunctionCall(Function::EncodeForURI, vec![e]) } / i("ENCODE_FOR_URI") "(" _ e: Expression() _ ")" { Expression::FunctionCall(Function::EncodeForUri, vec![e]) } /
i("CONTAINS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::Contains, vec![a, b]) } / i("CONTAINS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::Contains, vec![a, b]) } /
i("STRSTARTS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrStarts, vec![a, b]) } / i("STRSTARTS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrStarts, vec![a, b]) } /
i("STRENDS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrEnds, vec![a, b]) } / i("STRENDS") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrEnds, vec![a, b]) } /
@ -1778,19 +1801,19 @@ parser! {
i("TIMEZONE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Timezone, vec![e]) } / i("TIMEZONE") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Timezone, vec![e]) } /
i("TZ") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Tz, vec![e]) } / i("TZ") _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Tz, vec![e]) } /
i("NOW") _ NIL() { Expression::FunctionCall(Function::Now, vec![]) } / i("NOW") _ NIL() { Expression::FunctionCall(Function::Now, vec![]) } /
i("UUID") _ NIL() { Expression::FunctionCall(Function::UUID, vec![]) }/ i("UUID") _ NIL() { Expression::FunctionCall(Function::Uuid, vec![]) }/
i("STRUUID") _ NIL() { Expression::FunctionCall(Function::StrUUID, vec![]) } / i("STRUUID") _ NIL() { Expression::FunctionCall(Function::StrUuid, vec![]) } /
i("MD5") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::MD5, vec![e]) } / i("MD5") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Md5, vec![e]) } /
i("SHA1") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::SHA1, vec![e]) } / i("SHA1") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Sha1, vec![e]) } /
i("SHA256") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::SHA256, vec![e]) } / i("SHA256") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Sha256, vec![e]) } /
i("SHA384") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::SHA384, vec![e]) } / i("SHA384") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Sha384, vec![e]) } /
i("SHA512") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::SHA512, vec![e]) } / i("SHA512") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::Sha512, vec![e]) } /
i("COALESCE") e:ExpressionList() { Expression::Coalesce(e) } / i("COALESCE") e:ExpressionList() { Expression::Coalesce(e) } /
i("IF") _ "(" _ a:Expression() _ "," _ b:Expression() _ "," _ c:Expression() _ ")" { Expression::If(Box::new(a), Box::new(b), Box::new(c)) } / i("IF") _ "(" _ a:Expression() _ "," _ b:Expression() _ "," _ c:Expression() _ ")" { Expression::If(Box::new(a), Box::new(b), Box::new(c)) } /
i("STRLANG") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrLang, vec![a, b]) } / i("STRLANG") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrLang, vec![a, b]) } /
i("STRDT") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrDT, vec![a, b]) } / i("STRDT") _ "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::FunctionCall(Function::StrDt, vec![a, b]) } /
i("sameTerm") "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::SameTerm(Box::new(a), Box::new(b)) } / i("sameTerm") "(" _ a:Expression() _ "," _ b:Expression() _ ")" { Expression::SameTerm(Box::new(a), Box::new(b)) } /
(i("isIRI") / i("isURI")) _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsIRI, vec![e]) } / (i("isIRI") / i("isURI")) _ "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsIri, vec![e]) } /
i("isBLANK") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsBlank, vec![e]) } / i("isBLANK") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsBlank, vec![e]) } /
i("isLITERAL") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsLiteral, vec![e]) } / i("isLITERAL") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsLiteral, vec![e]) } /
i("isNUMERIC") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsNumeric, vec![e]) } / i("isNUMERIC") "(" _ e:Expression() _ ")" { Expression::FunctionCall(Function::IsNumeric, vec![e]) } /
@ -1853,52 +1876,43 @@ parser! {
//[129] //[129]
rule RDFLiteral() -> Literal = rule RDFLiteral() -> Literal =
v:String() _ "^^" _ t:iri() { Literal::new_typed_literal(v, t) } / value:String() _ "^^" _ datatype:iri() { Literal::Typed { value, datatype } } /
v:String() _ l:LANGTAG() {? Literal::new_language_tagged_literal(v, l).map_err(|_| "language tag parsing failed") } / value:String() _ language:LANGTAG() { Literal::LanguageTaggedString { value, language: language.into_inner() } } /
v:String() { Literal::new_simple_literal(v) } value:String() { Literal::Simple { value } }
//[130] //[130]
rule NumericLiteral() -> Literal = NumericLiteralUnsigned() / NumericLiteralPositive() / NumericLiteralNegative() rule NumericLiteral() -> Literal = NumericLiteralUnsigned() / NumericLiteralPositive() / NumericLiteralNegative()
//[131] //[131]
rule NumericLiteralUnsigned() -> Literal = rule NumericLiteralUnsigned() -> Literal =
d:$(DOUBLE()) {? match f64::from_str(d) { d:$(DOUBLE()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#double") } } /
Ok(value) => Ok(value.into()), d:$(DECIMAL()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#decimal") } } /
Err(_) => Err("Invalid xsd:double()") i:$(INTEGER()) { Literal::Typed { value: i.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#integer") } }
} } /
d:$(DECIMAL()) { Literal::new_typed_literal(d, xsd::DECIMAL) } /
i:$(INTEGER()) { Literal::new_typed_literal(i, xsd::INTEGER) }
//[132] //[132]
rule NumericLiteralPositive() -> Literal = rule NumericLiteralPositive() -> Literal =
d:$(DOUBLE_POSITIVE()) {? match f64::from_str(d) { d:$(DOUBLE_POSITIVE()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#double") } } /
Ok(value) => Ok(value.into()), d:$(DECIMAL_POSITIVE()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#decimal") } } /
Err(_) => Err("Invalid xsd:double()") i:$(INTEGER_POSITIVE()) { Literal::Typed { value: i.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#integer") } }
} } /
d:$(DECIMAL_POSITIVE()) { Literal::new_typed_literal(d, xsd::DECIMAL) } /
i:$(INTEGER_POSITIVE()) { Literal::new_typed_literal(i, xsd::INTEGER) }
//[133] //[133]
rule NumericLiteralNegative() -> Literal = rule NumericLiteralNegative() -> Literal =
d:$(DOUBLE_NEGATIVE()) {? match f64::from_str(d) { d:$(DOUBLE_NEGATIVE()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#double") } } /
Ok(value) => Ok(value.into()), d:$(DECIMAL_NEGATIVE()) { Literal::Typed { value: d.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#decimal") } } /
Err(_) => Err("Invalid xsd:double()") i:$(INTEGER_NEGATIVE()) { Literal::Typed { value: i.into(), datatype: iri("http://www.w3.org/2001/XMLSchema#integer") } }
} } /
d:$(DECIMAL_NEGATIVE()) { Literal::new_typed_literal(d, xsd::DECIMAL) } /
i:$(INTEGER_NEGATIVE()) { Literal::new_typed_literal(i, xsd::INTEGER) }
//[134] //[134]
rule BooleanLiteral() -> Literal = rule BooleanLiteral() -> Literal =
"true" { true.into() } / "true" { Literal::Typed { value: "true".into(), datatype: iri("http://www.w3.org/2001/XMLSchema#boolean") } } /
"false" { false.into() } "false" { Literal::Typed { value: "false".into(), datatype: iri("http://www.w3.org/2001/XMLSchema#boolean") } }
//[135] //[135]
rule String() -> String = STRING_LITERAL_LONG1() / STRING_LITERAL_LONG2() / STRING_LITERAL1() / STRING_LITERAL2() rule String() -> String = STRING_LITERAL_LONG1() / STRING_LITERAL_LONG2() / STRING_LITERAL1() / STRING_LITERAL2()
//[136] //[136]
rule iri() -> NamedNode = i:(IRIREF() / PrefixedName()) { rule iri() -> NamedNode = i:(IRIREF() / PrefixedName()) {
NamedNode::new_from_iri(i) iri(i.into_inner())
} }
//[137] //[137]
@ -1910,19 +1924,15 @@ parser! {
} } } }
//[138] //[138]
rule BlankNode() -> BlankNode = rule BlankNode() -> BlankNode = id:BLANK_NODE_LABEL() {?
b:BLANK_NODE_LABEL() {? let node = BlankNode { id: id.to_owned() };
match BlankNode::new(b) { if state.used_bnodes.contains(&node) {
Ok(node) => if state.used_bnodes.contains(&node) {
Err("Already used blank node id") Err("Already used blank node id")
} else { } else {
state.currently_used_bnodes.insert(node.clone()); state.currently_used_bnodes.insert(node.clone());
Ok(node) Ok(node)
},
Err(_) => Err("Invalid blank node identifier")
} }
} / } / ANON() { bnode() }
ANON() { BlankNode::default() }
//[139] //[139]
rule IRIREF() -> Iri<String> = "<" i:$((!['>'] [_])*) ">" {? rule IRIREF() -> Iri<String> = "<" i:$((!['>'] [_])*) ">" {?
@ -1957,8 +1967,8 @@ parser! {
rule VAR2() -> &'input str = "$" v:$(VARNAME()) { v } rule VAR2() -> &'input str = "$" v:$(VARNAME()) { v }
//[145] //[145]
rule LANGTAG() -> String = "@" l:$(['a' ..= 'z' | 'A' ..= 'Z']+ ("-" ['a' ..= 'z' | 'A' ..= 'Z' | '0' ..= '9']+)*) { rule LANGTAG() -> LanguageTag<String> = "@" l:$(['a' ..= 'z' | 'A' ..= 'Z']+ ("-" ['a' ..= 'z' | 'A' ..= 'Z' | '0' ..= '9']+)*) {?
l.to_ascii_lowercase() LanguageTag::parse(l.to_ascii_lowercase()).map_err(|_| "language tag parsing failed")
} }
//[146] //[146]

@ -0,0 +1,181 @@
use crate::algebra::*;
use crate::parser::{parse_query, ParseError};
use oxiri::Iri;
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
///
/// ```
/// use spargebra::Query;
///
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let mut query = Query::parse(query_str, None)?;
/// assert_eq!(query.to_string(), query_str);
/// # Result::Ok::<_, spargebra::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Query {
/// [SELECT](https://www.w3.org/TR/sparql11-query/#select)
Select {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
dataset: Option<QueryDataset>,
/// The query selection graph pattern
pattern: GraphPattern,
/// The query base IRI
base_iri: Option<Iri<String>>,
},
/// [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct)
Construct {
/// The query construction template
template: Vec<TriplePattern>,
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
dataset: Option<QueryDataset>,
/// The query selection graph pattern
pattern: GraphPattern,
/// The query base IRI
base_iri: Option<Iri<String>>,
},
/// [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe)
Describe {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
dataset: Option<QueryDataset>,
/// The query selection graph pattern
pattern: GraphPattern,
/// The query base IRI
base_iri: Option<Iri<String>>,
},
/// [ASK](https://www.w3.org/TR/sparql11-query/#ask)
Ask {
/// The [query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
dataset: Option<QueryDataset>,
/// The query selection graph pattern
pattern: GraphPattern,
/// The query base IRI
base_iri: Option<Iri<String>>,
},
}
impl Query {
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_query(query, base_iri)
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Query::Select {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
write!(
f,
"{}",
SparqlGraphRootPattern {
pattern,
dataset: dataset.as_ref()
}
)
}
Query::Construct {
template,
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
write!(f, "CONSTRUCT {{ ")?;
for triple in template.iter() {
write!(f, "{} ", SparqlTriplePattern(triple))?;
}
write!(f, "}}")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
Query::Describe {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{}>", base_iri.as_str())?;
}
write!(f, "DESCRIBE *")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
Query::Ask {
dataset,
pattern,
base_iri,
} => {
if let Some(base_iri) = base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
write!(f, "ASK")?;
if let Some(dataset) = dataset {
dataset.fmt(f)?;
}
write!(
f,
" WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
}
}
}
impl FromStr for Query {
type Err = ParseError;
fn from_str(query: &str) -> Result<Self, ParseError> {
Self::parse(query, None)
}
}
impl<'a> TryFrom<&'a str> for Query {
type Error = ParseError;
fn try_from(query: &str) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
impl<'a> TryFrom<&'a String> for Query {
type Error = ParseError;
fn try_from(query: &String) -> Result<Self, ParseError> {
Self::from_str(query)
}
}

@ -0,0 +1,426 @@
//! Data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) like IRI, literal or triples.
use std::fmt;
use std::fmt::Write;
/// An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
///
/// ```
/// use spargebra::term::NamedNode;
///
/// assert_eq!(
/// "<http://example.com/foo>",
/// NamedNode { iri: "http://example.com/foo".into() }.to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct NamedNode {
/// The [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) itself.
pub iri: String,
}
impl fmt::Display for NamedNode {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<{}>", self.iri)
}
}
/// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
///
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
///
/// ```
/// use spargebra::term::BlankNode;
///
/// assert_eq!(
/// "_:a1",
/// BlankNode { id: "a1".into() }.to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct BlankNode {
/// The [blank node identifier](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier).
pub id: String,
}
impl fmt::Display for BlankNode {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "_:{}", self.id)
}
}
/// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
///
/// The language tags should be lowercased [as suggested by the RDF specification](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string).
///
/// ```
/// use spargebra::term::NamedNode;
/// use spargebra::term::Literal;
///
/// assert_eq!(
/// "\"foo\\nbar\"",
/// Literal::Simple { value: "foo\nbar".into() }.to_string()
/// );
///
/// assert_eq!(
/// "\"1999-01-01\"^^<http://www.w3.org/2001/XMLSchema#date>",
/// Literal::Typed { value: "1999-01-01".into(), datatype: NamedNode { iri: "http://www.w3.org/2001/XMLSchema#date".into() }}.to_string()
/// );
///
/// assert_eq!(
/// "\"foo\"@en",
/// Literal::LanguageTaggedString { value: "foo".into(), language: "en".into() }.to_string()
/// );
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Literal {
/// A [simple literal](https://www.w3.org/TR/rdf11-concepts/#dfn-simple-literal) without datatype or language form.
Simple {
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
value: String,
},
/// A [language-tagged string](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tagged-string)
LanguageTaggedString {
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
value: String,
/// The [language tag](https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag).
language: String,
},
/// A literal with an explicit datatype
Typed {
/// The [lexical form](https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form).
value: String,
/// The [datatype IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri).
datatype: NamedNode,
},
}
impl fmt::Display for Literal {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Literal::Simple { value } => print_quoted_str(value, f),
Literal::LanguageTaggedString { value, language } => {
print_quoted_str(value, f)?;
write!(f, "@{}", language)
}
Literal::Typed { value, datatype } => {
print_quoted_str(value, f)?;
write!(f, "^^{}", datatype)
}
}
}
}
/// A [SPARQL query variable](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
///
/// ```
/// use spargebra::term::Variable;
///
/// assert_eq!(
/// "?foo",
/// Variable { name: "foo".into() }.to_string()
/// );
/// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct Variable {
pub name: String,
}
impl fmt::Display for Variable {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "?{}", self.name)
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedOrBlankNode {
NamedNode(NamedNode),
BlankNode(BlankNode),
}
impl fmt::Display for NamedOrBlankNode {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NamedOrBlankNode::NamedNode(node) => node.fmt(f),
NamedOrBlankNode::BlankNode(node) => node.fmt(f),
}
}
}
impl From<NamedNode> for NamedOrBlankNode {
#[inline]
fn from(node: NamedNode) -> Self {
NamedOrBlankNode::NamedNode(node)
}
}
impl From<BlankNode> for NamedOrBlankNode {
#[inline]
fn from(node: BlankNode) -> Self {
NamedOrBlankNode::BlankNode(node)
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedNodeOrLiteral {
NamedNode(NamedNode),
Literal(Literal),
}
impl fmt::Display for NamedNodeOrLiteral {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NamedNodeOrLiteral::NamedNode(node) => node.fmt(f),
NamedNodeOrLiteral::Literal(literal) => literal.fmt(f),
}
}
}
impl From<NamedNode> for NamedNodeOrLiteral {
#[inline]
fn from(node: NamedNode) -> Self {
NamedNodeOrLiteral::NamedNode(node)
}
}
impl From<Literal> for NamedNodeOrLiteral {
#[inline]
fn from(literal: Literal) -> Self {
NamedNodeOrLiteral::Literal(literal)
}
}
/// An RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term).
///
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
///
/// The default string formatter is returning an N-Triples, Turtle and SPARQL compatible representation.
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Term {
NamedNode(NamedNode),
BlankNode(BlankNode),
Literal(Literal),
}
impl fmt::Display for Term {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Term::NamedNode(node) => node.fmt(f),
Term::BlankNode(node) => node.fmt(f),
Term::Literal(literal) => literal.fmt(f),
}
}
}
impl From<NamedNode> for Term {
#[inline]
fn from(node: NamedNode) -> Self {
Term::NamedNode(node)
}
}
impl From<BlankNode> for Term {
#[inline]
fn from(node: BlankNode) -> Self {
Term::BlankNode(node)
}
}
impl From<Literal> for Term {
#[inline]
fn from(literal: Literal) -> Self {
Term::Literal(literal)
}
}
impl From<NamedOrBlankNode> for Term {
#[inline]
fn from(resource: NamedOrBlankNode) -> Self {
match resource {
NamedOrBlankNode::NamedNode(node) => Term::NamedNode(node),
NamedOrBlankNode::BlankNode(node) => Term::BlankNode(node),
}
}
}
/// A possible graph name.
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node), and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GraphName {
NamedNode(NamedNode),
DefaultGraph,
}
impl fmt::Display for GraphName {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphName::NamedNode(node) => node.fmt(f),
GraphName::DefaultGraph => write!(f, "DEFAULT"),
}
}
}
impl From<NamedNode> for GraphName {
#[inline]
fn from(node: NamedNode) -> Self {
GraphName::NamedNode(node)
}
}
/// A [RDF triple](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple) in a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
///
/// The default string formatter is returning a N-Quads representation.
///
/// ```
/// use spargebra::term::NamedNode;
/// use spargebra::term::Quad;
///
/// assert_eq!(
/// "<http://example.com/foo> <http://schema.org/sameAs> <http://example.com/foo> <http://example.com/> .",
/// Quad {
/// subject: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// predicate: NamedNode { iri: "http://schema.org/sameAs".into() },
/// object: NamedNode { iri: "http://example.com/foo".into() }.into(),
/// graph_name: NamedNode { iri: "http://example.com/".into() }.into(),
/// }.to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Quad {
pub subject: NamedOrBlankNode,
pub predicate: NamedNode,
pub object: Term,
pub graph_name: GraphName,
}
impl fmt::Display for Quad {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.graph_name == GraphName::DefaultGraph {
write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
} else {
write!(
f,
"{} {} {} {} .",
self.subject, self.predicate, self.object, self.graph_name
)
}
}
}
/// The union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedNodeOrVariable {
NamedNode(NamedNode),
Variable(Variable),
}
impl fmt::Display for NamedNodeOrVariable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NamedNodeOrVariable::NamedNode(node) => node.fmt(f),
NamedNodeOrVariable::Variable(var) => var.fmt(f),
}
}
}
impl From<NamedNode> for NamedNodeOrVariable {
fn from(node: NamedNode) -> Self {
NamedNodeOrVariable::NamedNode(node)
}
}
impl From<Variable> for NamedNodeOrVariable {
fn from(var: Variable) -> Self {
NamedNodeOrVariable::Variable(var)
}
}
/// The union of [terms](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term) and [variables](https://www.w3.org/TR/sparql11-query/#sparqlQueryVariables).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum TermOrVariable {
Term(Term),
Variable(Variable),
}
impl fmt::Display for TermOrVariable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TermOrVariable::Term(term) => term.fmt(f),
TermOrVariable::Variable(var) => var.fmt(f),
}
}
}
impl From<NamedNode> for TermOrVariable {
fn from(node: NamedNode) -> Self {
TermOrVariable::Term(node.into())
}
}
impl From<BlankNode> for TermOrVariable {
fn from(node: BlankNode) -> Self {
TermOrVariable::Term(node.into())
}
}
impl From<Literal> for TermOrVariable {
fn from(literal: Literal) -> Self {
TermOrVariable::Term(literal.into())
}
}
impl From<Variable> for TermOrVariable {
fn from(var: Variable) -> Self {
TermOrVariable::Variable(var)
}
}
impl From<Term> for TermOrVariable {
fn from(term: Term) -> Self {
TermOrVariable::Term(term)
}
}
impl From<NamedNodeOrVariable> for TermOrVariable {
fn from(element: NamedNodeOrVariable) -> Self {
match element {
NamedNodeOrVariable::NamedNode(node) => TermOrVariable::Term(node.into()),
NamedNodeOrVariable::Variable(var) => TermOrVariable::Variable(var),
}
}
}
#[inline]
pub(crate) fn print_quoted_str(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('"')?;
for c in string.chars() {
match c {
'\n' => f.write_str("\\n"),
'\r' => f.write_str("\\r"),
'"' => f.write_str("\\\""),
'\\' => f.write_str("\\\\"),
c => f.write_char(c),
}?;
}
f.write_char('"')
}

@ -0,0 +1,199 @@
use crate::algebra::*;
use crate::parser::{parse_update, ParseError};
use crate::term::*;
use oxiri::Iri;
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/)
///
/// ```
/// use spargebra::Update;
///
/// let update_str = "CLEAR ALL ;";
/// let update = Update::parse(update_str, None)?;
/// assert_eq!(update.to_string().trim(), update_str);
/// # Result::Ok::<_, spargebra::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Update {
/// The update base IRI
pub base_iri: Option<Iri<String>>,
/// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
pub operations: Vec<GraphUpdateOperation>,
}
impl Update {
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query
pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_update(update, base_iri)
}
}
impl fmt::Display for Update {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(base_iri) = &self.base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
for update in &self.operations {
writeln!(f, "{} ;", update)?;
}
Ok(())
}
}
impl FromStr for Update {
type Err = ParseError;
fn from_str(update: &str) -> Result<Self, ParseError> {
Self::parse(update, None)
}
}
impl<'a> TryFrom<&'a str> for Update {
type Error = ParseError;
fn try_from(update: &str) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
impl<'a> TryFrom<&'a String> for Update {
type Error = ParseError;
fn try_from(update: &String) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
/// The [graph update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum GraphUpdateOperation {
/// [insert data](https://www.w3.org/TR/sparql11-update/#def_insertdataoperation)
InsertData { data: Vec<Quad> },
/// [delete data](https://www.w3.org/TR/sparql11-update/#def_deletedataoperation)
DeleteData { data: Vec<Quad> },
/// [delete insert](https://www.w3.org/TR/sparql11-update/#def_deleteinsertoperation)
DeleteInsert {
delete: Vec<QuadPattern>,
insert: Vec<QuadPattern>,
using: Option<QueryDataset>,
pattern: Box<GraphPattern>,
},
/// [load](https://www.w3.org/TR/sparql11-update/#def_loadoperation)
Load {
silent: bool,
from: NamedNode,
to: Option<NamedNode>,
},
/// [clear](https://www.w3.org/TR/sparql11-update/#def_clearoperation)
Clear { silent: bool, graph: GraphTarget },
/// [create](https://www.w3.org/TR/sparql11-update/#def_createoperation)
Create { silent: bool, graph: NamedNode },
/// [drop](https://www.w3.org/TR/sparql11-update/#def_dropoperation)
Drop { silent: bool, graph: GraphTarget },
}
impl fmt::Display for GraphUpdateOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphUpdateOperation::InsertData { data } => {
writeln!(f, "INSERT DATA {{")?;
write_quads(data, f)?;
write!(f, "}}")
}
GraphUpdateOperation::DeleteData { data } => {
writeln!(f, "DELETE DATA {{")?;
write_quads(data, f)?;
write!(f, "}}")
}
GraphUpdateOperation::DeleteInsert {
delete,
insert,
using,
pattern,
} => {
if !delete.is_empty() {
writeln!(f, "DELETE {{")?;
for quad in delete {
writeln!(f, "\t{}", SparqlQuadPattern(quad))?;
}
writeln!(f, "}}")?;
}
if !insert.is_empty() {
writeln!(f, "INSERT {{")?;
for quad in insert {
writeln!(f, "\t{}", SparqlQuadPattern(quad))?;
}
writeln!(f, "}}")?;
}
if let Some(using) = using {
for g in &using.default {
writeln!(f, "USING {}", g)?;
}
if let Some(named) = &using.named {
for g in named {
writeln!(f, "USING NAMED {}", g)?;
}
}
}
write!(
f,
"WHERE {{ {} }}",
SparqlGraphRootPattern {
pattern,
dataset: None
}
)
}
GraphUpdateOperation::Load { silent, from, to } => {
write!(f, "LOAD ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{}", from)?;
if let Some(to) = to {
write!(f, " INTO GRAPH {}", to)?;
}
Ok(())
}
GraphUpdateOperation::Clear { silent, graph } => {
write!(f, "CLEAR ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{}", graph)
}
GraphUpdateOperation::Create { silent, graph } => {
write!(f, "CREATE ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "GRAPH {}", graph)
}
GraphUpdateOperation::Drop { silent, graph } => {
write!(f, "DROP ")?;
if *silent {
write!(f, "SILENT ")?;
}
write!(f, "{}", graph)
}
}
}
}
fn write_quads(quads: &[Quad], f: &mut fmt::Formatter<'_>) -> fmt::Result {
for quad in quads {
if quad.graph_name == GraphName::DefaultGraph {
writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?;
} else {
writeln!(
f,
"\tGRAPH {} {{ {} {} {} }}",
quad.graph_name, quad.subject, quad.predicate, quad.object
)?;
}
}
Ok(())
}

@ -24,10 +24,9 @@ fn sparql10_w3c_query_syntax_testsuite() -> Result<()> {
run_testsuite( run_testsuite(
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/manifest-syntax.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/manifest-syntax.ttl",
vec![ vec![
//Bad SPARQL query that should be rejected by the parser "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql4/manifest#syn-bad-38", // bnode scope
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql4/manifest#syn-bad-38", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql4/manifest#syn-bad-34", // bnode scope
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql4/manifest#syn-bad-34", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql3/manifest#syn-bad-26", // tokenizer
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/syntax-sparql3/manifest#syn-bad-26",
], ],
) )
} }

Loading…
Cancel
Save