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. 371
      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 crate::sparql::SparqlParseError;
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)
}
}
use std::error::Error;
use std::{fmt, io};
//TODO: convert to "!" when "never_type" is going to be stabilized
#[allow(clippy::empty_enum)]
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub(crate) enum Infallible {}
impl Error for Infallible {}
impl fmt::Display for Infallible {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
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 from(error: Infallible) -> Self {
match error {}
fn unwrap_infallible(self) -> T {
match self {
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 {
fn from(error: std::convert::Infallible) -> Self {
match error {}
@ -178,20 +44,16 @@ impl From<Infallible> for std::io::Error {
}
}
/// Traits that allows unwrapping only infallible results
pub(crate) trait UnwrapInfallible {
type Value;
fn unwrap_infallible(self) -> Self::Value;
impl From<Infallible> for std::convert::Infallible {
fn from(error: Infallible) -> Self {
match error {}
}
}
impl<T> UnwrapInfallible for Result<T, Infallible> {
type Value = T;
pub(crate) fn invalid_data_error(error: impl Into<Box<dyn Error + Send + Sync>>) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, error)
}
fn unwrap_infallible(self) -> T {
match self {
Ok(value) => value,
Err(error) => match error {},
}
}
pub(crate) fn invalid_input_error(error: impl Into<Box<dyn Error + Send + Sync>>) -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, error)
}

@ -1,5 +1,6 @@
//! Utilities to read RDF graphs and datasets
use crate::error::invalid_data_error;
use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*;
use oxiri::{Iri, IriParseError};
@ -8,7 +9,6 @@ use rio_api::parser::{QuadsParser, TriplesParser};
use rio_turtle::{NQuadsParser, NTriplesParser, TriGParser, TurtleParser};
use rio_xml::RdfXmlParser;
use std::collections::HashMap;
use std::error::Error;
use std::io;
use std::io::BufRead;
@ -58,7 +58,7 @@ impl GraphParser {
///
///assert_eq!(triples.len(), 1);
///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> {
self.base_iri = Iri::parse(base_iri.into())?.into_inner();
@ -71,14 +71,14 @@ impl GraphParser {
Ok(TripleReader {
mapper: RioMapper::default(),
parser: match self.format {
GraphFormat::NTriples => {
TripleReaderKind::NTriples(NTriplesParser::new(reader).map_err(invalid_input)?)
}
GraphFormat::NTriples => TripleReaderKind::NTriples(
NTriplesParser::new(reader).map_err(invalid_data_error)?,
),
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(
RdfXmlParser::new(reader, &self.base_iri).map_err(invalid_input)?,
RdfXmlParser::new(reader, &self.base_iri).map_err(invalid_data_error)?,
),
},
buffer: Vec::new(),
@ -125,15 +125,24 @@ impl<R: BufRead> Iterator for TripleReader<R> {
}
if let Err(error) = match &mut self.parser {
TripleReaderKind::NTriples(parser) => {
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data)
}
TripleReaderKind::Turtle(parser) => {
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data)
}
TripleReaderKind::RdfXml(parser) => {
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data)
}
TripleReaderKind::NTriples(parser) => Self::read(
parser,
&mut self.buffer,
&mut self.mapper,
invalid_data_error,
),
TripleReaderKind::Turtle(parser) => Self::read(
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));
}
@ -206,7 +215,7 @@ impl DatasetParser {
///
///assert_eq!(triples.len(), 1);
///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> {
self.base_iri = Iri::parse(base_iri.into())?.into_inner();
@ -220,10 +229,10 @@ impl DatasetParser {
mapper: RioMapper::default(),
parser: match self.format {
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(
TriGParser::new(reader, &self.base_iri).map_err(invalid_input)?,
TriGParser::new(reader, &self.base_iri).map_err(invalid_data_error)?,
),
},
buffer: Vec::new(),
@ -269,12 +278,18 @@ impl<R: BufRead> Iterator for QuadReader<R> {
}
if let Err(error) = match &mut self.parser {
QuadReaderKind::NQuads(parser) => {
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data)
}
QuadReaderKind::TriG(parser) => {
Self::read(parser, &mut self.buffer, &mut self.mapper, invalid_data)
}
QuadReaderKind::NQuads(parser) => Self::read(
parser,
&mut self.buffer,
&mut self.mapper,
invalid_data_error,
),
QuadReaderKind::TriG(parser) => Self::read(
parser,
&mut self.buffer,
&mut self.mapper,
invalid_data_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()?;
///
///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)]
pub struct GraphSerializer {
@ -74,7 +74,7 @@ impl GraphSerializer {
/// writer.finish()?;
///
///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]
pub struct TripleWriter<W: Write> {
@ -131,7 +131,7 @@ impl<W: Write> TripleWriter<W> {
/// 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());
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[allow(missing_copy_implementations)]
pub struct DatasetSerializer {
@ -174,7 +174,7 @@ impl DatasetSerializer {
/// 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());
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[must_use]
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())? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! }
//! # oxigraph::Result::Ok(())
//! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! ```
#![deny(
future_incompatible,
@ -109,8 +109,10 @@ pub mod model;
pub mod sparql;
pub mod store;
pub use error::Error;
pub type Result<T> = ::std::result::Result<T, Error>;
#[deprecated(note = "Use oxigraph::sparql::EvaluationError instead")]
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")]
pub type DatasetSyntax = crate::io::DatasetFormat;
#[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.
/// 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.
/// `BlankNode::default().to_string()` should return something like `_:00112233445566778899aabbccddeeff`
/// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation:
/// ```
/// use oxigraph::model::BlankNode;
///
/// assert_eq!(
/// "_:a122",
/// BlankNode::new("a122")?.to_string()
/// );
/// # Result::<_,oxigraph::model::BlankNodeIdParseError>::Ok(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct BlankNode(BlankNodeContent);

@ -10,8 +10,9 @@ use std::fmt;
///
/// assert_eq!(
/// "<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)]
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::Triple;
use crate::sparql::algebra::{DatasetSpec, GraphPattern, QueryVariants};
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*;
use crate::sparql::plan::*;
use crate::sparql::{Query, ServiceHandler};
use crate::store::numeric_encoder::*;
use crate::store::ReadableEncodedStore;
use crate::Error;
use crate::Result;
use digest::Digest;
use md5::Md5;
use oxilangtag::LanguageTag;
@ -29,13 +28,13 @@ use std::str;
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> {
dataset: Rc<DatasetView<S>>,
base_iri: Option<Rc<Iri<String>>>,
now: DateTime,
service_handler: Rc<dyn ServiceHandler>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
}
impl<S: ReadableEncodedStore + 'static> Clone for SimpleEvaluator<S> {
@ -53,7 +52,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
pub fn new(
dataset: Rc<DatasetView<S>>,
base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
) -> Self {
Self {
dataset,
@ -67,14 +66,14 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self,
plan: &PlanNode,
variables: Rc<Vec<Variable>>,
) -> Result<QueryResult> {
) -> Result<QueryResult, EvaluationError> {
let iter = self.eval_plan(plan, EncodedTuple::with_capacity(variables.len()));
Ok(QueryResult::Solutions(
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());
match self.eval_plan(plan, from).next() {
Some(Ok(_)) => Ok(QueryResult::Boolean(true)),
@ -87,7 +86,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self,
plan: &PlanNode,
construct: Rc<Vec<TripleTemplate>>,
) -> Result<QueryResult> {
) -> Result<QueryResult, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
Ok(QueryResult::Graph(QueryTriplesIterator {
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());
Ok(QueryResult::Graph(QueryTriplesIterator {
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) {
graph_name
} 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",
))));
return result;
@ -502,11 +502,11 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
graph_pattern: Rc<GraphPattern>,
variables: Rc<Vec<Variable>>,
from: &EncodedTuple,
) -> Result<EncodedTuplesIterator> {
) -> Result<EncodedTuplesIterator, EvaluationError> {
if let QueryResult::Solutions(iter) = self.service_handler.handle(
self.dataset.decode_named_node(
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 {
dataset: Rc::new(DatasetSpec::default()),
@ -516,7 +516,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
)? {
Ok(self.encode_bindings(variables, iter))
} else {
Err(Error::msg(
Err(EvaluationError::msg(
"The service call has not returned a set of solutions",
))
}
@ -570,7 +570,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
path: &PlanPropertyPath,
start: EncodedTerm,
graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<EncodedTerm>>> {
) -> Box<dyn Iterator<Item = Result<EncodedTerm, EvaluationError>>> {
match path {
PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset
@ -633,7 +633,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
path: &PlanPropertyPath,
end: EncodedTerm,
graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<EncodedTerm>>> {
) -> Box<dyn Iterator<Item = Result<EncodedTerm, EvaluationError>>> {
match path {
PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset
@ -695,7 +695,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
&self,
path: &PlanPropertyPath,
graph_name: EncodedTerm,
) -> Box<dyn Iterator<Item = Result<(EncodedTerm, EncodedTerm)>>> {
) -> Box<dyn Iterator<Item = Result<(EncodedTerm, EncodedTerm), EvaluationError>>> {
match path {
PlanPropertyPath::PredicatePath(p) => Box::new(
self.dataset
@ -770,7 +770,7 @@ impl<S: ReadableEncodedStore + 'static> SimpleEvaluator<S> {
fn get_subject_or_object_identity_pairs(
&self,
graph_name: EncodedTerm,
) -> impl Iterator<Item = Result<(EncodedTerm, EncodedTerm)>> {
) -> impl Iterator<Item = Result<(EncodedTerm, EncodedTerm), EvaluationError>> {
self.dataset
.quads_for_pattern(None, None, None, Some(graph_name))
.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 {
left: Vec<EncodedTuple>,
right_iter: EncodedTuplesIterator,
buffered_results: Vec<Result<EncodedTuple>>,
buffered_results: Vec<Result<EncodedTuple, EvaluationError>>,
}
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 {
if let Some(result) = self.buffered_results.pop() {
return Some(result);
@ -2190,9 +2190,9 @@ struct 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 {
match self.left_iter.next()? {
Ok(left_tuple) => {
@ -2217,9 +2217,9 @@ struct LeftJoinIterator<S: ReadableEncodedStore + 'static> {
}
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() {
return Some(tuple);
}
@ -2247,9 +2247,9 @@ struct BadLeftJoinIterator<S: ReadableEncodedStore + 'static> {
}
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() {
match right_tuple {
Ok(right_tuple) => {
@ -2298,9 +2298,9 @@ struct UnionIterator<S: ReadableEncodedStore + 'static> {
}
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 {
if let Some(tuple) = self.current_iterator.next() {
return Some(tuple);
@ -2320,14 +2320,14 @@ struct ConstructIterator<S: ReadableEncodedStore + 'static> {
eval: SimpleEvaluator<S>,
iter: EncodedTuplesIterator,
template: Rc<Vec<TripleTemplate>>,
buffered_results: Vec<Result<Triple>>,
buffered_results: Vec<Result<Triple, EvaluationError>>,
bnodes: Vec<BlankNode>,
}
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 {
if let Some(result) = self.buffered_results.pop() {
return Some(result);
@ -2379,7 +2379,7 @@ fn decode_triple(
subject: EncodedTerm,
predicate: EncodedTerm,
object: EncodedTerm,
) -> Result<Triple> {
) -> Result<Triple, EvaluationError> {
Ok(Triple::new(
decoder.decode_named_or_blank_node(subject)?,
decoder.decode_named_node(predicate)?,
@ -2390,13 +2390,13 @@ fn decode_triple(
struct DescribeIterator<S: ReadableEncodedStore + 'static> {
eval: SimpleEvaluator<S>,
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> {
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 {
if let Some(quad) = self.quads.next() {
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>>>(
start: impl IntoIterator<Item = Result<T>>,
fn transitive_closure<T: Copy + Eq + Hash, NI: Iterator<Item = Result<T, EvaluationError>>>(
start: impl IntoIterator<Item = Result<T, EvaluationError>>,
next: impl Fn(T) -> NI,
) -> impl Iterator<Item = Result<T>> {
) -> impl Iterator<Item = Result<T, EvaluationError>> {
//TODO: optimize
let mut all = HashSet::<T>::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>(
iter: impl Iterator<Item = Result<T>>,
) -> impl Iterator<Item = Result<T>> {
iter: impl Iterator<Item = Result<T, EvaluationError>>,
) -> impl Iterator<Item = Result<T, EvaluationError>> {
let mut already_seen = HashSet::with_capacity(iter.size_hint().0);
iter.filter(move |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 {
fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>>(
trait ResultIterator<T>: Iterator<Item = Result<T, EvaluationError>> + Sized {
fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O, EvaluationError>>>(
self,
f: F,
) -> FlatMapOk<T, O, Self, F, U>;
}
impl<T, I: Iterator<Item = Result<T>> + Sized> ResultIterator<T> for I {
fn flat_map_ok<O, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>>(
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, EvaluationError>>>(
self,
f: F,
) -> FlatMapOk<T, O, Self, F, U> {
@ -2534,21 +2534,26 @@ impl<T, I: Iterator<Item = Result<T>> + Sized> ResultIterator<T> for I {
struct FlatMapOk<
T,
O,
I: Iterator<Item = Result<T>>,
I: Iterator<Item = Result<T, EvaluationError>>,
F: FnMut(T) -> U,
U: IntoIterator<Item = Result<O>>,
U: IntoIterator<Item = Result<O, EvaluationError>>,
> {
inner: I,
f: F,
current: Option<U::IntoIter>,
}
impl<T, O, I: Iterator<Item = Result<T>>, F: FnMut(T) -> U, U: IntoIterator<Item = Result<O>>>
Iterator for FlatMapOk<T, O, I, F, U>
impl<
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 {
if let Some(current) = &mut self.current {
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/)
use crate::error::invalid_input_error;
use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*;
use crate::Error;
use crate::Result;
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 {
QueryResult::Boolean(value) => {
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"]}}")?;
}
QueryResult::Graph(_) => {
return Err(Error::msg(
return Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format",
));
)
.into());
}
}
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"\"")?;
for c in s.chars() {
match c {

@ -1,6 +1,7 @@
//! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation.
mod algebra;
mod error;
mod eval;
mod json_results;
mod model;
@ -12,27 +13,26 @@ mod xml_results;
use crate::model::NamedNode;
use crate::sparql::algebra::QueryVariants;
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::{DatasetView, PlanNode};
use crate::sparql::plan_builder::PlanBuilder;
use crate::store::ReadableEncodedStore;
use crate::Error;
use crate::Result;
pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator;
pub use crate::sparql::model::QueryTriplesIterator;
use std::convert::TryInto;
use std::rc::Rc;
#[deprecated(note = "Please directly use QuerySolutionsIterator type instead")]
pub type BindingsIterator<'a> = QuerySolutionsIterator;
pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultFormat;
#[deprecated(note = "Use QueryResultFormat instead")]
pub type QueryResultSyntax = QueryResultFormat;
pub use crate::sparql::error::EvaluationError;
pub use crate::sparql::model::Variable;
pub use crate::sparql::parser::ParseError;
pub use crate::sparql::parser::Query;
pub use crate::sparql::parser::SparqlParseError;
use std::convert::TryInto;
use std::rc::Rc;
use std::error::Error;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[deprecated(
@ -70,9 +70,9 @@ enum SimplePreparedQueryAction<S: ReadableEncodedStore + 'static> {
impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
pub(crate) fn new(
store: S,
query: impl TryInto<Query, Error = impl Into<Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> Result<Self> {
) -> Result<Self, EvaluationError> {
let dataset = Rc::new(DatasetView::new(store, options.default_graph_as_union));
Ok(Self(match query.try_into().map_err(|e| e.into())?.0 {
QueryVariants::Select {
@ -124,7 +124,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
}
/// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult> {
pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
match &self.0 {
SimplePreparedQueryAction::Select {
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.
///
/// 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::sparql::{QueryOptions, QueryResult, ServiceHandler, Query};
/// use oxigraph::sparql::{QueryOptions, QueryResult, ServiceHandler, Query, EvaluationError};
///
/// #[derive(Default)]
/// struct TestServiceHandler {
@ -159,7 +191,9 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
/// }
///
/// 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" {
/// self.store.query(query, QueryOptions::default())
/// } else {
@ -179,47 +213,41 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
/// )? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub trait ServiceHandler {
type Error: Error + Send + Sync + 'static;
/// 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;
impl ServiceHandler for EmptyServiceHandler {
fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult> {
Err(Error::msg("The SERVICE feature is not implemented"))
}
}
type Error = EvaluationError;
/// Options for SPARQL query evaluation
#[derive(Clone)]
pub struct QueryOptions {
pub(crate) default_graph_as_union: bool,
pub(crate) service_handler: Rc<dyn ServiceHandler>,
fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult, EvaluationError> {
Err(EvaluationError::msg(
"The SERVICE feature is not implemented",
))
}
}
impl Default for QueryOptions {
fn default() -> Self {
Self {
default_graph_as_union: false,
service_handler: Rc::new(EmptyServiceHandler),
}
}
struct ErrorConversionServiceHandler<S: ServiceHandler> {
handler: S,
}
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
}
impl<S: ServiceHandler> ServiceHandler for ErrorConversionServiceHandler<S> {
type Error = EvaluationError;
/// 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(service_handler);
self
fn handle(
&self,
service_name: NamedNode,
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;
#[allow(deprecated)]
use crate::io::{FileSyntax, GraphFormat};
use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::json_results::write_json_results;
use crate::sparql::xml_results::{read_xml_results, write_xml_results};
use crate::{Error, Result};
use rand::random;
use std::fmt;
use std::io::{BufRead, Write};
@ -21,13 +22,16 @@ pub enum 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 {
QueryResultFormat::Xml => read_xml_results(reader),
QueryResultFormat::Json => Err(Error::msg(
//TODO: implement
QueryResultFormat::Json => Err(invalid_input_error(
"JSON SPARQL results format parsing has not been implemented yet",
)),
)
.into()), //TODO: implement
}
}
@ -47,9 +51,13 @@ impl QueryResult {
/// let mut results = Vec::new();
/// 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());
/// # 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 {
QueryResultFormat::Xml => write_xml_results(self, writer),
QueryResultFormat::Json => write_json_results(self, writer),
@ -75,9 +83,13 @@ impl QueryResult {
/// let mut results = Vec::new();
/// store.query("CONSTRUCT WHERE { ?s ?p ?o }", QueryOptions::default())?.write_graph(&mut results, GraphFormat::NTriples)?;
/// 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 {
let mut writer = GraphSerializer::from_format(format).triple_writer(write)?;
for triple in triples {
@ -86,9 +98,10 @@ impl QueryResult {
writer.finish()?;
Ok(())
} else {
Err(Error::msg(
"Bindings or booleans could not be formatted as an RDF graph",
))
Err(
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"));
/// }
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub struct QuerySolutionsIterator {
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 {
pub fn new(
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 { variables, iter }
}
@ -237,14 +250,16 @@ impl QuerySolutionsIterator {
/// 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")]);
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn variables(&self) -> &[Variable] {
&*self.variables
}
#[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
}
@ -253,16 +268,16 @@ impl QuerySolutionsIterator {
self,
) -> (
Vec<Variable>,
Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>,
Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
) {
((*self.variables).clone(), self.iter)
}
}
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 {
values,
variables: self.variables.clone(),
@ -359,16 +374,16 @@ impl VariableSolutionIndex for Variable {
/// println!("{}", triple?);
/// }
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
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 {
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()
}
@ -409,7 +424,7 @@ impl Variable {
}
#[deprecated(note = "Please use as_str instead")]
pub fn name(&self) -> Result<&str> {
pub fn name(&self) -> Result<&str, EvaluationError> {
Ok(self.as_str())
}

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

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

@ -1,10 +1,9 @@
use crate::model::{BlankNode, Term};
use crate::sparql::algebra::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*;
use crate::sparql::plan::*;
use crate::store::numeric_encoder::{Encoder, ENCODED_DEFAULT_GRAPH};
use crate::Error;
use crate::Result;
use std::collections::{BTreeSet, HashSet};
use std::rc::Rc;
@ -13,7 +12,10 @@ pub(crate) struct PlanBuilder<E: Encoder> {
}
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 plan = PlanBuilder { encoder }.build_for_graph_pattern(
pattern,
@ -27,7 +29,7 @@ impl<E: Encoder> PlanBuilder<E> {
encoder: E,
template: &[TriplePattern],
mut variables: Vec<Variable>,
) -> Result<Vec<TripleTemplate>> {
) -> Result<Vec<TripleTemplate>, EvaluationError> {
PlanBuilder { encoder }.build_for_graph_template(template, &mut variables)
}
@ -36,7 +38,7 @@ impl<E: Encoder> PlanBuilder<E> {
pattern: &GraphPattern,
variables: &mut Vec<Variable>,
graph_name: PatternValue,
) -> Result<PlanNode> {
) -> Result<PlanNode, EvaluationError> {
Ok(match pattern {
GraphPattern::BGP(p) => self.build_for_bgp(p, variables, graph_name)?,
GraphPattern::Join(a, b) => PlanNode::Join {
@ -143,7 +145,7 @@ impl<E: Encoder> PlanBuilder<E> {
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)?,
},
GraphPattern::OrderBy(l, o) => {
let by: Result<Vec<_>> = o
let by: Result<Vec<_>, EvaluationError> = o
.iter()
.map(|comp| match comp {
OrderComparator::Asc(e) => Ok(Comparator::Asc(
@ -216,7 +218,7 @@ impl<E: Encoder> PlanBuilder<E> {
p: &[TripleOrPathPattern],
variables: &mut Vec<Variable>,
graph_name: PatternValue,
) -> Result<PlanNode> {
) -> Result<PlanNode, EvaluationError> {
let mut plan = PlanNode::Init;
for pattern in sort_bgp(p) {
plan = match pattern {
@ -242,7 +244,7 @@ impl<E: Encoder> PlanBuilder<E> {
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 {
PropertyPath::PredicatePath(p) => PlanPropertyPath::PredicatePath(
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(
p.iter()
.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,
variables: &mut Vec<Variable>,
graph_name: PatternValue,
) -> Result<PlanExpression> {
) -> Result<PlanExpression, EvaluationError> {
Ok(match expression {
Expression::NamedNode(node) => PlanExpression::Constant(
self.encoder.encode_named_node(node).map_err(|e| e.into())?,
@ -676,7 +678,7 @@ impl<E: Encoder> PlanBuilder<E> {
"string",
)?
} else {
return Err(Error::msg(format!(
return Err(EvaluationError::msg(format!(
"Not supported custom function {}",
expression
)));
@ -697,7 +699,7 @@ impl<E: Encoder> PlanBuilder<E> {
variables: &mut Vec<Variable>,
graph_name: PatternValue,
name: &'static str,
) -> Result<PlanExpression> {
) -> Result<PlanExpression, EvaluationError> {
if parameters.len() == 1 {
Ok(constructor(Box::new(self.build_for_expression(
&parameters[0],
@ -705,7 +707,7 @@ impl<E: Encoder> PlanBuilder<E> {
graph_name,
)?)))
} else {
Err(Error::msg(format!(
Err(EvaluationError::msg(format!(
"The xsd:{} casting takes only one parameter",
name
)))
@ -717,7 +719,7 @@ impl<E: Encoder> PlanBuilder<E> {
l: &[Expression],
variables: &mut Vec<Variable>,
graph_name: PatternValue,
) -> Result<Vec<PlanExpression>> {
) -> Result<Vec<PlanExpression>, EvaluationError> {
l.iter()
.map(|e| self.build_for_expression(e, variables, graph_name))
.collect()
@ -727,7 +729,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self,
term_or_variable: &TermOrVariable,
variables: &mut Vec<Variable>,
) -> Result<PatternValue> {
) -> Result<PatternValue, EvaluationError> {
Ok(match term_or_variable {
TermOrVariable::Variable(variable) => {
PatternValue::Variable(variable_key(variables, variable))
@ -746,7 +748,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self,
named_node_or_variable: &NamedNodeOrVariable,
variables: &mut Vec<Variable>,
) -> Result<PatternValue> {
) -> Result<PatternValue, EvaluationError> {
Ok(match named_node_or_variable {
NamedNodeOrVariable::NamedNode(named_node) => PatternValue::Constant(
self.encoder
@ -763,7 +765,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self,
bindings: &StaticBindings,
variables: &mut Vec<Variable>,
) -> Result<Vec<EncodedTuple>> {
) -> Result<Vec<EncodedTuple>, EvaluationError> {
let bindings_variables_keys = bindings
.variables()
.iter()
@ -791,7 +793,7 @@ impl<E: Encoder> PlanBuilder<E> {
aggregate: &Aggregation,
variables: &mut Vec<Variable>,
graph_name: PatternValue,
) -> Result<PlanAggregation> {
) -> Result<PlanAggregation, EvaluationError> {
Ok(match aggregate {
Aggregation::Count(e, distinct) => PlanAggregation {
function: PlanAggregationFunction::Count,
@ -840,7 +842,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self,
template: &[TriplePattern],
variables: &mut Vec<Variable>,
) -> Result<Vec<TripleTemplate>> {
) -> Result<Vec<TripleTemplate>, EvaluationError> {
let mut bnodes = Vec::default();
template
.iter()
@ -868,7 +870,7 @@ impl<E: Encoder> PlanBuilder<E> {
term_or_variable: &TermOrVariable,
variables: &mut Vec<Variable>,
bnodes: &mut Vec<BlankNode>,
) -> Result<TripleTemplateValue> {
) -> Result<TripleTemplateValue, EvaluationError> {
Ok(match term_or_variable {
TermOrVariable::Variable(variable) => {
TripleTemplateValue::Variable(variable_key(variables, variable))
@ -886,7 +888,7 @@ impl<E: Encoder> PlanBuilder<E> {
&mut self,
named_node_or_variable: &NamedNodeOrVariable,
variables: &mut Vec<Variable>,
) -> Result<TripleTemplateValue> {
) -> Result<TripleTemplateValue, EvaluationError> {
Ok(match named_node_or_variable {
NamedNodeOrVariable::Variable(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/)
use crate::error::{invalid_data_error, invalid_input_error};
use crate::model::*;
use crate::sparql::error::EvaluationError;
use crate::sparql::model::*;
use crate::Error;
use crate::Result;
use quick_xml::events::BytesDecl;
use quick_xml::events::BytesEnd;
use quick_xml::events::BytesStart;
@ -17,93 +17,138 @@ use std::io::Write;
use std::iter::empty;
use std::rc::Rc;
pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<()> {
let mut writer = Writer::new(sink);
pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<(), EvaluationError> {
match results {
QueryResult::Boolean(value) => {
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer.write_event(Event::Start(sparql_open))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"boolean")))?;
writer.write_event(Event::Text(BytesText::from_plain_str(if value {
"true"
} else {
"false"
})))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?;
write_boolean(value, sink).map_err(map_xml_error)?;
Ok(())
}
QueryResult::Solutions(solutions) => {
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer.write_event(Event::Start(sparql_open))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?;
for variable in solutions.variables() {
let mut variable_tag = BytesStart::borrowed_name(b"variable");
variable_tag.push_attribute(("name", variable.as_str()));
writer.write_event(Event::Empty(variable_tag))?;
}
writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"results")))?;
for solution in solutions {
let solution = solution?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"result")))?;
for (variable, value) in solution.iter() {
let mut binding_tag = BytesStart::borrowed_name(b"binding");
binding_tag.push_attribute(("name", variable.as_str()));
writer.write_event(Event::Start(binding_tag))?;
match value {
Term::NamedNode(uri) => {
writer.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))?;
writer.write_event(Event::Text(BytesText::from_plain_str(
uri.as_str(),
)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"uri")))?;
}
Term::BlankNode(bnode) => {
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))?;
writer.write_event(Event::Text(BytesText::from_plain_str(
bnode.as_str(),
)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"bnode")))?;
}
Term::Literal(literal) => {
let mut literal_tag = BytesStart::borrowed_name(b"literal");
if let Some(language) = literal.language() {
literal_tag.push_attribute(("xml:lang", language));
} else if !literal.is_plain() {
literal_tag
.push_attribute(("datatype", literal.datatype().as_str()));
}
writer.write_event(Event::Start(literal_tag))?;
writer.write_event(Event::Text(BytesText::from_plain_str(
literal.value(),
)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"literal")))?;
}
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)))?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer.write_event(Event::Start(sparql_open))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"boolean")))?;
writer.write_event(Event::Text(BytesText::from_plain_str(if value {
"true"
} else {
"false"
})))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?;
Ok(())
}
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");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer
.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() {
let mut variable_tag = BytesStart::borrowed_name(b"variable");
variable_tag.push_attribute(("name", variable.as_str()));
writer
.write_event(Event::Empty(variable_tag))
.map_err(map_xml_error)?;
}
writer
.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 {
let solution = solution?;
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"result")))
.map_err(map_xml_error)?;
for (variable, value) in solution.iter() {
let mut binding_tag = BytesStart::borrowed_name(b"binding");
binding_tag.push_attribute(("name", variable.as_str()));
writer
.write_event(Event::Start(binding_tag))
.map_err(map_xml_error)?;
match value {
Term::NamedNode(uri) => {
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))
.map_err(map_xml_error)?;
writer
.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) => {
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))
.map_err(map_xml_error)?;
writer
.write_event(Event::Text(BytesText::from_plain_str(bnode.as_str())))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"bnode")))
.map_err(map_xml_error)?;
}
Term::Literal(literal) => {
let mut literal_tag = BytesStart::borrowed_name(b"literal");
if let Some(language) = literal.language() {
literal_tag.push_attribute(("xml:lang", language));
} else if !literal.is_plain() {
literal_tag.push_attribute(("datatype", literal.datatype().as_str()));
}
writer.write_event(Event::End(BytesEnd::borrowed(b"binding")))?;
writer
.write_event(Event::Start(literal_tag))
.map_err(map_xml_error)?;
writer
.write_event(Event::Text(BytesText::from_plain_str(literal.value())))
.map_err(map_xml_error)?;
writer
.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(_) => {
return Err(Error::msg(
"Graphs could not be formatted to SPARQL query results XML format",
));
writer
.write_event(Event::End(BytesEnd::borrowed(b"binding")))
.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(())
}
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 {
Start,
Sparql,
@ -123,13 +168,16 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
//Read header
loop {
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 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: {}",
reader.decode(ns)?
)));
reader.decode(ns).map_err(map_xml_error)?
))
.into());
}
}
event
@ -140,14 +188,14 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
if event.name() == b"sparql" {
state = State::Sparql;
} 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 => {
if event.name() == b"head" {
state = State::Head;
} 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 => {
@ -155,12 +203,12 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
let name = event.attributes()
.filter_map(|attr| attr.ok())
.find(|attr| attr.key == b"name")
.ok_or_else(|| Error::msg("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader)?);
.ok_or_else(|| invalid_data_error("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader).map_err(map_xml_error)?);
} else if event.name() == b"link" {
// no op
} 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 => {
@ -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" {
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 {
State::Sparql => {
if event.name() == b"head" {
state = State::AfterHead;
} 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 => {
@ -199,12 +247,12 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
let name = event.attributes()
.filter_map(|v| v.ok())
.find(|attr| attr.key == b"name")
.ok_or_else(|| Error::msg("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader)?);
.ok_or_else(|| invalid_data_error("No name attribute found for the <variable> tag"))?;
variables.push(name.unescape_and_decode_value(&reader).map_err(map_xml_error)?);
} else if event.name() == b"link" {
// no op
} 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 => {
@ -214,13 +262,13 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
Box::new(empty()),
)))
} 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) => {
let value = event.unescaped()?;
let value = event.unescaped().map_err(map_xml_error)?;
return match state {
State::Boolean => {
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" {
Ok(QueryResult::Boolean(false))
} 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 {
state = State::AfterHead;
} 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> {
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()
}
}
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 {
Start,
Result,
@ -283,13 +331,15 @@ impl<R: BufRead> ResultsIterator<R> {
loop {
let (ns, event) = self
.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 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: {}",
self.reader.decode(ns)?
)));
self.reader.decode(ns).map_err(map_xml_error)?
))
.into());
}
}
match event {
@ -298,10 +348,11 @@ impl<R: BufRead> ResultsIterator<R> {
if event.name() == b"result" {
state = State::Result;
} else {
return Err(Error::msg(format!(
return Err(invalid_data_error(format!(
"Expecting <result>, found {}",
self.reader.decode(event.name())?
)));
self.reader.decode(event.name()).map_err(map_xml_error)?
))
.into());
}
}
State::Result => {
@ -311,26 +362,33 @@ impl<R: BufRead> ResultsIterator<R> {
.filter_map(|v| v.ok())
.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 => {
return Err(Error::msg(
return Err(invalid_data_error(
"No name attribute found for the <binding> tag",
));
)
.into());
}
}
state = State::Binding;
} else {
return Err(Error::msg(format!(
return Err(invalid_data_error(format!(
"Expecting <binding>, found {}",
self.reader.decode(event.name())?
)));
self.reader.decode(event.name()).map_err(map_xml_error)?
))
.into());
}
}
State::Binding => {
if term.is_some() {
return Err(Error::msg(
return Err(invalid_data_error(
"There is already a value for the current binding",
));
)
.into());
}
if event.name() == b"uri" {
state = State::Uri;
@ -340,37 +398,67 @@ impl<R: BufRead> ResultsIterator<R> {
for attr in event.attributes() {
if let Ok(attr) = attr {
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" {
datatype = Some(NamedNode::new(
attr.unescape_and_decode_value(&self.reader)?,
)?);
let iri = attr
.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;
} else {
return Err(Error::msg(format!(
return Err(invalid_data_error(format!(
"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) => {
let data = event.unescaped()?;
let data = event.unescaped().map_err(map_xml_error)?;
match state {
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 => {
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 => {
term = Some(
build_literal(
self.reader.decode(&data)?,
self.reader.decode(&data).map_err(map_xml_error)?,
lang.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: {}",
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 {
new_bindings[self.mapping[var]] = term.clone()
} 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;
state = State::Result;
@ -418,12 +509,22 @@ fn build_literal(
value: impl Into<String>,
lang: Option<String>,
datatype: Option<NamedNode>,
) -> Result<Literal> {
) -> Result<Literal, EvaluationError> {
match datatype {
Some(datatype) => Ok(Literal::new_typed_literal(value, datatype)),
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)),
},
}
}
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::io::{DatasetFormat, GraphFormat};
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::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
};
use crate::Error;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
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())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[derive(Clone)]
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())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> crate::Result<QueryResult> {
) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec()
}
@ -132,13 +131,13 @@ impl MemoryStore {
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn prepare_query(
&self,
query: impl TryInto<Query, Error = impl Into<Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> crate::Result<MemoryPreparedQuery> {
) -> Result<MemoryPreparedQuery, EvaluationError> {
Ok(MemoryPreparedQuery(SimplePreparedQuery::new(
self.clone(),
query,
@ -163,7 +162,7 @@ impl MemoryStore {
/// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// assert_eq!(vec![quad], results);
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn quads_for_pattern(
&self,
@ -232,7 +231,7 @@ impl MemoryStore {
///
/// // quad filter
/// assert!(store.contains(&quad));
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn transaction<'a, E>(
&'a self,
@ -266,7 +265,7 @@ impl MemoryStore {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?;
/// 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.
@ -300,7 +299,7 @@ impl MemoryStore {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?;
/// 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.
@ -356,7 +355,7 @@ impl MemoryStore {
/// let mut buffer = Vec::new();
/// store.dump_graph(&mut buffer, GraphFormat::NTriples, &GraphName::DefaultGraph)?;
/// 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.
@ -390,7 +389,7 @@ impl MemoryStore {
/// let mut buffer = Vec::new();
/// store.dump_dataset(&mut buffer, DatasetFormat::NQuads)?;
/// 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.
@ -954,7 +953,7 @@ pub struct MemoryPreparedQuery(SimplePreparedQuery<MemoryStore>);
impl MemoryPreparedQuery {
/// Evaluates the query and returns its results
pub fn exec(&self) -> crate::Result<QueryResult> {
pub fn exec(&self) -> Result<QueryResult, EvaluationError> {
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 ex = NamedNode::new("http://example.com")?;
/// 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(
&mut self,
@ -1022,7 +1021,7 @@ impl<'a> MemoryTransaction<'a> {
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::new("http://example.com")?;
/// 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(
&mut self,

@ -16,6 +16,7 @@ pub use crate::store::rocksdb::RocksDbStore;
#[cfg(feature = "sled")]
pub use crate::store::sled::SledStore;
use crate::error::{invalid_data_error, invalid_input_error};
use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*;
use crate::store::numeric_encoder::*;
@ -90,7 +91,7 @@ where
IoOrParseError<P::Error>: From<P::Error>,
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 to_graph_name = store
.encode_graph_name(to_graph_name)
@ -163,7 +164,7 @@ where
IoOrParseError<P::Error>: From<P::Error>,
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 result: Result<(), IoOrParseError<_>> = parser.parse_all(&mut move |q| {
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 {
match 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)]
use crate::error::{Infallible, UnwrapInfallible};
use crate::error::{invalid_data_error, Infallible, UnwrapInfallible};
use crate::model::vocab::rdf;
use crate::model::vocab::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]
fn test_encoding() {
let mut store = MemoryStrStore::default();

@ -1,9 +1,9 @@
//! 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::model::*;
use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*;
use crate::store::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
@ -48,7 +48,7 @@ use std::{fmt, str};
/// #
/// # };
/// # remove_dir_all("example.db")?;
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[derive(Clone)]
pub struct RocksDbStore {
@ -95,9 +95,9 @@ impl RocksDbStore {
/// See `MemoryStore` for a usage example.
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> Result<QueryResult, crate::Error> {
) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec()
}
@ -107,9 +107,9 @@ impl RocksDbStore {
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> Result<RocksDbPreparedQuery, crate::Error> {
) -> Result<RocksDbPreparedQuery, EvaluationError> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
query,
@ -194,7 +194,7 @@ impl RocksDbStore {
format: GraphFormat,
to_graph_name: &GraphName,
base_iri: Option<&str>,
) -> Result<(), crate::Error> {
) -> Result<(), io::Error> {
let mut transaction = self.auto_batch_writer();
load_graph(&mut transaction, reader, format, to_graph_name, base_iri)?;
Ok(transaction.apply()?)
@ -215,7 +215,7 @@ impl RocksDbStore {
reader: impl BufRead,
format: DatasetFormat,
base_iri: Option<&str>,
) -> Result<(), crate::Error> {
) -> Result<(), io::Error> {
let mut transaction = self.auto_batch_writer();
load_dataset(&mut transaction, reader, format, base_iri)?;
Ok(transaction.apply()?)
@ -476,7 +476,7 @@ impl StrLookup for RocksDbStore {
.map_err(map_err)?
.map(String::from_utf8)
.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 {
/// 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()
}
}
@ -892,14 +892,14 @@ fn map_err(e: Error) -> io::Error {
}
#[test]
fn store() -> Result<(), crate::Error> {
fn store() -> Result<(), io::Error> {
use crate::model::*;
use rand::random;
use std::env::temp_dir;
use std::fs::remove_dir_all;
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_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.
use crate::error::{Infallible, UnwrapInfallible};
use crate::error::{invalid_data_error, Infallible, UnwrapInfallible};
use crate::io::{DatasetFormat, GraphFormat};
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::{
dump_dataset, dump_graph, load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore,
@ -47,7 +47,7 @@ use std::{fmt, io, str};
/// #
/// # };
/// # remove_dir_all("example.db")?;
/// # oxigraph::Result::Ok(())
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[derive(Clone)]
pub struct SledStore {
@ -90,9 +90,9 @@ impl SledStore {
/// See `MemoryStore` for a usage example.
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> Result<QueryResult, crate::Error> {
) -> Result<QueryResult, EvaluationError> {
self.prepare_query(query, options)?.exec()
}
@ -101,9 +101,9 @@ impl SledStore {
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions,
) -> Result<SledPreparedQuery, crate::Error> {
) -> Result<SledPreparedQuery, EvaluationError> {
Ok(SledPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
query,
@ -393,7 +393,7 @@ impl StrLookup for SledStore {
.get(id.to_be_bytes())?
.map(|v| String::from_utf8(v.to_vec()))
.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 {
/// 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()
}
}
@ -829,19 +829,19 @@ fn decode_quad(encoded: &[u8]) -> Result<EncodedQuad, io::Error> {
GSPO_PREFIX => Ok(cursor.read_gspo_quad()?),
GPOS_PREFIX => Ok(cursor.read_gpos_quad()?),
GOSP_PREFIX => Ok(cursor.read_gosp_quad()?),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Invalid quad type identifier: {}", encoded[0]),
)),
_ => Err(invalid_data_error(format!(
"Invalid quad type identifier: {}",
encoded[0]
))),
}
}
#[test]
fn store() -> Result<(), crate::Error> {
fn store() -> Result<(), io::Error> {
use crate::model::*;
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_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);

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

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

@ -1,7 +1,9 @@
use crate::model::*;
use oxigraph::model::*;
use oxigraph::sparql::{QueryResult, QuerySolution, QuerySolutionsIterator, QueryTriplesIterator};
use pyo3::exceptions::{IOError, TypeError, ValueError};
use oxigraph::sparql::{
EvaluationError, QueryResult, QuerySolution, QuerySolutionsIterator, QueryTriplesIterator,
};
use pyo3::exceptions::{IOError, RuntimeError, TypeError, ValueError};
use pyo3::prelude::*;
use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::fmt::Write;
@ -113,7 +115,7 @@ impl PyIterProtocol for QuerySolutionIter {
.inner
.next()
.transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve
.map_err(map_evaluation_error)?
.map(move |inner| PyQuerySolution { inner }))
}
}
@ -134,7 +136,7 @@ impl PyIterProtocol for TripleResultIter {
.inner
.next()
.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)))
}
}
@ -147,3 +149,12 @@ pub fn map_io_err(error: io::Error) -> PyErr {
_ => 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::*;
use oxigraph::sparql::*;
use oxigraph::{Error, MemoryStore};
use oxigraph::MemoryStore;
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use std::{fmt, io};
pub fn evaluate_sparql_tests(
manifest: impl Iterator<Item = Result<Test>>,
@ -168,10 +168,21 @@ impl 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
.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,
QueryOptions::default().with_service_handler(self.clone()),
@ -183,7 +194,7 @@ fn to_dataset(result: QueryResult, with_order: bool) -> Result<MemoryStore> {
match result {
QueryResult::Graph(graph) => Ok(graph
.map(|t| t.map(|t| t.in_graph(None)))
.collect::<Result<_, Error>>()?),
.collect::<Result<_, _>>()?),
QueryResult::Boolean(value) => {
let store = MemoryStore::new();
let result_set = BlankNode::default();

Loading…
Cancel
Save