Provides an API to edit the SPARQL query dataset

pull/69/head
Tpt 4 years ago
parent be275d71d7
commit 7a110902d2
  1. 313
      lib/src/sparql/algebra.rs
  2. 214
      lib/src/sparql/dataset.rs
  3. 5
      lib/src/sparql/eval.rs
  4. 64
      lib/src/sparql/mod.rs
  5. 276
      lib/src/sparql/parser.rs
  6. 2
      lib/src/sparql/service.rs
  7. 8
      lib/src/sparql/update.rs
  8. 10
      python/src/memory_store.rs
  9. 10
      python/src/sled_store.rs
  10. 51
      python/src/sparql.rs
  11. 51
      server/src/main.rs
  12. 9
      wikibase/src/main.rs

@ -2,12 +2,150 @@
use crate::model::*; use crate::model::*;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::sparql::parser::{parse_query, parse_update, ParseError};
use oxiri::Iri; use oxiri::Iri;
use rio_api::model as rio; use rio_api::model as rio;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::ops::Add;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr;
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
///
/// ```
/// use oxigraph::model::NamedNode;
/// use oxigraph::sparql::Query;
///
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let mut query = Query::parse(query_str, None)?;
///
/// assert_eq!(query.to_string(), query_str);
///
/// // We edit the query dataset specification
/// query.dataset_mut().set_default_graph(vec![NamedNode::new("http://example.com").unwrap().into()]);
/// assert_eq!(query.to_string(), "SELECT ?s ?p ?o FROM <http://example.com> WHERE { ?s ?p ?o . }");
/// # Result::Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(pub(crate) QueryVariants);
impl Query {
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_query(query, base_iri)
}
/// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
pub fn dataset(&self) -> &QueryDataset {
match &self.0 {
QueryVariants::Select { dataset, .. } => dataset,
QueryVariants::Construct { dataset, .. } => dataset,
QueryVariants::Describe { dataset, .. } => dataset,
QueryVariants::Ask { dataset, .. } => dataset,
}
}
/// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
pub fn dataset_mut(&mut self) -> &mut QueryDataset {
match &mut self.0 {
QueryVariants::Select { dataset, .. } => dataset,
QueryVariants::Construct { dataset, .. } => dataset,
QueryVariants::Describe { dataset, .. } => dataset,
QueryVariants::Ask { dataset, .. } => dataset,
}
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for Query {
type Err = ParseError;
fn from_str(query: &str) -> Result<Self, ParseError> {
Self::parse(query, None)
}
}
impl<'a> TryFrom<&'a str> for Query {
type Error = ParseError;
fn try_from(query: &str) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
impl<'a> TryFrom<&'a String> for Query {
type Error = ParseError;
fn try_from(query: &String) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/)
///
/// ```
/// use oxigraph::sparql::Update;
///
/// let update_str = "CLEAR ALL ;";
/// let update = Update::parse(update_str, None)?;
///
/// assert_eq!(update.to_string().trim(), update_str);
/// # Result::Ok::<_, oxigraph::sparql::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Update {
pub(crate) base_iri: Option<Rc<Iri<String>>>,
pub(crate) operations: Vec<GraphUpdateOperation>,
}
impl Update {
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query
pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
parse_update(update, base_iri)
}
}
impl fmt::Display for Update {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(base_iri) = &self.base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
for update in &self.operations {
writeln!(f, "{} ;", update)?;
}
Ok(())
}
}
impl FromStr for Update {
type Err = ParseError;
fn from_str(update: &str) -> Result<Self, ParseError> {
Self::parse(update, None)
}
}
impl<'a> TryFrom<&'a str> for Update {
type Error = ParseError;
fn try_from(update: &str) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
impl<'a> TryFrom<&'a String> for Update {
type Error = ParseError;
fn try_from(update: &String) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedNodeOrVariable { pub enum NamedNodeOrVariable {
@ -986,7 +1124,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> {
"{{ {} }}", "{{ {} }}",
SparqlGraphRootPattern { SparqlGraphRootPattern {
algebra: p, algebra: p,
dataset: &EMPTY_DATASET dataset: &QueryDataset::default()
} }
), ),
} }
@ -995,7 +1133,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> {
struct SparqlGraphRootPattern<'a> { struct SparqlGraphRootPattern<'a> {
algebra: &'a GraphPattern, algebra: &'a GraphPattern,
dataset: &'a DatasetSpec, dataset: &'a QueryDataset,
} }
impl<'a> fmt::Display for SparqlGraphRootPattern<'a> { impl<'a> fmt::Display for SparqlGraphRootPattern<'a> {
@ -1041,7 +1179,7 @@ impl<'a> fmt::Display for SparqlGraphRootPattern<'a> {
} }
write!( write!(
f, f,
"{} {} WHERE {{ {} }}", "{}{} WHERE {{ {} }}",
build_sparql_select_arguments(project), build_sparql_select_arguments(project),
self.dataset, self.dataset,
SparqlGraphPattern(p) SparqlGraphPattern(p)
@ -1304,79 +1442,130 @@ impl<'a> fmt::Display for SparqlOrderComparator<'a> {
} }
} }
#[derive(Eq, PartialEq, Debug, Clone, Hash, Default)] /// A SPARQL query [dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
pub struct DatasetSpec { #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub default: Vec<NamedNode>, pub struct QueryDataset {
pub named: Vec<NamedNode>, default: Option<Vec<GraphName>>,
named: Option<Vec<NamedOrBlankNode>>,
} }
impl DatasetSpec { impl Default for QueryDataset {
pub fn new_with_default(graph: NamedNode) -> Self { fn default() -> Self {
Self {
default: vec![graph],
named: Vec::default(),
}
}
pub fn new_with_named(graph: NamedNode) -> Self {
Self { Self {
default: Vec::default(), default: Some(vec![GraphName::DefaultGraph]),
named: vec![graph], named: None,
} }
} }
pub fn is_empty(&self) -> bool {
self.default.is_empty() && self.named.is_empty()
}
} }
impl Add for DatasetSpec { impl QueryDataset {
type Output = Self; /// Checks if this dataset specification is the default one
/// (i.e. the default graph is the store default graph and all the store named graphs are available)
fn add(mut self, rhs: Self) -> Self { ///
self.default.extend_from_slice(&rhs.default); /// ```
self.named.extend_from_slice(&rhs.named); /// use oxigraph::sparql::Query;
self ///
} /// assert!(Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?.dataset().is_default_dataset());
} /// assert!(!Query::parse("SELECT ?s ?p ?o FROM <http://example.com> WHERE { ?s ?p ?o . }", None)?.dataset().is_default_dataset());
///
impl fmt::Display for DatasetSpec { /// # Result::Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn is_default_dataset(&self) -> bool {
self.default
.as_ref()
.map_or(false, |t| t == &[GraphName::DefaultGraph])
&& self.named.is_none()
}
/// Returns the list of the store graphs that are available to the query as the default graph or `None` if the union of all graphs is used as the default graph
/// This list is by default only the store default graph
pub fn default_graph_graphs(&self) -> Option<&[GraphName]> {
self.default.as_deref()
}
/// Sets if the default graph for the query should be the union of all the graphs in the queried store
pub fn set_default_graph_as_union(&mut self) {
self.default = None;
}
/// Sets the list of graphs the query should consider as being part of the default graph.
///
/// By default only the store default graph is considered.
/// ```
/// use oxigraph::model::NamedNode;
/// use oxigraph::sparql::Query;
///
/// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
/// query.dataset_mut().set_default_graph(vec![NamedNode::new("http://example.com")?.into()]);
/// assert_eq!(query.to_string(), "SELECT ?s ?p ?o FROM <http://example.com> WHERE { ?s ?p ?o . }");
///
/// # Result::Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn set_default_graph(&mut self, graphs: Vec<GraphName>) {
self.default = Some(graphs)
}
/// Returns the list of the available named graphs for the query or `None` if all graphs are available
pub fn available_named_graphs(&self) -> Option<&[NamedOrBlankNode]> {
self.named.as_deref()
}
/// Sets the list of allowed named graphs in the query.
///
/// ```
/// use oxigraph::model::NamedNode;
/// use oxigraph::sparql::Query;
///
/// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
/// query.dataset_mut().set_available_named_graphs(vec![NamedNode::new("http://example.com")?.into()]);
/// assert_eq!(query.to_string(), "SELECT ?s ?p ?o FROM NAMED <http://example.com> WHERE { ?s ?p ?o . }");
///
/// # Result::Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn set_available_named_graphs(&mut self, named_graphs: Vec<NamedOrBlankNode>) {
self.named = Some(named_graphs);
}
}
impl fmt::Display for QueryDataset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for g in &self.default { //TODO: does not encode everything
write!(f, "FROM {} ", g)?; if let Some(graphs) = &self.default {
for g in graphs {
if !g.is_default_graph() {
write!(f, " FROM {}", g)?;
}
}
} }
for g in &self.named { if let Some(graphs) = &self.named {
write!(f, "FROM NAMED {} ", g)?; for g in graphs {
write!(f, " FROM NAMED {}", g)?;
}
} }
Ok(()) Ok(())
} }
} }
const EMPTY_DATASET: DatasetSpec = DatasetSpec {
default: Vec::new(),
named: Vec::new(),
};
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum QueryVariants { pub enum QueryVariants {
Select { Select {
dataset: Rc<DatasetSpec>, dataset: QueryDataset,
algebra: Rc<GraphPattern>, algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Construct { Construct {
construct: Rc<Vec<TriplePattern>>, construct: Rc<Vec<TriplePattern>>,
dataset: Rc<DatasetSpec>, dataset: QueryDataset,
algebra: Rc<GraphPattern>, algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Describe { Describe {
dataset: Rc<DatasetSpec>, dataset: QueryDataset,
algebra: Rc<GraphPattern>, algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Ask { Ask {
dataset: Rc<DatasetSpec>, dataset: QueryDataset,
algebra: Rc<GraphPattern>, algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
}, },
@ -1410,11 +1599,11 @@ impl fmt::Display for QueryVariants {
} }
write!( write!(
f, f,
"}} {} WHERE {{ {} }}", "}}{} WHERE {{ {} }}",
dataset, dataset,
SparqlGraphRootPattern { SparqlGraphRootPattern {
algebra, algebra,
dataset: &EMPTY_DATASET dataset: &QueryDataset::default()
} }
) )
} }
@ -1428,11 +1617,11 @@ impl fmt::Display for QueryVariants {
} }
write!( write!(
f, f,
"DESCRIBE * {} WHERE {{ {} }}", "DESCRIBE *{} WHERE {{ {} }}",
dataset, dataset,
SparqlGraphRootPattern { SparqlGraphRootPattern {
algebra, algebra,
dataset: &EMPTY_DATASET dataset: &QueryDataset::default()
} }
) )
} }
@ -1446,11 +1635,11 @@ impl fmt::Display for QueryVariants {
} }
write!( write!(
f, f,
"ASK {} WHERE {{ {} }}", "ASK{} WHERE {{ {} }}",
dataset, dataset,
SparqlGraphRootPattern { SparqlGraphRootPattern {
algebra, algebra,
dataset: &EMPTY_DATASET dataset: &QueryDataset::default()
} }
) )
} }
@ -1469,7 +1658,7 @@ pub enum GraphUpdateOperation {
DeleteInsert { DeleteInsert {
delete: Vec<QuadPattern>, delete: Vec<QuadPattern>,
insert: Vec<QuadPattern>, insert: Vec<QuadPattern>,
using: DatasetSpec, using: QueryDataset,
algebra: GraphPattern, algebra: GraphPattern,
}, },
/// [load](https://www.w3.org/TR/sparql11-update/#def_loadoperation) /// [load](https://www.w3.org/TR/sparql11-update/#def_loadoperation)
@ -1523,18 +1712,24 @@ impl fmt::Display for GraphUpdateOperation {
} }
writeln!(f, "}}")?; writeln!(f, "}}")?;
} }
for g in &using.default { if let Some(using_default) = using.default_graph_graphs() {
writeln!(f, "USING {}", g)?; for g in using_default {
if !g.is_default_graph() {
writeln!(f, "USING {}", g)?;
}
}
} }
for g in &using.named { if let Some(using_named) = using.available_named_graphs() {
writeln!(f, "USING NAMED {}", g)?; for g in using_named {
writeln!(f, "USING NAMED {}", g)?;
}
} }
write!( write!(
f, f,
"WHERE {{ {} }}", "WHERE {{ {} }}",
SparqlGraphRootPattern { SparqlGraphRootPattern {
algebra, algebra,
dataset: &DatasetSpec::default() dataset: &QueryDataset::default()
} }
) )
} }

