1use crate::{
4 model::{GraphName, NamedNode, Quad},
5 query::algebra::{Expression, GraphPattern, GraphTarget, QuadPattern, Update, UpdateOperation},
6 query::{AlgebraTriplePattern, TermPattern},
7 vocab::xsd,
8 OxirsError, Result, Store,
9};
10use std::collections::HashMap;
11
12pub struct UpdateExecutor<'a> {
14 store: &'a dyn Store,
15}
16
17impl<'a> UpdateExecutor<'a> {
18 pub fn new(store: &'a dyn Store) -> Self {
20 Self { store }
21 }
22
23 pub fn execute(&self, update: &Update) -> Result<()> {
25 for operation in &update.operations {
26 self.execute_operation(operation)?;
27 }
28 Ok(())
29 }
30
31 fn execute_operation(&self, operation: &UpdateOperation) -> Result<()> {
33 match operation {
34 UpdateOperation::InsertData { data } => self.execute_insert_data(data),
35 UpdateOperation::DeleteData { data } => self.execute_delete_data(data),
36 UpdateOperation::DeleteWhere { pattern } => self.execute_delete_where(pattern),
37 UpdateOperation::Modify {
38 delete,
39 insert,
40 where_clause,
41 using: _,
42 } => self.execute_modify(delete, insert, where_clause),
43 UpdateOperation::Load {
44 source,
45 destination,
46 silent,
47 } => self.execute_load(source, destination, *silent),
48 UpdateOperation::Clear { graph, silent } => self.execute_clear(graph, *silent),
49 UpdateOperation::Create { graph, silent } => self.execute_create(graph, *silent),
50 UpdateOperation::Drop { graph, silent } => self.execute_drop(graph, *silent),
51 UpdateOperation::Copy {
52 source,
53 destination,
54 silent,
55 } => self.execute_copy(source, destination, *silent),
56 UpdateOperation::Move {
57 source,
58 destination,
59 silent,
60 } => self.execute_move(source, destination, *silent),
61 UpdateOperation::Add {
62 source,
63 destination,
64 silent,
65 } => self.execute_add(source, destination, *silent),
66 }
67 }
68
69 fn execute_insert_data(&self, data: &[Quad]) -> Result<()> {
71 for quad in data {
72 self.store.insert_quad(quad.clone())?;
73 }
74 Ok(())
75 }
76
77 fn execute_delete_data(&self, data: &[Quad]) -> Result<()> {
79 for quad in data {
80 self.store.remove_quad(quad)?;
81 }
82 Ok(())
83 }
84
85 fn execute_delete_where(&self, patterns: &[QuadPattern]) -> Result<()> {
87 for pattern in patterns {
89 let matching_quads = self.find_matching_quads(pattern)?;
90 for quad in matching_quads {
91 self.store.remove_quad(&quad)?;
92 }
93 }
94 Ok(())
95 }
96
97 fn execute_modify(
99 &self,
100 delete_patterns: &Option<Vec<QuadPattern>>,
101 insert_patterns: &Option<Vec<QuadPattern>>,
102 where_clause: &GraphPattern,
103 ) -> Result<()> {
104 let solutions = self.evaluate_graph_pattern(where_clause)?;
106
107 for solution in solutions {
109 if let Some(delete_patterns) = delete_patterns {
111 for pattern in delete_patterns {
112 if let Some(quad) = self.instantiate_quad_pattern(pattern, &solution)? {
113 self.store.remove_quad(&quad)?;
114 }
115 }
116 }
117
118 if let Some(insert_patterns) = insert_patterns {
120 for pattern in insert_patterns {
121 if let Some(quad) = self.instantiate_quad_pattern(pattern, &solution)? {
122 self.store.insert_quad(quad)?;
123 }
124 }
125 }
126 }
127
128 Ok(())
129 }
130
131 fn execute_load(
133 &self,
134 source: &NamedNode,
135 _destination: &Option<NamedNode>,
136 _silent: bool,
137 ) -> Result<()> {
138 Err(OxirsError::Update(format!(
141 "LOAD operation not implemented for source: {source}"
142 )))
143 }
144
145 fn execute_clear(&self, graph: &GraphTarget, _silent: bool) -> Result<()> {
147 match graph {
148 GraphTarget::Default => {
149 let default_graph = GraphName::DefaultGraph;
151 let quads = self
152 .store
153 .find_quads(None, None, None, Some(&default_graph))?;
154 for quad in quads {
155 self.store.remove_quad(&quad)?;
156 }
157 }
158 GraphTarget::Named(graph_name) => {
159 let graph = GraphName::NamedNode(graph_name.clone());
161 let quads = self.store.find_quads(None, None, None, Some(&graph))?;
162 for quad in quads {
163 self.store.remove_quad(&quad)?;
164 }
165 }
166 GraphTarget::All => {
167 let quads = self.store.find_quads(None, None, None, None)?;
169 for quad in quads {
170 self.store.remove_quad(&quad)?;
171 }
172 }
173 }
174 Ok(())
175 }
176
177 fn execute_create(&self, _graph: &NamedNode, _silent: bool) -> Result<()> {
179 Ok(())
182 }
183
184 fn execute_drop(&self, graph: &GraphTarget, _silent: bool) -> Result<()> {
186 self.execute_clear(graph, _silent)
189 }
190
191 fn execute_copy(
193 &self,
194 source: &GraphTarget,
195 destination: &GraphTarget,
196 _silent: bool,
197 ) -> Result<()> {
198 self.execute_clear(destination, true)?;
200
201 let source_quads = self.get_quads_from_target(source)?;
202 for quad in source_quads {
203 let dest_quad = self.move_quad_to_target(&quad, destination)?;
204 self.store.insert_quad(dest_quad)?;
205 }
206 Ok(())
207 }
208
209 fn execute_move(
211 &self,
212 source: &GraphTarget,
213 destination: &GraphTarget,
214 _silent: bool,
215 ) -> Result<()> {
216 self.execute_copy(source, destination, true)?;
218 self.execute_drop(source, true)?;
219 Ok(())
220 }
221
222 fn execute_add(
224 &self,
225 source: &GraphTarget,
226 destination: &GraphTarget,
227 _silent: bool,
228 ) -> Result<()> {
229 let source_quads = self.get_quads_from_target(source)?;
231 for quad in source_quads {
232 let dest_quad = self.move_quad_to_target(&quad, destination)?;
233 self.store.insert_quad(dest_quad)?;
234 }
235 Ok(())
236 }
237
238 fn find_matching_quads(&self, pattern: &QuadPattern) -> Result<Vec<Quad>> {
240 let subject = self.term_pattern_to_subject(&pattern.subject)?;
242 let predicate = self.term_pattern_to_predicate(&pattern.predicate)?;
243 let object = self.term_pattern_to_object(&pattern.object)?;
244 let graph = pattern
245 .graph
246 .as_ref()
247 .map(|g| self.term_pattern_to_graph_name(g))
248 .transpose()?;
249
250 self.store.find_quads(
251 subject.as_ref(),
252 predicate.as_ref(),
253 object.as_ref(),
254 graph.as_ref(),
255 )
256 }
257
258 fn term_pattern_to_subject(
260 &self,
261 pattern: &TermPattern,
262 ) -> Result<Option<crate::model::Subject>> {
263 match pattern {
264 TermPattern::NamedNode(n) => Ok(Some(crate::model::Subject::NamedNode(n.clone()))),
265 TermPattern::BlankNode(b) => Ok(Some(crate::model::Subject::BlankNode(b.clone()))),
266 TermPattern::Variable(_) => Ok(None), TermPattern::Literal(_) => Err(OxirsError::Update(
268 "Subject cannot be a literal".to_string(),
269 )),
270 }
271 }
272
273 fn term_pattern_to_predicate(
275 &self,
276 pattern: &TermPattern,
277 ) -> Result<Option<crate::model::Predicate>> {
278 match pattern {
279 TermPattern::NamedNode(n) => Ok(Some(crate::model::Predicate::NamedNode(n.clone()))),
280 TermPattern::Variable(_) => Ok(None), TermPattern::BlankNode(_) | TermPattern::Literal(_) => Err(OxirsError::Update(
282 "Predicate must be a named node".to_string(),
283 )),
284 }
285 }
286
287 fn term_pattern_to_object(
289 &self,
290 pattern: &TermPattern,
291 ) -> Result<Option<crate::model::Object>> {
292 match pattern {
293 TermPattern::NamedNode(n) => Ok(Some(crate::model::Object::NamedNode(n.clone()))),
294 TermPattern::BlankNode(b) => Ok(Some(crate::model::Object::BlankNode(b.clone()))),
295 TermPattern::Literal(l) => Ok(Some(crate::model::Object::Literal(l.clone()))),
296 TermPattern::Variable(_) => Ok(None), }
298 }
299
300 fn term_pattern_to_graph_name(&self, pattern: &TermPattern) -> Result<GraphName> {
302 match pattern {
303 TermPattern::NamedNode(n) => Ok(GraphName::NamedNode(n.clone())),
304 TermPattern::Variable(_) => Ok(GraphName::DefaultGraph), TermPattern::BlankNode(_) | TermPattern::Literal(_) => Err(OxirsError::Update(
306 "Graph name must be a named node".to_string(),
307 )),
308 }
309 }
310
311 fn evaluate_graph_pattern(
313 &self,
314 pattern: &GraphPattern,
315 ) -> Result<Vec<HashMap<String, crate::model::Term>>> {
316 use crate::query::{QueryEngine, QueryResult};
317
318 let query_engine = QueryEngine::new();
320
321 let sparql_query = self.graph_pattern_to_sparql(pattern)?;
323
324 match query_engine.query(&sparql_query, self.store)? {
326 QueryResult::Select {
327 variables: _,
328 bindings,
329 } => {
330 let mut solutions = Vec::new();
332 for binding in bindings {
333 let mut solution = HashMap::new();
334 for (var_name, term) in binding {
335 solution.insert(var_name, term);
336 }
337 solutions.push(solution);
338 }
339 Ok(solutions)
340 }
341 _ => Err(OxirsError::Update(
342 "Expected SELECT query result for WHERE clause evaluation".to_string(),
343 )),
344 }
345 }
346
347 fn instantiate_quad_pattern(
349 &self,
350 pattern: &QuadPattern,
351 solution: &HashMap<String, crate::model::Term>,
352 ) -> Result<Option<Quad>> {
353 use crate::model::*;
354
355 let subject = match &pattern.subject {
357 TermPattern::Variable(var) => {
358 if let Some(term) = solution.get(var.name()) {
359 match term {
360 Term::NamedNode(n) => Subject::NamedNode(n.clone()),
361 Term::BlankNode(b) => Subject::BlankNode(b.clone()),
362 _ => return Ok(None), }
364 } else {
365 return Ok(None); }
367 }
368 TermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
369 TermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
370 TermPattern::Literal(_) => return Ok(None), };
372
373 let predicate = match &pattern.predicate {
375 TermPattern::Variable(var) => {
376 if let Some(Term::NamedNode(n)) = solution.get(var.name()) {
377 Predicate::NamedNode(n.clone())
378 } else {
379 return Ok(None); }
381 }
382 TermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
383 _ => return Ok(None), };
385
386 let object = match &pattern.object {
388 TermPattern::Variable(var) => {
389 if let Some(term) = solution.get(var.name()) {
390 match term {
391 Term::NamedNode(n) => Object::NamedNode(n.clone()),
392 Term::BlankNode(b) => Object::BlankNode(b.clone()),
393 Term::Literal(l) => Object::Literal(l.clone()),
394 _ => return Ok(None), }
396 } else {
397 return Ok(None); }
399 }
400 TermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
401 TermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
402 TermPattern::Literal(l) => Object::Literal(l.clone()),
403 };
404
405 let graph_name = match &pattern.graph {
407 Some(graph_pattern) => match graph_pattern {
408 TermPattern::Variable(var) => {
409 if let Some(Term::NamedNode(n)) = solution.get(var.name()) {
410 GraphName::NamedNode(n.clone())
411 } else {
412 return Ok(None); }
414 }
415 TermPattern::NamedNode(n) => GraphName::NamedNode(n.clone()),
416 _ => return Ok(None), },
418 None => GraphName::DefaultGraph,
419 };
420
421 Ok(Some(Quad::new(subject, predicate, object, graph_name)))
422 }
423
424 fn get_quads_from_target(&self, target: &GraphTarget) -> Result<Vec<Quad>> {
426 match target {
427 GraphTarget::Default => {
428 let graph = GraphName::DefaultGraph;
429 self.store.find_quads(None, None, None, Some(&graph))
430 }
431 GraphTarget::Named(graph_name) => {
432 let graph = GraphName::NamedNode(graph_name.clone());
433 self.store.find_quads(None, None, None, Some(&graph))
434 }
435 GraphTarget::All => self.store.find_quads(None, None, None, None),
436 }
437 }
438
439 fn move_quad_to_target(&self, quad: &Quad, target: &GraphTarget) -> Result<Quad> {
441 match target {
442 GraphTarget::Default => Ok(Quad::new(
443 quad.subject().clone(),
444 quad.predicate().clone(),
445 quad.object().clone(),
446 GraphName::DefaultGraph,
447 )),
448 GraphTarget::Named(graph_name) => Ok(Quad::new(
449 quad.subject().clone(),
450 quad.predicate().clone(),
451 quad.object().clone(),
452 GraphName::NamedNode(graph_name.clone()),
453 )),
454 GraphTarget::All => {
455 Ok(quad.clone())
457 }
458 }
459 }
460
461 fn graph_pattern_to_sparql(&self, pattern: &GraphPattern) -> Result<String> {
463 use crate::query::algebra::*;
464
465 match pattern {
466 GraphPattern::Bgp(triple_patterns) => {
467 let mut sparql = String::from("SELECT * WHERE { ");
468 for (i, triple_pattern) in triple_patterns.iter().enumerate() {
469 if i > 0 {
470 sparql.push_str(" . ");
471 }
472 sparql.push_str(&self.triple_pattern_to_sparql(triple_pattern)?);
473 }
474 sparql.push_str(" }");
475 Ok(sparql)
476 }
477 GraphPattern::Join(left, right) => {
478 let left_sparql = self.graph_pattern_to_sparql(left)?;
479 let right_sparql = self.graph_pattern_to_sparql(right)?;
480
481 let left_where = self.extract_where_clause(&left_sparql)?;
483 let right_where = self.extract_where_clause(&right_sparql)?;
484
485 Ok(format!("SELECT * WHERE {{ {left_where} . {right_where} }}"))
486 }
487 GraphPattern::Filter { expr, inner } => {
488 let inner_sparql = self.graph_pattern_to_sparql(inner)?;
489 let inner_where = self.extract_where_clause(&inner_sparql)?;
490 let filter_expr = self.expression_to_sparql(expr)?;
491
492 Ok(format!(
493 "SELECT * WHERE {{ {inner_where} FILTER ({filter_expr}) }}"
494 ))
495 }
496 GraphPattern::Union(left, right) => {
497 let left_sparql = self.graph_pattern_to_sparql(left)?;
498 let right_sparql = self.graph_pattern_to_sparql(right)?;
499
500 let left_where = self.extract_where_clause(&left_sparql)?;
501 let right_where = self.extract_where_clause(&right_sparql)?;
502
503 Ok(format!(
504 "SELECT * WHERE {{ {{ {left_where} }} UNION {{ {right_where} }} }}"
505 ))
506 }
507 _ => Err(OxirsError::Update(format!(
508 "Graph pattern type not yet supported in SPARQL conversion: {pattern:?}"
509 ))),
510 }
511 }
512
513 fn triple_pattern_to_sparql(&self, pattern: &AlgebraTriplePattern) -> Result<String> {
515 let subject = self.term_pattern_to_sparql(&pattern.subject)?;
516 let predicate = self.term_pattern_to_sparql(&pattern.predicate)?;
517 let object = self.term_pattern_to_sparql(&pattern.object)?;
518
519 Ok(format!("{subject} {predicate} {object}"))
520 }
521
522 fn term_pattern_to_sparql(&self, pattern: &TermPattern) -> Result<String> {
524 match pattern {
525 TermPattern::Variable(var) => Ok(format!("?{}", var.name())),
526 TermPattern::NamedNode(node) => Ok(format!("<{}>", node.as_str())),
527 TermPattern::BlankNode(blank) => Ok(format!("_:{}", blank.as_str())),
528 TermPattern::Literal(literal) => {
529 if let Some(lang) = literal.language() {
530 Ok(format!("\"{}\"@{}", literal.value(), lang))
531 } else if literal.datatype() != xsd::STRING.as_ref() {
532 Ok(format!("\"{}\"^^<{}>", literal.value(), literal.datatype()))
533 } else {
534 Ok(format!("\"{}\"", literal.value()))
535 }
536 }
537 }
538 }
539
540 #[allow(clippy::only_used_in_recursion)]
542 fn expression_to_sparql(&self, expr: &Expression) -> Result<String> {
543 match expr {
544 Expression::Variable(var) => Ok(format!("?{}", var.name())),
545 Expression::Term(term) => match term {
546 crate::model::Term::NamedNode(n) => Ok(format!("<{}>", n.as_str())),
547 crate::model::Term::BlankNode(b) => Ok(format!("_:{}", b.as_str())),
548 crate::model::Term::Literal(l) => {
549 if let Some(lang) = l.language() {
550 Ok(format!("\"{}\"@{}", l.value(), lang))
551 } else if l.datatype() != xsd::STRING.as_ref() {
552 Ok(format!("\"{}\"^^<{}>", l.value(), l.datatype()))
553 } else {
554 Ok(format!("\"{}\"", l.value()))
555 }
556 }
557 _ => Err(OxirsError::Update(
558 "Unsupported term type in expression".to_string(),
559 )),
560 },
561 Expression::Equal(left, right) => {
562 let left_sparql = self.expression_to_sparql(left)?;
563 let right_sparql = self.expression_to_sparql(right)?;
564 Ok(format!("({left_sparql} = {right_sparql})"))
565 }
566 Expression::And(left, right) => {
567 let left_sparql = self.expression_to_sparql(left)?;
568 let right_sparql = self.expression_to_sparql(right)?;
569 Ok(format!("({left_sparql} && {right_sparql})"))
570 }
571 Expression::Or(left, right) => {
572 let left_sparql = self.expression_to_sparql(left)?;
573 let right_sparql = self.expression_to_sparql(right)?;
574 Ok(format!("({left_sparql} || {right_sparql})"))
575 }
576 Expression::Not(inner) => {
577 let inner_sparql = self.expression_to_sparql(inner)?;
578 Ok(format!("(!{inner_sparql})"))
579 }
580 _ => Err(OxirsError::Update(format!(
581 "Expression type not yet supported in SPARQL conversion: {expr:?}"
582 ))),
583 }
584 }
585
586 fn extract_where_clause(&self, sparql: &str) -> Result<String> {
588 if let Some(start) = sparql.find("WHERE {") {
589 let where_start = start + 7; if let Some(end) = sparql.rfind('}') {
591 let where_content = &sparql[where_start..end].trim();
592 Ok(where_content.to_string())
593 } else {
594 Err(OxirsError::Update(
595 "Malformed SPARQL query: missing closing brace".to_string(),
596 ))
597 }
598 } else {
599 Err(OxirsError::Update(
600 "Malformed SPARQL query: missing WHERE clause".to_string(),
601 ))
602 }
603 }
604}
605
606#[derive(Default)]
608pub struct UpdateParser;
609
610impl UpdateParser {
611 pub fn new() -> Self {
613 Self
614 }
615
616 pub fn parse(&self, update_str: &str) -> Result<Update> {
618 let trimmed = update_str.trim();
622
623 let (prefixes, remaining) = self.extract_prefixes(trimmed)?;
625
626 if remaining.contains("INSERT DATA") {
628 self.parse_insert_data(&remaining, prefixes)
629 } else if remaining.contains("DELETE DATA") {
630 self.parse_delete_data(&remaining, prefixes)
631 } else if self.is_delete_where_shorthand(&remaining) {
632 self.parse_delete_where(&remaining, prefixes)
633 } else if remaining.contains("DELETE") && remaining.contains("WHERE") {
634 self.parse_delete_modify(&remaining, prefixes)
635 } else if remaining.contains("INSERT") && remaining.contains("WHERE") {
636 self.parse_insert_where(&remaining, prefixes)
637 } else if remaining.contains("CLEAR") {
638 self.parse_clear(&remaining, prefixes)
639 } else {
640 Err(OxirsError::Parse(format!(
641 "Unsupported UPDATE operation: {}",
642 remaining
643 )))
644 }
645 }
646
647 fn is_delete_where_shorthand(&self, update_str: &str) -> bool {
649 if let Some(delete_pos) = update_str.find("DELETE") {
652 if let Some(where_pos) = update_str.find("WHERE") {
653 let between = &update_str[delete_pos + 6..where_pos];
654 return !between.contains('{');
656 }
657 }
658 false
659 }
660
661 fn extract_prefixes(&self, update_str: &str) -> Result<(HashMap<String, NamedNode>, String)> {
663 let mut prefixes = HashMap::new();
664 let mut remaining = update_str.to_string();
665
666 loop {
668 let trimmed = remaining.trim();
669
670 if let Some(prefix_start) = trimmed.find("PREFIX") {
671 if prefix_start == 0 || trimmed[..prefix_start].chars().all(|c| c.is_whitespace()) {
673 let after_prefix = &trimmed[prefix_start + 6..];
675
676 if let Some(colon_pos) = after_prefix.find(':') {
677 if let Some(iri_start) = after_prefix.find('<') {
678 if let Some(iri_end) = after_prefix.find('>') {
679 let prefix = after_prefix[..colon_pos].trim().to_string();
680 let iri_str = &after_prefix[iri_start + 1..iri_end];
681 let iri_node = NamedNode::new(iri_str).map_err(|e| {
682 OxirsError::Parse(format!("Invalid prefix IRI: {e}"))
683 })?;
684 prefixes.insert(prefix, iri_node);
685
686 remaining = after_prefix[iri_end + 1..].to_string();
688 continue;
689 }
690 }
691 }
692 }
693 }
694
695 break;
697 }
698
699 Ok((prefixes, remaining.trim().to_string()))
700 }
701
702 fn parse_insert_data(
704 &self,
705 update_str: &str,
706 prefixes: HashMap<String, NamedNode>,
707 ) -> Result<Update> {
708 use crate::query::algebra::UpdateOperation;
709
710 let data_start = update_str.find('{');
712 let data_end = update_str.rfind('}');
713
714 if let (Some(start), Some(end)) = (data_start, data_end) {
715 let data_block = update_str[start + 1..end].trim();
716
717 let quads = self.parse_quad_data(data_block, &prefixes)?;
719
720 Ok(Update {
721 base: None,
722 prefixes,
723 operations: vec![UpdateOperation::InsertData { data: quads }],
724 })
725 } else {
726 Err(OxirsError::Parse(
727 "Malformed INSERT DATA: missing data block".to_string(),
728 ))
729 }
730 }
731
732 fn parse_delete_data(
734 &self,
735 update_str: &str,
736 prefixes: HashMap<String, NamedNode>,
737 ) -> Result<Update> {
738 use crate::query::algebra::UpdateOperation;
739
740 let data_start = update_str.find('{');
742 let data_end = update_str.rfind('}');
743
744 if let (Some(start), Some(end)) = (data_start, data_end) {
745 let data_block = update_str[start + 1..end].trim();
746
747 let quads = self.parse_quad_data(data_block, &prefixes)?;
749
750 Ok(Update {
751 base: None,
752 prefixes,
753 operations: vec![UpdateOperation::DeleteData { data: quads }],
754 })
755 } else {
756 Err(OxirsError::Parse(
757 "Malformed DELETE DATA: missing data block".to_string(),
758 ))
759 }
760 }
761
762 fn parse_quad_data(
764 &self,
765 data_block: &str,
766 prefixes: &HashMap<String, NamedNode>,
767 ) -> Result<Vec<Quad>> {
768 use crate::format::format::RdfFormat;
769 use crate::format::RdfParser;
770 use std::io::Cursor;
771
772 let mut turtle_doc = String::new();
774 for (prefix, iri) in prefixes {
775 turtle_doc.push_str(&format!("@prefix {}: <{}> .\n", prefix, iri.as_str()));
776 }
777 turtle_doc.push_str("\n");
778 turtle_doc.push_str(data_block);
779
780 let parser = RdfParser::new(RdfFormat::Turtle);
782 let turtle_bytes = turtle_doc.into_bytes();
783 let cursor = Cursor::new(turtle_bytes);
784
785 let quads: Vec<Quad> = parser
786 .for_reader(cursor)
787 .collect::<std::result::Result<Vec<_>, _>>()
788 .map_err(|e| OxirsError::Parse(format!("Failed to parse UPDATE data: {}", e)))?;
789
790 Ok(quads)
791 }
792
793 fn parse_delete_modify(
795 &self,
796 update_str: &str,
797 prefixes: HashMap<String, NamedNode>,
798 ) -> Result<Update> {
799 use crate::query::algebra::UpdateOperation;
800
801 let delete_pos = update_str.find("DELETE");
803 let where_pos = update_str.find("WHERE");
804
805 if delete_pos.is_none() || where_pos.is_none() {
806 return Err(OxirsError::Parse(
807 "Malformed DELETE/WHERE: missing DELETE or WHERE keyword".to_string(),
808 ));
809 }
810
811 let delete_start = update_str[delete_pos.unwrap()..].find('{');
812 let delete_end = update_str[delete_pos.unwrap()..].find('}');
813
814 if delete_start.is_none() || delete_end.is_none() {
815 return Err(OxirsError::Parse(
816 "Malformed DELETE/WHERE: missing template block".to_string(),
817 ));
818 }
819
820 let template_start = delete_pos.unwrap() + delete_start.unwrap() + 1;
821 let template_end = delete_pos.unwrap() + delete_end.unwrap();
822 let template_block = update_str[template_start..template_end].trim();
823
824 let delete_patterns = self.parse_template_patterns(template_block, &prefixes)?;
826
827 let where_start = update_str[where_pos.unwrap()..].find('{');
829 let where_end = update_str[where_pos.unwrap()..].rfind('}');
830
831 if where_start.is_none() || where_end.is_none() {
832 return Err(OxirsError::Parse(
833 "Malformed DELETE/WHERE: missing WHERE pattern block".to_string(),
834 ));
835 }
836
837 let where_pattern_start = where_pos.unwrap() + where_start.unwrap() + 1;
838 let where_pattern_end = where_pos.unwrap() + where_end.unwrap();
839 let where_block = update_str[where_pattern_start..where_pattern_end].trim();
840
841 let where_pattern = self.parse_where_pattern(where_block, &prefixes)?;
843
844 Ok(Update {
845 base: None,
846 prefixes,
847 operations: vec![UpdateOperation::Modify {
848 delete: Some(delete_patterns),
849 insert: None,
850 where_clause: Box::new(where_pattern),
851 using: crate::query::algebra::Dataset {
852 default: vec![],
853 named: vec![],
854 },
855 }],
856 })
857 }
858
859 fn parse_delete_where(
861 &self,
862 update_str: &str,
863 prefixes: HashMap<String, NamedNode>,
864 ) -> Result<Update> {
865 use crate::query::algebra::UpdateOperation;
866
867 let pattern_start = update_str.find('{');
869 let pattern_end = update_str.rfind('}');
870
871 if let (Some(start), Some(end)) = (pattern_start, pattern_end) {
872 let pattern_block = update_str[start + 1..end].trim();
873
874 let patterns = self.parse_quad_patterns(pattern_block, &prefixes)?;
876
877 Ok(Update {
878 base: None,
879 prefixes,
880 operations: vec![UpdateOperation::DeleteWhere { pattern: patterns }],
881 })
882 } else {
883 Err(OxirsError::Parse(
884 "Malformed DELETE WHERE: missing pattern block".to_string(),
885 ))
886 }
887 }
888
889 fn parse_insert_where(
891 &self,
892 update_str: &str,
893 prefixes: HashMap<String, NamedNode>,
894 ) -> Result<Update> {
895 use crate::query::algebra::UpdateOperation;
896
897 let insert_pos = update_str.find("INSERT");
899 let where_pos = update_str.find("WHERE");
900
901 if insert_pos.is_none() || where_pos.is_none() {
902 return Err(OxirsError::Parse(
903 "Malformed INSERT WHERE: missing INSERT or WHERE keyword".to_string(),
904 ));
905 }
906
907 let insert_start = update_str[insert_pos.unwrap()..].find('{');
908 let insert_end = update_str[insert_pos.unwrap()..].find('}');
909
910 if insert_start.is_none() || insert_end.is_none() {
911 return Err(OxirsError::Parse(
912 "Malformed INSERT WHERE: missing template block".to_string(),
913 ));
914 }
915
916 let template_start = insert_pos.unwrap() + insert_start.unwrap() + 1;
917 let template_end = insert_pos.unwrap() + insert_end.unwrap();
918 let template_block = update_str[template_start..template_end].trim();
919
920 let insert_patterns = self.parse_template_patterns(template_block, &prefixes)?;
922
923 let where_start = update_str[where_pos.unwrap()..].find('{');
925 let where_end = update_str[where_pos.unwrap()..].rfind('}');
926
927 if where_start.is_none() || where_end.is_none() {
928 return Err(OxirsError::Parse(
929 "Malformed INSERT WHERE: missing WHERE pattern block".to_string(),
930 ));
931 }
932
933 let where_pattern_start = where_pos.unwrap() + where_start.unwrap() + 1;
934 let where_pattern_end = where_pos.unwrap() + where_end.unwrap();
935 let where_block = update_str[where_pattern_start..where_pattern_end].trim();
936
937 let where_pattern = self.parse_where_pattern(where_block, &prefixes)?;
939
940 Ok(Update {
941 base: None,
942 prefixes,
943 operations: vec![UpdateOperation::Modify {
944 delete: None,
945 insert: Some(insert_patterns),
946 where_clause: Box::new(where_pattern),
947 using: crate::query::algebra::Dataset {
948 default: vec![],
949 named: vec![],
950 },
951 }],
952 })
953 }
954
955 fn parse_quad_patterns(
957 &self,
958 pattern_block: &str,
959 prefixes: &HashMap<String, NamedNode>,
960 ) -> Result<Vec<crate::query::algebra::QuadPattern>> {
961 use crate::format::format::RdfFormat;
962 use crate::format::RdfParser;
963 use crate::query::algebra::QuadPattern;
964 use std::io::Cursor;
965
966 let mut turtle_doc = String::new();
968 for (prefix, iri) in prefixes {
969 turtle_doc.push_str(&format!("@prefix {}: <{}> .\n", prefix, iri.as_str()));
970 }
971 turtle_doc.push_str("\n");
972 turtle_doc.push_str(pattern_block);
973
974 let parser = RdfParser::new(RdfFormat::Turtle);
976 let turtle_bytes = turtle_doc.into_bytes();
977 let cursor = Cursor::new(turtle_bytes);
978
979 let quads: Vec<Quad> = parser
980 .for_reader(cursor)
981 .collect::<std::result::Result<Vec<_>, _>>()
982 .map_err(|e| OxirsError::Parse(format!("Failed to parse pattern: {}", e)))?;
983
984 let quad_patterns: Vec<QuadPattern> = quads
986 .into_iter()
987 .map(|quad| QuadPattern {
988 subject: self.subject_to_term_pattern(&quad.subject()),
989 predicate: self.predicate_to_term_pattern(&quad.predicate()),
990 object: self.object_to_term_pattern(&quad.object()),
991 graph: Some(self.graph_to_term_pattern(&quad.graph_name())),
992 })
993 .collect();
994
995 Ok(quad_patterns)
996 }
997
998 fn parse_template_patterns(
1000 &self,
1001 template_block: &str,
1002 prefixes: &HashMap<String, NamedNode>,
1003 ) -> Result<Vec<crate::query::algebra::QuadPattern>> {
1004 use crate::query::algebra::QuadPattern;
1005
1006 let pattern_lines: Vec<&str> = template_block
1008 .split('.')
1009 .map(|s| s.trim())
1010 .filter(|s| !s.is_empty() && *s != "}")
1011 .collect();
1012
1013 let mut quad_patterns = Vec::new();
1014
1015 for line in pattern_lines {
1016 let parts: Vec<&str> = line.split_whitespace().collect();
1018 if parts.len() >= 3 {
1019 let subject = self.parse_term_pattern_with_prefix(parts[0], prefixes)?;
1020 let predicate = self.parse_term_pattern_with_prefix(parts[1], prefixes)?;
1021 let object = self.parse_term_pattern_with_prefix(parts[2], prefixes)?;
1022
1023 quad_patterns.push(QuadPattern {
1024 subject,
1025 predicate,
1026 object,
1027 graph: None, });
1029 }
1030 }
1031
1032 Ok(quad_patterns)
1033 }
1034
1035 fn parse_where_pattern(
1037 &self,
1038 pattern_block: &str,
1039 prefixes: &HashMap<String, NamedNode>,
1040 ) -> Result<crate::query::algebra::GraphPattern> {
1041 use crate::query::algebra::{AlgebraTriplePattern, GraphPattern};
1042
1043 let pattern_lines: Vec<&str> = pattern_block
1048 .split('.')
1049 .map(|s| s.trim())
1050 .filter(|s| !s.is_empty() && !s.starts_with("FILTER"))
1051 .collect();
1052
1053 let mut triple_patterns = Vec::new();
1054
1055 for line in pattern_lines {
1056 let parts: Vec<&str> = line.split_whitespace().collect();
1058 if parts.len() >= 3 {
1059 let subject = self.parse_term_pattern_with_prefix(parts[0], prefixes)?;
1060 let predicate = self.parse_term_pattern_with_prefix(parts[1], prefixes)?;
1061 let object = self.parse_term_pattern_with_prefix(parts[2], prefixes)?;
1062
1063 triple_patterns.push(AlgebraTriplePattern {
1064 subject,
1065 predicate,
1066 object,
1067 });
1068 }
1069 }
1070
1071 Ok(GraphPattern::Bgp(triple_patterns))
1072 }
1073
1074 #[allow(dead_code)]
1076 fn parse_term_pattern(&self, term_str: &str) -> Result<TermPattern> {
1077 let trimmed = term_str.trim();
1078
1079 if trimmed.starts_with('?') {
1080 let var_name = &trimmed[1..];
1082 let var = crate::model::Variable::new(var_name)
1083 .map_err(|e| OxirsError::Parse(format!("Invalid variable: {e}")))?;
1084 Ok(TermPattern::Variable(var))
1085 } else if trimmed.starts_with('<') && trimmed.ends_with('>') {
1086 let iri = &trimmed[1..trimmed.len() - 1];
1088 let node =
1089 NamedNode::new(iri).map_err(|e| OxirsError::Parse(format!("Invalid IRI: {e}")))?;
1090 Ok(TermPattern::NamedNode(node))
1091 } else if trimmed.starts_with('"') {
1092 let lit_value = trimmed.trim_matches('"');
1095 Ok(TermPattern::Literal(
1096 crate::model::Literal::new_simple_literal(lit_value),
1097 ))
1098 } else {
1099 Err(OxirsError::Parse(format!(
1100 "Cannot parse term pattern: {}",
1101 term_str
1102 )))
1103 }
1104 }
1105
1106 fn parse_term_pattern_with_prefix(
1108 &self,
1109 term_str: &str,
1110 prefixes: &HashMap<String, NamedNode>,
1111 ) -> Result<TermPattern> {
1112 let trimmed = term_str.trim();
1113
1114 if trimmed.starts_with('?') {
1115 let var_name = &trimmed[1..];
1117 let var = crate::model::Variable::new(var_name)
1118 .map_err(|e| OxirsError::Parse(format!("Invalid variable: {e}")))?;
1119 Ok(TermPattern::Variable(var))
1120 } else if trimmed.starts_with('<') && trimmed.ends_with('>') {
1121 let iri = &trimmed[1..trimmed.len() - 1];
1123 let node =
1124 NamedNode::new(iri).map_err(|e| OxirsError::Parse(format!("Invalid IRI: {e}")))?;
1125 Ok(TermPattern::NamedNode(node))
1126 } else if trimmed.starts_with('"') {
1127 let lit_value = trimmed.trim_matches('"');
1130 Ok(TermPattern::Literal(
1131 crate::model::Literal::new_simple_literal(lit_value),
1132 ))
1133 } else if trimmed.contains(':') {
1134 let parts: Vec<&str> = trimmed.splitn(2, ':').collect();
1136 if parts.len() == 2 {
1137 let prefix = parts[0];
1138 let local = parts[1];
1139
1140 if let Some(base_iri) = prefixes.get(prefix) {
1141 let full_iri = format!("{}{}", base_iri.as_str(), local);
1143 let node = NamedNode::new(&full_iri)
1144 .map_err(|e| OxirsError::Parse(format!("Invalid expanded IRI: {e}")))?;
1145 Ok(TermPattern::NamedNode(node))
1146 } else {
1147 Err(OxirsError::Parse(format!("Unknown prefix: {}", prefix)))
1148 }
1149 } else {
1150 Err(OxirsError::Parse(format!(
1151 "Invalid prefixed name: {}",
1152 term_str
1153 )))
1154 }
1155 } else {
1156 Err(OxirsError::Parse(format!(
1157 "Cannot parse term pattern: {}",
1158 term_str
1159 )))
1160 }
1161 }
1162
1163 fn subject_to_term_pattern(&self, subject: &crate::model::Subject) -> TermPattern {
1165 use crate::model::Subject;
1166 match subject {
1167 Subject::NamedNode(n) => TermPattern::NamedNode(n.clone()),
1168 Subject::BlankNode(b) => TermPattern::BlankNode(b.clone()),
1169 Subject::Variable(v) => TermPattern::Variable(v.clone()),
1170 Subject::QuotedTriple(_) => {
1171 TermPattern::Variable(
1173 crate::model::Variable::new("quotedTriple")
1174 .expect("quotedTriple is a valid variable name"),
1175 )
1176 }
1177 }
1178 }
1179
1180 fn predicate_to_term_pattern(&self, predicate: &crate::model::Predicate) -> TermPattern {
1182 use crate::model::Predicate;
1183 match predicate {
1184 Predicate::NamedNode(n) => TermPattern::NamedNode(n.clone()),
1185 Predicate::Variable(v) => TermPattern::Variable(v.clone()),
1186 }
1187 }
1188
1189 fn object_to_term_pattern(&self, object: &crate::model::Object) -> TermPattern {
1191 use crate::model::Object;
1192 match object {
1193 Object::NamedNode(n) => TermPattern::NamedNode(n.clone()),
1194 Object::BlankNode(b) => TermPattern::BlankNode(b.clone()),
1195 Object::Literal(l) => TermPattern::Literal(l.clone()),
1196 Object::Variable(v) => TermPattern::Variable(v.clone()),
1197 Object::QuotedTriple(_) => {
1198 TermPattern::Variable(
1200 crate::model::Variable::new("quotedTripleObj")
1201 .expect("quotedTripleObj is a valid variable name"),
1202 )
1203 }
1204 }
1205 }
1206
1207 fn graph_to_term_pattern(&self, graph: &GraphName) -> TermPattern {
1209 match graph {
1210 GraphName::NamedNode(n) => TermPattern::NamedNode(n.clone()),
1211 GraphName::BlankNode(b) => TermPattern::BlankNode(b.clone()),
1212 GraphName::Variable(v) => TermPattern::Variable(v.clone()),
1213 GraphName::DefaultGraph => {
1214 TermPattern::Variable(
1216 crate::model::Variable::new("defaultGraph")
1217 .expect("defaultGraph is a valid variable name"),
1218 )
1219 }
1220 }
1221 }
1222
1223 fn parse_clear(
1225 &self,
1226 update_str: &str,
1227 prefixes: HashMap<String, NamedNode>,
1228 ) -> Result<Update> {
1229 use crate::query::algebra::{GraphTarget, UpdateOperation};
1230
1231 let trimmed = update_str.trim();
1232 let silent = trimmed.contains("SILENT");
1233
1234 let graph_target = if trimmed.contains("DEFAULT") {
1235 GraphTarget::Default
1236 } else if trimmed.contains("ALL") {
1237 GraphTarget::All
1238 } else if let Some(graph_start) = trimmed.find("GRAPH") {
1239 let after_graph = &trimmed[graph_start + 5..].trim();
1241 if let Some(iri_start) = after_graph.find('<') {
1242 if let Some(iri_end) = after_graph.find('>') {
1243 let iri_str = &after_graph[iri_start + 1..iri_end];
1244 let graph_node = NamedNode::new(iri_str)
1245 .map_err(|e| OxirsError::Parse(format!("Invalid graph IRI: {e}")))?;
1246 GraphTarget::Named(graph_node)
1247 } else {
1248 return Err(OxirsError::Parse(
1249 "Malformed graph IRI in CLEAR".to_string(),
1250 ));
1251 }
1252 } else {
1253 return Err(OxirsError::Parse("Missing graph IRI in CLEAR".to_string()));
1254 }
1255 } else {
1256 GraphTarget::Default };
1258
1259 Ok(Update {
1260 base: None,
1261 prefixes,
1262 operations: vec![UpdateOperation::Clear {
1263 graph: graph_target,
1264 silent,
1265 }],
1266 })
1267 }
1268}