SPARQL UPDATE options

pull/71/head
Tpt 4 years ago
parent 34f0efbb39
commit 700e47af1e
  1. 2
      lib/src/sparql/algebra.rs
  2. 46
      lib/src/sparql/mod.rs
  3. 32
      lib/src/sparql/parser.rs
  4. 11
      lib/src/sparql/update.rs
  5. 11
      lib/src/store/memory.rs
  6. 11
      lib/src/store/rocksdb.rs
  7. 11
      lib/src/store/sled.rs
  8. 41
      server/src/main.rs

@ -215,7 +215,7 @@ impl<'a> TryFrom<&'a String> for Query {
pub struct Update { pub struct Update {
/// The update base IRI /// The update base IRI
pub base_iri: Option<Iri<String>>, pub base_iri: Option<Iri<String>>,
/// The update operations /// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
pub operations: Vec<GraphUpdateOperation>, pub operations: Vec<GraphUpdateOperation>,
} }

@ -144,6 +144,42 @@ impl QueryOptions {
} }
} }
/// Options for SPARQL update evaluation
#[derive(Clone)]
pub struct UpdateOptions {
query_options: QueryOptions,
}
impl UpdateOptions {
/// The options related to the querying part of the updates
#[inline]
pub fn query_options(&self) -> &QueryOptions {
&self.query_options
}
/// The options related to the querying part of the updates
#[inline]
pub fn query_options_mut(&mut self) -> &mut QueryOptions {
&mut self.query_options
}
}
impl Default for UpdateOptions {
#[inline]
fn default() -> Self {
Self {
query_options: QueryOptions::default(),
}
}
}
impl From<QueryOptions> for UpdateOptions {
#[inline]
fn from(query_options: QueryOptions) -> Self {
Self { query_options }
}
}
pub(crate) fn evaluate_update< pub(crate) fn evaluate_update<
R: ReadableEncodedStore + Clone + 'static, R: ReadableEncodedStore + Clone + 'static,
W: StrContainer<StrId = R::StrId> + WritableEncodedStore<StrId = R::StrId>, W: StrContainer<StrId = R::StrId> + WritableEncodedStore<StrId = R::StrId>,
@ -151,15 +187,11 @@ pub(crate) fn evaluate_update<
read: R, read: R,
write: &mut W, write: &mut W,
update: Update, update: Update,
options: UpdateOptions,
) -> Result<(), EvaluationError> ) -> Result<(), EvaluationError>
where where
io::Error: From<StoreOrParseError<W::Error>>, io::Error: From<StoreOrParseError<W::Error>>,
{ {
SimpleUpdateEvaluator::new( SimpleUpdateEvaluator::new(read, write, update.base_iri.map(Rc::new), options)
read, .eval_all(&update.operations)
write,
update.base_iri.map(Rc::new),
Rc::new(EmptyServiceHandler),
)
.eval_all(&update.operations)
} }