@ -1,5 +1,4 @@
use crate::model::{GraphName, NamedOrBlankNode}; use crate::sparql::algebra::QueryDataset;
use crate::sparql::algebra::DatasetSpec;
use crate::sparql::EvaluationError; use crate::sparql::EvaluationError;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup,
@ -12,61 +11,45 @@ use std::iter::empty;
pub(crate) struct DatasetView<S: ReadableEncodedStore> { pub(crate) struct DatasetView<S: ReadableEncodedStore> {
store: S, store: S,
extra: RefCell<Rodeo>, extra: RefCell<Rodeo>,
default_graph_as_union: bool, dataset: EncodedDatasetSpec<S::StrId>,
dataset: Option<EncodedDatasetSpec<S::StrId>>,
} }
impl<S: ReadableEncodedStore> DatasetView<S> { impl<S: ReadableEncodedStore> DatasetView<S> {
pub fn new( pub fn new(store: S, dataset: &QueryDataset) -> Result<Self, EvaluationError> {
store: S, let dataset = EncodedDatasetSpec {
default_graph_as_union: bool, default: dataset
default_graphs: &[GraphName], .default_graph_graphs()
named_graphs: &[NamedOrBlankNode], .map(|graphs| {
dataset: &DatasetSpec, graphs
) -> Result<Self, EvaluationError> { .iter()
let dataset = if !default_graphs.is_empty() || !named_graphs.is_empty() { .flat_map(|g| store.get_encoded_graph_name(g.as_ref()).transpose())
Some(EncodedDatasetSpec { .collect::<Result<Vec<_>, _>>()
default: default_graphs })
.iter() .transpose()
.flat_map(|g| store.get_encoded_graph_name(g.as_ref()).transpose()) .map_err(|e| e.into())?,
.collect::<Result<Vec<_>, _>>() named: dataset
.map_err(|e| e.into())?, .available_named_graphs()
named: named_graphs .map(|graphs| {
.iter() graphs
.flat_map(|g| { .iter()
store .flat_map(|g| {
.get_encoded_named_or_blank_node(g.as_ref()) store
.transpose() .get_encoded_named_or_blank_node(g.as_ref())
}) .transpose()
.collect::<Result<Vec<_>, _>>() })
.map_err(|e| e.into())?, .collect::<Result<Vec<_>, _>>()
}) })
} else if dataset.is_empty() { .transpose()
None .map_err(|e| e.into())?,
} else {
Some(EncodedDatasetSpec {
default: dataset
.default
.iter()
.flat_map(|g| store.get_encoded_named_node(g.as_ref()).transpose())
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())?,
named: dataset
.named
.iter()
.flat_map(|g| store.get_encoded_named_node(g.as_ref()).transpose())
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())?,
})
}; };
Ok(Self { Ok(Self {
store, store,
extra: RefCell::new(Rodeo::default()), extra: RefCell::new(Rodeo::default()),
default_graph_as_union,
dataset, dataset,
}) })
} }
#[allow(clippy::needless_collect)]
fn encoded_quads_for_pattern_in_dataset( fn encoded_quads_for_pattern_in_dataset(
&self, &self,
subject: Option<EncodedTerm<S::StrId>>, subject: Option<EncodedTerm<S::StrId>>,
@ -75,56 +58,85 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
graph_name: Option<EncodedTerm<S::StrId>>, graph_name: Option<EncodedTerm<S::StrId>>,
) -> Box<dyn Iterator<Item = Result<EncodedQuad<DatasetStrId<S::StrId>>, EvaluationError>>> ) -> Box<dyn Iterator<Item = Result<EncodedQuad<DatasetStrId<S::StrId>>, EvaluationError>>>
{ {
if let Some(dataset) = &self.dataset { if let Some(graph_name) = graph_name {
if let Some(graph_name) = graph_name { if graph_name.is_default_graph() {
if graph_name == EncodedTerm::DefaultGraph { if let Some(default_graph_graphs) = &self.dataset.default {
let iters = dataset if default_graph_graphs.len() == 1 {
.default // Single graph optimization
.iter() Box::new(
.map(|graph_name| { map_iter(self.store.encoded_quads_for_pattern(
self.store.encoded_quads_for_pattern(
subject, subject,
predicate, predicate,
object, object,
Some(*graph_name), Some(default_graph_graphs[0]),
) ))
}) .map(|quad| {
.collect::<Vec<_>>(); let quad = quad?;
Box::new(map_iter(iters.into_iter().flatten()).map(|quad| { Ok(EncodedQuad::new(
let quad = quad?; quad.subject,
Ok(EncodedQuad::new( quad.predicate,
quad.subject, quad.object,
quad.predicate, EncodedTerm::DefaultGraph,
quad.object, ))
EncodedTerm::DefaultGraph, }),
)) )
})) } else {
} else if dataset.named.contains(&graph_name) { let iters = default_graph_graphs
Box::new(map_iter(self.store.encoded_quads_for_pattern( .iter()
subject, .map(|graph_name| {
predicate, self.store.encoded_quads_for_pattern(
object, subject,
Some(graph_name), predicate,
))) object,
Some(*graph_name),
)
})
.collect::<Vec<_>>();
Box::new(map_iter(iters.into_iter().flatten()).map(|quad| {
let quad = quad?;
Ok(EncodedQuad::new(
quad.subject,
quad.predicate,
quad.object,
EncodedTerm::DefaultGraph,
))
}))
}
} else { } else {
Box::new(empty()) Box::new(map_iter(
self.store
.encoded_quads_for_pattern(subject, predicate, object, None),
))
} }
} else if self
.dataset
.named
.as_ref()
.map_or(true, |d| d.contains(&graph_name))
{
Box::new(map_iter(self.store.encoded_quads_for_pattern(
subject,
predicate,
object,
Some(graph_name),
)))
} else { } else {
let iters = dataset Box::new(empty())
.named
.iter()
.map(|graph_name| {
self.store.encoded_quads_for_pattern(
subject,
predicate,
object,
Some(*graph_name),
)
})
.collect::<Vec<_>>();
Box::new(map_iter(iters.into_iter().flatten()))
} }
} else if graph_name == None { } else if let Some(named_graphs) = &self.dataset.named {
let iters = named_graphs
.iter()
.map(|graph_name| {
self.store.encoded_quads_for_pattern(
subject,
predicate,
object,
Some(*graph_name),
)
})
.collect::<Vec<_>>();
Box::new(map_iter(iters.into_iter().flatten()))
} else {
Box::new( Box::new(
map_iter( map_iter(
self.store self.store
@ -135,10 +147,6 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
Ok(quad) => quad.graph_name != EncodedTerm::DefaultGraph, Ok(quad) => quad.graph_name != EncodedTerm::DefaultGraph,
}), }),
) )
} else {
Box::new(map_iter(self.store.encoded_quads_for_pattern(
subject, predicate, object, graph_name,
)))
} }
} }
} }
@ -186,21 +194,7 @@ impl<S: ReadableEncodedStore> ReadableEncodedStore for DatasetView<S> {
if let Some((subject, predicate, object, graph_name)) = if let Some((subject, predicate, object, graph_name)) =
try_map_quad_pattern(subject, predicate, object, graph_name) try_map_quad_pattern(subject, predicate, object, graph_name)
{ {
if graph_name == Some(EncodedTerm::DefaultGraph) && self.default_graph_as_union { self.encoded_quads_for_pattern_in_dataset(subject, predicate, object, graph_name)
Box::new(
self.encoded_quads_for_pattern_in_dataset(
subject,
predicate,
object,
Some(EncodedTerm::DefaultGraph),
)
.chain(
self.encoded_quads_for_pattern_in_dataset(subject, predicate, object, None),
),
)
} else {
self.encoded_quads_for_pattern_in_dataset(subject, predicate, object, graph_name)
}
} else { } else {
Box::new(empty()) Box::new(empty())
} }
@ -278,6 +272,6 @@ pub enum DatasetStrId<I: StrId> {
impl<I: StrId> StrId for DatasetStrId<I> {} impl<I: StrId> StrId for DatasetStrId<I> {}
struct EncodedDatasetSpec<I: StrId> { struct EncodedDatasetSpec<I: StrId> {
default: Vec<EncodedTerm<I>>, default: Option<Vec<EncodedTerm<I>>>,
named: Vec<EncodedTerm<I>>, named: Option<Vec<EncodedTerm<I>>>,
} }

@ -2,10 +2,9 @@ use crate::model::vocab::{rdf, xsd};
use crate::model::xsd::*; use crate::model::xsd::*;
use crate::model::Triple; use crate::model::Triple;
use crate::model::{BlankNode, LiteralRef, NamedNodeRef}; use crate::model::{BlankNode, LiteralRef, NamedNodeRef};
use crate::sparql::algebra::{DatasetSpec, GraphPattern, QueryVariants}; use crate::sparql::algebra::{GraphPattern, Query, QueryDataset, QueryVariants};
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::model::*; use crate::sparql::model::*;
use crate::sparql::parser::Query;
use crate::sparql::plan::*; use crate::sparql::plan::*;
use crate::sparql::service::ServiceHandler; use crate::sparql::service::ServiceHandler;
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
@ -527,7 +526,7 @@ where
.ok_or_else(|| EvaluationError::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: QueryDataset::default(),
algebra: graph_pattern, algebra: graph_pattern,
base_iri: self.base_iri.clone(), base_iri: self.base_iri.clone(),
}), }),

