Converts oxigraph::Error into oxigraph::sparql::EvaluationError

pull/46/head
Tpt 4 years ago
parent 5640b0a32d
commit 9038ab3921
  1. 192
      lib/src/error.rs
  2. 73
      lib/src/io/read.rs
  3. 8
      lib/src/io/write.rs
  4. 8
      lib/src/lib.rs
  5. 11
      lib/src/model/blank_node.rs
  6. 5
      lib/src/model/named_node.rs
  7. 109
      lib/src/sparql/error.rs
  8. 105
      lib/src/sparql/eval.rs
  9. 16
      lib/src/sparql/json_results.rs
  10. 114
      lib/src/sparql/mod.rs
  11. 65
      lib/src/sparql/model.rs
  12. 41
      lib/src/sparql/parser.rs
  13. 12
      lib/src/sparql/plan.rs
  14. 46
      lib/src/sparql/plan_builder.rs
  15. 301
      lib/src/sparql/xml_results.rs
  16. 35
      lib/src/store/memory.rs
  17. 7
      lib/src/store/mod.rs
  18. 6
      lib/src/store/numeric_encoder.rs
  19. 26
      lib/src/store/rocksdb.rs
  20. 30
      lib/src/store/sled.rs
  21. 2
      python/src/memory_store.rs
  22. 2
      python/src/sled_store.rs
  23. 19
      python/src/store_utils.rs
  24. 21
      testsuite/src/sparql_evaluator.rs

@ -1,171 +1,37 @@
use crate::model::{BlankNodeIdParseError, IriParseError, LanguageTagParseError}; use std::error::Error;
use crate::sparql::SparqlParseError; use std::{fmt, io};
use rio_turtle::TurtleError;
use rio_xml::RdfXmlError;
use std::error;
use std::fmt;
use std::io;
use std::string::FromUtf8Error;
/// The Oxigraph error type.
///
/// The `wrap` method allows us to make this type wrap any implementation of `std::error::Error`.
/// This type also avoids heap allocations for the most common cases of Oxigraph errors.
#[derive(Debug)]
pub struct Error {
inner: ErrorKind,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
ErrorKind::Msg { msg } => write!(f, "{}", msg),
ErrorKind::Io(e) => e.fmt(f),
ErrorKind::FromUtf8(e) => e.fmt(f),
ErrorKind::Iri(e) => e.fmt(f),
ErrorKind::BlankNode(e) => e.fmt(f),
ErrorKind::LanguageTag(e) => e.fmt(f),
ErrorKind::Other(e) => e.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
ErrorKind::Msg { .. } => None,
ErrorKind::Io(e) => Some(e),
ErrorKind::FromUtf8(e) => Some(e),
ErrorKind::Iri(e) => Some(e),
ErrorKind::BlankNode(e) => Some(e),
ErrorKind::LanguageTag(e) => Some(e),
ErrorKind::Other(e) => Some(e.as_ref()),
}
}
}
impl Error {
/// Wraps another error.
pub fn wrap(error: impl error::Error + Send + Sync + 'static) -> Self {
Self {
inner: ErrorKind::Other(Box::new(error)),
}
}
/// Builds an error from a printable error message.
pub fn msg(msg: impl Into<String>) -> Self {
Self {
inner: ErrorKind::Msg { msg: msg.into() },
}
}
}
#[derive(Debug)]
enum ErrorKind {
Msg { msg: String },
Io(io::Error),
FromUtf8(FromUtf8Error),
Iri(IriParseError),
BlankNode(BlankNodeIdParseError),
LanguageTag(LanguageTagParseError),
Other(Box<dyn error::Error + Send + Sync + 'static>),
}
impl From<Infallible> for Error {
fn from(error: Infallible) -> Self {
match error {}
}
}
impl From<std::convert::Infallible> for Error {
fn from(error: std::convert::Infallible) -> Self {
match error {}
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
Self {
inner: ErrorKind::Io(error),
}
}
}
impl From<FromUtf8Error> for Error {
fn from(error: FromUtf8Error) -> Self {
Self {
inner: ErrorKind::FromUtf8(error),
}
}
}
impl From<IriParseError> for Error {
fn from(error: IriParseError) -> Self {
Self {
inner: ErrorKind::Iri(error),
}
}
}
impl From<BlankNodeIdParseError> for Error {
fn from(error: BlankNodeIdParseError) -> Self {
Self {
inner: ErrorKind::BlankNode(error),
}
}
}
impl From<LanguageTagParseError> for Error {
fn from(error: LanguageTagParseError) -> Self {
Self {
inner: ErrorKind::LanguageTag(error),
}
}
}
impl From<TurtleError> for Error {
fn from(error: TurtleError) -> Self {
Self::wrap(error)
}
}
impl From<RdfXmlError> for Error {
fn from(error: RdfXmlError) -> Self {
Self::wrap(error)
}
}
impl From<quick_xml::Error> for Error {
fn from(error: quick_xml::Error) -> Self {
Self::wrap(error)
}
}
impl From<SparqlParseError> for Error {
fn from(error: SparqlParseError) -> Self {
Self::wrap(error)
}
}
//TODO: convert to "!" when "never_type" is going to be stabilized //TODO: convert to "!" when "never_type" is going to be stabilized
#[allow(clippy::empty_enum)] #[allow(clippy::empty_enum)]
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub(crate) enum Infallible {} pub(crate) enum Infallible {}
impl Error for Infallible {}
impl fmt::Display for Infallible { impl fmt::Display for Infallible {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {} match *self {}
} }
} }
impl std::error::Error for Infallible {} impl<T> UnwrapInfallible for Result<T, Infallible> {
type Value = T;
impl From<Infallible> for std::convert::Infallible { fn unwrap_infallible(self) -> T {
fn from(error: Infallible) -> Self { match self {
match error {} Ok(value) => value,
Err(error) => match error {},
}
} }
} }
/// Traits that allows unwrapping only infallible results
pub(crate) trait UnwrapInfallible {
type Value;
fn unwrap_infallible(self) -> Self::Value;
}
impl From<std::convert::Infallible> for Infallible { impl From<std::convert::Infallible> for Infallible {
fn from(error: std::convert::Infallible) -> Self { fn from(error: std::convert::Infallible) -> Self {
match error {} match error {}
@ -178,20 +44,16 @@ impl From<Infallible> for std::io::Error {
} }
} }
/// Traits that allows unwrapping only infallible results impl From<Infallible> for std::convert::Infallible {
pub(crate) trait UnwrapInfallible { fn from(error: Infallible) -> Self {
type Value; match error {}
}
fn unwrap_infallible(self) -> Self::Value;
} }
impl<T> UnwrapInfallible for Result<T, Infallible> { pub(crate) fn invalid_data_error(error: impl Into<Box<dyn Error + Send + Sync>>) -> io::Error {
type Value = T; io::Error::new(io::ErrorKind::InvalidData, error)
}
fn unwrap_infallible(self) -> T { pub(crate) fn invalid_input_error(error: impl Into<Box<dyn Error + Send + Sync>>) -> io::Error {
match self { io::Error::new(io::ErrorKind::InvalidInput, error)
Ok(value) => value,
Err(error) => match error {},
}
}
} }

@ -1,5 +1,6 @@
//! Utilities to read RDF graphs and datasets //! Utilities to read RDF graphs and datasets
use crate::error::invalid_data_error;
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use oxiri::{Iri, IriParseError}; use oxiri::{Iri, IriParseError};
@ -8,7 +9,6 @@ use rio_api::parser::{QuadsParser, TriplesParser};
use rio_turtle::{NQuadsParser, NTriplesParser, TriGParser, TurtleParser}; use rio_turtle::{NQuadsParser, NTriplesParser, TriGParser, TurtleParser};
use rio_xml::RdfXmlParser; use rio_xml::RdfXmlParser;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::io; use std::io;
use std::io::BufRead; use std::io::BufRead;
@ -58,7 +58,7 @@ impl GraphParser {
/// ///
///assert_eq!(triples.len(), 1); ///assert_eq!(triples.len(), 1);
///assert_eq!(triples[0].subject.to_string(), "<http://example.com/s>"); ///assert_eq!(triples[0].subject.to_string(), "<http://example.com/s>");
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> { pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
self.base_iri = Iri::parse(base_iri.into())?.into_inner(); self.base_iri = Iri::parse(base_iri.into())?.into_inner();
@ -71,14 +71,14 @@ impl GraphParser {
Ok(TripleReader { Ok(TripleReader {
mapper: RioMapper::default(), mapper: RioMapper::default(),
parser: match self.format { parser: match self.format {
GraphFormat::NTriples => { GraphFormat::NTriples => TripleReaderKind::NTriples(
TripleReaderKind::NTriples(NTriplesParser::new(reader).map_err(invalid_input)?) NTriplesParser::new(reader).map_err(invalid_data_error)?,
} ),
GraphFormat::Turtle => TripleReaderKind::Turtle( GraphFormat::Turtle => TripleReaderKind::Turtle(
TurtleParser::new(reader, &self.base_iri).map_err(invalid_input)?, TurtleParser::new(reader, &self.base_iri).map_err(invalid_data_error)?,
), ),
GraphFormat::RdfXml => TripleReaderKind::RdfXml( GraphFormat::RdfXml => TripleReaderKind::RdfXml(
RdfXmlParser::new(reader, &self.base_iri).map_err(invalid_input)?, RdfXmlParser::new(reader, &self.base_iri).map_err(invalid_data_error)?,
), ),
}, },
buffer: Vec::new(), buffer: Vec::new(),
@ -125,15 +125,24 @@ impl<R: BufRead> Iterator for TripleReader<R> {
} }
if let Err(error) = match &mut self.parser { if let Err(error) = match &mut self.parser {
TripleReaderKind::NTriples(parser) => { TripleReaderKind::NTriples(parser) => Self::read(
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data) parser,
} &mut self.buffer,
TripleReaderKind::Turtle(parser) => { &mut self.mapper,
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data) invalid_data_error,
} ),
TripleReaderKind::RdfXml(parser) => { TripleReaderKind::Turtle(parser) => Self::read(
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data) parser,
} &mut self.buffer,
&mut self.mapper,
invalid_data_error,
),
TripleReaderKind::RdfXml(parser) => Self::read(
parser,
&mut self.buffer,
&mut self.mapper,
invalid_data_error,
),
}? { }? {
return Some(Err(error)); return Some(Err(error));
} }
@ -206,7 +215,7 @@ impl DatasetParser {
/// ///
///assert_eq!(triples.len(), 1); ///assert_eq!(triples.len(), 1);
///assert_eq!(triples[0].subject.to_string(), "<http://example.com/s>"); ///assert_eq!(triples[0].subject.to_string(), "<http://example.com/s>");
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> { pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
self.base_iri = Iri::parse(base_iri.into())?.into_inner(); self.base_iri = Iri::parse(base_iri.into())?.into_inner();
@ -220,10 +229,10 @@ impl DatasetParser {
mapper: RioMapper::default(), mapper: RioMapper::default(),
parser: match self.format { parser: match self.format {
DatasetFormat::NQuads => { DatasetFormat::NQuads => {
QuadReaderKind::NQuads(NQuadsParser::new(reader).map_err(invalid_input)?) QuadReaderKind::NQuads(NQuadsParser::new(reader).map_err(invalid_data_error)?)
} }
DatasetFormat::TriG => QuadReaderKind::TriG( DatasetFormat::TriG => QuadReaderKind::TriG(
TriGParser::new(reader, &self.base_iri).map_err(invalid_input)?, TriGParser::new(reader, &self.base_iri).map_err(invalid_data_error)?,
), ),
}, },
buffer: Vec::new(), buffer: Vec::new(),
@ -269,12 +278,18 @@ impl<R: BufRead> Iterator for QuadReader<R> {
} }
if let Err(error) = match &mut self.parser { if let Err(error) = match &mut self.parser {
QuadReaderKind::NQuads(parser) => { QuadReaderKind::NQuads(parser) => Self::read(
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data) parser,
} &mut self.buffer,
QuadReaderKind::TriG(parser) => { &mut self.mapper,
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data) invalid_data_error,
} ),
QuadReaderKind::TriG(parser) => Self::read(
parser,
&mut self.buffer,
&mut self.mapper,
invalid_data_error,
),
}? { }? {
return Some(Err(error)); return Some(Err(error));
} }
@ -371,11 +386,3 @@ impl<'a> RioMapper {
} }
} }
} }
fn invalid_input(error: impl Error + Send + Sync + 'static) -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, error)
}
fn invalid_data(error: impl Error + Send + Sync + 'static) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, error) //TODO: drop
}

