Provides an API to edit the SPARQL query dataset

pull/69/head
Tpt 4 years ago
parent be275d71d7
commit 7a110902d2
  1. 305
      lib/src/sparql/algebra.rs
  2. 120
      lib/src/sparql/dataset.rs
  3. 5
      lib/src/sparql/eval.rs
  4. 64
      lib/src/sparql/mod.rs
  5. 192
      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. 49
      python/src/sparql.rs
  11. 49
      server/src/main.rs
  12. 9
      wikibase/src/main.rs

@ -2,12 +2,150 @@
use crate::model::*;
use crate::sparql::model::*;
use crate::sparql::parser::{parse_query, parse_update, ParseError};
use oxiri::Iri;
use rio_api::model as rio;
use std::collections::BTreeSet;
use std::convert::TryFrom;
use std::fmt;
use std::ops::Add;
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)]
pub enum NamedNodeOrVariable {
@ -986,7 +1124,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> {
"{{ {} }}",
SparqlGraphRootPattern {
algebra: p,
dataset: &EMPTY_DATASET
dataset: &QueryDataset::default()
}
),
}
@ -995,7 +1133,7 @@ impl<'a> fmt::Display for SparqlGraphPattern<'a> {
struct SparqlGraphRootPattern<'a> {
algebra: &'a GraphPattern,
dataset: &'a DatasetSpec,
dataset: &'a QueryDataset,
}
impl<'a> fmt::Display for SparqlGraphRootPattern<'a> {
@ -1304,79 +1442,130 @@ impl<'a> fmt::Display for SparqlOrderComparator<'a> {
}
}
#[derive(Eq, PartialEq, Debug, Clone, Hash, Default)]
pub struct DatasetSpec {
pub default: Vec<NamedNode>,
pub named: Vec<NamedNode>,
}
impl DatasetSpec {
pub fn new_with_default(graph: NamedNode) -> Self {
Self {
default: vec![graph],
named: Vec::default(),
}
/// A SPARQL query [dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct QueryDataset {
default: Option<Vec<GraphName>>,
named: Option<Vec<NamedOrBlankNode>>,
}
pub fn new_with_named(graph: NamedNode) -> Self {
impl Default for QueryDataset {
fn default() -> Self {
Self {
default: Vec::default(),
named: vec![graph],
}
}
pub fn is_empty(&self) -> bool {
self.default.is_empty() && self.named.is_empty()
}
}
impl Add for DatasetSpec {
type Output = Self;
fn add(mut self, rhs: Self) -> Self {
self.default.extend_from_slice(&rhs.default);
self.named.extend_from_slice(&rhs.named);
self
}
}
impl fmt::Display for DatasetSpec {
default: Some(vec![GraphName::DefaultGraph]),
named: None,
}
}
}
impl QueryDataset {
/// 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)
///
/// ```
/// use oxigraph::sparql::Query;
///
/// 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());
///
/// # 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 {
for g in &self.default {
//TODO: does not encode everything
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 {
for g in graphs {
write!(f, " FROM NAMED {}", g)?;
}
}
Ok(())
}
}
const EMPTY_DATASET: DatasetSpec = DatasetSpec {
default: Vec::new(),
named: Vec::new(),
};
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum QueryVariants {
Select {
dataset: Rc<DatasetSpec>,
dataset: QueryDataset,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Construct {
construct: Rc<Vec<TriplePattern>>,
dataset: Rc<DatasetSpec>,
dataset: QueryDataset,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Describe {
dataset: Rc<DatasetSpec>,
dataset: QueryDataset,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Ask {
dataset: Rc<DatasetSpec>,
dataset: QueryDataset,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
@ -1414,7 +1603,7 @@ impl fmt::Display for QueryVariants {
dataset,
SparqlGraphRootPattern {
algebra,
dataset: &EMPTY_DATASET
dataset: &QueryDataset::default()
}
)
}
@ -1432,7 +1621,7 @@ impl fmt::Display for QueryVariants {
dataset,
SparqlGraphRootPattern {
algebra,
dataset: &EMPTY_DATASET
dataset: &QueryDataset::default()
}
)
}
@ -1450,7 +1639,7 @@ impl fmt::Display for QueryVariants {
dataset,
SparqlGraphRootPattern {
algebra,
dataset: &EMPTY_DATASET
dataset: &QueryDataset::default()
}
)
}
@ -1469,7 +1658,7 @@ pub enum GraphUpdateOperation {
DeleteInsert {
delete: Vec<QuadPattern>,
insert: Vec<QuadPattern>,
using: DatasetSpec,
using: QueryDataset,
algebra: GraphPattern,
},
/// [load](https://www.w3.org/TR/sparql11-update/#def_loadoperation)
@ -1523,18 +1712,24 @@ impl fmt::Display for GraphUpdateOperation {
}
writeln!(f, "}}")?;
}
for g in &using.default {
if let Some(using_default) = using.default_graph_graphs() {
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() {
for g in using_named {
writeln!(f, "USING NAMED {}", g)?;
}
}
write!(
f,
"WHERE {{ {} }}",
SparqlGraphRootPattern {
algebra,
dataset: &DatasetSpec::default()
dataset: &QueryDataset::default()
}
)
}

@ -1,5 +1,4 @@
use crate::model::{GraphName, NamedOrBlankNode};
use crate::sparql::algebra::DatasetSpec;
use crate::sparql::algebra::QueryDataset;
use crate::sparql::EvaluationError;
use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup,
@ -12,26 +11,26 @@ use std::iter::empty;
pub(crate) struct DatasetView<S: ReadableEncodedStore> {
store: S,
extra: RefCell<Rodeo>,
default_graph_as_union: bool,
dataset: Option<EncodedDatasetSpec<S::StrId>>,
dataset: EncodedDatasetSpec<S::StrId>,
}
impl<S: ReadableEncodedStore> DatasetView<S> {
pub fn new(
store: S,
default_graph_as_union: bool,
default_graphs: &[GraphName],
named_graphs: &[NamedOrBlankNode],
dataset: &DatasetSpec,
) -> Result<Self, EvaluationError> {
let dataset = if !default_graphs.is_empty() || !named_graphs.is_empty() {
Some(EncodedDatasetSpec {
default: default_graphs
pub fn new(store: S, dataset: &QueryDataset) -> Result<Self, EvaluationError> {
let dataset = EncodedDatasetSpec {
default: dataset
.default_graph_graphs()
.map(|graphs| {
graphs
.iter()
.flat_map(|g| store.get_encoded_graph_name(g.as_ref()).transpose())
.collect::<Result<Vec<_>, _>>()
})
.transpose()
.map_err(|e| e.into())?,
named: named_graphs
named: dataset
.available_named_graphs()
.map(|graphs| {
graphs
.iter()
.flat_map(|g| {
store
@ -39,34 +38,18 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
.transpose()
})
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.into())?,
})
} else if dataset.is_empty() {
None
} 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<_>, _>>()
.transpose()
.map_err(|e| e.into())?,
})
};
Ok(Self {
store,
extra: RefCell::new(Rodeo::default()),
default_graph_as_union,
dataset,
})
}
#[allow(clippy::needless_collect)]
fn encoded_quads_for_pattern_in_dataset(
&self,
subject: Option<EncodedTerm<S::StrId>>,
@ -75,11 +58,30 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
graph_name: Option<EncodedTerm<S::StrId>>,
) -> Box<dyn Iterator<Item = Result<EncodedQuad<DatasetStrId<S::StrId>>, EvaluationError>>>
{
if let Some(dataset) = &self.dataset {
if let Some(graph_name) = graph_name {
if graph_name == EncodedTerm::DefaultGraph {
let iters = dataset
.default
if graph_name.is_default_graph() {
if let Some(default_graph_graphs) = &self.dataset.default {
if default_graph_graphs.len() == 1 {
// Single graph optimization
Box::new(
map_iter(self.store.encoded_quads_for_pattern(
subject,
predicate,
object,
Some(default_graph_graphs[0]),
))
.map(|quad| {
let quad = quad?;
Ok(EncodedQuad::new(
quad.subject,
quad.predicate,
quad.object,
EncodedTerm::DefaultGraph,
))
}),
)
} else {
let iters = default_graph_graphs
.iter()
.map(|graph_name| {
self.store.encoded_quads_for_pattern(
@ -99,7 +101,19 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
EncodedTerm::DefaultGraph,
))
}))
} else if dataset.named.contains(&graph_name) {
}
} else {
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,
@ -109,9 +123,8 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
} else {
Box::new(empty())
}
} else {
let iters = dataset
.named
} else if let Some(named_graphs) = &self.dataset.named {
let iters = named_graphs
.iter()
.map(|graph_name| {
self.store.encoded_quads_for_pattern(
@ -123,8 +136,7 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
})
.collect::<Vec<_>>();
Box::new(map_iter(iters.into_iter().flatten()))
}
} else if graph_name == None {
} else {
Box::new(
map_iter(
self.store
@ -135,10 +147,6 @@ impl<S: ReadableEncodedStore> DatasetView<S> {
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)) =
try_map_quad_pattern(subject, predicate, object, graph_name)
{
if graph_name == Some(EncodedTerm::DefaultGraph) && self.default_graph_as_union {
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 {
Box::new(empty())
}
@ -278,6 +272,6 @@ pub enum DatasetStrId<I: StrId> {
impl<I: StrId> StrId for DatasetStrId<I> {}
struct EncodedDatasetSpec<I: StrId> {
default: Vec<EncodedTerm<I>>,
named: Vec<EncodedTerm<I>>,
default: Option<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::Triple;
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::model::*;
use crate::sparql::parser::Query;
use crate::sparql::plan::*;
use crate::sparql::service::ServiceHandler;
use crate::store::numeric_encoder::*;
@ -527,7 +526,7 @@ where
.ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?,
)?,
Query(QueryVariants::Select {
dataset: Rc::new(DatasetSpec::default()),
dataset: QueryDataset::default(),
algebra: graph_pattern,
base_iri: self.base_iri.clone(),
}),

@ -17,8 +17,8 @@ mod service;
mod update;
mod xml_results;
use crate::model::{GraphName, NamedOrBlankNode};
use crate::sparql::algebra::QueryVariants;
pub use crate::sparql::algebra::{Query, Update};
use crate::sparql::dataset::DatasetView;
pub use crate::sparql::error::EvaluationError;
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::{Variable, VariableNameParseError};
pub use crate::sparql::parser::ParseError;
pub use crate::sparql::parser::{Query, Update};
use crate::sparql::plan::{PlanNode, TripleTemplate};
use crate::sparql::plan_builder::PlanBuilder;
pub use crate::sparql::service::ServiceHandler;
@ -80,13 +79,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri,
dataset,
} => {
let dataset = Rc::new(DatasetView::new(
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let dataset = Rc::new(DatasetView::new(store, &dataset)?);
let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Select {
plan: Rc::new(plan),
@ -99,13 +92,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri,
dataset,
} => {
let dataset = Rc::new(DatasetView::new(
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let dataset = Rc::new(DatasetView::new(store, &dataset)?);
let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Ask {
plan: Rc::new(plan),
@ -118,13 +105,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri,
dataset,
} => {
let dataset = Rc::new(DatasetView::new(
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let dataset = Rc::new(DatasetView::new(store, &dataset)?);
let (plan, variables) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Construct {
plan: Rc::new(plan),
@ -141,13 +122,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
base_iri,
dataset,
} => {
let dataset = Rc::new(DatasetView::new(
store,
options.default_graph_as_union,
&options.default_graphs,
&options.named_graphs,
&dataset,
)?);
let dataset = Rc::new(DatasetView::new(store, &dataset)?);
let (plan, _) = PlanBuilder::build(dataset.as_ref(), &algebra)?;
SimplePreparedQueryAction::Describe {
plan: Rc::new(plan),
@ -181,9 +156,6 @@ 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) default_graphs: Vec<GraphName>,
pub(crate) named_graphs: Vec<NamedOrBlankNode>,
pub(crate) service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
}
@ -191,38 +163,12 @@ impl Default for QueryOptions {
#[inline]
fn default() -> Self {
Self {
default_graph_as_union: false,
default_graphs: Vec::new(),
named_graphs: Vec::new(),
service_handler: Rc::new(EmptyServiceHandler),
}
}
}
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.
///
/// Requires the `"http_client"` optional feature.

@ -8,36 +8,14 @@ use peg::parser;
use peg::str::LineCol;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::error::Error;
use std::rc::Rc;
use std::str::Chars;
use std::str::FromStr;
use std::{char, fmt};
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
///
/// ```
/// use oxigraph::sparql::Query;
///
/// 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> {
pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result<Query, 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| {
@ -54,7 +32,7 @@ impl Query {
aggregations: Vec::default(),
};
Ok(Self(
Ok(Query(
parser::QueryUnit(&unescape_unicode_codepoints(query), &mut state).map_err(|e| {
ParseError {
inner: ParseErrorKind::Parser(e),
@ -62,64 +40,9 @@ impl Query {
})?,
))
}
}
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 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> {
pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, 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| {
@ -136,40 +59,17 @@ impl Update {
aggregations: Vec::default(),
};
let operations = parser::UpdateInit(&unescape_unicode_codepoints(update), &mut state)
.map_err(|e| ParseError {
let operations =
parser::UpdateInit(&unescape_unicode_codepoints(update), &mut state).map_err(|e| {
ParseError {
inner: ParseErrorKind::Parser(e),
}
})?;
Ok(Self {
Ok(Update {
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 {
type Error = ParseError;
fn try_from(update: &String) -> Result<Self, ParseError> {
Self::from_str(update)
}
}
/// Error returned during SPARQL parsing.
#[derive(Debug)]
@ -488,7 +388,7 @@ fn copy_graph(
Variable::new_unchecked("o"),
to.into(),
)],
using: DatasetSpec::default(),
using: QueryDataset::default(),
algebra: match from {
NamedOrDefaultGraphTarget::NamedNode(from) => {
GraphPattern::Graph(from.into(), Box::new(bgp))
@ -771,7 +671,7 @@ parser! {
//[7]
rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Select {
dataset: Rc::new(d),
dataset: d,
algebra: Rc::new(build_select(s, w, g, h, o, l, v, state)),
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() {
QueryVariants::Construct {
construct: Rc::new(c),
dataset: Rc::new(d),
dataset: d,
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
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() {
QueryVariants::Construct {
construct: Rc::new(c.clone()),
dataset: Rc::new(d),
dataset: d,
algebra: Rc::new(build_select(
Selection::default(),
GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()),
@ -832,14 +732,14 @@ parser! {
rule DescribeQuery() -> QueryVariants =
i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
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)),
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() {
QueryVariants::Describe {
dataset: Rc::new(d),
dataset: d,
algebra: Rc::new(build_select(Selection {
option: SelectionOption::Default,
variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri {
@ -855,27 +755,41 @@ parser! {
//[12]
rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Ask {
dataset: Rc::new(d),
dataset: d,
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone()
}
}
//[13]
rule DatasetClause() -> DatasetSpec = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d }
rule DatasetClauses() -> DatasetSpec = d:DatasetClauses_item()* {
d.into_iter().fold(DatasetSpec::default(), |mut a, b| a + b)
rule DatasetClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("FROM") _ d:(DefaultGraphClause() / NamedGraphClause()) { d }
rule DatasetClauses() -> QueryDataset = d:DatasetClause() ** (_) {
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]
rule DefaultGraphClause() -> DatasetSpec = s:SourceSelector() {
DatasetSpec::new_with_default(s)
rule DefaultGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = s:SourceSelector() {
(Some(s.into()), None)
}
//[15]
rule NamedGraphClause() -> DatasetSpec = i("NAMED") _ s:SourceSelector() {
DatasetSpec::new_with_named(s)
rule NamedGraphClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ s:SourceSelector() {
(None, Some(s.into()))
}
//[16]
@ -1034,13 +948,13 @@ parser! {
vec![GraphUpdateOperation::DeleteInsert {
delete: d,
insert: Vec::new(),
using: DatasetSpec::default(),
using: QueryDataset::default(),
algebra
}]
}
//[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 mut delete = delete.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));
}
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 {
delete,
insert,
using: using.into_iter().fold(DatasetSpec::default(), |mut a, b| a + b),
using,
algebra
}]
}
@ -1086,12 +1016,12 @@ parser! {
rule InsertClause() -> Vec<QuadPattern> = i("INSERT") _ q:QuadPattern() { q }
//[44]
rule UsingClause() -> DatasetSpec = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d }
rule UsingClause_default() -> DatasetSpec = i:iri() {
DatasetSpec::new_with_default(i)
rule UsingClause() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("USING") _ d:(UsingClause_default() / UsingClause_named()) { d }
rule UsingClause_default() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i:iri() {
(Some(i.into()), None)
}
rule UsingClause_named() -> DatasetSpec = i("NAMED") _ i:iri() {
DatasetSpec::new_with_named(i)
rule UsingClause_named() -> (Option<GraphName>, Option<NamedOrBlankNode>) = i("NAMED") _ i:iri() {
(None, Some(i.into()))
}
//[45]

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

@ -2,8 +2,8 @@ use crate::error::{invalid_data_error, invalid_input_error};
use crate::io::GraphFormat;
use crate::model::{BlankNode, GraphNameRef, NamedNode, Term};
use crate::sparql::algebra::{
DatasetSpec, GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern,
TermOrVariable,
GraphPattern, GraphTarget, GraphUpdateOperation, NamedNodeOrVariable, QuadPattern,
QueryDataset, TermOrVariable,
};
use crate::sparql::dataset::{DatasetStrId, DatasetView};
use crate::sparql::eval::SimpleEvaluator;
@ -111,10 +111,10 @@ where
&mut self,
delete: &[QuadPattern],
insert: &[QuadPattern],
using: &DatasetSpec,
using: &QueryDataset,
algebra: &GraphPattern,
) -> 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 evaluator = SimpleEvaluator::<DatasetView<R>>::new(
dataset.clone(),

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

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

@ -10,28 +10,35 @@ use pyo3::prelude::{
use pyo3::{PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::vec::IntoIter;
pub fn build_query_options(
pub fn parse_query(
query: &str,
use_default_graph_as_union: bool,
default_graph: Option<&PyAny>,
named_graphs: Option<&PyAny>,
) -> PyResult<QueryOptions> {
let mut options = QueryOptions::default();
if use_default_graph_as_union {
options = options.with_default_graph_as_union();
}
) -> PyResult<Query> {
let mut query = Query::parse(query, None).map_err(|e| map_evaluation_error(e.into()))?;
if let Some(default_graph) = default_graph {
if let Ok(default_graphs) = default_graph.iter() {
if default_graph.is_empty()? {
if use_default_graph_as_union && default_graph.is_some() {
return Err(PyValueError::new_err(
"The query() method default_graph argument cannot be empty list",
"The query() method use_default_graph_as_union and default_graph arguments should not be set at the same time",
));
}
for default_graph in default_graphs {
options = options.with_default_graph(default_graph?.extract::<PyGraphName>()?);
if use_default_graph_as_union {
query.dataset_mut().set_default_graph_as_union();
}
if let Some(default_graph) = default_graph {
if let Ok(default_graphs) = default_graph.iter() {
query.dataset_mut().set_default_graph(
default_graphs
.map(|graph| Ok(graph?.extract::<PyGraphName>()?.into()))
.collect::<PyResult<_>>()?,
)
} 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 {
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()
@ -40,17 +47,15 @@ pub fn build_query_options(
}
if let Some(named_graphs) = named_graphs {
if named_graphs.is_empty()? {
return Err(PyValueError::new_err(
"The query() method named_graphs argument cannot be empty",
));
}
for named_graph in named_graphs.iter()? {
options = options.with_named_graph(named_graph?.extract::<PyNamedOrBlankNode>()?);
}
query.dataset_mut().set_available_named_graphs(
named_graphs
.iter()?
.map(|graph| Ok(graph?.extract::<PyNamedOrBlankNode>()?.into()))
.collect::<PyResult<_>>()?,
)
}
Ok(options)
Ok(query)
}
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 http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode};
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 std::io::BufReader;
use std::str::FromStr;
@ -101,9 +101,7 @@ async fn handle_request(request: Request, store: Store) -> Result<Response> {
{
Ok(()) => Response::new(StatusCode::NoContent),
Err(error) => {
let mut error = Error::from(error);
error.set_status(StatusCode::BadRequest);
return Err(error);
return Err(bad_request(error));
}
}
} else {
@ -229,29 +227,26 @@ async fn evaluate_sparql_query(
request: Request,
) -> Result<Response> {
spawn_blocking(move || {
let query = Query::parse(&query, None).map_err(|e| {
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
e
})?;
let mut query = Query::parse(&query, None).map_err(bad_request)?;
let default_graph_uris = default_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<GraphName>>>()
.map_err(bad_request)?;
let named_graph_uris = named_graph_uris
.into_iter()
.map(|e| Ok(NamedNode::new(e)?.into()))
.collect::<Result<Vec<NamedOrBlankNode>>>()
.map_err(bad_request)?;
let mut options = QueryOptions::default().with_simple_service_handler();
for default_graph_uri in default_graph_uris {
options =
options.with_default_graph(NamedNode::new(default_graph_uri).map_err(|e| {
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
e
})?)
}
for named_graph_uri in named_graph_uris {
options = options.with_named_graph(NamedNode::new(named_graph_uri).map_err(|e| {
let mut e = Error::from(e);
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
.dataset_mut()
.set_available_named_graphs(named_graph_uris);
}
let options = QueryOptions::default().with_simple_service_handler();
let results = store.query(query, options)?;
//TODO: stream
if let QueryResults::Graph(_) = results {
@ -419,6 +414,12 @@ fn content_negotiation<F>(
.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> {
inner: R,
}

@ -175,14 +175,15 @@ async fn evaluate_sparql_query(
) -> Result<Response> {
spawn_blocking(move || {
//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);
e.set_status(StatusCode::BadRequest);
e
})?;
let options = QueryOptions::default()
.with_default_graph_as_union()
.with_simple_service_handler();
if query.dataset().is_default_dataset() {
query.dataset_mut().set_default_graph_as_union();
}
let options = QueryOptions::default().with_simple_service_handler();
let results = store.query(query, options)?;
if let QueryResults::Graph(_) = results {
let format = content_negotiation(

Loading…
Cancel
Save