@ -17,8 +17,8 @@ mod service;
mod update; mod update;
mod xml_results; mod xml_results;
use crate::model::{GraphName, NamedOrBlankNode};
use crate::sparql::algebra::QueryVariants; use crate::sparql::algebra::QueryVariants;
pub use crate::sparql::algebra::{Query, Update};
use crate::sparql::dataset::DatasetView; use crate::sparql::dataset::DatasetView;
pub use crate::sparql::error::EvaluationError; pub use crate::sparql::error::EvaluationError;
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
@ -29,7 +29,6 @@ pub use crate::sparql::model::QuerySolutionIter;
pub use crate::sparql::model::QueryTripleIter; pub use crate::sparql::model::QueryTripleIter;
pub use crate::sparql::model::{Variable, VariableNameParseError}; pub use crate::sparql::model::{Variable, VariableNameParseError};
pub use crate::sparql::parser::ParseError; pub use crate::sparql::parser::ParseError;
pub use crate::sparql::parser::{Query, Update};
use crate::sparql::plan::{PlanNode, TripleTemplate}; use crate::sparql::plan::{PlanNode, TripleTemplate};
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
pub use crate::sparql::service::ServiceHandler; pub use crate::sparql::service::ServiceHandler;
@ -80,13 +79,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri, base_iri,
dataset, dataset,
} => { } => {
let dataset = Rc::new(DatasetView::new( let dataset = Rc::new(DatasetView::new(store, &dataset)?);
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?; let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan: Rc::new(plan), plan: Rc::new(plan),
@ -99,13 +92,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri, base_iri,
dataset, dataset,
} => { } => {
let dataset = Rc::new(DatasetView::new( let dataset = Rc::new(DatasetView::new(store, &dataset)?);
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?; let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Ask { SimplePreparedQueryAction::Ask {
plan: Rc::new(plan), plan: Rc::new(plan),
@ -118,13 +105,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri, base_iri,
dataset, dataset,
} => { } => {
let dataset = Rc::new(DatasetView::new( let dataset = Rc::new(DatasetView::new(store, &dataset)?);
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?; let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Construct { SimplePreparedQueryAction::Construct {
plan: Rc::new(plan), plan: Rc::new(plan),
@ -141,13 +122,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri, base_iri,
dataset, dataset,
} => { } => {
let dataset = Rc::new(DatasetView::new( let dataset = Rc::new(DatasetView::new(store, &dataset)?);
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?; let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Describe { SimplePreparedQueryAction::Describe {
plan: Rc::new(plan), plan: Rc::new(plan),
@ -181,9 +156,6 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
/// Options for SPARQL query evaluation /// Options for SPARQL query evaluation
#[derive(Clone)] #[derive(Clone)]
pub struct QueryOptions { pub struct QueryOptions {
pub(crate) default_graph_as_union: bool,
pub(crate) default_graphs: Vec<GraphName>,
pub(crate) named_graphs: Vec<NamedOrBlankNode>,
pub(crate) service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>, pub(crate) service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
} }
@ -191,38 +163,12 @@ impl Default for QueryOptions {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
Self { Self {
default_graph_as_union: false,
default_graphs: Vec::new(),
named_graphs: Vec::new(),
service_handler: Rc::new(EmptyServiceHandler), service_handler: Rc::new(EmptyServiceHandler),
} }
} }
} }
impl QueryOptions { impl QueryOptions {
/// Consider the union of all graphs in the store as the default graph
#[inline]
pub fn with_default_graph_as_union(mut self) -> Self {
self.default_graph_as_union = true;
self
}
/// Adds a graph to the set of graphs considered by the SPARQL query as the queried dataset default graph.
/// It overrides the `FROM` and `FROM NAMED` elements of the evaluated query.
#[inline]
pub fn with_default_graph(mut self, default_graph_name: impl Into<GraphName>) -> Self {
self.default_graphs.push(default_graph_name.into());
self
}
/// Adds a named graph to the set of graphs considered by the SPARQL query as the queried dataset named graphs.
/// It overrides the `FROM` and `FROM NAMED` elements of the evaluated query.
#[inline]
pub fn with_named_graph(mut self, named_graph_name: impl Into<NamedOrBlankNode>) -> Self {
self.named_graphs.push(named_graph_name.into());
self
}
/// Use a simple HTTP 1.1 client built into Oxigraph to execute [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICE calls. /// Use a simple HTTP 1.1 client built into Oxigraph to execute [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICE calls.
/// ///
/// Requires the `"http_client"` optional feature. /// Requires the `"http_client"` optional feature.

@ -8,167 +8,67 @@ use peg::parser;
use peg::str::LineCol; use peg::str::LineCol;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::error::Error; use std::error::Error;
use std::rc::Rc; use std::rc::Rc;
use std::str::Chars; use std::str::Chars;
use std::str::FromStr; use std::str::FromStr;
use std::{char, fmt}; use std::{char, fmt};
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query
/// pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result<Query, ParseError> {
/// ``` let mut state = ParserState {
/// use oxigraph::sparql::Query; base_iri: if let Some(base_iri) = base_iri {
/// Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| {
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let query = Query::parse(query_str, None)?;
///
/// assert_eq!(query.to_string(), query_str);
/// # Result::Ok::<_, oxigraph::sparql::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(pub(crate) QueryVariants);
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl Query {
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
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| {
ParseError {
inner: ParseErrorKind::InvalidBaseIri(e),
}
})?))
} else {
None
},
namespaces: HashMap::default(),
used_bnodes: HashSet::default(),
currently_used_bnodes: HashSet::default(),
aggregations: Vec::default(),
};
Ok(Self(
parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| {
ParseError { ParseError {
inner: ParseErrorKind::Parser(e), inner: ParseErrorKind::InvalidBaseIri(e),
} }
})?, })?))
)) } else {
} None
} },
namespaces: HashMap::default(),
impl FromStr for Query { used_bnodes: HashSet::default(),
type Err = ParseError; currently_used_bnodes: HashSet::default(),
aggregations: Vec::default(),
fn from_str(query: &str) -> Result<Self, ParseError> { };
Self::parse(query, None)
} Ok(Query(
} parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| {
ParseError {
impl<'a> TryFrom<&'a str> for Query {
type Error = ParseError;
fn try_from(query: &str) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
impl<'a> TryFrom<&'a String> for Query {
type Error = ParseError;
fn try_from(query: &String) -> Result<Self, ParseError> {
Self::from_str(query)
}
}
/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/)
///
/// ```
/// use oxigraph::sparql::Update;
///
/// let update_str = "CLEAR ALL ;";
/// let update = Update::parse(update_str, None)?;
///
/// assert_eq!(update.to_string().trim(), update_str);
/// # Result::Ok::<_, oxigraph::sparql::ParseError>(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Update {
pub(crate) base_iri: Option<Rc<Iri<String>>>,
pub(crate) operations: Vec<GraphUpdateOperation>,
}
impl fmt::Display for Update {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(base_iri) = &self.base_iri {
writeln!(f, "BASE <{}>", base_iri)?;
}
for update in &self.operations {
writeln!(f, "{} ;", update)?;
}
Ok(())
}
}
impl Update {
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query
pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, ParseError> {
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| {
ParseError {
inner: ParseErrorKind::InvalidBaseIri(e),
}
})?))
} else {
None
},
namespaces: HashMap::default(),
used_bnodes: HashSet::default(),
currently_used_bnodes: HashSet::default(),
aggregations: Vec::default(),
};
let operations = parser::UpdateInit(&unescape_unicode_codepoints(update), &mut state)
.map_err(|e| ParseError {
inner: ParseErrorKind::Parser(e), inner: ParseErrorKind::Parser(e),
})?; }
Ok(Self { })?,
operations, ))
base_iri: state.base_iri,
})
}
}
impl FromStr for Update {
type Err = ParseError;
fn from_str(update: &str) -> Result<Self, ParseError> {
Self::parse(update, None)
}
}
impl<'a> TryFrom<&'a str> for Update {
type Error = ParseError;
fn try_from(update: &str) -> Result<Self, ParseError> {
Self::from_str(update)
}
} }
impl<'a> TryFrom<&'a String> for Update { /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query
type Error = ParseError; pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, ParseError> {
let mut state = ParserState {
fn try_from(update: &String) -> Result<Self, ParseError> { base_iri: if let Some(base_iri) = base_iri {
Self::from_str(update) Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| {
} ParseError {
inner: ParseErrorKind::InvalidBaseIri(e),
}
})?))
} else {
None
},
namespaces: HashMap::default(),
used_bnodes: HashSet::default(),
currently_used_bnodes: HashSet::default(),
aggregations: Vec::default(),
};
let operations =
parser::UpdateInit(&unescape_unicode_codepoints(update), &mut state).map_err(|e| {
ParseError {
inner: ParseErrorKind::Parser(e),
}
})?;
Ok(Update {
operations,
base_iri: state.base_iri,
})
} }
/// Error returned during SPARQL parsing. /// Error returned during SPARQL parsing.
@ -488,7 +388,7 @@ fn copy_graph(
Variable::new_unchecked("o"), Variable::new_unchecked("o"),
to.into(), to.into(),
)], )],
using: DatasetSpec::default(), using: QueryDataset::default(),
algebra: match from { algebra: match from {
NamedOrDefaultGraphTarget::NamedNode(from) => { NamedOrDefaultGraphTarget::NamedNode(from) => {
GraphPattern::Graph(from.into(), Box::new(bgp)) GraphPattern::Graph(from.into(), Box::new(bgp))
@ -771,7 +671,7 @@ parser! {
//[7] //[7]
rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Select { QueryVariants::Select {
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select(s, w, g, h, o, l, v, state)), algebra: Rc::new(build_select(s, w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
@ -808,7 +708,7 @@ parser! {
i("CONSTRUCT") _ c:ConstructTemplate() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("CONSTRUCT") _ c:ConstructTemplate() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Construct { QueryVariants::Construct {
construct: Rc::new(c), construct: Rc::new(c),
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)), algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
@ -816,7 +716,7 @@ parser! {
i("CONSTRUCT") _ d:DatasetClauses() _ i("WHERE") _ "{" _ c:ConstructQuery_optional_triple_template() _ "}" _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("CONSTRUCT") _ d:DatasetClauses() _ i("WHERE") _ "{" _ c:ConstructQuery_optional_triple_template() _ "}" _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Construct { QueryVariants::Construct {
construct: Rc::new(c.clone()), construct: Rc::new(c.clone()),
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select( algebra: Rc::new(build_select(
Selection::default(), Selection::default(),
GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()), GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()),
@ -832,14 +732,14 @@ parser! {
rule DescribeQuery() -> QueryVariants = rule DescribeQuery() -> QueryVariants =
i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe { QueryVariants::Describe {
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)), algebra: Rc::new(build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} / } /
i("DESCRIBE") _ p:DescribeQuery_item()+ _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("DESCRIBE") _ p:DescribeQuery_item()+ _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe { QueryVariants::Describe {
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select(Selection { algebra: Rc::new(build_select(Selection {
option: SelectionOption::Default, option: SelectionOption::Default,
variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri { variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri {
@ -855,27 +755,41 @@ parser! {
//[12] //[12]
rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Ask { QueryVariants::Ask {
dataset: Rc::new(d), dataset: d,
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)), algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }
//[13] //[13]
rule DatasetClause() -> DatasetSpec = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d } rule DatasetClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d }
rule DatasetClauses() -> DatasetSpec = d:DatasetClauses_item()* { rule DatasetClauses() -> QueryDataset = d:DatasetClause() ** (_) {
d.into_iter().fold(DatasetSpec::default(), |mut a, b| a + b) let mut dataset = QueryDataset::default();
if !d.is_empty() {
let mut default = Vec::new();
let mut named = Vec::new();
for (d, n) in d {
if let Some(d) = d {
default.push(d);
}
if let Some(n) = n {
named.push(n);
}
}
dataset.set_default_graph(default);
dataset.set_available_named_graphs(named);
}
dataset
} }
rule DatasetClauses_item() -> DatasetSpec = d:DatasetClause() _ { d }
//[14] //[14]
rule DefaultGraphClause() -> DatasetSpec = s:SourceSelector() { rule DefaultGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = s:SourceSelector() {
DatasetSpec::new_with_default(s) (Some(s.into()), None)
} }
//[15] //[15]
rule NamedGraphClause() -> DatasetSpec = i("NAMED") _ s:SourceSelector() { rule NamedGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ s:SourceSelector() {
DatasetSpec::new_with_named(s) (None, Some(s.into()))
} }
//[16] //[16]
@ -1034,13 +948,13 @@ parser! {
vec![GraphUpdateOperation::DeleteInsert { vec![GraphUpdateOperation::DeleteInsert {
delete: d, delete: d,
insert: Vec::new(), insert: Vec::new(),
using: DatasetSpec::default(), using: QueryDataset::default(),
algebra algebra
}] }]
} }
//[41] //[41]
rule Modify() -> Vec<GraphUpdateOperation> = with:Modify_with()? _ Modify_clear() c:Modify_clauses() _ using:(UsingClause() ** (_)) _ i("WHERE") _ algebra:GroupGraphPattern() { rule Modify() -> Vec<GraphUpdateOperation> = with:Modify_with()? _ Modify_clear() c:Modify_clauses() _ u:(UsingClause() ** (_)) _ i("WHERE") _ algebra:GroupGraphPattern() {
let (delete, insert) = c; let (delete, insert) = c;
let mut delete = delete.unwrap_or_else(Vec::new); let mut delete = delete.unwrap_or_else(Vec::new);
let mut insert = insert.unwrap_or_else(Vec::new); let mut insert = insert.unwrap_or_else(Vec::new);
@ -1061,10 +975,26 @@ parser! {
algebra = GraphPattern::Graph(with.into(), Box::new(algebra)); algebra = GraphPattern::Graph(with.into(), Box::new(algebra));
} }
let mut using = QueryDataset::default();
if !u.is_empty() {
let mut using_default = Vec::new();
let mut using_named = Vec::new();
for (d, n) in u {
if let Some(d) = d {
using_default.push(d)
}
if let Some(n) = n {
using_named.push(n)
}
}
using.set_default_graph(using_default);
using.set_available_named_graphs(using_named);
}
vec![GraphUpdateOperation::DeleteInsert { vec![GraphUpdateOperation::DeleteInsert {
delete, delete,
insert, insert,
using: using.into_iter().fold(DatasetSpec::default(), |mut a, b| a + b), using,
algebra algebra
}] }]
} }
@ -1086,12 +1016,12 @@ parser! {
rule InsertClause() -> Vec<QuadPattern> = i("INSERT") _ q:QuadPattern() { q } rule InsertClause() -> Vec<QuadPattern> = i("INSERT") _ q:QuadPattern() { q }
//[44] //[44]
rule UsingClause() -> DatasetSpec = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d } rule UsingClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d }
rule UsingClause_default() -> DatasetSpec = i:iri() { rule UsingClause_default() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i:iri() {
DatasetSpec::new_with_default(i) (Some(i.into()), None)
} }
rule UsingClause_named() -> DatasetSpec = i("NAMED") _ i:iri() { rule UsingClause_named() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ i:iri() {
DatasetSpec::new_with_named(i) (None, Some(i.into()))
} }
//[45] //[45]

@ -1,9 +1,9 @@
use crate::error::{invalid_data_error, invalid_input_error}; use crate::error::{invalid_data_error, invalid_input_error};
use crate::model::NamedNode; use crate::model::NamedNode;
use crate::sparql::algebra::Query;
use crate::sparql::error::EvaluationError; use crate::sparql::error::EvaluationError;
use crate::sparql::http::Client; use crate::sparql::http::Client;
use crate::sparql::model::QueryResults; use crate::sparql::model::QueryResults;
use crate::sparql::parser::Query;
use crate::sparql::QueryResultsFormat; use crate::sparql::QueryResultsFormat;
use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; use http::header::{ACCEPT, CONTENT_TYPE, USER_AGENT};
use http::{Method, Request, StatusCode}; use http::{Method, Request, StatusCode};

@ -2,8 +2,8 @@ use crate::error::{invalid_data_error, invalid_input_error};
use crate::io::GraphFormat; use crate::io::GraphFormat;
use crate::model::{BlankNode, GraphNameRef, NamedNode, Term}; use crate::model::{BlankNode, GraphNameRef, NamedNode, Term};
use crate::sparql::algebra::{ use crate::sparql::algebra::{
DatasetSpec, GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern, GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern,
TermOrVariable, QueryDataset, TermOrVariable,
}; };
use crate::sparql::dataset::{DatasetStrId, DatasetView}; use crate::sparql::dataset::{DatasetStrId, DatasetView};
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
@ -111,10 +111,10 @@ where
&mut self, &mut self,
delete: &[QuadPattern], delete: &[QuadPattern],
insert: &[QuadPattern], insert: &[QuadPattern],
using: &DatasetSpec, using: &QueryDataset,
algebra: &GraphPattern, algebra: &GraphPattern,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
let dataset = Rc::new(DatasetView::new(self.read.clone(), false, &[], &[], using)?); let dataset = Rc::new(DatasetView::new(self.read.clone(), using)?);
let (plan, variables) = PlanBuilder::build(dataset.as_ref(), algebra)?; let (plan, variables) = PlanBuilder::build(dataset.as_ref(), algebra)?;
let evaluator = SimpleEvaluator::<DatasetView<R>>::new( let evaluator = SimpleEvaluator::<DatasetView<R>>::new(
dataset.clone(), dataset.clone(),

@ -3,6 +3,7 @@ use crate::model::*;
use crate::sparql::*; use crate::sparql::*;
use crate::store_utils::*; use crate::store_utils::*;
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::sparql::QueryOptions;
use oxigraph::store::memory::*; use oxigraph::store::memory::*;
use pyo3::basic::CompareOp; use pyo3::basic::CompareOp;
use pyo3::exceptions::{PyNotImplementedError, PyValueError}; use pyo3::exceptions::{PyNotImplementedError, PyValueError};
@ -157,10 +158,15 @@ impl PyMemoryStore {
named_graphs: Option<&PyAny>, named_graphs: Option<&PyAny>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<PyObject> { ) -> PyResult<PyObject> {
let options = build_query_options(use_default_graph_as_union, default_graph, named_graphs)?; let query = parse_query(
query,
use_default_graph_as_union,
default_graph,
named_graphs,
)?;
let results = self let results = self
.inner .inner
.query(query, options) .query(query, QueryOptions::default())
.map_err(map_evaluation_error)?; .map_err(map_evaluation_error)?;
query_results_to_python(py, results) query_results_to_python(py, results)
} }

@ -3,6 +3,7 @@ use crate::model::*;
use crate::sparql::*; use crate::sparql::*;
use crate::store_utils::*; use crate::store_utils::*;
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::sparql::QueryOptions;
use oxigraph::store::sled::*; use oxigraph::store::sled::*;
use pyo3::exceptions::PyValueError; use pyo3::exceptions::PyValueError;
use pyo3::prelude::{ use pyo3::prelude::{
@ -171,10 +172,15 @@ impl PySledStore {
named_graphs: Option<&PyAny>, named_graphs: Option<&PyAny>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<PyObject> { ) -> PyResult<PyObject> {
let options = build_query_options(use_default_graph_as_union, default_graph, named_graphs)?; let query = parse_query(
query,
use_default_graph_as_union,
default_graph,
named_graphs,
)?;
let results = self let results = self
.inner .inner
.query(query, options) .query(query, QueryOptions::default())
.map_err(map_evaluation_error)?; .map_err(map_evaluation_error)?;
query_results_to_python(py, results) query_results_to_python(py, results)
} }

@ -10,47 +10,52 @@ use pyo3::prelude::{
use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol}; use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::vec::IntoIter; use std::vec::IntoIter;
pub fn build_query_options( pub fn parse_query(
query: &str,
use_default_graph_as_union: bool, use_default_graph_as_union: bool,
default_graph: Option<&PyAny>, default_graph: Option<&PyAny>,
named_graphs: Option<&PyAny>, named_graphs: Option<&PyAny>,
) -> PyResult<QueryOptions> { ) -> PyResult<Query> {
let mut options = QueryOptions::default(); let mut query = Query::parse(query, None).map_err(|e| map_evaluation_error(e.into()))?;
if use_default_graph_as_union && default_graph.is_some() {
return Err(PyValueError::new_err(
"The query() method use_default_graph_as_union and default_graph arguments should not be set at the same time",
));
}
if use_default_graph_as_union { if use_default_graph_as_union {
options = options.with_default_graph_as_union(); query.dataset_mut().set_default_graph_as_union();
} }
if let Some(default_graph) = default_graph { if let Some(default_graph) = default_graph {
if let Ok(default_graphs) = default_graph.iter() { if let Ok(default_graphs) = default_graph.iter() {
if default_graph.is_empty()? { query.dataset_mut().set_default_graph(
return Err(PyValueError::new_err( default_graphs
"The query() method default_graph argument cannot be empty list", .map(|graph| Ok(graph?.extract::<PyGraphName>()?.into()))
)); .collect::<PyResult<_>>()?,
} )
for default_graph in default_graphs {
options = options.with_default_graph(default_graph?.extract::<PyGraphName>()?);
}
} else if let Ok(default_graph) = default_graph.extract::<PyGraphName>() { } else if let Ok(default_graph) = default_graph.extract::<PyGraphName>() {
options = options.with_default_graph(default_graph); query
.dataset_mut()
.set_default_graph(vec![default_graph.into()]);
} else { } else {
return Err(PyValueError::new_err( return Err(PyValueError::new_err(
format!("The query() method default_graph argument should be a NamedNode, a BlankNode, the DefaultGraph or a not empty list of them. {} found", default_graph.get_type() format!("The query() method default_graph argument should be a NamedNode, a BlankNode, the DefaultGraph or a not empty list of them. {} found", default_graph.get_type()
))); )));
} }
} }
if let Some(named_graphs) = named_graphs { if let Some(named_graphs) = named_graphs {
if named_graphs.is_empty()? { query.dataset_mut().set_available_named_graphs(
return Err(PyValueError::new_err( named_graphs
"The query() method named_graphs argument cannot be empty", .iter()?
)); .map(|graph| Ok(graph?.extract::<PyNamedOrBlankNode>()?.into()))
} .collect::<PyResult<_>>()?,
for named_graph in named_graphs.iter()? { )
options = options.with_named_graph(named_graph?.extract::<PyNamedOrBlankNode>()?);
}
} }
Ok(options) Ok(query)
} }
pub fn query_results_to_python(py: Python<'_>, results: QueryResults) -> PyResult<PyObject> { pub fn query_results_to_python(py: Python<'_>, results: QueryResults) -> PyResult<PyObject> {

@ -17,7 +17,7 @@ use async_std::prelude::*;
use async_std::task::{block_on, spawn, spawn_blocking}; use async_std::task::{block_on, spawn, spawn_blocking};
use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode}; use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode};
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::{GraphName, NamedNode}; use oxigraph::model::{GraphName, NamedNode, NamedOrBlankNode};
use oxigraph::sparql::{Query, QueryOptions, QueryResults, QueryResultsFormat, Update}; use oxigraph::sparql::{Query, QueryOptions, QueryResults, QueryResultsFormat, Update};
use std::io::BufReader; use std::io::BufReader;
use std::str::FromStr; use std::str::FromStr;
@ -101,9 +101,7 @@ async fn handle_request(request: Request, store: Store) -> Result<Response> {
{ {
Ok(()) => Response::new(StatusCode::NoContent), Ok(()) => Response::new(StatusCode::NoContent),
Err(error) => { Err(error) => {
let mut error = Error::from(error); return Err(bad_request(error));
error.set_status(StatusCode::BadRequest);
return Err(error);
} }
} }
} else { } else {
@ -229,29 +227,26 @@ async fn evaluate_sparql_query(
request: Request, request: Request,
) -> Result<Response> { ) -> Result<Response> {
spawn_blocking(move || { spawn_blocking(move || {
let query = Query::parse(&query, None).map_err(|e| { let mut query = Query::parse(&query, None).map_err(bad_request)?;
let mut e = Error::from(e); let default_graph_uris = default_graph_uris
e.set_status(StatusCode::BadRequest); .into_iter()
e .map(|e| Ok(NamedNode::new(e)?.into()))
})?; .collect::<Result<Vec<GraphName>>>()
.map_err(bad_request)?;
let mut options = QueryOptions::default().with_simple_service_handler(); let named_graph_uris = named_graph_uris
for default_graph_uri in default_graph_uris { .into_iter()
options = .map(|e| Ok(NamedNode::new(e)?.into()))
options.with_default_graph(NamedNode::new(default_graph_uri).map_err(|e| { .collect::<Result<Vec<NamedOrBlankNode>>>()
let mut e = Error::from(e); .map_err(bad_request)?;
e.set_status(StatusCode::BadRequest);
e if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
})?) query.dataset_mut().set_default_graph(default_graph_uris);
} query
for named_graph_uri in named_graph_uris { .dataset_mut()
options = options.with_named_graph(NamedNode::new(named_graph_uri).map_err(|e| { .set_available_named_graphs(named_graph_uris);
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
e
})?)
} }
let options = QueryOptions::default().with_simple_service_handler();
let results = store.query(query, options)?; let results = store.query(query, options)?;
//TODO: stream //TODO: stream
if let QueryResults::Graph(_) = results { if let QueryResults::Graph(_) = results {
@ -419,6 +414,12 @@ fn content_negotiation<F>(
.ok_or_else(|| Error::from_str(StatusCode::InternalServerError, "Unknown mime type")) .ok_or_else(|| Error::from_str(StatusCode::InternalServerError, "Unknown mime type"))
} }
fn bad_request(e: impl Into<Error>) -> Error {
let mut e = e.into();
e.set_status(StatusCode::BadRequest);
e
}
struct SyncAsyncReader<R: Unpin> { struct SyncAsyncReader<R: Unpin> {
inner: R, inner: R,
} }

@ -175,14 +175,15 @@ async fn evaluate_sparql_query(
) -> Result<Response> { ) -> Result<Response> {
spawn_blocking(move || { spawn_blocking(move || {
//TODO: stream //TODO: stream
let query = Query::parse(&query, None).map_err(|e| { let mut query = Query::parse(&query, None).map_err(|e| {
let mut e = Error::from(e); let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest); e.set_status(StatusCode::BadRequest);
e e
})?; })?;
let options = QueryOptions::default() if query.dataset().is_default_dataset() {
.with_default_graph_as_union() query.dataset_mut().set_default_graph_as_union();
.with_simple_service_handler(); }
let options = QueryOptions::default().with_simple_service_handler();
let results = store.query(query, options)?; let results = store.query(query, options)?;
if let QueryResults::Graph(_) = results { if let QueryResults::Graph(_) = results {
let format = content_negotiation( let format = content_negotiation(

Loading…
Cancel
Save