@ -29,7 +29,7 @@ use std::io::Write;
/// writer.finish()?; /// writer.finish()?;
/// ///
///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n".as_bytes()); ///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n".as_bytes());
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[allow(missing_copy_implementations)] #[allow(missing_copy_implementations)]
pub struct GraphSerializer { pub struct GraphSerializer {
@ -74,7 +74,7 @@ impl GraphSerializer {
/// writer.finish()?; /// writer.finish()?;
/// ///
///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n".as_bytes()); ///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> .\n".as_bytes());
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[must_use] #[must_use]
pub struct TripleWriter<W: Write> { pub struct TripleWriter<W: Write> {
@ -131,7 +131,7 @@ impl<W: Write> TripleWriter<W> {
/// writer.finish()?; /// writer.finish()?;
/// ///
///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n".as_bytes()); ///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n".as_bytes());
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[allow(missing_copy_implementations)] #[allow(missing_copy_implementations)]
pub struct DatasetSerializer { pub struct DatasetSerializer {
@ -174,7 +174,7 @@ impl DatasetSerializer {
/// writer.finish()?; /// writer.finish()?;
/// ///
///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n".as_bytes()); ///assert_eq!(buffer.as_slice(), "<http://example.com/s> <http://example.com/p> <http://example.com/o> <http://example.com/g> .\n".as_bytes());
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[must_use] #[must_use]
pub struct QuadWriter<W: Write> { pub struct QuadWriter<W: Write> {

@ -34,7 +34,7 @@
//! if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { //! if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); //! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! } //! }
//! # oxigraph::Result::Ok(()) //! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! ``` //! ```
#![deny( #![deny(
future_incompatible, future_incompatible,
@ -109,8 +109,10 @@ pub mod model;
pub mod sparql; pub mod sparql;
pub mod store; pub mod store;
pub use error::Error; #[deprecated(note = "Use oxigraph::sparql::EvaluationError instead")]
pub type Result<T> = ::std::result::Result<T, Error>; pub type Error = crate::sparql::EvaluationError;
#[deprecated(note = "Use Result<_, oxigraph::sparql::EvaluationError> instead")]
pub type Result<T> = ::std::result::Result<T, crate::sparql::EvaluationError>;
#[deprecated(note = "Use oxigraph::io::DatasetFormat instead")] #[deprecated(note = "Use oxigraph::io::DatasetFormat instead")]
pub type DatasetSyntax = crate::io::DatasetFormat; pub type DatasetSyntax = crate::io::DatasetFormat;
#[deprecated(note = "Use oxigraph::io::FileSyntax instead")] #[deprecated(note = "Use oxigraph::io::FileSyntax instead")]

@ -12,9 +12,16 @@ use std::str;
/// It is also possible to create a blank node from a blank node identifier using the `BlankNode::new` method. /// It is also possible to create a blank node from a blank node identifier using the `BlankNode::new` method.
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars. /// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation. /// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation:
/// `BlankNode::default().to_string()` should return something like `_:00112233445566778899aabbccddeeff` /// ```
/// use oxigraph::model::BlankNode;
/// ///
/// assert_eq!(
/// "_:a122",
/// BlankNode::new("a122")?.to_string()
/// );
/// # Result::<_,oxigraph::model::BlankNodeIdParseError>::Ok(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct BlankNode(BlankNodeContent); pub struct BlankNode(BlankNodeContent);

@ -10,8 +10,9 @@ use std::fmt;
/// ///
/// assert_eq!( /// assert_eq!(
/// "<http://example.com/foo>", /// "<http://example.com/foo>",
/// NamedNode::new("http://example.com/foo").unwrap().to_string() /// NamedNode::new("http://example.com/foo")?.to_string()
/// ) /// );
/// # Result::<_,oxigraph::model::IriParseError>::Ok(())
/// ``` /// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct NamedNode { pub struct NamedNode {

@ -0,0 +1,109 @@
use crate::error::Infallible;
use crate::sparql::ParseError;
use std::error;
use std::fmt;
use std::io;
/// SPARQL evaluation error.
///
/// The `wrap` method allows us to make this type wrap any implementation of `std::error::Error`.
/// This type also avoids heap allocations for the most common cases of evaluation errors.
#[derive(Debug)]
#[non_exhaustive]
pub enum EvaluationError {
/// An error in SPARQL query parsing
Parsing(ParseError),
/// An error returned during store IOs or during results write
Io(io::Error),
/// An error returned during the query evaluation itself
Query(QueryError),
}
#[derive(Debug)]
pub struct QueryError {
inner: QueryErrorKind,
}
#[derive(Debug)]
enum QueryErrorKind {
Msg { msg: String },
Other(Box<dyn error::Error + Send + Sync + 'static>),
}
impl fmt::Display for EvaluationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parsing(error) => error.fmt(f),
Self::Io(error) => error.fmt(f),
Self::Query(error) => error.fmt(f),
}
}
}
impl fmt::Display for QueryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
QueryErrorKind::Msg { msg } => write!(f, "{}", msg),
QueryErrorKind::Other(error) => error.fmt(f),
}
}
}
impl error::Error for EvaluationError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Parsing(e) => Some(e),
Self::Io(e) => Some(e),
Self::Query(e) => Some(e),
}
}
}
impl error::Error for QueryError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.inner {
QueryErrorKind::Msg { .. } => None,
QueryErrorKind::Other(e) => Some(e.as_ref()),
}
}
}
impl EvaluationError {
/// Wraps another error.
pub(crate) fn wrap(error: impl error::Error + Send + Sync + 'static) -> Self {
Self::Query(QueryError {
inner: QueryErrorKind::Other(Box::new(error)),
})
}
/// Builds an error from a printable error message.
pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self::Query(QueryError {
inner: QueryErrorKind::Msg { msg: msg.into() },
})
}
}
impl From<Infallible> for EvaluationError {
fn from(error: Infallible) -> Self {
match error {}
}
}
impl From<std::convert::Infallible> for EvaluationError {
fn from(error: std::convert::Infallible) -> Self {
match error {}
}
}
impl From<ParseError> for EvaluationError {
fn from(error: ParseError) -> Self {
Self::Parsing(error)
}
}
impl From<io::Error> for EvaluationError {
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}