@ -1036,21 +1036,6 @@ parser! {
let mut insert = insert.unwrap_or_else(Vec::new); let mut insert = insert.unwrap_or_else(Vec::new);
let mut pattern = pattern; let mut pattern = pattern;
if let Some(with) = with {
// We inject WITH everywhere
delete = delete.into_iter().map(|q| if q.graph_name.is_none() {
QuadPattern::new(q.subject, q.predicate, q.object, Some(with.clone().into()))
} else {
q
}).collect();
insert = insert.into_iter().map(|q| if q.graph_name.is_none() {
QuadPattern::new(q.subject, q.predicate, q.object, Some(with.clone().into()))
} else {
q
}).collect();
pattern = GraphPattern::Graph { graph_name: with.into(), inner: Box::new(pattern) };
}
let mut using = QueryDataset::default(); let mut using = QueryDataset::default();
if !u.is_empty() { if !u.is_empty() {
let mut using_default = Vec::new(); let mut using_default = Vec::new();
@ -1067,6 +1052,23 @@ parser! {
using.set_available_named_graphs(using_named); using.set_available_named_graphs(using_named);
} }
if let Some(with) = with {
// We inject WITH everywhere
delete = delete.into_iter().map(|q| if q.graph_name.is_none() {
QuadPattern::new(q.subject, q.predicate, q.object, Some(with.clone().into()))
} else {
q
}).collect();
insert = insert.into_iter().map(|q| if q.graph_name.is_none() {
QuadPattern::new(q.subject, q.predicate, q.object, Some(with.clone().into()))
} else {
q
}).collect();
if using.is_default_dataset() {
using.set_default_graph(vec![with.into()]);
}
}
vec![GraphUpdateOperation::DeleteInsert { vec![GraphUpdateOperation::DeleteInsert {
delete, delete,
insert, insert,

@ -10,8 +10,7 @@ use crate::sparql::eval::SimpleEvaluator;
use crate::sparql::http::Client; use crate::sparql::http::Client;
use crate::sparql::plan::EncodedTuple; use crate::sparql::plan::EncodedTuple;
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
use crate::sparql::service::ServiceHandler; use crate::sparql::{EvaluationError, UpdateOptions, Variable};
use crate::sparql::{EvaluationError, Variable};
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrLookup, WriteEncoder, EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrLookup, WriteEncoder,
}; };
@ -27,7 +26,7 @@ pub(crate) struct SimpleUpdateEvaluator<'a, R, W> {
read: R, read: R,
write: &'a mut W, write: &'a mut W,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>, options: UpdateOptions,
client: Client, client: Client,
} }
@ -43,13 +42,13 @@ where
read: R, read: R,
write: &'a mut W, write: &'a mut W,
base_iri: Option<Rc<Iri<String>>>, base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>, options: UpdateOptions,
) -> Self { ) -> Self {
Self { Self {
read, read,
write, write,
base_iri, base_iri,
service_handler, options,
client: Client::new(), client: Client::new(),
} }
} }
@ -119,7 +118,7 @@ where
let evaluator = SimpleEvaluator::<DatasetView<R>>::new( let evaluator = SimpleEvaluator::<DatasetView<R>>::new(
dataset.clone(), dataset.clone(),
self.base_iri.clone(), self.base_iri.clone(),
self.service_handler.clone(), self.options.query_options.service_handler.clone(),
); );
let mut bnodes = HashMap::new(); let mut bnodes = HashMap::new();
for tuple in evaluator.eval_plan(&plan, EncodedTuple::with_capacity(variables.len())) { for tuple in evaluator.eval_plan(&plan, EncodedTuple::with_capacity(variables.len())) {

@ -5,6 +5,7 @@ use crate::io::{DatasetFormat, DatasetParser, GraphFormat, GraphParser};
use crate::model::*; use crate::model::*;
use crate::sparql::{ use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update, evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
}; };
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, WriteEncoder, Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, WriteEncoder,
@ -234,11 +235,21 @@ impl MemoryStore {
pub fn update( pub fn update(
&self, &self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>, update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
) -> Result<(), EvaluationError> {
self.update_opt(update, UpdateOptions::default())
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
pub fn update_opt(
&self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
options: UpdateOptions,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
evaluate_update( evaluate_update(
self.clone(), self.clone(),
&mut &*self, &mut &*self,
update.try_into().map_err(|e| e.into())?, update.try_into().map_err(|e| e.into())?,
options,
) )
} }

@ -5,6 +5,7 @@ use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{ use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update, evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
}; };
use crate::store::binary_encoder::*; use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
@ -215,12 +216,22 @@ impl RocksDbStore {
pub fn update( pub fn update(
&self, &self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>, update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
) -> Result<(), EvaluationError> {
self.update_opt(update, UpdateOptions::default())
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
pub fn update_opt(
&self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
options: UpdateOptions,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
let mut writer = self.auto_batch_writer(); let mut writer = self.auto_batch_writer();
evaluate_update( evaluate_update(
self.clone(), self.clone(),
&mut writer, &mut writer,
update.try_into().map_err(|e| e.into())?, update.try_into().map_err(|e| e.into())?,
options,
)?; )?;
Ok(writer.apply()?) Ok(writer.apply()?)
} }

@ -5,6 +5,7 @@ use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{ use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update, evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
}; };
use crate::store::binary_encoder::*; use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
@ -207,11 +208,21 @@ impl SledStore {
pub fn update( pub fn update(
&self, &self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>, update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
) -> Result<(), EvaluationError> {
self.update_opt(update, UpdateOptions::default())
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
pub fn update_opt(
&self,
update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
options: UpdateOptions,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
evaluate_update( evaluate_update(
self.clone(), self.clone(),
&mut &*self, &mut &*self,
update.try_into().map_err(|e| e.into())?, update.try_into().map_err(|e| e.into())?,
options,
) )
} }

@ -18,15 +18,15 @@ use async_std::task::{block_on, spawn};
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, NamedOrBlankNode}; use oxigraph::model::{GraphName, NamedNode, NamedOrBlankNode};
use oxigraph::sparql::algebra::GraphUpdateOperation;
use oxigraph::sparql::{Query, QueryResults, QueryResultsFormat, Update}; use oxigraph::sparql::{Query, QueryResults, QueryResultsFormat, Update};
use std::io::BufReader;
use std::str::FromStr;
use url::form_urlencoded;
#[cfg(feature = "rocksdb")] #[cfg(feature = "rocksdb")]
use oxigraph::RocksDbStore as Store; use oxigraph::RocksDbStore as Store;
#[cfg(all(feature = "sled", not(feature = "rocksdb")))] #[cfg(all(feature = "sled", not(feature = "rocksdb")))]
use oxigraph::SledStore as Store; use oxigraph::SledStore as Store;
use std::io::BufReader;
use std::str::FromStr;
use url::form_urlencoded;
const MAX_SPARQL_BODY_SIZE: u64 = 1_048_576; const MAX_SPARQL_BODY_SIZE: u64 = 1_048_576;
const HTML_ROOT_PAGE: &str = include_str!("../templates/query.html"); const HTML_ROOT_PAGE: &str = include_str!("../templates/query.html");
@ -304,17 +304,36 @@ async fn evaluate_sparql_update(
default_graph_uris: Vec<String>, default_graph_uris: Vec<String>,
named_graph_uris: Vec<String>, named_graph_uris: Vec<String>,
) -> Result<Response> { ) -> Result<Response> {
if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() { let mut update = Update::parse(&update, None).map_err(|e| {
return Ok(simple_response(
StatusCode::BadRequest,
"using-graph-uri and using-named-graph-uri parameters are not supported yet",
));
}
let update = Update::parse(&update, 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 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)?;
if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
for operation in &mut update.operations {
if let GraphUpdateOperation::DeleteInsert { using, .. } = operation {
if !using.is_default_dataset() {
let result = Ok(simple_response(
StatusCode::BadRequest,
"using-graph-uri and using-named-graph-uri must not be used with a SPARQL UPDATE containing USING",
));
return result;
}
using.set_default_graph(default_graph_uris.clone());
using.set_available_named_graphs(named_graph_uris.clone());
}
}
}
store.update(update)?; store.update(update)?;
Ok(Response::new(StatusCode::NoContent)) Ok(Response::new(StatusCode::NoContent))
} }

Loading…
Cancel
Save