1use crate::io::{RdfFormat, RdfParser};
2use crate::model::{GraphName as OxGraphName, GraphNameRef, Quad as OxQuad};
3use crate::sparql::algebra::QueryDataset;
4use crate::sparql::dataset::DatasetView;
5use crate::sparql::http::Client;
6use crate::sparql::{EvaluationError, Update, UpdateOptions};
7use crate::storage::StorageWriter;
8use oxiri::Iri;
9use oxrdfio::LoadedDocument;
10use rustc_hash::FxHashMap;
11use sparesults::QuerySolution;
12use spareval::{QueryEvaluator, QueryResults};
13use spargebra::algebra::{GraphPattern, GraphTarget};
14use spargebra::term::{
15 BlankNode, GraphName, GraphNamePattern, GroundQuad, GroundQuadPattern, GroundSubject,
16 GroundTerm, GroundTermPattern, GroundTriple, GroundTriplePattern, NamedNode, NamedNodePattern,
17 Quad, QuadPattern, Subject, Term, TermPattern, Triple, TriplePattern,
18};
19use spargebra::{GraphUpdateOperation, Query};
20use std::io;
21use std::io::Read;
22
23pub fn evaluate_update<'a, 'b: 'a>(
24 transaction: &'a mut StorageWriter<'b>,
25 update: &Update,
26 options: &UpdateOptions,
27) -> Result<(), EvaluationError> {
28 SimpleUpdateEvaluator {
29 transaction,
30 base_iri: update.inner.base_iri.clone(),
31 query_evaluator: options.query_options.clone().into_evaluator(),
32 client: Client::new(
33 options.query_options.http_timeout,
34 options.query_options.http_redirection_limit,
35 ),
36 }
37 .eval_all(&update.inner.operations, &update.using_datasets)
38}
39
40struct SimpleUpdateEvaluator<'a, 'b> {
41 transaction: &'a mut StorageWriter<'b>,
42 base_iri: Option<Iri<String>>,
43 query_evaluator: QueryEvaluator,
44 client: Client,
45}
46
47impl<'a, 'b: 'a> SimpleUpdateEvaluator<'a, 'b> {
48 fn eval_all(
49 &mut self,
50 updates: &[GraphUpdateOperation],
51 using_datasets: &[Option<QueryDataset>],
52 ) -> Result<(), EvaluationError> {
53 for (update, using_dataset) in updates.iter().zip(using_datasets) {
54 self.eval(update, using_dataset)?;
55 }
56 Ok(())
57 }
58
59 fn eval(
60 &mut self,
61 update: &GraphUpdateOperation,
62 using_dataset: &Option<QueryDataset>,
63 ) -> Result<(), EvaluationError> {
64 match update {
65 GraphUpdateOperation::InsertData { data } => self.eval_insert_data(data),
66 GraphUpdateOperation::DeleteData { data } => self.eval_delete_data(data),
67 GraphUpdateOperation::DeleteInsert {
68 delete,
69 insert,
70 pattern,
71 ..
72 } => self.eval_delete_insert(
73 delete,
74 insert,
75 using_dataset.as_ref().unwrap_or(&QueryDataset::new()),
76 pattern,
77 ),
78 GraphUpdateOperation::Load {
79 silent,
80 source,
81 destination,
82 } => {
83 if let Err(error) = self.eval_load(source, destination) {
84 if *silent {
85 Ok(())
86 } else {
87 Err(error)
88 }
89 } else {
90 Ok(())
91 }
92 }
93 GraphUpdateOperation::Clear { graph, silent } => self.eval_clear(graph, *silent),
94 GraphUpdateOperation::Create { graph, silent } => self.eval_create(graph, *silent),
95 GraphUpdateOperation::Drop { graph, silent } => self.eval_drop(graph, *silent),
96 }
97 }
98
99 fn eval_insert_data(&mut self, data: &[Quad]) -> Result<(), EvaluationError> {
100 let mut bnodes = FxHashMap::default();
101 for quad in data {
102 let quad = Self::convert_quad(quad, &mut bnodes);
103 self.transaction.insert(quad.as_ref())?;
104 }
105 Ok(())
106 }
107
108 fn eval_delete_data(&mut self, data: &[GroundQuad]) -> Result<(), EvaluationError> {
109 for quad in data {
110 let quad = Self::convert_ground_quad(quad);
111 self.transaction.remove(quad.as_ref())?;
112 }
113 Ok(())
114 }
115
116 fn eval_delete_insert(
117 &mut self,
118 delete: &[GroundQuadPattern],
119 insert: &[QuadPattern],
120 using: &QueryDataset,
121 algebra: &GraphPattern,
122 ) -> Result<(), EvaluationError> {
123 let QueryResults::Solutions(solutions) = self.query_evaluator.clone().execute(
124 DatasetView::new(self.transaction.reader(), using),
125 &Query::Select {
126 dataset: None,
127 pattern: algebra.clone(),
128 base_iri: self.base_iri.clone(),
129 },
130 )?
131 else {
132 unreachable!("We provided a SELECT query, we must get back solutions")
133 };
134
135 let mut bnodes = FxHashMap::default();
136 for solution in solutions {
137 let solution = solution?;
138 for quad in delete {
139 if let Some(quad) = Self::fill_ground_quad_pattern(quad, &solution) {
140 self.transaction.remove(quad.as_ref())?;
141 }
142 }
143 for quad in insert {
144 if let Some(quad) = Self::fill_quad_pattern(quad, &solution, &mut bnodes) {
145 self.transaction.insert(quad.as_ref())?;
146 }
147 }
148 bnodes.clear();
149 }
150 Ok(())
151 }
152
153 fn eval_load(&mut self, from: &NamedNode, to: &GraphName) -> Result<(), EvaluationError> {
154 let (content_type, body) = self
155 .client
156 .get(
157 from.as_str(),
158 "application/n-triples, text/turtle, application/rdf+xml",
159 )
160 .map_err(|e| EvaluationError::Service(Box::new(e)))?;
161 let format = RdfFormat::from_media_type(&content_type)
162 .ok_or_else(|| EvaluationError::UnsupportedContentType(content_type))?;
163 let to_graph_name = match to {
164 GraphName::NamedNode(graph_name) => graph_name.into(),
165 GraphName::DefaultGraph => GraphNameRef::DefaultGraph,
166 };
167 let client = self.client.clone();
168 let parser = RdfParser::from_format(format)
169 .rename_blank_nodes()
170 .without_named_graphs()
171 .with_default_graph(to_graph_name)
172 .with_base_iri(from.as_str())
173 .map_err(|e| {
174 EvaluationError::Service(Box::new(io::Error::new(
175 io::ErrorKind::InvalidInput,
176 format!("Invalid URL: {from}: {e}"),
177 )))
178 })?
179 .for_reader(body)
180 .with_document_loader(move |url| {
181 let (content_type, mut body) = client.get(
182 url,
183 "application/n-triples, text/turtle, application/rdf+xml, application/ld+json",
184 )?;
185 let mut content = Vec::new();
186 body.read_to_end(&mut content)?;
187 Ok(LoadedDocument {
188 url: url.into(),
189 content,
190 format: RdfFormat::from_media_type(&content_type)
191 .ok_or_else(|| EvaluationError::UnsupportedContentType(content_type))?,
192 })
193 });
194 for q in parser {
195 self.transaction.insert(q?.as_ref())?;
196 }
197 Ok(())
198 }
199
200 fn eval_create(&mut self, graph_name: &NamedNode, silent: bool) -> Result<(), EvaluationError> {
201 if self.transaction.insert_named_graph(graph_name.into())? || silent {
202 Ok(())
203 } else {
204 Err(EvaluationError::GraphAlreadyExists(graph_name.clone()))
205 }
206 }
207
208 fn eval_clear(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> {
209 match graph {
210 GraphTarget::NamedNode(graph_name) => {
211 if self
212 .transaction
213 .reader()
214 .contains_named_graph(&graph_name.as_ref().into())?
215 {
216 Ok(self.transaction.clear_graph(graph_name.into())?)
217 } else if silent {
218 Ok(())
219 } else {
220 Err(EvaluationError::GraphDoesNotExist(graph_name.clone()))
221 }
222 }
223 GraphTarget::DefaultGraph => {
224 self.transaction.clear_graph(GraphNameRef::DefaultGraph)?;
225 Ok(())
226 }
227 GraphTarget::NamedGraphs => Ok(self.transaction.clear_all_named_graphs()?),
228 GraphTarget::AllGraphs => Ok(self.transaction.clear_all_graphs()?),
229 }
230 }
231
232 fn eval_drop(&mut self, graph: &GraphTarget, silent: bool) -> Result<(), EvaluationError> {
233 match graph {
234 GraphTarget::NamedNode(graph_name) => {
235 if self.transaction.remove_named_graph(graph_name.into())? || silent {
236 Ok(())
237 } else {
238 Err(EvaluationError::GraphDoesNotExist(graph_name.clone()))
239 }
240 }
241 GraphTarget::DefaultGraph => {
242 Ok(self.transaction.clear_graph(GraphNameRef::DefaultGraph)?)
243 }
244 GraphTarget::NamedGraphs => Ok(self.transaction.remove_all_named_graphs()?),
245 GraphTarget::AllGraphs => Ok(self.transaction.clear()?),
246 }
247 }
248
249 fn convert_quad(quad: &Quad, bnodes: &mut FxHashMap<BlankNode, BlankNode>) -> OxQuad {
250 OxQuad {
251 subject: match &quad.subject {
252 Subject::NamedNode(subject) => subject.clone().into(),
253 Subject::BlankNode(subject) => Self::convert_blank_node(subject, bnodes).into(),
254 Subject::Triple(subject) => Self::convert_triple(subject, bnodes).into(),
255 },
256 predicate: quad.predicate.clone(),
257 object: match &quad.object {
258 Term::NamedNode(object) => object.clone().into(),
259 Term::BlankNode(object) => Self::convert_blank_node(object, bnodes).into(),
260 Term::Literal(object) => object.clone().into(),
261 Term::Triple(subject) => Self::convert_triple(subject, bnodes).into(),
262 },
263 graph_name: match &quad.graph_name {
264 GraphName::NamedNode(graph_name) => graph_name.clone().into(),
265 GraphName::DefaultGraph => OxGraphName::DefaultGraph,
266 },
267 }
268 }
269
270 fn convert_triple(triple: &Triple, bnodes: &mut FxHashMap<BlankNode, BlankNode>) -> Triple {
271 Triple {
272 subject: match &triple.subject {
273 Subject::NamedNode(subject) => subject.clone().into(),
274 Subject::BlankNode(subject) => Self::convert_blank_node(subject, bnodes).into(),
275 Subject::Triple(subject) => Self::convert_triple(subject, bnodes).into(),
276 },
277 predicate: triple.predicate.clone(),
278 object: match &triple.object {
279 Term::NamedNode(object) => object.clone().into(),
280 Term::BlankNode(object) => Self::convert_blank_node(object, bnodes).into(),
281 Term::Literal(object) => object.clone().into(),
282 Term::Triple(subject) => Self::convert_triple(subject, bnodes).into(),
283 },
284 }
285 }
286
287 fn convert_blank_node(
288 node: &BlankNode,
289 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
290 ) -> BlankNode {
291 bnodes.entry(node.clone()).or_default().clone()
292 }
293
294 fn convert_ground_quad(quad: &GroundQuad) -> OxQuad {
295 OxQuad {
296 subject: match &quad.subject {
297 GroundSubject::NamedNode(subject) => subject.clone().into(),
298 GroundSubject::Triple(subject) => Self::convert_ground_triple(subject).into(),
299 },
300 predicate: quad.predicate.clone(),
301 object: match &quad.object {
302 GroundTerm::NamedNode(object) => object.clone().into(),
303 GroundTerm::Literal(object) => object.clone().into(),
304 GroundTerm::Triple(subject) => Self::convert_ground_triple(subject).into(),
305 },
306 graph_name: match &quad.graph_name {
307 GraphName::NamedNode(graph_name) => graph_name.clone().into(),
308 GraphName::DefaultGraph => OxGraphName::DefaultGraph,
309 },
310 }
311 }
312
313 fn convert_ground_triple(triple: &GroundTriple) -> Triple {
314 Triple {
315 subject: match &triple.subject {
316 GroundSubject::NamedNode(subject) => subject.clone().into(),
317 GroundSubject::Triple(subject) => Self::convert_ground_triple(subject).into(),
318 },
319 predicate: triple.predicate.clone(),
320 object: match &triple.object {
321 GroundTerm::NamedNode(object) => object.clone().into(),
322 GroundTerm::Literal(object) => object.clone().into(),
323 GroundTerm::Triple(subject) => Self::convert_ground_triple(subject).into(),
324 },
325 }
326 }
327
328 fn fill_quad_pattern(
329 quad: &QuadPattern,
330 solution: &QuerySolution,
331 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
332 ) -> Option<OxQuad> {
333 Some(OxQuad {
334 subject: match Self::fill_term_or_var(&quad.subject, solution, bnodes)? {
335 Term::NamedNode(node) => node.into(),
336 Term::BlankNode(node) => node.into(),
337 Term::Triple(triple) => triple.into(),
338 Term::Literal(_) => return None,
339 },
340 predicate: Self::fill_named_node_or_var(&quad.predicate, solution)?,
341 object: Self::fill_term_or_var(&quad.object, solution, bnodes)?,
342 graph_name: Self::fill_graph_name_or_var(&quad.graph_name, solution)?,
343 })
344 }
345
346 fn fill_term_or_var(
347 term: &TermPattern,
348 solution: &QuerySolution,
349 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
350 ) -> Option<Term> {
351 Some(match term {
352 TermPattern::NamedNode(term) => term.clone().into(),
353 TermPattern::BlankNode(bnode) => Self::convert_blank_node(bnode, bnodes).into(),
354 TermPattern::Literal(term) => term.clone().into(),
355 TermPattern::Triple(triple) => {
356 Self::fill_triple_pattern(triple, solution, bnodes)?.into()
357 }
358 TermPattern::Variable(v) => solution.get(v)?.clone(),
359 })
360 }
361
362 fn fill_named_node_or_var(
363 term: &NamedNodePattern,
364 solution: &QuerySolution,
365 ) -> Option<NamedNode> {
366 Some(match term {
367 NamedNodePattern::NamedNode(term) => term.clone(),
368 NamedNodePattern::Variable(v) => {
369 if let Term::NamedNode(s) = solution.get(v)? {
370 s.clone()
371 } else {
372 return None;
373 }
374 }
375 })
376 }
377
378 fn fill_graph_name_or_var(
379 term: &GraphNamePattern,
380 solution: &QuerySolution,
381 ) -> Option<OxGraphName> {
382 Some(match term {
383 GraphNamePattern::NamedNode(term) => term.clone().into(),
384 GraphNamePattern::DefaultGraph => OxGraphName::DefaultGraph,
385 GraphNamePattern::Variable(v) => match solution.get(v)? {
386 Term::NamedNode(node) => node.clone().into(),
387 Term::BlankNode(node) => node.clone().into(),
388 Term::Triple(_) | Term::Literal(_) => return None,
389 },
390 })
391 }
392
393 fn fill_triple_pattern(
394 triple: &TriplePattern,
395 solution: &QuerySolution,
396 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
397 ) -> Option<Triple> {
398 Some(Triple {
399 subject: match Self::fill_term_or_var(&triple.subject, solution, bnodes)? {
400 Term::NamedNode(node) => node.into(),
401 Term::BlankNode(node) => node.into(),
402 Term::Triple(triple) => triple.into(),
403 Term::Literal(_) => return None,
404 },
405 predicate: Self::fill_named_node_or_var(&triple.predicate, solution)?,
406 object: Self::fill_term_or_var(&triple.object, solution, bnodes)?,
407 })
408 }
409 fn fill_ground_quad_pattern(
410 quad: &GroundQuadPattern,
411 solution: &QuerySolution,
412 ) -> Option<OxQuad> {
413 Some(OxQuad {
414 subject: match Self::fill_ground_term_or_var(&quad.subject, solution)? {
415 Term::NamedNode(node) => node.into(),
416 Term::BlankNode(node) => node.into(),
417 Term::Triple(triple) => triple.into(),
418 Term::Literal(_) => return None,
419 },
420 predicate: Self::fill_named_node_or_var(&quad.predicate, solution)?,
421 object: Self::fill_ground_term_or_var(&quad.object, solution)?,
422 graph_name: Self::fill_graph_name_or_var(&quad.graph_name, solution)?,
423 })
424 }
425
426 fn fill_ground_term_or_var(term: &GroundTermPattern, solution: &QuerySolution) -> Option<Term> {
427 Some(match term {
428 GroundTermPattern::NamedNode(term) => term.clone().into(),
429 GroundTermPattern::Literal(term) => term.clone().into(),
430 GroundTermPattern::Triple(triple) => {
431 Self::fill_ground_triple_pattern(triple, solution)?.into()
432 }
433 GroundTermPattern::Variable(v) => solution.get(v)?.clone(),
434 })
435 }
436
437 fn fill_ground_triple_pattern(
438 triple: &GroundTriplePattern,
439 solution: &QuerySolution,
440 ) -> Option<Triple> {
441 Some(Triple {
442 subject: match Self::fill_ground_term_or_var(&triple.subject, solution)? {
443 Term::NamedNode(node) => node.into(),
444 Term::BlankNode(node) => node.into(),
445 Term::Triple(triple) => triple.into(),
446 Term::Literal(_) => return None,
447 },
448 predicate: Self::fill_named_node_or_var(&triple.predicate, solution)?,
449 object: Self::fill_ground_term_or_var(&triple.object, solution)?,
450 })
451 }
452}