@ -2,13 +2,12 @@ use crate::model::xsd::*;
use crate::model::BlankNode; use crate::model::BlankNode;
use crate::model::Triple; use crate::model::Triple;
use crate::sparql::algebra::{DatasetSpec, GraphPattern, QueryVariants}; use crate::sparql::algebra::{DatasetSpec, GraphPattern, QueryVariants};
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::sparql::plan::*; use crate::sparql::plan::*;
use crate::sparql::{Query, ServiceHandler}; use crate::sparql::{Query, ServiceHandler};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::ReadableEncodedStore; use crate::store::ReadableEncodedStore;
use crate::Error;
use crate::Result;
use digest::Digest; use digest::Digest;
use md5::Md5; use md5::Md5;
use oxilangtag::LanguageTag; use oxilangtag::LanguageTag;
@ -29,13 +28,13 @@ use std::str;
const REGEX_SIZE_LIMIT: usize = 1_000_000; const REGEX_SIZE_LIMIT: usize = 1_000_000;
type EncodedTuplesIterator = Box<dyn Iterator<Item = Result<EncodedTuple>>>; type EncodedTuplesIterator = Box<dyn Iterator<Item = Result<EncodedTuple, EvaluationError>>>;
pub(crate) struct SimpleEvaluator<S: ReadableEncodedStore + 'static> { pub(crate) struct SimpleEvaluator<S: ReadableEncodedStore + 'static> {
dataset: Rc<DatasetView<S>>, dataset: Rc<DatasetView<S>>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
now: DateTime, now: DateTime,
service_handler: Rc<dyn ServiceHandler>, service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
} }
impl<S: ReadableEncodedStore + 'static> Clone for SimpleEvaluator<S> { impl<S: ReadableEncodedStore + 'static> Clone for SimpleEvaluator<S> {
@ -53,7 +52,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
pub fn new( pub fn new(
dataset: Rc<DatasetView<S>>, dataset: Rc<DatasetView<S>>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler>, service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
) -> Self { ) -> Self {
Self { Self {
dataset, dataset,
@ -67,14 +66,14 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self, &self,
plan: &PlanNode, plan: &PlanNode,
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
) -> Result<QueryResult> { ) -> Result<QueryResult, EvaluationError> {
let iter = self.eval_plan(plan, EncodedTuple::with_capacity(variables.len())); let iter = self.eval_plan(plan, EncodedTuple::with_capacity(variables.len()));
Ok(QueryResult::Solutions( Ok(QueryResult::Solutions(
self.decode_bindings(iter, variables), self.decode_bindings(iter, variables),
)) ))
} }
pub fn evaluate_ask_plan(&self, plan: &PlanNode) -> Result<QueryResult> { pub fn evaluate_ask_plan(&self, plan: &PlanNode) -> Result<QueryResult, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
match self.eval_plan(plan, from).next() { match self.eval_plan(plan, from).next() {
Some(Ok(_)) => Ok(QueryResult::Boolean(true)), Some(Ok(_)) => Ok(QueryResult::Boolean(true)),
@ -87,7 +86,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self, &self,
plan: &PlanNode, plan: &PlanNode,
construct: Rc<Vec<TripleTemplate>>, construct: Rc<Vec<TripleTemplate>>,
) -> Result<QueryResult> { ) -> Result<QueryResult, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
Ok(QueryResult::Graph(QueryTriplesIterator { Ok(QueryResult::Graph(QueryTriplesIterator {
iter: Box::new(ConstructIterator { iter: Box::new(ConstructIterator {
@ -100,7 +99,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
})) }))
} }
pub fn evaluate_describe_plan(&self, plan: &PlanNode) -> Result<QueryResult> { pub fn evaluate_describe_plan(&self, plan: &PlanNode) -> Result<QueryResult, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
Ok(QueryResult::Graph(QueryTriplesIterator { Ok(QueryResult::Graph(QueryTriplesIterator {
iter: Box::new(DescribeIterator { iter: Box::new(DescribeIterator {
@ -228,7 +227,8 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
if let Some(graph_name) = get_pattern_value(&graph_name, &tuple) { if let Some(graph_name) = get_pattern_value(&graph_name, &tuple) {
graph_name graph_name
} else { } else {
let result: EncodedTuplesIterator = Box::new(once(Err(Error::msg( let result: EncodedTuplesIterator =
Box::new(once(Err(EvaluationError::msg(
"Unknown graph name is not allowed when evaluating property path", "Unknown graph name is not allowed when evaluating property path",
)))); ))));
return result; return result;
@ -502,11 +502,11 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
graph_pattern: Rc<GraphPattern>, graph_pattern: Rc<GraphPattern>,
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
from: &EncodedTuple, from: &EncodedTuple,
) -> Result<EncodedTuplesIterator> { ) -> Result<EncodedTuplesIterator, EvaluationError> {
if let QueryResult::Solutions(iter) = self.service_handler.handle( if let QueryResult::Solutions(iter) = self.service_handler.handle(
self.dataset.decode_named_node( self.dataset.decode_named_node(
get_pattern_value(service_name, from) get_pattern_value(service_name, from)
.ok_or_else(|| Error::msg("The SERVICE name is not bound"))?, .ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?,
)?, )?,
Query(QueryVariants::Select { Query(QueryVariants::Select {
dataset: Rc::new(DatasetSpec::default()), dataset: Rc::new(DatasetSpec::default()),
@ -516,7 +516,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
)? { )? {
Ok(self.encode_bindings(variables, iter)) Ok(self.encode_bindings(variables, iter))
} else { } else {
Err(Error::msg( Err(EvaluationError::msg(
"The service call has not returned a set of solutions", "The service call has not returned a set of solutions",
)) ))
} }
@ -570,7 +570,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
path: &PlanPropertyPath, path: &PlanPropertyPath,
start: EncodedTerm, start: EncodedTerm,
graph_name: EncodedTerm, graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<EncodedTerm>>> { ) -> Box<dyn Iterator<Item = Result<EncodedTerm, EvaluationError>>> {
match path { match path {
PlanPropertyPath::PredicatePath(p) => Box::new( PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset self.dataset
@ -633,7 +633,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
path: &PlanPropertyPath, path: &PlanPropertyPath,
end: EncodedTerm, end: EncodedTerm,
graph_name: EncodedTerm, graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<EncodedTerm>>> { ) -> Box<dyn Iterator<Item = Result<EncodedTerm, EvaluationError>>> {
match path { match path {
PlanPropertyPath::PredicatePath(p) => Box::new( PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset self.dataset
@ -695,7 +695,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self, &self,
path: &PlanPropertyPath, path: &PlanPropertyPath,
graph_name: EncodedTerm, graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<(EncodedTerm, EncodedTerm)>>> { ) -> Box<dyn Iterator<Item = Result<(EncodedTerm, EncodedTerm), EvaluationError>>> {
match path { match path {
PlanPropertyPath::PredicatePath(p) => Box::new( PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset self.dataset
@ -770,7 +770,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
fn get_subject_or_object_identity_pairs( fn get_subject_or_object_identity_pairs(
&self, &self,
graph_name: EncodedTerm, graph_name: EncodedTerm,
) -> impl Iterator<Item = Result<(EncodedTerm, EncodedTerm)>> { ) -> impl Iterator<Item = Result<(EncodedTerm, EncodedTerm), EvaluationError>> {
self.dataset self.dataset
.quads_for_pattern(None, None, None, Some(graph_name)) .quads_for_pattern(None, None, None, Some(graph_name))
.flat_map_ok(|t| once(Ok(t.subject)).chain(once(Ok(t.object)))) .flat_map_ok(|t| once(Ok(t.subject)).chain(once(Ok(t.object))))
@ -2160,13 +2160,13 @@ pub fn are_compatible_and_not_disjointed(a: &EncodedTuple, b: &EncodedTuple) ->
struct JoinIterator { struct JoinIterator {
left: Vec<EncodedTuple>, left: Vec<EncodedTuple>,
right_iter: EncodedTuplesIterator, right_iter: EncodedTuplesIterator,
buffered_results: Vec<Result<EncodedTuple>>, buffered_results: Vec<Result<EncodedTuple, EvaluationError>>,
} }
impl Iterator for JoinIterator { impl Iterator for JoinIterator {
type Item = Result<EncodedTuple>; type Item = Result<EncodedTuple, EvaluationError>;
fn next(&mut self) -> Option<Result<EncodedTuple>> { fn next(&mut self) -> Option<Result<EncodedTuple, EvaluationError>> {
loop { loop {
if let Some(result) = self.buffered_results.pop() { if let Some(result) = self.buffered_results.pop() {
return Some(result); return Some(result);
@ -2190,9 +2190,9 @@ struct AntiJoinIterator {
} }
impl Iterator for AntiJoinIterator { impl Iterator for AntiJoinIterator {
type Item = Result<EncodedTuple>; type Item = Result<EncodedTuple, EvaluationError>;
fn next(&mut self) -> Option<Result<EncodedTuple>> { fn next(&mut self) -> Option<Result<EncodedTuple, EvaluationError>> {
loop { loop {
match self.left_iter.next()? { match self.left_iter.next()? {
Ok(left_tuple) => { Ok(left_tuple) => {
@ -2217,9 +2217,9 @@ struct LeftJoinIterator<S: ReadableEncodedStore + 'static> {
} }
impl<S: ReadableEncodedStore + 'static> Iterator for LeftJoinIterator<S> { impl<S: ReadableEncodedStore + 'static> Iterator for LeftJoinIterator<S> {
type Item = Result<EncodedTuple>; type Item = Result<EncodedTuple, EvaluationError>;
fn next(&mut self) -> Option<Result<EncodedTuple>> { fn next(&mut self) -> Option<Result<EncodedTuple, EvaluationError>> {
if let Some(tuple) = self.current_right.next() { if let Some(tuple) = self.current_right.next() {
return Some(tuple); return Some(tuple);
} }
@ -2247,9 +2247,9 @@ struct BadLeftJoinIterator<S: ReadableEncodedStore + 'static> {
} }
impl<S: ReadableEncodedStore + 'static> Iterator for BadLeftJoinIterator<S> { impl<S: ReadableEncodedStore + 'static> Iterator for BadLeftJoinIterator<S> {
type Item = Result<EncodedTuple>; type Item = Result<EncodedTuple, EvaluationError>;
fn next(&mut self) -> Option<Result<EncodedTuple>> { fn next(&mut self) -> Option<Result<EncodedTuple, EvaluationError>> {
while let Some(right_tuple) = self.current_right.next() { while let Some(right_tuple) = self.current_right.next() {
match right_tuple { match right_tuple {
Ok(right_tuple) => { Ok(right_tuple) => {
@ -2298,9 +2298,9 @@ struct UnionIterator<S: ReadableEncodedStore + 'static> {
} }
impl<S: ReadableEncodedStore + 'static> Iterator for UnionIterator<S> { impl<S: ReadableEncodedStore + 'static> Iterator for UnionIterator<S> {
type Item = Result<EncodedTuple>; type Item = Result<EncodedTuple, EvaluationError>;
fn next(&mut self) -> Option<Result<EncodedTuple>> { fn next(&mut self) -> Option<Result<EncodedTuple, EvaluationError>> {
loop { loop {
if let Some(tuple) = self.current_iterator.next() { if let Some(tuple) = self.current_iterator.next() {
return Some(tuple); return Some(tuple);
@ -2320,14 +2320,14 @@ struct ConstructIterator<S: ReadableEncodedStore + 'static> {
eval: SimpleEvaluator<S>, eval: SimpleEvaluator<S>,
iter: EncodedTuplesIterator, iter: EncodedTuplesIterator,
template: Rc<Vec<TripleTemplate>>, template: Rc<Vec<TripleTemplate>>,
buffered_results: Vec<Result<Triple>>, buffered_results: Vec<Result<Triple, EvaluationError>>,
bnodes: Vec<BlankNode>, bnodes: Vec<BlankNode>,
} }
impl<S: ReadableEncodedStore + 'static> Iterator for ConstructIterator<S> { impl<S: ReadableEncodedStore + 'static> Iterator for ConstructIterator<S> {
type Item = Result<Triple>; type Item = Result<Triple, EvaluationError>;
fn next(&mut self) -> Option<Result<Triple>> { fn next(&mut self) -> Option<Result<Triple, EvaluationError>> {
loop { loop {
if let Some(result) = self.buffered_results.pop() { if let Some(result) = self.buffered_results.pop() {
return Some(result); return Some(result);
@ -2379,7 +2379,7 @@ fn decode_triple(
subject: EncodedTerm, subject: EncodedTerm,
predicate: EncodedTerm, predicate: EncodedTerm,
object: EncodedTerm, object: EncodedTerm,
) -> Result<Triple> { ) -> Result<Triple, EvaluationError> {
Ok(Triple::new( Ok(Triple::new(
decoder.decode_named_or_blank_node(subject)?, decoder.decode_named_or_blank_node(subject)?,
decoder.decode_named_node(predicate)?, decoder.decode_named_node(predicate)?,
@ -2390,13 +2390,13 @@ fn decode_triple(
struct DescribeIterator<S: ReadableEncodedStore + 'static> { struct DescribeIterator<S: ReadableEncodedStore + 'static> {
eval: SimpleEvaluator<S>, eval: SimpleEvaluator<S>,
iter: EncodedTuplesIterator, iter: EncodedTuplesIterator,
quads: Box<dyn Iterator<Item = Result<EncodedQuad>>>, quads: Box<dyn Iterator<Item = Result<EncodedQuad, EvaluationError>>>,
} }
impl<S: ReadableEncodedStore + 'static> Iterator for DescribeIterator<S> { impl<S: ReadableEncodedStore + 'static> Iterator for DescribeIterator<S> {
type Item = Result<Triple>; type Item = Result<Triple, EvaluationError>;
fn next(&mut self) -> Option<Result<Triple>> { fn next(&mut self) -> Option<Result<Triple, EvaluationError>> {
loop { loop {
if let Some(quad) = self.quads.next() { if let Some(quad) = self.quads.next() {
return Some(match quad { return Some(match quad {
@ -2449,10 +2449,10 @@ impl<T1, T2, I1: Iterator<Item = T1>, I2: Iterator<Item = T2>> Iterator
} }
} }
fn transitive_closure<T: Copy + Eq + Hash, NI: Iterator<Item = Result<T>>>( fn transitive_closure<T: Copy + Eq + Hash, NI: Iterator<Item = Result<T, EvaluationError>>>(
start: impl IntoIterator<Item = Result<T>>, start: impl IntoIterator<Item = Result<T, EvaluationError>>,
next: impl Fn(T) -> NI, next: impl Fn(T) -> NI,
) -> impl Iterator<Item = Result<T>> { ) -> impl Iterator<Item = Result<T, EvaluationError>> {
//TODO: optimize //TODO: optimize
let mut all = HashSet::<T>::default(); let mut all = HashSet::<T>::default();
let mut errors = Vec::default(); let mut errors = Vec::default();
@ -2494,8 +2494,8 @@ fn transitive_closure<T: Copy + Eq + Hash, NI: Iterator<Item = Result<T>>>(
} }
fn hash_deduplicate<T: Eq + Hash + Clone>( fn hash_deduplicate<T: Eq + Hash + Clone>(
iter: impl Iterator<Item = Result<T>>, iter: impl Iterator<Item = Result<T, EvaluationError>>,
) -> impl Iterator<Item = Result<T>> { ) -> impl Iterator<Item = Result<T, EvaluationError>> {
let mut already_seen = HashSet::with_capacity(iter.size_hint().0); let mut already_seen = HashSet::with_capacity(iter.size_hint().0);
iter.filter(move |e| { iter.filter(move |e| {
if let Ok(e) = e { if let Ok(e) = e {
@ -2511,15 +2511,15 @@ fn hash_deduplicate<T: Eq + Hash + Clone>(
}) })
} }
trait ResultIterator<T>: Iterator<Item = Result<T>> + Sized { trait ResultIterator<T>: Iterator<Item = Result<T, EvaluationError>> + Sized {
fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>>( fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O, EvaluationError>>>(
self, self,
f: F, f: F,
) -> FlatMapOk<T, O, Self, F, U>; ) -> FlatMapOk<T, O, Self, F, U>;
} }
impl<T, I: Iterator<Item = Result<T>> + Sized> ResultIterator<T> for I { impl<T, I: Iterator<Item = Result<T, EvaluationError>> + Sized> ResultIterator<T> for I {
fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>>( fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O, EvaluationError>>>(
self, self,
f: F, f: F,
) -> FlatMapOk<T, O, Self, F, U> { ) -> FlatMapOk<T, O, Self, F, U> {
@ -2534,21 +2534,26 @@ impl<T, I: Iterator<Item = Result<T>> + Sized> ResultIterator<T> for I {
struct FlatMapOk< struct FlatMapOk<
T, T,
O, O,
I: Iterator<Item = Result<T>>, I: Iterator<Item = Result<T, EvaluationError>>,
F: FnMut(T) -> U, F: FnMut(T) -> U,
U: IntoIterator<Item = Result<O>>, U: IntoIterator<Item = Result<O, EvaluationError>>,
> { > {
inner: I, inner: I,
f: F, f: F,
current: Option<U::IntoIter>, current: Option<U::IntoIter>,
} }
impl<T, O, I: Iterator<Item = Result<T>>, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>> impl<
Iterator for FlatMapOk<T, O, I, F, U> T,
O,
I: Iterator<Item = Result<T, EvaluationError>>,
F: FnMut(T) -> U,
U: IntoIterator<Item = Result<O, EvaluationError>>,
> Iterator for FlatMapOk<T, O, I, F, U>
{ {
type Item = Result<O>; type Item = Result<O, EvaluationError>;
fn next(&mut self) -> Option<Result<O>> { fn next(&mut self) -> Option<Result<O, EvaluationError>> {
loop { loop {
if let Some(current) = &mut self.current { if let Some(current) = &mut self.current {
if let Some(next) = current.next() { if let Some(next) = current.next() {

@ -1,12 +1,15 @@
//! Implementation of [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) //! Implementation of [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/)
use crate::error::invalid_input_error;
use crate::model::*; use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::Error;
use crate::Result;
use std::io::Write; use std::io::Write;
pub fn write_json_results(results: QueryResult, mut sink: impl Write) -> Result<()> { pub fn write_json_results(
results: QueryResult,
mut sink: impl Write,
) -> Result<(), EvaluationError> {
match results { match results {
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
sink.write_all(b"{\"head\":{},\"boolean\":")?; sink.write_all(b"{\"head\":{},\"boolean\":")?;
@ -73,15 +76,16 @@ pub fn write_json_results(results: QueryResult, mut sink: impl Write) -> Result<
sink.write_all(b"]}}")?; sink.write_all(b"]}}")?;
} }
QueryResult::Graph(_) => { QueryResult::Graph(_) => {
return Err(Error::msg( return Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format", "Graphs could not be formatted to SPARQL query results XML format",
)); )
.into());
} }
} }
Ok(()) Ok(())
} }
fn write_escaped_json_string(s: &str, mut sink: impl Write) -> Result<()> { fn write_escaped_json_string(s: &str, mut sink: impl Write) -> Result<(), EvaluationError> {
sink.write_all(b"\"")?; sink.write_all(b"\"")?;
for c in s.chars() { for c in s.chars() {
match c { match c {

@ -1,6 +1,7 @@
//! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation. //! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation.
mod algebra; mod algebra;
mod error;
mod eval; mod eval;
mod json_results; mod json_results;
mod model; mod model;
@ -12,27 +13,26 @@ mod xml_results;
use crate::model::NamedNode; use crate::model::NamedNode;
use crate::sparql::algebra::QueryVariants; use crate::sparql::algebra::QueryVariants;
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator;
pub use crate::sparql::model::QueryTriplesIterator;
use crate::sparql::plan::TripleTemplate; use crate::sparql::plan::TripleTemplate;
use crate::sparql::plan::{DatasetView, PlanNode}; use crate::sparql::plan::{DatasetView, PlanNode};
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
use crate::store::ReadableEncodedStore; use crate::store::ReadableEncodedStore;
use crate::Error; use std::convert::TryInto;
use crate::Result; use std::rc::Rc;
pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator;
pub use crate::sparql::model::QueryTriplesIterator;
#[deprecated(note = "Please directly use QuerySolutionsIterator type instead")] #[deprecated(note = "Please directly use QuerySolutionsIterator type instead")]
pub type BindingsIterator<'a> = QuerySolutionsIterator; pub type BindingsIterator<'a> = QuerySolutionsIterator;
pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultFormat; pub use crate::sparql::model::QueryResultFormat;
#[deprecated(note = "Use QueryResultFormat instead")] #[deprecated(note = "Use QueryResultFormat instead")]
pub type QueryResultSyntax = QueryResultFormat; pub type QueryResultSyntax = QueryResultFormat;
pub use crate::sparql::error::EvaluationError;
pub use crate::sparql::model::Variable; pub use crate::sparql::model::Variable;
pub use crate::sparql::parser::ParseError;
pub use crate::sparql::parser::Query; pub use crate::sparql::parser::Query;
pub use crate::sparql::parser::SparqlParseError; use std::error::Error;
use std::convert::TryInto;
use std::rc::Rc;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[deprecated( #[deprecated(
@ -70,9 +70,9 @@ enum SimplePreparedQueryAction<S: ReadableEncodedStore + 'static> {
impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> { impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
pub(crate) fn new( pub(crate) fn new(
store: S, store: S,
query: impl TryInto<Query, Error = impl Into<Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<Self> { ) -> Result<Self, EvaluationError> {
let dataset = Rc::new(DatasetView::new(store, options.default_graph_as_union)); let dataset = Rc::new(DatasetView::new(store, options.default_graph_as_union));
Ok(Self(match query.try_into().map_err(|e| e.into())?.0 { Ok(Self(match query.try_into().map_err(|e| e.into())?.0 {
QueryVariants::Select { QueryVariants::Select {
@ -124,7 +124,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
} }
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult> { pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
match &self.0 { match &self.0 {
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan, plan,
@ -144,14 +144,46 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
} }
} }
/// Options for SPARQL query evaluation
#[derive(Clone)]
pub struct QueryOptions {
pub(crate) default_graph_as_union: bool,
pub(crate) service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
}
impl Default for QueryOptions {
fn default() -> Self {
Self {
default_graph_as_union: false,
service_handler: Rc::new(EmptyServiceHandler),
}
}
}
impl QueryOptions {
/// Consider the union of all graphs in the store as the default graph
pub const fn with_default_graph_as_union(mut self) -> Self {
self.default_graph_as_union = true;
self
}
/// Use a given `ServiceHandler` to execute SPARQL SERVICE calls
pub fn with_service_handler(mut self, service_handler: impl ServiceHandler + 'static) -> Self {
self.service_handler = Rc::new(ErrorConversionServiceHandler {
handler: service_handler,
});
self
}
}
/// Handler for SPARQL SERVICEs. /// Handler for SPARQL SERVICEs.
/// ///
/// Might be used to implement [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) /// Might be used to implement [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/)
/// ///
/// ``` /// ```
/// use oxigraph::{MemoryStore, Result}; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryOptions, QueryResult, ServiceHandler, Query}; /// use oxigraph::sparql::{QueryOptions, QueryResult, ServiceHandler, Query, EvaluationError};
/// ///
/// #[derive(Default)] /// #[derive(Default)]
/// struct TestServiceHandler { /// struct TestServiceHandler {
@ -159,7 +191,9 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
/// } /// }
/// ///
/// impl ServiceHandler for TestServiceHandler { /// impl ServiceHandler for TestServiceHandler {
/// fn handle(&self,service_name: NamedNode, query: Query) -> Result<QueryResult> { /// type Error = EvaluationError;
///
/// fn handle(&self,service_name: NamedNode, query: Query) -> Result<QueryResult,EvaluationError> {
/// if service_name == "http://example.com/service" { /// if service_name == "http://example.com/service" {
/// self.store.query(query, QueryOptions::default()) /// self.store.query(query, QueryOptions::default())
/// } else { /// } else {
@ -179,47 +213,41 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
/// )? { /// )? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub trait ServiceHandler { pub trait ServiceHandler {
type Error: Error + Send + Sync + 'static;
/// Evaluates a `Query` against a given service identified by a `NamedNode`. /// Evaluates a `Query` against a given service identified by a `NamedNode`.
fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult>; fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult, Self::Error>;
} }
struct EmptyServiceHandler; struct EmptyServiceHandler;
impl ServiceHandler for EmptyServiceHandler { impl ServiceHandler for EmptyServiceHandler {
fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult> { type Error = EvaluationError;
Err(Error::msg("The SERVICE feature is not implemented"))
}
}
/// Options for SPARQL query evaluation fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult, EvaluationError> {
#[derive(Clone)] Err(EvaluationError::msg(
pub struct QueryOptions { "The SERVICE feature is not implemented",
pub(crate) default_graph_as_union: bool, ))
pub(crate) service_handler: Rc<dyn ServiceHandler>, }
} }
impl Default for QueryOptions { struct ErrorConversionServiceHandler<S: ServiceHandler> {
fn default() -> Self { handler: S,
Self {
default_graph_as_union: false,
service_handler: Rc::new(EmptyServiceHandler),
}
}
} }
impl QueryOptions { impl<S: ServiceHandler> ServiceHandler for ErrorConversionServiceHandler<S> {
/// Consider the union of all graphs in the store as the default graph type Error = EvaluationError;
pub const fn with_default_graph_as_union(mut self) -> Self {
self.default_graph_as_union = true;
self
}
/// Use a given `ServiceHandler` to execute SPARQL SERVICE calls fn handle(
pub fn with_service_handler(mut self, service_handler: impl ServiceHandler + 'static) -> Self { &self,
self.service_handler = Rc::new(service_handler); service_name: NamedNode,
self query: Query,
) -> Result<QueryResult, EvaluationError> {
self.handler
.handle(service_name, query)
.map_err(EvaluationError::wrap)
} }
} }

@ -1,10 +1,11 @@
use crate::error::invalid_input_error;
use crate::io::GraphSerializer; use crate::io::GraphSerializer;
#[allow(deprecated)] #[allow(deprecated)]
use crate::io::{FileSyntax, GraphFormat}; use crate::io::{FileSyntax, GraphFormat};
use crate::model::*; use crate::model::*;
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 crate::{Error, Result};
use rand::random; use rand::random;
use std::fmt; use std::fmt;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
@ -21,13 +22,16 @@ pub enum QueryResult {
} }
impl QueryResult { impl QueryResult {
pub fn read(reader: impl BufRead + 'static, format: QueryResultFormat) -> Result<Self> { pub fn read(
reader: impl BufRead + 'static,
format: QueryResultFormat,
) -> Result<Self, EvaluationError> {
match format { match format {
QueryResultFormat::Xml => read_xml_results(reader), QueryResultFormat::Xml => read_xml_results(reader),
QueryResultFormat::Json => Err(Error::msg( QueryResultFormat::Json => Err(invalid_input_error(
//TODO: implement
"JSON SPARQL results format parsing has not been implemented yet", "JSON SPARQL results format parsing has not been implemented yet",
)), )
.into()), //TODO: implement
} }
} }
@ -47,9 +51,13 @@ impl QueryResult {
/// let mut results = Vec::new(); /// let mut results = Vec::new();
/// store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?.write(&mut results, QueryResultFormat::Json)?; /// store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?.write(&mut results, QueryResultFormat::Json)?;
/// assert_eq!(results, "{\"head\":{\"vars\":[\"s\"]},\"results\":{\"bindings\":[{\"s\":{\"type\":\"uri\",\"value\":\"http://example.com\"}}]}}".as_bytes()); /// assert_eq!(results, "{\"head\":{\"vars\":[\"s\"]},\"results\":{\"bindings\":[{\"s\":{\"type\":\"uri\",\"value\":\"http://example.com\"}}]}}".as_bytes());
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn write(self, writer: impl Write, format: QueryResultFormat) -> Result<()> { pub fn write(
self,
writer: impl Write,
format: QueryResultFormat,
) -> Result<(), EvaluationError> {
match format { match format {
QueryResultFormat::Xml => write_xml_results(self, writer), QueryResultFormat::Xml => write_xml_results(self, writer),
QueryResultFormat::Json => write_json_results(self, writer), QueryResultFormat::Json => write_json_results(self, writer),
@ -75,9 +83,13 @@ impl QueryResult {
/// let mut results = Vec::new(); /// let mut results = Vec::new();
/// store.query("CONSTRUCT WHERE { ?s ?p ?o }", QueryOptions::default())?.write_graph(&mut results, GraphFormat::NTriples)?; /// store.query("CONSTRUCT WHERE { ?s ?p ?o }", QueryOptions::default())?.write_graph(&mut results, GraphFormat::NTriples)?;
/// assert_eq!(results, graph); /// assert_eq!(results, graph);
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn write_graph(self, write: impl Write, format: GraphFormat) -> Result<()> { pub fn write_graph(
self,
write: impl Write,
format: GraphFormat,
) -> Result<(), EvaluationError> {
if let QueryResult::Graph(triples) = self { if let QueryResult::Graph(triples) = self {
let mut writer = GraphSerializer::from_format(format).triple_writer(write)?; let mut writer = GraphSerializer::from_format(format).triple_writer(write)?;
for triple in triples { for triple in triples {
@ -86,9 +98,10 @@ impl QueryResult {
writer.finish()?; writer.finish()?;
Ok(()) Ok(())
} else { } else {
Err(Error::msg( Err(
"Bindings or booleans could not be formatted as an RDF graph", invalid_input_error("Bindings or booleans could not be formatted as an RDF graph")
)) .into(),
)
} }
} }
} }
@ -212,17 +225,17 @@ impl FileSyntax for QueryResultFormat {
/// println!("{:?}", solution?.get("s")); /// println!("{:?}", solution?.get("s"));
/// } /// }
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub struct QuerySolutionsIterator { pub struct QuerySolutionsIterator {
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
} }
impl QuerySolutionsIterator { impl QuerySolutionsIterator {
pub fn new( pub fn new(
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
) -> Self { ) -> Self {
Self { variables, iter } Self { variables, iter }
} }
@ -237,14 +250,16 @@ impl QuerySolutionsIterator {
/// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]); /// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]);
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn variables(&self) -> &[Variable] { pub fn variables(&self) -> &[Variable] {
&*self.variables &*self.variables
} }
#[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")] #[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")]
pub fn into_values_iter(self) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>> { pub fn into_values_iter(
self,
) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>> {
self.iter self.iter
} }
@ -253,16 +268,16 @@ impl QuerySolutionsIterator {
self, self,
) -> ( ) -> (
Vec<Variable>, Vec<Variable>,
Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>, Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
) { ) {
((*self.variables).clone(), self.iter) ((*self.variables).clone(), self.iter)
} }
} }
impl Iterator for QuerySolutionsIterator { impl Iterator for QuerySolutionsIterator {
type Item = Result<QuerySolution>; type Item = Result<QuerySolution, EvaluationError>;
fn next(&mut self) -> Option<Result<QuerySolution>> { fn next(&mut self) -> Option<Result<QuerySolution, EvaluationError>> {
Some(self.iter.next()?.map(|values| QuerySolution { Some(self.iter.next()?.map(|values| QuerySolution {
values, values,
variables: self.variables.clone(), variables: self.variables.clone(),
@ -359,16 +374,16 @@ impl VariableSolutionIndex for Variable {
/// println!("{}", triple?); /// println!("{}", triple?);
/// } /// }
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub struct QueryTriplesIterator { pub struct QueryTriplesIterator {
pub(crate) iter: Box<dyn Iterator<Item = Result<Triple>>>, pub(crate) iter: Box<dyn Iterator<Item = Result<Triple, EvaluationError>>>,
} }
impl Iterator for QueryTriplesIterator { impl Iterator for QueryTriplesIterator {
type Item = Result<Triple>; type Item = Result<Triple, EvaluationError>;
fn next(&mut self) -> Option<Result<Triple>> { fn next(&mut self) -> Option<Result<Triple, EvaluationError>> {
self.iter.next() self.iter.next()
} }
@ -409,7 +424,7 @@ impl Variable {
} }
#[deprecated(note = "Please use as_str instead")] #[deprecated(note = "Please use as_str instead")]
pub fn name(&self) -> Result<&str> { pub fn name(&self) -> Result<&str, EvaluationError> {
Ok(self.as_str()) Ok(self.as_str())
} }

@ -4,7 +4,6 @@ use crate::model::*;
use crate::sparql::algebra::*; use crate::sparql::algebra::*;
use crate::sparql::model::*; use crate::sparql::model::*;
use oxiri::{Iri, IriParseError}; use oxiri::{Iri, IriParseError};
use peg::error::ParseError;
use peg::parser; use peg::parser;
use peg::str::LineCol; use peg::str::LineCol;
use std::borrow::Cow; use std::borrow::Cow;
@ -25,7 +24,7 @@ use std::{char, fmt};
/// let query = Query::parse(query_str, None)?; /// let query = Query::parse(query_str, None)?;
/// ///
/// assert_eq!(query.to_string(), query_str); /// assert_eq!(query.to_string(), query_str);
/// # oxigraph::Result::Ok(()) /// # Result::Ok::<_, oxigraph::sparql::ParseError>(())
/// ``` /// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(pub(crate) QueryVariants); pub struct Query(pub(crate) QueryVariants);
@ -38,12 +37,12 @@ impl fmt::Display for Query {
impl Query { impl Query {
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the 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, SparqlParseError> { pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
let mut state = ParserState { let mut state = ParserState {
base_iri: if let Some(base_iri) = base_iri { base_iri: if let Some(base_iri) = base_iri {
Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| { Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| {
SparqlParseError { ParseError {
inner: SparqlParseErrorKind::InvalidBaseIri(e), inner: ParseErrorKind::InvalidBaseIri(e),
} }
})?)) })?))
} else { } else {
@ -57,8 +56,8 @@ impl Query {
Ok(Self( Ok(Self(
parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| { parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| {
SparqlParseError { ParseError {
inner: SparqlParseErrorKind::Parser(e), inner: ParseErrorKind::Parser(e),
} }
})?, })?,
)) ))
@ -66,53 +65,53 @@ impl Query {
} }
impl FromStr for Query { impl FromStr for Query {
type Err = SparqlParseError; type Err = ParseError;
fn from_str(query: &str) -> Result<Self, SparqlParseError> { fn from_str(query: &str) -> Result<Self, ParseError> {
Self::parse(query, None) Self::parse(query, None)
} }
} }
impl<'a> TryFrom<&'a str> for Query { impl<'a> TryFrom<&'a str> for Query {
type Error = SparqlParseError; type Error = ParseError;
fn try_from(query: &str) -> Result<Self, SparqlParseError> { fn try_from(query: &str) -> Result<Self, ParseError> {
Self::from_str(query) Self::from_str(query)
} }
} }
impl<'a> TryFrom<&'a String> for Query { impl<'a> TryFrom<&'a String> for Query {
type Error = SparqlParseError; type Error = ParseError;
fn try_from(query: &String) -> Result<Self, SparqlParseError> { fn try_from(query: &String) -> Result<Self, ParseError> {
Self::from_str(query) Self::from_str(query)
} }
} }
/// Error returned during SPARQL parsing. /// Error returned during SPARQL parsing.
#[derive(Debug)] #[derive(Debug)]
pub struct SparqlParseError { pub struct ParseError {
inner: SparqlParseErrorKind, inner: ParseErrorKind,
} }
#[derive(Debug)] #[derive(Debug)]
enum SparqlParseErrorKind { enum ParseErrorKind {
InvalidBaseIri(IriParseError), InvalidBaseIri(IriParseError),
Parser(ParseError<LineCol>), Parser(peg::error::ParseError<LineCol>),
} }
impl fmt::Display for SparqlParseError { impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner { match &self.inner {
SparqlParseErrorKind::InvalidBaseIri(e) => { ParseErrorKind::InvalidBaseIri(e) => {
write!(f, "Invalid SPARQL base IRI provided: {}", e) write!(f, "Invalid SPARQL base IRI provided: {}", e)
} }
SparqlParseErrorKind::Parser(e) => e.fmt(f), ParseErrorKind::Parser(e) => e.fmt(f),
} }
} }
} }
impl Error for SparqlParseError {} impl Error for ParseError {}
struct FocusedTriplePattern<F> { struct FocusedTriplePattern<F> {
focus: F, focus: F,

@ -1,12 +1,12 @@
use crate::error::UnwrapInfallible; use crate::error::UnwrapInfallible;
use crate::sparql::algebra::GraphPattern; use crate::sparql::algebra::GraphPattern;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::Variable; use crate::sparql::model::Variable;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrHash, StrLookup, EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrHash, StrLookup,
ENCODED_DEFAULT_GRAPH, ENCODED_DEFAULT_GRAPH,
}; };
use crate::store::ReadableEncodedStore; use crate::store::ReadableEncodedStore;
use crate::Result;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::io; use std::io;
@ -581,7 +581,7 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
predicate: Option<EncodedTerm>, predicate: Option<EncodedTerm>,
object: Option<EncodedTerm>, object: Option<EncodedTerm>,
graph_name: Option<EncodedTerm>, graph_name: Option<EncodedTerm>,
) -> Box<dyn Iterator<Item = Result<EncodedQuad>>> { ) -> Box<dyn Iterator<Item = Result<EncodedQuad, EvaluationError>>> {
if graph_name == None { if graph_name == None {
Box::new( Box::new(
map_io_err( map_io_err(
@ -625,15 +625,15 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
} }
fn map_io_err<'a, T>( fn map_io_err<'a, T>(
iter: impl Iterator<Item = std::result::Result<T, impl Into<io::Error>>> + 'a, iter: impl Iterator<Item = Result<T, impl Into<io::Error>>> + 'a,
) -> impl Iterator<Item = Result<T>> + 'a { ) -> impl Iterator<Item = Result<T, EvaluationError>> + 'a {
iter.map(|e| e.map_err(|e| e.into().into())) iter.map(|e| e.map_err(|e| e.into().into()))
} }
impl<S: ReadableEncodedStore> StrLookup for DatasetView<S> { impl<S: ReadableEncodedStore> StrLookup for DatasetView<S> {
type Error = <S as StrLookup>::Error; type Error = <S as StrLookup>::Error;
fn get_str(&self, id: StrHash) -> std::result::Result<Option<String>, Self::Error> { fn get_str(&self, id: StrHash) -> Result<Option<String>, Self::Error> {
if let Some(value) = self.extra.borrow().get_str(id).unwrap_infallible() { if let Some(value) = self.extra.borrow().get_str(id).unwrap_infallible() {
Ok(Some(value)) Ok(Some(value))
} else { } else {
@ -650,7 +650,7 @@ struct DatasetViewStrContainer<'a, S: ReadableEncodedStore> {
impl<'a, S: ReadableEncodedStore> StrContainer for DatasetViewStrContainer<'a, S> { impl<'a, S: ReadableEncodedStore> StrContainer for DatasetViewStrContainer<'a, S> {
type Error = <S as StrLookup>::Error; type Error = <S as StrLookup>::Error;
fn insert_str(&mut self, key: StrHash, value: &str) -> std::result::Result<(), Self::Error> { fn insert_str(&mut self, key: StrHash, value: &str) -> Result<(), Self::Error> {
if self.store.get_str(key)?.is_none() { if self.store.get_str(key)?.is_none() {
self.extra.insert_str(key, value).unwrap(); self.extra.insert_str(key, value).unwrap();
Ok(()) Ok(())

@ -1,10 +1,9 @@
use crate::model::{BlankNode, Term}; use crate::model::{BlankNode, Term};
use crate::sparql::algebra::*; use crate::sparql::algebra::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::sparql::plan::*; use crate::sparql::plan::*;
use crate::store::numeric_encoder::{Encoder, ENCODED_DEFAULT_GRAPH}; use crate::store::numeric_encoder::{Encoder, ENCODED_DEFAULT_GRAPH};
use crate::Error;
use crate::Result;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
use std::rc::Rc; use std::rc::Rc;
@ -13,7 +12,10 @@ pub(crate) struct PlanBuilder<E: Encoder> {
} }
impl<E: Encoder> PlanBuilder<E> { impl<E: Encoder> PlanBuilder<E> {
pub fn build(encoder: E, pattern: &GraphPattern) -> Result<(PlanNode, Vec<Variable>)> { pub fn build(
encoder: E,
pattern: &GraphPattern,
) -> Result<(PlanNode, Vec<Variable>), EvaluationError> {
let mut variables = Vec::default(); let mut variables = Vec::default();
let plan = PlanBuilder { encoder }.build_for_graph_pattern( let plan = PlanBuilder { encoder }.build_for_graph_pattern(
pattern, pattern,
@ -27,7 +29,7 @@ impl<E: Encoder> PlanBuilder<E> {
encoder: E, encoder: E,
template: &[TriplePattern], template: &[TriplePattern],
mut variables: Vec<Variable>, mut variables: Vec<Variable>,
) -> Result<Vec<TripleTemplate>> { ) -> Result<Vec<TripleTemplate>, EvaluationError> {
PlanBuilder { encoder }.build_for_graph_template(template, &mut variables) PlanBuilder { encoder }.build_for_graph_template(template, &mut variables)
} }
@ -36,7 +38,7 @@ impl<E: Encoder> PlanBuilder<E> {
pattern: &GraphPattern, pattern: &GraphPattern,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<PlanNode> { ) -> 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::Join(a, b) => PlanNode::Join { GraphPattern::Join(a, b) => PlanNode::Join {
@ -143,7 +145,7 @@ impl<E: Encoder> PlanBuilder<E> {
variable_key(variables, v), variable_key(variables, v),
)) ))
}) })
.collect::<Result<Vec<_>>>()?, .collect::<Result<Vec<_>, EvaluationError>>()?,
), ),
} }
} }
@ -151,7 +153,7 @@ impl<E: Encoder> PlanBuilder<E> {
tuples: self.encode_bindings(bs, variables)?, tuples: self.encode_bindings(bs, variables)?,
}, },
GraphPattern::OrderBy(l, o) => { GraphPattern::OrderBy(l, o) => {
let by: Result<Vec<_>> = o let by: Result<Vec<_>, EvaluationError> = o
.iter() .iter()
.map(|comp| match comp { .map(|comp| match comp {
OrderComparator::Asc(e) => Ok(Comparator::Asc( OrderComparator::Asc(e) => Ok(Comparator::Asc(
@ -216,7 +218,7 @@ impl<E: Encoder> PlanBuilder<E> {
p: &[TripleOrPathPattern], p: &[TripleOrPathPattern],
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<PlanNode> { ) -> Result<PlanNode, EvaluationError> {
let mut plan = PlanNode::Init; let mut plan = PlanNode::Init;
for pattern in sort_bgp(p) { for pattern in sort_bgp(p) {
plan = match pattern { plan = match pattern {
@ -242,7 +244,7 @@ impl<E: Encoder> PlanBuilder<E> {
Ok(plan) Ok(plan)
} }
fn build_for_path(&mut self, path: &PropertyPath) -> Result<PlanPropertyPath> { fn build_for_path(&mut self, path: &PropertyPath) -> Result<PlanPropertyPath, EvaluationError> {
Ok(match path { Ok(match path {
PropertyPath::PredicatePath(p) => PlanPropertyPath::PredicatePath( PropertyPath::PredicatePath(p) => PlanPropertyPath::PredicatePath(
self.encoder.encode_named_node(p).map_err(|e| e.into())?, self.encoder.encode_named_node(p).map_err(|e| e.into())?,
@ -270,7 +272,7 @@ impl<E: Encoder> PlanBuilder<E> {
PropertyPath::NegatedPropertySet(p) => PlanPropertyPath::NegatedPropertySet(Rc::new( PropertyPath::NegatedPropertySet(p) => PlanPropertyPath::NegatedPropertySet(Rc::new(
p.iter() p.iter()
.map(|p| self.encoder.encode_named_node(p).map_err(|e| e.into())) .map(|p| self.encoder.encode_named_node(p).map_err(|e| e.into()))
.collect::<std::result::Result<Vec<_>, _>>()?, .collect::<Result<Vec<_>, _>>()?,
)), )),
}) })
} }
@ -280,7 +282,7 @@ impl<E: Encoder> PlanBuilder<E> {
expression: &Expression, expression: &Expression,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<PlanExpression> { ) -> Result<PlanExpression, EvaluationError> {
Ok(match expression { Ok(match expression {
Expression::NamedNode(node) => PlanExpression::Constant( Expression::NamedNode(node) => PlanExpression::Constant(
self.encoder.encode_named_node(node).map_err(|e| e.into())?, self.encoder.encode_named_node(node).map_err(|e| e.into())?,
@ -676,7 +678,7 @@ impl<E: Encoder> PlanBuilder<E> {
"string", "string",
)? )?
} else { } else {
return Err(Error::msg(format!( return Err(EvaluationError::msg(format!(
"Not supported custom function {}", "Not supported custom function {}",
expression expression
))); )));
@ -697,7 +699,7 @@ impl<E: Encoder> PlanBuilder<E> {
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
name: &'static str, name: &'static str,
) -> Result<PlanExpression> { ) -> Result<PlanExpression, EvaluationError> {
if parameters.len() == 1 { if parameters.len() == 1 {
Ok(constructor(Box::new(self.build_for_expression( Ok(constructor(Box::new(self.build_for_expression(
&parameters[0], &parameters[0],
@ -705,7 +707,7 @@ impl<E: Encoder> PlanBuilder<E> {
graph_name, graph_name,
)?))) )?)))
} else { } else {
Err(Error::msg(format!( Err(EvaluationError::msg(format!(
"The xsd:{} casting takes only one parameter", "The xsd:{} casting takes only one parameter",
name name
))) )))
@ -717,7 +719,7 @@ impl<E: Encoder> PlanBuilder<E> {
l: &[Expression], l: &[Expression],
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<Vec<PlanExpression>> { ) -> Result<Vec<PlanExpression>, EvaluationError> {
l.iter() l.iter()
.map(|e| self.build_for_expression(e, variables, graph_name)) .map(|e| self.build_for_expression(e, variables, graph_name))
.collect() .collect()
@ -727,7 +729,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self, &mut self,
term_or_variable: &TermOrVariable, term_or_variable: &TermOrVariable,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<PatternValue> { ) -> Result<PatternValue, EvaluationError> {
Ok(match term_or_variable { Ok(match term_or_variable {
TermOrVariable::Variable(variable) => { TermOrVariable::Variable(variable) => {
PatternValue::Variable(variable_key(variables, variable)) PatternValue::Variable(variable_key(variables, variable))
@ -746,7 +748,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self, &mut self,
named_node_or_variable: &NamedNodeOrVariable, named_node_or_variable: &NamedNodeOrVariable,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<PatternValue> { ) -> Result<PatternValue, EvaluationError> {
Ok(match named_node_or_variable { Ok(match named_node_or_variable {
NamedNodeOrVariable::NamedNode(named_node) => PatternValue::Constant( NamedNodeOrVariable::NamedNode(named_node) => PatternValue::Constant(
self.encoder self.encoder
@ -763,7 +765,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self, &mut self,
bindings: &StaticBindings, bindings: &StaticBindings,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<Vec<EncodedTuple>> { ) -> Result<Vec<EncodedTuple>, EvaluationError> {
let bindings_variables_keys = bindings let bindings_variables_keys = bindings
.variables() .variables()
.iter() .iter()
@ -791,7 +793,7 @@ impl<E: Encoder> PlanBuilder<E> {
aggregate: &Aggregation, aggregate: &Aggregation,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
graph_name: PatternValue, graph_name: PatternValue,
) -> Result<PlanAggregation> { ) -> Result<PlanAggregation, EvaluationError> {
Ok(match aggregate { Ok(match aggregate {
Aggregation::Count(e, distinct) => PlanAggregation { Aggregation::Count(e, distinct) => PlanAggregation {
function: PlanAggregationFunction::Count, function: PlanAggregationFunction::Count,
@ -840,7 +842,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self, &mut self,
template: &[TriplePattern], template: &[TriplePattern],
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<Vec<TripleTemplate>> { ) -> Result<Vec<TripleTemplate>, EvaluationError> {
let mut bnodes = Vec::default(); let mut bnodes = Vec::default();
template template
.iter() .iter()
@ -868,7 +870,7 @@ impl<E: Encoder> PlanBuilder<E> {
term_or_variable: &TermOrVariable, term_or_variable: &TermOrVariable,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
bnodes: &mut Vec<BlankNode>, bnodes: &mut Vec<BlankNode>,
) -> Result<TripleTemplateValue> { ) -> Result<TripleTemplateValue, EvaluationError> {
Ok(match term_or_variable { Ok(match term_or_variable {
TermOrVariable::Variable(variable) => { TermOrVariable::Variable(variable) => {
TripleTemplateValue::Variable(variable_key(variables, variable)) TripleTemplateValue::Variable(variable_key(variables, variable))
@ -886,7 +888,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self, &mut self,
named_node_or_variable: &NamedNodeOrVariable, named_node_or_variable: &NamedNodeOrVariable,
variables: &mut Vec<Variable>, variables: &mut Vec<Variable>,
) -> Result<TripleTemplateValue> { ) -> Result<TripleTemplateValue, EvaluationError> {
Ok(match named_node_or_variable { Ok(match named_node_or_variable {
NamedNodeOrVariable::Variable(variable) => { NamedNodeOrVariable::Variable(variable) => {
TripleTemplateValue::Variable(variable_key(variables, variable)) TripleTemplateValue::Variable(variable_key(variables, variable))

@ -1,9 +1,9 @@
//! Implementation of [SPARQL Query Results XML Format](http://www.w3.org/TR/rdf-sparql-XMLres/) //! Implementation of [SPARQL Query Results XML Format](http://www.w3.org/TR/rdf-sparql-XMLres/)
use crate::error::{invalid_data_error, invalid_input_error};
use crate::model::*; use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::Error;
use crate::Result;
use quick_xml::events::BytesDecl; use quick_xml::events::BytesDecl;
use quick_xml::events::BytesEnd; use quick_xml::events::BytesEnd;
use quick_xml::events::BytesStart; use quick_xml::events::BytesStart;
@ -17,10 +17,22 @@ use std::io::Write;
use std::iter::empty; use std::iter::empty;
use std::rc::Rc; use std::rc::Rc;
pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<()> { pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<(), EvaluationError> {
let mut writer = Writer::new(sink);
match results { match results {
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
write_boolean(value, sink).map_err(map_xml_error)?;
Ok(())
}
QueryResult::Solutions(solutions) => write_solutions(solutions, sink),
QueryResult::Graph(_) => Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format",
)
.into()),
}
}
fn write_boolean(value: bool, sink: impl Write) -> Result<(), quick_xml::Error> {
let mut writer = Writer::new(sink);
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?; writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql"); let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#")); sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
@ -35,75 +47,108 @@ pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<()> {
})))?; })))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?;
} Ok(())
QueryResult::Solutions(solutions) => { }
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
fn write_solutions(
solutions: QuerySolutionsIterator,
sink: impl Write,
) -> Result<(), EvaluationError> {
let mut writer = Writer::new(sink);
writer
.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
.map_err(map_xml_error)?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql"); let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#")); sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer.write_event(Event::Start(sparql_open))?; writer
writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?; .write_event(Event::Start(sparql_open))
.map_err(map_xml_error)?;
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"head")))
.map_err(map_xml_error)?;
for variable in solutions.variables() { for variable in solutions.variables() {
let mut variable_tag = BytesStart::borrowed_name(b"variable"); let mut variable_tag = BytesStart::borrowed_name(b"variable");
variable_tag.push_attribute(("name", variable.as_str())); variable_tag.push_attribute(("name", variable.as_str()));
writer.write_event(Event::Empty(variable_tag))?; writer
.write_event(Event::Empty(variable_tag))
.map_err(map_xml_error)?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?; writer
writer.write_event(Event::Start(BytesStart::borrowed_name(b"results")))?; .write_event(Event::End(BytesEnd::borrowed(b"head")))
.map_err(map_xml_error)?;
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"results")))
.map_err(map_xml_error)?;
for solution in solutions { for solution in solutions {
let solution = solution?; let solution = solution?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"result")))?; writer
.write_event(Event::Start(BytesStart::borrowed_name(b"result")))
.map_err(map_xml_error)?;
for (variable, value) in solution.iter() { for (variable, value) in solution.iter() {
let mut binding_tag = BytesStart::borrowed_name(b"binding"); let mut binding_tag = BytesStart::borrowed_name(b"binding");
binding_tag.push_attribute(("name", variable.as_str())); binding_tag.push_attribute(("name", variable.as_str()));
writer.write_event(Event::Start(binding_tag))?; writer
.write_event(Event::Start(binding_tag))
.map_err(map_xml_error)?;
match value { match value {
Term::NamedNode(uri) => { Term::NamedNode(uri) => {
writer.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))?; writer
writer.write_event(Event::Text(BytesText::from_plain_str( .write_event(Event::Start(BytesStart::borrowed_name(b"uri")))
uri.as_str(), .map_err(map_xml_error)?;
)))?; writer
writer.write_event(Event::End(BytesEnd::borrowed(b"uri")))?; .write_event(Event::Text(BytesText::from_plain_str(uri.as_str())))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"uri")))
.map_err(map_xml_error)?;
} }
Term::BlankNode(bnode) => { Term::BlankNode(bnode) => {
writer writer
.write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))?; .write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))
writer.write_event(Event::Text(BytesText::from_plain_str( .map_err(map_xml_error)?;
bnode.as_str(), writer
)))?; .write_event(Event::Text(BytesText::from_plain_str(bnode.as_str())))
writer.write_event(Event::End(BytesEnd::borrowed(b"bnode")))?; .map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"bnode")))
.map_err(map_xml_error)?;
} }
Term::Literal(literal) => { Term::Literal(literal) => {
let mut literal_tag = BytesStart::borrowed_name(b"literal"); let mut literal_tag = BytesStart::borrowed_name(b"literal");
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
literal_tag.push_attribute(("xml:lang", language)); literal_tag.push_attribute(("xml:lang", language));
} else if !literal.is_plain() { } else if !literal.is_plain() {
literal_tag literal_tag.push_attribute(("datatype", literal.datatype().as_str()));
.push_attribute(("datatype", literal.datatype().as_str()));
} }
writer.write_event(Event::Start(literal_tag))?; writer
writer.write_event(Event::Text(BytesText::from_plain_str( .write_event(Event::Start(literal_tag))
literal.value(), .map_err(map_xml_error)?;
)))?; writer
writer.write_event(Event::End(BytesEnd::borrowed(b"literal")))?; .write_event(Event::Text(BytesText::from_plain_str(literal.value())))
} .map_err(map_xml_error)?;
} writer
writer.write_event(Event::End(BytesEnd::borrowed(b"binding")))?; .write_event(Event::End(BytesEnd::borrowed(b"literal")))
} .map_err(map_xml_error)?;
writer.write_event(Event::End(BytesEnd::borrowed(b"result")))?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"results")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?;
} }
QueryResult::Graph(_) => { writer
return Err(Error::msg( .write_event(Event::End(BytesEnd::borrowed(b"binding")))
"Graphs could not be formatted to SPARQL query results XML format", .map_err(map_xml_error)?;
));
} }
writer
.write_event(Event::End(BytesEnd::borrowed(b"result")))
.map_err(map_xml_error)?;
} }
writer
.write_event(Event::End(BytesEnd::borrowed(b"results")))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"sparql")))
.map_err(map_xml_error)?;
Ok(()) Ok(())
} }
pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> { pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, EvaluationError> {
enum State { enum State {
Start, Start,
Sparql, Sparql,
@ -123,13 +168,16 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
//Read header //Read header
loop { loop {
let event = { let event = {
let (ns, event) = reader.read_namespaced_event(&mut buffer, &mut namespace_buffer)?; let (ns, event) = reader
.read_namespaced_event(&mut buffer, &mut namespace_buffer)
.map_err(map_xml_error)?;
if let Some(ns) = ns { if let Some(ns) = ns {
if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() { if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Unexpected namespace found in RDF/XML query result: {}", "Unexpected namespace found in RDF/XML query result: {}",
reader.decode(ns)? reader.decode(ns).map_err(map_xml_error)?
))); ))
.into());
} }
} }
event event
@ -140,14 +188,14 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
if event.name() == b"sparql" { if event.name() == b"sparql" {
state = State::Sparql; state = State::Sparql;
} else { } else {
return Err(Error::msg(format!("Expecting <sparql> tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting <sparql> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
} }
State::Sparql => { State::Sparql => {
if event.name() == b"head" { if event.name() == b"head" {
state = State::Head; state = State::Head;
} else { } else {
return Err(Error::msg(format!("Expecting <head> tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
} }
State::Head => { State::Head => {
@ -155,12 +203,12 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
let name = event.attributes() let name = event.attributes()
.filter_map(|attr| attr.ok()) .filter_map(|attr| attr.ok())
.find(|attr| attr.key == b"name") .find(|attr| attr.key == b"name")
.ok_or_else(|| Error::msg("No name attribute found for the <variable> tag"))?; .ok_or_else(|| invalid_data_error("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader)?); variables.push(name.unescape_and_decode_value(&reader).map_err(map_xml_error)?);
} else if event.name() == b"link" { } else if event.name() == b"link" {
// no op // no op
} else { } else {
return Err(Error::msg(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
} }
State::AfterHead => { State::AfterHead => {
@ -181,17 +229,17 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
}), }),
))); )));
} else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" { } else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" {
return Err(Error::msg(format!("Expecting sparql tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting sparql tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
} }
State::Boolean => return Err(Error::msg(format!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name())?))) State::Boolean => return Err(invalid_data_error(format!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name()).map_err(map_xml_error)?)).into())
}, },
Event::Empty(event) => match state { Event::Empty(event) => match state {
State::Sparql => { State::Sparql => {
if event.name() == b"head" { if event.name() == b"head" {
state = State::AfterHead; state = State::AfterHead;
} else { } else {
return Err(Error::msg(format!("Expecting <head> tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
} }
State::Head => { State::Head => {
@ -199,12 +247,12 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
let name = event.attributes() let name = event.attributes()
.filter_map(|v| v.ok()) .filter_map(|v| v.ok())
.find(|attr| attr.key == b"name") .find(|attr| attr.key == b"name")
.ok_or_else(|| Error::msg("No name attribute found for the <variable> tag"))?; .ok_or_else(|| invalid_data_error("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader)?); variables.push(name.unescape_and_decode_value(&reader).map_err(map_xml_error)?);
} else if event.name() == b"link" { } else if event.name() == b"link" {
// no op // no op
} else { } else {
return Err(Error::msg(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name())?))); return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into());
} }
}, },
State::AfterHead => { State::AfterHead => {
@ -214,13 +262,13 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
Box::new(empty()), Box::new(empty()),
))) )))
} else { } else {
return Err(Error::msg(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name())?))) return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)).into())
} }
} }
_ => return Err(Error::msg(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name())?))) _ => return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)).into())
}, },
Event::Text(event) => { Event::Text(event) => {
let value = event.unescaped()?; let value = event.unescaped().map_err(map_xml_error)?;
return match state { return match state {
State::Boolean => { State::Boolean => {
return if value.as_ref() == b"true" { return if value.as_ref() == b"true" {
@ -228,18 +276,18 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
} else if value.as_ref() == b"false" { } else if value.as_ref() == b"false" {
Ok(QueryResult::Boolean(false)) Ok(QueryResult::Boolean(false))
} else { } else {
Err(Error::msg(format!("Unexpected boolean value. Found {}", reader.decode(&value)?))) Err(invalid_data_error(format!("Unexpected boolean value. Found {}", reader.decode(&value).map_err(map_xml_error)?)).into())
}; };
} }
_ => Err(Error::msg(format!("Unexpected textual value found: {}", reader.decode(&value)?))) _ => Err(invalid_data_error(format!("Unexpected textual value found: {}", reader.decode(&value).map_err(map_xml_error)?)).into())
}; };
}, },
Event::End(_) => if let State::Head = state { Event::End(_) => if let State::Head = state {
state = State::AfterHead; state = State::AfterHead;
} else { } else {
return Err(Error::msg("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag")); return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag").into());
}, },
Event::Eof => return Err(Error::msg("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag")), Event::Eof => return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag").into()),
_ => (), _ => (),
} }
} }
@ -253,15 +301,15 @@ struct ResultsIterator<R: BufRead> {
} }
impl<R: BufRead> Iterator for ResultsIterator<R> { impl<R: BufRead> Iterator for ResultsIterator<R> {
type Item = Result<Vec<Option<Term>>>; type Item = Result<Vec<Option<Term>>, EvaluationError>;
fn next(&mut self) -> Option<Result<Vec<Option<Term>>>> { fn next(&mut self) -> Option<Result<Vec<Option<Term>>, EvaluationError>> {
self.read_next().transpose() self.read_next().transpose()
} }
} }
impl<R: BufRead> ResultsIterator<R> { impl<R: BufRead> ResultsIterator<R> {
fn read_next(&mut self) -> Result<Option<Vec<Option<Term>>>> { fn read_next(&mut self) -> Result<Option<Vec<Option<Term>>>, EvaluationError> {
enum State { enum State {
Start, Start,
Result, Result,
@ -283,13 +331,15 @@ impl<R: BufRead> ResultsIterator<R> {
loop { loop {
let (ns, event) = self let (ns, event) = self
.reader .reader
.read_namespaced_event(&mut self.buffer, &mut self.namespace_buffer)?; .read_namespaced_event(&mut self.buffer, &mut self.namespace_buffer)
.map_err(map_xml_error)?;
if let Some(ns) = ns { if let Some(ns) = ns {
if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() { if ns != b"http://www.w3.org/2005/sparql-results#".as_ref() {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Unexpected namespace found in RDF/XML query result: {}", "Unexpected namespace found in RDF/XML query result: {}",
self.reader.decode(ns)? self.reader.decode(ns).map_err(map_xml_error)?
))); ))
.into());
} }
} }
match event { match event {
@ -298,10 +348,11 @@ impl<R: BufRead> ResultsIterator<R> {
if event.name() == b"result" { if event.name() == b"result" {
state = State::Result; state = State::Result;
} else { } else {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Expecting <result>, found {}", "Expecting <result>, found {}",
self.reader.decode(event.name())? self.reader.decode(event.name()).map_err(map_xml_error)?
))); ))
.into());
} }
} }
State::Result => { State::Result => {
@ -311,26 +362,33 @@ impl<R: BufRead> ResultsIterator<R> {
.filter_map(|v| v.ok()) .filter_map(|v| v.ok())
.find(|attr| attr.key == b"name") .find(|attr| attr.key == b"name")
{ {
Some(attr) => current_var = Some(attr.unescaped_value()?.to_vec()), Some(attr) => {
current_var = Some(
attr.unescaped_value().map_err(map_xml_error)?.to_vec(),
)
}
None => { None => {
return Err(Error::msg( return Err(invalid_data_error(
"No name attribute found for the <binding> tag", "No name attribute found for the <binding> tag",
)); )
.into());
} }
} }
state = State::Binding; state = State::Binding;
} else { } else {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Expecting <binding>, found {}", "Expecting <binding>, found {}",
self.reader.decode(event.name())? self.reader.decode(event.name()).map_err(map_xml_error)?
))); ))
.into());
} }
} }
State::Binding => { State::Binding => {
if term.is_some() { if term.is_some() {
return Err(Error::msg( return Err(invalid_data_error(
"There is already a value for the current binding", "There is already a value for the current binding",
)); )
.into());
} }
if event.name() == b"uri" { if event.name() == b"uri" {
state = State::Uri; state = State::Uri;
@ -340,37 +398,67 @@ impl<R: BufRead> ResultsIterator<R> {
for attr in event.attributes() { for attr in event.attributes() {
if let Ok(attr) = attr { if let Ok(attr) = attr {
if attr.key == b"xml:lang" { if attr.key == b"xml:lang" {
lang = Some(attr.unescape_and_decode_value(&self.reader)?); lang = Some(
attr.unescape_and_decode_value(&self.reader)
.map_err(map_xml_error)?,
);
} else if attr.key == b"datatype" { } else if attr.key == b"datatype" {
datatype = Some(NamedNode::new( let iri = attr
attr.unescape_and_decode_value(&self.reader)?, .unescape_and_decode_value(&self.reader)
)?); .map_err(map_xml_error)?;
datatype = Some(NamedNode::new(&iri).map_err(|e| {
invalid_data_error(format!(
"Invalid datatype IRI '{}': {}",
iri, e
))
})?);
} }
} }
} }
state = State::Literal; state = State::Literal;
} else { } else {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Expecting <uri>, <bnode> or <literal> found {}", "Expecting <uri>, <bnode> or <literal> found {}",
self.reader.decode(event.name())? self.reader.decode(event.name()).map_err(map_xml_error)?
))); ))
.into());
} }
} }
_ => (), _ => (),
}, },
Event::Text(event) => { Event::Text(event) => {
let data = event.unescaped()?; let data = event.unescaped().map_err(map_xml_error)?;
match state { match state {
State::Uri => { State::Uri => {
term = Some(NamedNode::new(self.reader.decode(&data)?)?.into()) let iri = self.reader.decode(&data).map_err(map_xml_error)?;
term = Some(
NamedNode::new(iri)
.map_err(|e| {
invalid_data_error(format!(
"Invalid IRI value '{}': {}",
iri, e
))
})?
.into(),
)
} }
State::BNode => { State::BNode => {
term = Some(BlankNode::new(self.reader.decode(&data)?)?.into()) let bnode = self.reader.decode(&data).map_err(map_xml_error)?;
term = Some(
BlankNode::new(bnode)
.map_err(|e| {
invalid_data_error(format!(
"Invalid blank node value '{}': {}",
bnode, e
))
})?
.into(),
)
} }
State::Literal => { State::Literal => {
term = Some( term = Some(
build_literal( build_literal(
self.reader.decode(&data)?, self.reader.decode(&data).map_err(map_xml_error)?,
lang.take(), lang.take(),
datatype.take(), datatype.take(),
)? )?
@ -378,10 +466,11 @@ impl<R: BufRead> ResultsIterator<R> {
); );
} }
_ => { _ => {
return Err(Error::msg(format!( return Err(invalid_data_error(format!(
"Unexpected textual value found: {}", "Unexpected textual value found: {}",
self.reader.decode(&data)? self.reader.decode(&data).map_err(map_xml_error)?
))); ))
.into());
} }
} }
} }
@ -392,7 +481,9 @@ impl<R: BufRead> ResultsIterator<R> {
if let Some(var) = &current_var { if let Some(var) = &current_var {
new_bindings[self.mapping[var]] = term.clone() new_bindings[self.mapping[var]] = term.clone()
} else { } else {
return Err(Error::msg("No name found for <binding> tag")); return Err(
invalid_data_error("No name found for <binding> tag").into()
);
} }
term = None; term = None;
state = State::Result; state = State::Result;
@ -418,12 +509,22 @@ fn build_literal(
value: impl Into<String>, value: impl Into<String>,
lang: Option<String>, lang: Option<String>,
datatype: Option<NamedNode>, datatype: Option<NamedNode>,
) -> Result<Literal> { ) -> Result<Literal, EvaluationError> {
match datatype { match datatype {
Some(datatype) => Ok(Literal::new_typed_literal(value, datatype)), Some(datatype) => Ok(Literal::new_typed_literal(value, datatype)),
None => match lang { None => match lang {
Some(lang) => Ok(Literal::new_language_tagged_literal(value, lang)?), Some(lang) => Literal::new_language_tagged_literal(value, &lang).map_err(|e| {
invalid_data_error(format!("Invalid xml:lang value '{}': {}", lang, e)).into()
}),
None => Ok(Literal::new_simple_literal(value)), None => Ok(Literal::new_simple_literal(value)),
}, },
} }
} }
fn map_xml_error(error: quick_xml::Error) -> EvaluationError {
match error {
quick_xml::Error::Io(error) => error,
_ => invalid_data_error(error),
}
.into()
}

@ -3,12 +3,11 @@
use crate::error::{Infallible, UnwrapInfallible}; use crate::error::{Infallible, UnwrapInfallible};
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{ use crate::store::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore, dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
}; };
use crate::Error;
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::TryInto; use std::convert::TryInto;
@ -45,7 +44,7 @@ use std::{fmt, io};
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[derive(Clone)] #[derive(Clone)]
pub struct MemoryStore { pub struct MemoryStore {
@ -102,13 +101,13 @@ impl MemoryStore {
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn query( pub fn query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> crate::Result<QueryResult> { ) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -132,13 +131,13 @@ impl MemoryStore {
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? { /// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn prepare_query( pub fn prepare_query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> crate::Result<MemoryPreparedQuery> { ) -> Result<MemoryPreparedQuery, EvaluationError> {
Ok(MemoryPreparedQuery(SimplePreparedQuery::new( Ok(MemoryPreparedQuery(SimplePreparedQuery::new(
self.clone(), self.clone(),
query, query,
@ -163,7 +162,7 @@ impl MemoryStore {
/// // quad filter /// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// assert_eq!(vec![quad], results); /// assert_eq!(vec![quad], results);
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn quads_for_pattern( pub fn quads_for_pattern(
&self, &self,
@ -232,7 +231,7 @@ impl MemoryStore {
/// ///
/// // quad filter /// // quad filter
/// assert!(store.contains(&quad)); /// assert!(store.contains(&quad));
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn transaction<'a, E>( pub fn transaction<'a, E>(
&'a self, &'a self,
@ -266,7 +265,7 @@ impl MemoryStore {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
/// ///
/// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind. /// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind.
@ -300,7 +299,7 @@ impl MemoryStore {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
/// ///
/// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind. /// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind.
@ -356,7 +355,7 @@ impl MemoryStore {
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// store.dump_graph(&mut buffer, GraphFormat::NTriples, &GraphName::DefaultGraph)?; /// store.dump_graph(&mut buffer, GraphFormat::NTriples, &GraphName::DefaultGraph)?;
/// assert_eq!(file, buffer.as_slice()); /// assert_eq!(file, buffer.as_slice());
/// # oxigraph::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
/// ///
/// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind. /// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind.
@ -390,7 +389,7 @@ impl MemoryStore {
/// let mut buffer = Vec::new(); /// let mut buffer = Vec::new();
/// store.dump_dataset(&mut buffer, DatasetFormat::NQuads)?; /// store.dump_dataset(&mut buffer, DatasetFormat::NQuads)?;
/// assert_eq!(file, buffer.as_slice()); /// assert_eq!(file, buffer.as_slice());
/// # oxigraph::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
/// ///
/// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind. /// Errors related to parameter validation like the base IRI use the `INVALID_INPUT` error kind.
@ -954,7 +953,7 @@ pub struct MemoryPreparedQuery(SimplePreparedQuery<MemoryStore>);
impl MemoryPreparedQuery { impl MemoryPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> crate::Result<QueryResult> { pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }
@ -992,7 +991,7 @@ impl<'a> MemoryTransaction<'a> {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # oxigraph::Result::Ok(()) /// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(())
/// ``` /// ```
pub fn load_graph( pub fn load_graph(
&mut self, &mut self,
@ -1022,7 +1021,7 @@ impl<'a> MemoryTransaction<'a> {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # oxigraph::Result::Ok(()) /// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(())
/// ``` /// ```
pub fn load_dataset( pub fn load_dataset(
&mut self, &mut self,

@ -16,6 +16,7 @@ pub use crate::store::rocksdb::RocksDbStore;
#[cfg(feature = "sled")] #[cfg(feature = "sled")]
pub use crate::store::sled::SledStore; pub use crate::store::sled::SledStore;
use crate::error::{invalid_data_error, invalid_input_error};
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
@ -90,7 +91,7 @@ where
IoOrParseError<P::Error>: From<P::Error>, IoOrParseError<P::Error>: From<P::Error>,
P::Error: Send + Sync + 'static, P::Error: Send + Sync + 'static,
{ {
let mut parser = parser.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; let mut parser = parser.map_err(invalid_input_error)?;
let mut bnode_map = HashMap::default(); let mut bnode_map = HashMap::default();
let to_graph_name = store let to_graph_name = store
.encode_graph_name(to_graph_name) .encode_graph_name(to_graph_name)
@ -163,7 +164,7 @@ where
IoOrParseError<P::Error>: From<P::Error>, IoOrParseError<P::Error>: From<P::Error>,
P::Error: Send + Sync + 'static, P::Error: Send + Sync + 'static,
{ {
let mut parser = parser.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; let mut parser = parser.map_err(invalid_input_error)?;
let mut bnode_map = HashMap::default(); let mut bnode_map = HashMap::default();
let result: Result<(), IoOrParseError<_>> = parser.parse_all(&mut move |q| { let result: Result<(), IoOrParseError<_>> = parser.parse_all(&mut move |q| {
let quad = store let quad = store
@ -228,7 +229,7 @@ impl<E: Sized + Error + Send + Sync + 'static> From<IoOrParseError<E>> for io::E
fn from(error: IoOrParseError<E>) -> Self { fn from(error: IoOrParseError<E>) -> Self {
match error { match error {
IoOrParseError::Io(error) => error, IoOrParseError::Io(error) => error,
IoOrParseError::Parse(error) => io::Error::new(io::ErrorKind::InvalidData, error), IoOrParseError::Parse(error) => invalid_data_error(error),
} }
} }
} }

@ -1,6 +1,6 @@
#![allow(clippy::unreadable_literal)] #![allow(clippy::unreadable_literal)]
use crate::error::{Infallible, UnwrapInfallible}; use crate::error::{invalid_data_error, Infallible, UnwrapInfallible};
use crate::model::vocab::rdf; use crate::model::vocab::rdf;
use crate::model::vocab::xsd; use crate::model::vocab::xsd;
use crate::model::xsd::*; use crate::model::xsd::*;
@ -1321,10 +1321,6 @@ fn get_required_str(lookup: &impl StrLookup, id: StrHash) -> Result<String, io::
}) })
} }
fn invalid_data_error(msg: impl Into<String>) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg.into())
}
#[test] #[test]
fn test_encoding() { fn test_encoding() {
let mut store = MemoryStrStore::default(); let mut store = MemoryStrStore::default();

@ -1,9 +1,9 @@
//! Store based on the [RocksDB](https://rocksdb.org/) key-value database. //! Store based on the [RocksDB](https://rocksdb.org/) key-value database.
use crate::error::{Infallible, UnwrapInfallible}; use crate::error::{invalid_data_error, Infallible, UnwrapInfallible};
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{ use crate::store::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore, dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
@ -48,7 +48,7 @@ use std::{fmt, str};
/// # /// #
/// # }; /// # };
/// # remove_dir_all("example.db")?; /// # remove_dir_all("example.db")?;
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[derive(Clone)] #[derive(Clone)]
pub struct RocksDbStore { pub struct RocksDbStore {
@ -95,9 +95,9 @@ impl RocksDbStore {
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn query( pub fn query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResult, crate::Error> { ) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -107,9 +107,9 @@ impl RocksDbStore {
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn prepare_query( pub fn prepare_query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<RocksDbPreparedQuery, crate::Error> { ) -> Result<RocksDbPreparedQuery, EvaluationError> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new( Ok(RocksDbPreparedQuery(SimplePreparedQuery::new(
(*self).clone(), (*self).clone(),
query, query,
@ -194,7 +194,7 @@ impl RocksDbStore {
format: GraphFormat, format: GraphFormat,
to_graph_name: &GraphName, to_graph_name: &GraphName,
base_iri: Option<&str>, base_iri: Option<&str>,
) -> Result<(), crate::Error> { ) -> Result<(), io::Error> {
let mut transaction = self.auto_batch_writer(); let mut transaction = self.auto_batch_writer();
load_graph(&mut transaction, reader, format, to_graph_name, base_iri)?; load_graph(&mut transaction, reader, format, to_graph_name, base_iri)?;
Ok(transaction.apply()?) Ok(transaction.apply()?)
@ -215,7 +215,7 @@ impl RocksDbStore {
reader: impl BufRead, reader: impl BufRead,
format: DatasetFormat, format: DatasetFormat,
base_iri: Option<&str>, base_iri: Option<&str>,
) -> Result<(), crate::Error> { ) -> Result<(), io::Error> {
let mut transaction = self.auto_batch_writer(); let mut transaction = self.auto_batch_writer();
load_dataset(&mut transaction, reader, format, base_iri)?; load_dataset(&mut transaction, reader, format, base_iri)?;
Ok(transaction.apply()?) Ok(transaction.apply()?)
@ -476,7 +476,7 @@ impl StrLookup for RocksDbStore {
.map_err(map_err)? .map_err(map_err)?
.map(String::from_utf8) .map(String::from_utf8)
.transpose() .transpose()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(invalid_data_error)
} }
} }
@ -553,7 +553,7 @@ pub struct RocksDbPreparedQuery(SimplePreparedQuery<RocksDbStore>);
impl RocksDbPreparedQuery { impl RocksDbPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, crate::Error> { pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }
@ -892,14 +892,14 @@ fn map_err(e: Error) -> io::Error {
} }
#[test] #[test]
fn store() -> Result<(), crate::Error> { fn store() -> Result<(), io::Error> {
use crate::model::*; use crate::model::*;
use rand::random; use rand::random;
use std::env::temp_dir; use std::env::temp_dir;
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
let main_s = NamedOrBlankNode::from(BlankNode::default()); let main_s = NamedOrBlankNode::from(BlankNode::default());
let main_p = NamedNode::new("http://example.com")?; let main_p = NamedNode::new("http://example.com").unwrap();
let main_o = Term::from(Literal::from(1)); let main_o = Term::from(Literal::from(1));
let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None); let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);

@ -1,9 +1,9 @@
//! Store based on the [Sled](https://sled.rs/) key-value database. //! Store based on the [Sled](https://sled.rs/) key-value database.
use crate::error::{Infallible, UnwrapInfallible}; use crate::error::{invalid_data_error, Infallible, UnwrapInfallible};
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{ use crate::store::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore, dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
@ -47,7 +47,7 @@ use std::{fmt, io, str};
/// # /// #
/// # }; /// # };
/// # remove_dir_all("example.db")?; /// # remove_dir_all("example.db")?;
/// # oxigraph::Result::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
#[derive(Clone)] #[derive(Clone)]
pub struct SledStore { pub struct SledStore {
@ -90,9 +90,9 @@ impl SledStore {
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn query( pub fn query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResult, crate::Error> { ) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -101,9 +101,9 @@ impl SledStore {
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn prepare_query( pub fn prepare_query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<SledPreparedQuery, crate::Error> { ) -> Result<SledPreparedQuery, EvaluationError> {
Ok(SledPreparedQuery(SimplePreparedQuery::new( Ok(SledPreparedQuery(SimplePreparedQuery::new(
(*self).clone(), (*self).clone(),
query, query,
@ -393,7 +393,7 @@ impl StrLookup for SledStore {
.get(id.to_be_bytes())? .get(id.to_be_bytes())?
.map(|v| String::from_utf8(v.to_vec())) .map(|v| String::from_utf8(v.to_vec()))
.transpose() .transpose()
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) .map_err(invalid_data_error)
} }
} }
@ -712,7 +712,7 @@ pub struct SledPreparedQuery(SimplePreparedQuery<SledStore>);
impl SledPreparedQuery { impl SledPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, crate::Error> { pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }
@ -829,19 +829,19 @@ fn decode_quad(encoded: &[u8]) -> Result<EncodedQuad, io::Error> {
GSPO_PREFIX => Ok(cursor.read_gspo_quad()?), GSPO_PREFIX => Ok(cursor.read_gspo_quad()?),
GPOS_PREFIX => Ok(cursor.read_gpos_quad()?), GPOS_PREFIX => Ok(cursor.read_gpos_quad()?),
GOSP_PREFIX => Ok(cursor.read_gosp_quad()?), GOSP_PREFIX => Ok(cursor.read_gosp_quad()?),
_ => Err(io::Error::new( _ => Err(invalid_data_error(format!(
io::ErrorKind::InvalidData, "Invalid quad type identifier: {}",
format!("Invalid quad type identifier: {}", encoded[0]), encoded[0]
)), ))),
} }
} }
#[test] #[test]
fn store() -> Result<(), crate::Error> { fn store() -> Result<(), io::Error> {
use crate::model::*; use crate::model::*;
let main_s = NamedOrBlankNode::from(BlankNode::default()); let main_s = NamedOrBlankNode::from(BlankNode::default());
let main_p = NamedNode::new("http://example.com")?; let main_p = NamedNode::new("http://example.com").unwrap();
let main_o = Term::from(Literal::from(1)); let main_o = Term::from(Literal::from(1));
let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None); let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);

@ -59,7 +59,7 @@ impl PyMemoryStore {
let results = self let results = self
.inner .inner
.query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(|e| ValueError::py_err(e.to_string()))?; .map_err(map_evaluation_error)?;
query_results_to_python(py, results) query_results_to_python(py, results)
} }

@ -61,7 +61,7 @@ impl PySledStore {
let results = self let results = self
.inner .inner
.query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(|e| ValueError::py_err(e.to_string()))?; .map_err(map_evaluation_error)?;
query_results_to_python(py, results) query_results_to_python(py, results)
} }

@ -1,7 +1,9 @@
use crate::model::*; use crate::model::*;
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::sparql::{QueryResult, QuerySolution, QuerySolutionsIterator, QueryTriplesIterator}; use oxigraph::sparql::{
use pyo3::exceptions::{IOError, TypeError, ValueError}; EvaluationError, QueryResult, QuerySolution, QuerySolutionsIterator, QueryTriplesIterator,
};
use pyo3::exceptions::{IOError, RuntimeError, TypeError, ValueError};
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol}; use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::fmt::Write; use std::fmt::Write;
@ -113,7 +115,7 @@ impl PyIterProtocol for QuerySolutionIter {
.inner .inner
.next() .next()
.transpose() .transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve .map_err(map_evaluation_error)?
.map(move |inner| PyQuerySolution { inner })) .map(move |inner| PyQuerySolution { inner }))
} }
} }
@ -134,7 +136,7 @@ impl PyIterProtocol for TripleResultIter {
.inner .inner
.next() .next()
.transpose() .transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve .map_err(map_evaluation_error)?
.map(move |t| triple_to_python(slf.py(), t))) .map(move |t| triple_to_python(slf.py(), t)))
} }
} }
@ -147,3 +149,12 @@ pub fn map_io_err(error: io::Error) -> PyErr {
_ => IOError::py_err(error.to_string()), _ => IOError::py_err(error.to_string()),
} }
} }
pub fn map_evaluation_error(error: EvaluationError) -> PyErr {
match error {
EvaluationError::Parsing(error) => ValueError::py_err(error.to_string()),
EvaluationError::Io(error) => map_io_err(error),
EvaluationError::Query(error) => ValueError::py_err(error.to_string()),
_ => RuntimeError::py_err(error.to_string()),
}
}

@ -7,11 +7,11 @@ use chrono::Utc;
use oxigraph::model::vocab::*; use oxigraph::model::vocab::*;
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::sparql::*; use oxigraph::sparql::*;
use oxigraph::{Error, MemoryStore}; use oxigraph::MemoryStore;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io};
pub fn evaluate_sparql_tests( pub fn evaluate_sparql_tests(
manifest: impl Iterator<Item = Result<Test>>, manifest: impl Iterator<Item = Result<Test>>,
@ -168,10 +168,21 @@ impl StaticServiceHandler {
} }
impl ServiceHandler for StaticServiceHandler { impl ServiceHandler for StaticServiceHandler {
fn handle(&self, service_name: NamedNode, query: Query) -> oxigraph::Result<QueryResult> { type Error = EvaluationError;
fn handle(
&self,
service_name: NamedNode,
query: Query,
) -> std::result::Result<QueryResult, EvaluationError> {
self.services self.services
.get(&service_name) .get(&service_name)
.ok_or_else(|| Error::msg(format!("Service {} not found", service_name)))? .ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("Service {} not found", service_name),
)
})?
.query( .query(
query, query,
QueryOptions::default().with_service_handler(self.clone()), QueryOptions::default().with_service_handler(self.clone()),
@ -183,7 +194,7 @@ fn to_dataset(result: QueryResult, with_order: bool) -> Result<MemoryStore> {
match result { match result {
QueryResult::Graph(graph) => Ok(graph QueryResult::Graph(graph) => Ok(graph
.map(|t| t.map(|t| t.in_graph(None))) .map(|t| t.map(|t| t.in_graph(None)))
.collect::<Result<_, Error>>()?), .collect::<Result<_, _>>()?),
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
let store = MemoryStore::new(); let store = MemoryStore::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();

Loading…
Cancel
Save