SPARQL UPDATE options

pull/71/head
Tpt 4 years ago
parent 34f0efbb39
commit 700e47af1e
  1. 2
      lib/src/sparql/algebra.rs
  2. 44
      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 {
/// The update base IRI
pub base_iri: Option<Iri<String>>,
/// The update operations
/// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate)
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<
R: ReadableEncodedStore + Clone + 'static,
W: StrContainer<StrId = R::StrId> + WritableEncodedStore<StrId = R::StrId>,
@ -151,15 +187,11 @@ pub(crate) fn evaluate_update<
read: R,
write: &mut W,
update: Update,
options: UpdateOptions,
) -> Result<(), EvaluationError>
where
io::Error: From<StoreOrParseError<W::Error>>,
{
SimpleUpdateEvaluator::new(
read,
write,
update.base_iri.map(Rc::new),
Rc::new(EmptyServiceHandler),
)
SimpleUpdateEvaluator::new(read, write, update.base_iri.map(Rc::new), options)
.eval_all(&update.operations)
}

@ -1036,21 +1036,6 @@ parser! {
let mut insert = insert.unwrap_or_else(Vec::new);
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();
if !u.is_empty() {
let mut using_default = Vec::new();
@ -1067,6 +1052,23 @@ parser! {
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 {
delete,
insert,

@ -10,8 +10,7 @@ use crate::sparql::eval::SimpleEvaluator;
use crate::sparql::http::Client;
use crate::sparql::plan::EncodedTuple;
use crate::sparql::plan_builder::PlanBuilder;
use crate::sparql::service::ServiceHandler;
use crate::sparql::{EvaluationError, Variable};
use crate::sparql::{EvaluationError, UpdateOptions, Variable};
use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, ReadEncoder, StrContainer, StrLookup, WriteEncoder,
};
@ -27,7 +26,7 @@ pub(crate) struct SimpleUpdateEvaluator<'a, R, W> {
read: R,
write: &'a mut W,
base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
options: UpdateOptions,
client: Client,
}
@ -43,13 +42,13 @@ where
read: R,
write: &'a mut W,
base_iri: Option<Rc<Iri<String>>>,
service_handler: Rc<dyn ServiceHandler<Error = EvaluationError>>,
options: UpdateOptions,
) -> Self {
Self {
read,
write,
base_iri,
service_handler,
options,
client: Client::new(),
}
}
@ -119,7 +118,7 @@ where
let evaluator = SimpleEvaluator::<DatasetView<R>>::new(
dataset.clone(),
self.base_iri.clone(),
self.service_handler.clone(),
self.options.query_options.service_handler.clone(),
);
let mut bnodes = HashMap::new();
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::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
};
use crate::store::numeric_encoder::{
Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, WriteEncoder,
@ -234,11 +235,21 @@ impl MemoryStore {
pub fn update(
&self,
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> {
evaluate_update(
self.clone(),
&mut &*self,
update.try_into().map_err(|e| e.into())?,
options,
)
}

@ -5,6 +5,7 @@ use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*;
use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
};
use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{
@ -215,12 +216,22 @@ impl RocksDbStore {
pub fn update(
&self,
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> {
let mut writer = self.auto_batch_writer();
evaluate_update(
self.clone(),
&mut writer,
update.try_into().map_err(|e| e.into())?,
options,
)?;
Ok(writer.apply()?)
}

@ -5,6 +5,7 @@ use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*;
use crate::sparql::{
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
UpdateOptions,
};
use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{
@ -207,11 +208,21 @@ impl SledStore {
pub fn update(
&self,
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> {
evaluate_update(
self.clone(),
&mut &*self,
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 oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::{GraphName, NamedNode, NamedOrBlankNode};
use oxigraph::sparql::algebra::GraphUpdateOperation;
use oxigraph::sparql::{Query, QueryResults, QueryResultsFormat, Update};
use std::io::BufReader;
use std::str::FromStr;
use url::form_urlencoded;
#[cfg(feature = "rocksdb")]
use oxigraph::RocksDbStore as Store;
#[cfg(all(feature = "sled", not(feature = "rocksdb")))]
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 HTML_ROOT_PAGE: &str = include_str!("../templates/query.html");
@ -304,17 +304,36 @@ async fn evaluate_sparql_update(
default_graph_uris: Vec<String>,
named_graph_uris: Vec<String>,
) -> Result<Response> {
if !default_graph_uris.is_empty() || !named_graph_uris.is_empty() {
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 update = Update::parse(&update, None).map_err(|e| {
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
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)?;
Ok(Response::new(StatusCode::NoContent))
}

Loading…
Cancel
Save