1use std::collections::HashMap;
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct Triple {
36 pub s: String,
37 pub p: String,
38 pub o: String,
39}
40
41impl Triple {
42 pub fn new(s: impl Into<String>, p: impl Into<String>, o: impl Into<String>) -> Self {
44 Self {
45 s: s.into(),
46 p: p.into(),
47 o: o.into(),
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Hash)]
55pub enum PatternTerm {
56 Iri(String),
57 Literal(String),
58 Variable(String),
59 BlankNode(String),
60}
61
62impl PatternTerm {
63 pub fn is_variable(&self) -> bool {
65 matches!(self, PatternTerm::Variable(_))
66 }
67
68 pub fn variable_name(&self) -> Option<&str> {
70 if let PatternTerm::Variable(name) = self {
71 Some(name.as_str())
72 } else {
73 None
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct TriplePattern {
81 pub s: PatternTerm,
82 pub p: PatternTerm,
83 pub o: PatternTerm,
84}
85
86impl TriplePattern {
87 pub fn new(s: PatternTerm, p: PatternTerm, o: PatternTerm) -> Self {
89 Self { s, o, p }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum DropType {
100 Graph,
102 Default,
104 Named,
106 All,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub enum ClearType {
113 Graph,
115 Default,
117 Named,
119 All,
121}
122
123#[derive(Debug, Clone, PartialEq)]
129pub enum SparqlUpdate {
130 InsertData(Vec<Triple>),
132
133 DeleteData(Vec<Triple>),
135
136 InsertWhere {
138 template: Vec<TriplePattern>,
139 where_clause: Vec<TriplePattern>,
140 },
141
142 DeleteWhere {
144 template: Vec<TriplePattern>,
145 where_clause: Vec<TriplePattern>,
146 },
147
148 Modify {
150 delete: Vec<TriplePattern>,
151 insert: Vec<TriplePattern>,
152 where_clause: Vec<TriplePattern>,
153 },
154
155 CreateGraph { iri: String, silent: bool },
157
158 DropGraph {
160 iri: Option<String>,
161 silent: bool,
162 drop_type: DropType,
163 },
164
165 ClearGraph {
167 iri: Option<String>,
168 silent: bool,
169 clear_type: ClearType,
170 },
171
172 CopyGraph {
174 source: String,
175 target: String,
176 silent: bool,
177 },
178
179 MoveGraph {
181 source: String,
182 target: String,
183 silent: bool,
184 },
185
186 AddGraph {
188 source: String,
189 target: String,
190 silent: bool,
191 },
192
193 Load {
195 iri: String,
196 into: Option<String>,
197 silent: bool,
198 },
199}
200
201#[derive(Debug, Clone, PartialEq)]
207pub struct ParseError {
208 pub message: String,
209 pub position: usize,
211}
212
213impl ParseError {
214 fn at(position: usize, message: impl Into<String>) -> Self {
215 Self {
216 message: message.into(),
217 position,
218 }
219 }
220}
221
222impl std::fmt::Display for ParseError {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 write!(
225 f,
226 "parse error at position {}: {}",
227 self.position, self.message
228 )
229 }
230}
231
232impl std::error::Error for ParseError {}
233
234pub struct SparqlUpdateParser;
246
247impl SparqlUpdateParser {
248 pub fn parse(input: &str) -> Result<Vec<SparqlUpdate>, ParseError> {
250 let tokens = tokenise(input);
251 let mut cursor = 0usize;
252 let mut results = Vec::new();
253
254 skip_prefixes(&tokens, &mut cursor);
256
257 while cursor < tokens.len() {
258 skip_prefixes(&tokens, &mut cursor);
259 if cursor >= tokens.len() {
260 break;
261 }
262 let update = parse_one_operation(&tokens, &mut cursor)?;
263 results.push(update);
264 if cursor < tokens.len() && tokens[cursor] == ";" {
266 cursor += 1;
267 }
268 }
269
270 Ok(results)
271 }
272
273 pub fn parse_one(input: &str) -> Result<SparqlUpdate, ParseError> {
275 let mut updates = Self::parse(input)?;
276 match updates.len() {
277 0 => Err(ParseError::at(0, "no update operation found")),
278 1 => Ok(updates.remove(0)),
279 n => Err(ParseError::at(
280 0,
281 format!("expected exactly one operation, found {n}"),
282 )),
283 }
284 }
285}
286
287fn tokenise(input: &str) -> Vec<String> {
297 let mut tokens = Vec::new();
298 let chars: Vec<char> = input.chars().collect();
299 let mut i = 0;
300
301 while i < chars.len() {
302 match chars[i] {
303 c if c.is_whitespace() => i += 1,
305 '#' => {
307 while i < chars.len() && chars[i] != '\n' {
308 i += 1;
309 }
310 }
311 '<' => {
313 let mut tok = String::from('<');
314 i += 1;
315 while i < chars.len() && chars[i] != '>' {
316 tok.push(chars[i]);
317 i += 1;
318 }
319 if i < chars.len() {
320 tok.push('>');
321 i += 1;
322 }
323 tokens.push(tok);
324 }
325 '"' => {
327 let mut tok = String::from('"');
328 i += 1;
329 while i < chars.len() && chars[i] != '"' {
330 if chars[i] == '\\' && i + 1 < chars.len() {
331 tok.push(chars[i]);
332 i += 1;
333 }
334 tok.push(chars[i]);
335 i += 1;
336 }
337 if i < chars.len() {
338 tok.push('"');
339 i += 1;
340 }
341 tokens.push(tok);
342 }
343 '\'' => {
345 let mut tok = String::from('\'');
346 i += 1;
347 while i < chars.len() && chars[i] != '\'' {
348 if chars[i] == '\\' && i + 1 < chars.len() {
349 tok.push(chars[i]);
350 i += 1;
351 }
352 tok.push(chars[i]);
353 i += 1;
354 }
355 if i < chars.len() {
356 tok.push('\'');
357 i += 1;
358 }
359 tokens.push(tok);
360 }
361 '?' | '$' => {
363 let mut tok = String::from('?');
364 i += 1;
365 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
366 tok.push(chars[i]);
367 i += 1;
368 }
369 tokens.push(tok);
370 }
371 '{' | '}' | '(' | ')' | '.' | ',' | ';' => {
373 tokens.push(chars[i].to_string());
374 i += 1;
375 }
376 '^' => {
378 if i + 1 < chars.len() && chars[i + 1] == '^' {
379 tokens.push("^^".to_string());
380 i += 2;
381 } else {
382 tokens.push("^".to_string());
383 i += 1;
384 }
385 }
386 '@' => {
388 let mut tok = String::from('@');
389 i += 1;
390 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '-') {
391 tok.push(chars[i]);
392 i += 1;
393 }
394 tokens.push(tok);
395 }
396 c if c.is_alphabetic() || c == '_' => {
398 let mut tok = String::new();
399 while i < chars.len()
400 && (chars[i].is_alphanumeric()
401 || chars[i] == '_'
402 || chars[i] == ':'
403 || chars[i] == '-')
404 {
405 tok.push(chars[i]);
406 i += 1;
407 }
408 tokens.push(tok);
409 }
410 _ => {
412 let mut tok = String::new();
413 while i < chars.len()
414 && !chars[i].is_whitespace()
415 && !matches!(chars[i], '{' | '}' | '(' | ')' | ',' | ';')
416 {
417 tok.push(chars[i]);
418 i += 1;
419 }
420 if !tok.is_empty() {
421 tokens.push(tok);
422 }
423 }
424 }
425 }
426
427 tokens
428}
429
430fn peek(tokens: &[String], cursor: usize) -> Option<&str> {
435 tokens.get(cursor).map(|s| s.as_str())
436}
437
438fn expect<'a>(
439 tokens: &'a [String],
440 cursor: &mut usize,
441 expected: &str,
442) -> Result<&'a str, ParseError> {
443 match tokens.get(*cursor) {
444 Some(tok) if tok.to_uppercase() == expected.to_uppercase() => {
445 *cursor += 1;
446 Ok(tok.as_str())
447 }
448 Some(tok) => Err(ParseError::at(
449 *cursor,
450 format!("expected '{expected}', found '{tok}'"),
451 )),
452 None => Err(ParseError::at(
453 *cursor,
454 format!("expected '{expected}', found end of input"),
455 )),
456 }
457}
458
459fn consume_keyword(tokens: &[String], cursor: &mut usize, keyword: &str) -> bool {
460 match tokens.get(*cursor) {
461 Some(tok) if tok.to_uppercase() == keyword.to_uppercase() => {
462 *cursor += 1;
463 true
464 }
465 _ => false,
466 }
467}
468
469fn consume(tokens: &[String], cursor: &mut usize) -> Result<String, ParseError> {
471 tokens
472 .get(*cursor)
473 .map(|t| {
474 *cursor += 1;
475 t.clone()
476 })
477 .ok_or_else(|| ParseError::at(*cursor, "unexpected end of input"))
478}
479
480fn parse_iri(tokens: &[String], cursor: &mut usize) -> Result<String, ParseError> {
482 match tokens.get(*cursor) {
483 Some(tok) if tok.starts_with('<') && tok.ends_with('>') => {
484 let iri = tok[1..tok.len() - 1].to_string();
485 *cursor += 1;
486 Ok(iri)
487 }
488 Some(tok) => Err(ParseError::at(
489 *cursor,
490 format!("expected IRI, found '{tok}'"),
491 )),
492 None => Err(ParseError::at(*cursor, "expected IRI, found end of input")),
493 }
494}
495
496fn parse_silent(tokens: &[String], cursor: &mut usize) -> bool {
498 consume_keyword(tokens, cursor, "SILENT")
499}
500
501fn skip_prefixes(tokens: &[String], cursor: &mut usize) {
503 while let Some(tok) = tokens.get(*cursor) {
504 if tok.to_uppercase() != "PREFIX" {
505 break;
506 }
507 *cursor += 1; *cursor += 1;
510 *cursor += 1;
512 }
513}
514
515fn parse_triple_block(tokens: &[String], cursor: &mut usize) -> Result<Vec<Triple>, ParseError> {
522 expect(tokens, cursor, "{")?;
523 let mut triples = Vec::new();
524
525 while let Some(tok) = tokens.get(*cursor) {
526 if tok == "}" {
527 break;
528 }
529 if tok == "." {
530 *cursor += 1;
531 continue;
532 }
533
534 let s = parse_term_str(tokens, cursor)?;
535 let p = parse_term_str(tokens, cursor)?;
536 let o = parse_term_str(tokens, cursor)?;
537 triples.push(Triple::new(s, p, o));
538
539 if matches!(peek(tokens, *cursor), Some(".")) {
541 *cursor += 1;
542 }
543 }
544
545 expect(tokens, cursor, "}")?;
546 Ok(triples)
547}
548
549fn parse_pattern_block(
551 tokens: &[String],
552 cursor: &mut usize,
553) -> Result<Vec<TriplePattern>, ParseError> {
554 expect(tokens, cursor, "{")?;
555 let mut patterns = Vec::new();
556
557 while let Some(tok) = tokens.get(*cursor) {
558 if tok == "}" {
559 break;
560 }
561 if tok == "." {
562 *cursor += 1;
563 continue;
564 }
565
566 let s = parse_pattern_term(tokens, cursor)?;
567 let p = parse_pattern_term(tokens, cursor)?;
568 let o = parse_pattern_term(tokens, cursor)?;
569 patterns.push(TriplePattern::new(s, p, o));
570
571 if matches!(peek(tokens, *cursor), Some(".")) {
572 *cursor += 1;
573 }
574 }
575
576 expect(tokens, cursor, "}")?;
577 Ok(patterns)
578}
579
580fn parse_term_str(tokens: &[String], cursor: &mut usize) -> Result<String, ParseError> {
582 let tok = consume(tokens, cursor)?;
583 if tok.starts_with('<') && tok.ends_with('>') {
585 return Ok(tok[1..tok.len() - 1].to_string());
586 }
587 Ok(tok)
588}
589
590fn parse_pattern_term(tokens: &[String], cursor: &mut usize) -> Result<PatternTerm, ParseError> {
592 let tok = consume(tokens, cursor)?;
593
594 if let Some(stripped) = tok.strip_prefix('?') {
596 return Ok(PatternTerm::Variable(stripped.to_string()));
597 }
598
599 if tok.starts_with('<') && tok.ends_with('>') {
601 return Ok(PatternTerm::Iri(tok[1..tok.len() - 1].to_string()));
602 }
603
604 if let Some(stripped) = tok.strip_prefix("_:") {
606 return Ok(PatternTerm::BlankNode(stripped.to_string()));
607 }
608
609 if tok.starts_with('"') || tok.starts_with('\'') {
611 if matches!(peek(tokens, *cursor), Some(t) if t.starts_with('@')) {
613 let _lang = consume(tokens, cursor)?;
614 } else if matches!(peek(tokens, *cursor), Some("^^")) {
615 *cursor += 1; let _dt = consume(tokens, cursor)?;
617 }
618 return Ok(PatternTerm::Literal(tok));
619 }
620
621 Ok(PatternTerm::Iri(tok))
623}
624
625fn parse_one_operation(tokens: &[String], cursor: &mut usize) -> Result<SparqlUpdate, ParseError> {
630 let keyword = match tokens.get(*cursor) {
631 Some(k) => k.to_uppercase(),
632 None => return Err(ParseError::at(*cursor, "unexpected end of input")),
633 };
634
635 match keyword.as_str() {
636 "INSERT" => {
637 *cursor += 1;
638 if matches!(peek(tokens, *cursor), Some(t) if t.to_uppercase() == "DATA") {
639 *cursor += 1;
640 let triples = parse_triple_block(tokens, cursor)?;
641 Ok(SparqlUpdate::InsertData(triples))
642 } else {
643 let template = parse_pattern_block(tokens, cursor)?;
645 expect(tokens, cursor, "WHERE")?;
646 let where_clause = parse_pattern_block(tokens, cursor)?;
647 Ok(SparqlUpdate::InsertWhere {
648 template,
649 where_clause,
650 })
651 }
652 }
653 "DELETE" => {
654 *cursor += 1;
655 if matches!(peek(tokens, *cursor), Some(t) if t.to_uppercase() == "DATA") {
656 *cursor += 1;
657 let triples = parse_triple_block(tokens, cursor)?;
658 Ok(SparqlUpdate::DeleteData(triples))
659 } else if matches!(peek(tokens, *cursor), Some(t) if t.to_uppercase() == "WHERE") {
660 *cursor += 1;
662 let where_clause = parse_pattern_block(tokens, cursor)?;
663 Ok(SparqlUpdate::DeleteWhere {
664 template: vec![],
665 where_clause,
666 })
667 } else {
668 let delete_template = parse_pattern_block(tokens, cursor)?;
670 let insert_template = if matches!(peek(tokens, *cursor), Some(t) if t.to_uppercase() == "INSERT")
671 {
672 *cursor += 1;
673 parse_pattern_block(tokens, cursor)?
674 } else {
675 vec![]
676 };
677 expect(tokens, cursor, "WHERE")?;
678 let where_clause = parse_pattern_block(tokens, cursor)?;
679 Ok(SparqlUpdate::Modify {
680 delete: delete_template,
681 insert: insert_template,
682 where_clause,
683 })
684 }
685 }
686 "CREATE" => {
687 *cursor += 1;
688 let silent = parse_silent(tokens, cursor);
689 consume_keyword(tokens, cursor, "GRAPH");
690 let iri = parse_iri(tokens, cursor)?;
691 Ok(SparqlUpdate::CreateGraph { iri, silent })
692 }
693 "DROP" => {
694 *cursor += 1;
695 let silent = parse_silent(tokens, cursor);
696 parse_graph_target_update(tokens, cursor, |iri, drop_type| SparqlUpdate::DropGraph {
697 iri,
698 silent,
699 drop_type: drop_type.into_drop(),
700 })
701 }
702 "CLEAR" => {
703 *cursor += 1;
704 let silent = parse_silent(tokens, cursor);
705 parse_graph_target_update(tokens, cursor, |iri, clear_type| SparqlUpdate::ClearGraph {
706 iri,
707 silent,
708 clear_type: clear_type.into_clear(),
709 })
710 }
711 "COPY" => {
712 *cursor += 1;
713 let silent = parse_silent(tokens, cursor);
714 let source = parse_iri(tokens, cursor)?;
715 expect(tokens, cursor, "TO")?;
716 let target = parse_iri(tokens, cursor)?;
717 Ok(SparqlUpdate::CopyGraph {
718 source,
719 target,
720 silent,
721 })
722 }
723 "MOVE" => {
724 *cursor += 1;
725 let silent = parse_silent(tokens, cursor);
726 let source = parse_iri(tokens, cursor)?;
727 expect(tokens, cursor, "TO")?;
728 let target = parse_iri(tokens, cursor)?;
729 Ok(SparqlUpdate::MoveGraph {
730 source,
731 target,
732 silent,
733 })
734 }
735 "ADD" => {
736 *cursor += 1;
737 let silent = parse_silent(tokens, cursor);
738 let source = parse_iri(tokens, cursor)?;
739 expect(tokens, cursor, "TO")?;
740 let target = parse_iri(tokens, cursor)?;
741 Ok(SparqlUpdate::AddGraph {
742 source,
743 target,
744 silent,
745 })
746 }
747 "LOAD" => {
748 *cursor += 1;
749 let silent = parse_silent(tokens, cursor);
750 let iri = parse_iri(tokens, cursor)?;
751 let into = if consume_keyword(tokens, cursor, "INTO") {
752 consume_keyword(tokens, cursor, "GRAPH");
753 Some(parse_iri(tokens, cursor)?)
754 } else {
755 None
756 };
757 Ok(SparqlUpdate::Load { iri, into, silent })
758 }
759 other => Err(ParseError::at(
760 *cursor,
761 format!("unknown update operation keyword: '{other}'"),
762 )),
763 }
764}
765
766enum GraphScope {
772 GraphIri,
773 Default,
774 Named,
775 All,
776}
777
778impl GraphScope {
779 fn into_drop(self) -> DropType {
780 match self {
781 GraphScope::GraphIri => DropType::Graph,
782 GraphScope::Default => DropType::Default,
783 GraphScope::Named => DropType::Named,
784 GraphScope::All => DropType::All,
785 }
786 }
787
788 fn into_clear(self) -> ClearType {
789 match self {
790 GraphScope::GraphIri => ClearType::Graph,
791 GraphScope::Default => ClearType::Default,
792 GraphScope::Named => ClearType::Named,
793 GraphScope::All => ClearType::All,
794 }
795 }
796}
797
798fn parse_graph_target_update<F>(
799 tokens: &[String],
800 cursor: &mut usize,
801 builder: F,
802) -> Result<SparqlUpdate, ParseError>
803where
804 F: FnOnce(Option<String>, GraphScope) -> SparqlUpdate,
805{
806 let keyword = tokens.get(*cursor).map(|t| t.to_uppercase());
807 match keyword.as_deref() {
808 Some("DEFAULT") => {
809 *cursor += 1;
810 let scope = GraphScope::Default;
811 Ok(builder(None, scope))
812 }
813 Some("NAMED") => {
814 *cursor += 1;
815 let scope = GraphScope::Named;
816 Ok(builder(None, scope))
817 }
818 Some("ALL") => {
819 *cursor += 1;
820 let scope = GraphScope::All;
821 Ok(builder(None, scope))
822 }
823 Some("GRAPH") => {
824 *cursor += 1;
825 let iri = parse_iri(tokens, cursor)?;
826 let scope = GraphScope::GraphIri;
827 Ok(builder(Some(iri), scope))
828 }
829 Some(tok) if tok.starts_with('<') => {
831 let iri = parse_iri(tokens, cursor)?;
832 let scope = GraphScope::GraphIri;
833 Ok(builder(Some(iri), scope))
834 }
835 _ => Err(ParseError::at(
836 *cursor,
837 "expected graph scope (DEFAULT | NAMED | ALL | GRAPH <iri>)",
838 )),
839 }
840}
841
842#[derive(Debug, Clone, Default, PartialEq, Eq)]
848pub struct UpdateResult {
849 pub triples_inserted: usize,
851 pub triples_deleted: usize,
853 pub graphs_affected: usize,
855}
856
857#[derive(Debug, Clone, PartialEq)]
863pub struct ArqError(pub String);
864
865impl std::fmt::Display for ArqError {
866 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
867 write!(f, "ARQ error: {}", self.0)
868 }
869}
870
871impl std::error::Error for ArqError {}
872
873pub struct UpdateExecutor {
884 triples: Vec<Triple>,
886 named_graphs: HashMap<String, Vec<Triple>>,
888}
889
890impl UpdateExecutor {
891 pub fn new() -> Self {
893 Self {
894 triples: Vec::new(),
895 named_graphs: HashMap::new(),
896 }
897 }
898
899 pub fn execute(&mut self, update: &SparqlUpdate) -> Result<UpdateResult, ArqError> {
901 match update {
902 SparqlUpdate::InsertData(triples) => {
903 let count = triples.len();
904 self.triples.extend(triples.iter().cloned());
905 Ok(UpdateResult {
906 triples_inserted: count,
907 triples_deleted: 0,
908 graphs_affected: 0,
909 })
910 }
911 SparqlUpdate::DeleteData(triples) => {
912 let before = self.triples.len();
913 for t in triples {
914 self.triples.retain(|existing| existing != t);
915 }
916 let deleted = before - self.triples.len();
917 Ok(UpdateResult {
918 triples_inserted: 0,
919 triples_deleted: deleted,
920 graphs_affected: 0,
921 })
922 }
923 SparqlUpdate::InsertWhere {
924 template,
925 where_clause,
926 } => {
927 let bindings = self.match_patterns(where_clause);
928 let mut inserted = 0usize;
929 for binding in &bindings {
930 if let Some(triple) = instantiate_template_triple(template.first(), binding) {
931 for t in &triple {
932 if !self.triples.contains(t) {
933 self.triples.push(t.clone());
934 inserted += 1;
935 }
936 }
937 }
938 }
939 Ok(UpdateResult {
940 triples_inserted: inserted,
941 triples_deleted: 0,
942 graphs_affected: 0,
943 })
944 }
945 SparqlUpdate::DeleteWhere { where_clause, .. } => {
946 let bindings = self.match_patterns(where_clause);
947 let to_delete: Vec<Triple> = bindings
948 .into_iter()
949 .filter_map(|b| {
950 let s = b.get("s").cloned()?;
951 let p = b.get("p").cloned()?;
952 let o = b.get("o").cloned()?;
953 Some(Triple::new(s, p, o))
954 })
955 .collect();
956 let before = self.triples.len();
957 for t in &to_delete {
958 self.triples.retain(|e| e != t);
959 }
960 let deleted = before - self.triples.len();
961 Ok(UpdateResult {
962 triples_inserted: 0,
963 triples_deleted: deleted,
964 graphs_affected: 0,
965 })
966 }
967 SparqlUpdate::Modify {
968 delete,
969 insert,
970 where_clause,
971 } => {
972 let bindings = self.match_patterns(where_clause);
973 let mut inserted = 0usize;
974 let mut deleted_count = 0usize;
975 for binding in &bindings {
976 for tp in delete {
978 if let Some(t) = instantiate_one(tp, binding) {
979 let before = self.triples.len();
980 self.triples.retain(|e| e != &t);
981 deleted_count += before - self.triples.len();
982 }
983 }
984 for tp in insert {
986 if let Some(t) = instantiate_one(tp, binding) {
987 if !self.triples.contains(&t) {
988 self.triples.push(t);
989 inserted += 1;
990 }
991 }
992 }
993 }
994 Ok(UpdateResult {
995 triples_inserted: inserted,
996 triples_deleted: deleted_count,
997 graphs_affected: 0,
998 })
999 }
1000 SparqlUpdate::CreateGraph { iri, silent } => {
1001 if self.named_graphs.contains_key(iri) && !silent {
1002 return Err(ArqError(format!("graph <{iri}> already exists")));
1003 }
1004 self.named_graphs.entry(iri.clone()).or_default();
1005 Ok(UpdateResult {
1006 triples_inserted: 0,
1007 triples_deleted: 0,
1008 graphs_affected: 1,
1009 })
1010 }
1011 SparqlUpdate::DropGraph {
1012 iri,
1013 silent,
1014 drop_type,
1015 } => {
1016 let count = match drop_type {
1017 DropType::Graph => {
1018 let key = iri.as_deref().unwrap_or("");
1019 if self.named_graphs.remove(key).is_none() && !silent {
1020 return Err(ArqError(format!("graph <{key}> does not exist")));
1021 }
1022 1
1023 }
1024 DropType::Default => {
1025 self.triples.clear();
1026 1
1027 }
1028 DropType::Named => {
1029 let count = self.named_graphs.len();
1030 self.named_graphs.clear();
1031 count
1032 }
1033 DropType::All => {
1034 let ng = self.named_graphs.len();
1035 self.named_graphs.clear();
1036 self.triples.clear();
1037 ng + 1
1038 }
1039 };
1040 Ok(UpdateResult {
1041 triples_inserted: 0,
1042 triples_deleted: 0,
1043 graphs_affected: count,
1044 })
1045 }
1046 SparqlUpdate::ClearGraph {
1047 iri,
1048 silent,
1049 clear_type,
1050 } => {
1051 let count = match clear_type {
1052 ClearType::Graph => {
1053 let key = iri.as_deref().unwrap_or("");
1054 match self.named_graphs.get_mut(key) {
1055 Some(g) => {
1056 g.clear();
1057 1
1058 }
1059 None if *silent => 0,
1060 None => return Err(ArqError(format!("graph <{key}> does not exist"))),
1061 }
1062 }
1063 ClearType::Default => {
1064 self.triples.clear();
1065 1
1066 }
1067 ClearType::Named => {
1068 for g in self.named_graphs.values_mut() {
1069 g.clear();
1070 }
1071 self.named_graphs.len()
1072 }
1073 ClearType::All => {
1074 self.triples.clear();
1075 for g in self.named_graphs.values_mut() {
1076 g.clear();
1077 }
1078 self.named_graphs.len() + 1
1079 }
1080 };
1081 Ok(UpdateResult {
1082 triples_inserted: 0,
1083 triples_deleted: 0,
1084 graphs_affected: count,
1085 })
1086 }
1087 SparqlUpdate::CopyGraph {
1088 source,
1089 target,
1090 silent: _,
1091 } => {
1092 let src_triples: Vec<Triple> =
1093 self.named_graphs.get(source).cloned().unwrap_or_default();
1094 let count = src_triples.len();
1095 let tgt = self.named_graphs.entry(target.clone()).or_default();
1096 tgt.clear();
1097 tgt.extend(src_triples);
1098 Ok(UpdateResult {
1099 triples_inserted: count,
1100 triples_deleted: 0,
1101 graphs_affected: 1,
1102 })
1103 }
1104 SparqlUpdate::MoveGraph {
1105 source,
1106 target,
1107 silent: _,
1108 } => {
1109 let src_triples = self.named_graphs.remove(source).unwrap_or_default();
1110 let count = src_triples.len();
1111 let tgt = self.named_graphs.entry(target.clone()).or_default();
1112 tgt.clear();
1113 tgt.extend(src_triples);
1114 Ok(UpdateResult {
1115 triples_inserted: count,
1116 triples_deleted: 0,
1117 graphs_affected: 2,
1118 })
1119 }
1120 SparqlUpdate::AddGraph {
1121 source,
1122 target,
1123 silent: _,
1124 } => {
1125 let src_triples: Vec<Triple> =
1126 self.named_graphs.get(source).cloned().unwrap_or_default();
1127 let count = src_triples.len();
1128 let tgt = self.named_graphs.entry(target.clone()).or_default();
1129 tgt.extend(src_triples);
1130 Ok(UpdateResult {
1131 triples_inserted: count,
1132 triples_deleted: 0,
1133 graphs_affected: 1,
1134 })
1135 }
1136 SparqlUpdate::Load { iri, into, silent } => {
1137 if *silent {
1140 Ok(UpdateResult::default())
1141 } else {
1142 Err(ArqError(format!(
1143 "LOAD is not supported in the in-memory executor (iri=<{iri}>, into={into:?})"
1144 )))
1145 }
1146 }
1147 }
1148 }
1149
1150 pub fn execute_all(&mut self, updates: &[SparqlUpdate]) -> Result<Vec<UpdateResult>, ArqError> {
1152 updates.iter().map(|u| self.execute(u)).collect()
1153 }
1154
1155 pub fn triple_count(&self) -> usize {
1157 self.triples.len()
1158 }
1159
1160 pub fn graph_count(&self) -> usize {
1162 self.named_graphs.len()
1163 }
1164
1165 pub fn get_graph(&self, iri: &str) -> Option<&Vec<Triple>> {
1167 self.named_graphs.get(iri)
1168 }
1169
1170 pub fn default_graph(&self) -> &Vec<Triple> {
1172 &self.triples
1173 }
1174}
1175
1176impl Default for UpdateExecutor {
1177 fn default() -> Self {
1178 Self::new()
1179 }
1180}
1181
1182type Binding = HashMap<String, String>;
1187
1188fn match_patterns(triples: &[Triple], patterns: &[TriplePattern]) -> Vec<Binding> {
1193 let mut results: Vec<Binding> = vec![HashMap::new()];
1194
1195 for pattern in patterns {
1196 let mut next: Vec<Binding> = Vec::new();
1197 for binding in &results {
1198 for triple in triples {
1199 if let Some(new_binding) = match_pattern(triple, pattern, binding) {
1200 next.push(new_binding);
1201 }
1202 }
1203 }
1204 results = next;
1205 }
1206
1207 results
1208}
1209
1210fn match_pattern(triple: &Triple, pattern: &TriplePattern, existing: &Binding) -> Option<Binding> {
1213 let mut binding = existing.clone();
1214 bind_term(&triple.s, &pattern.s, &mut binding)?;
1215 bind_term(&triple.p, &pattern.p, &mut binding)?;
1216 bind_term(&triple.o, &pattern.o, &mut binding)?;
1217 Some(binding)
1218}
1219
1220fn bind_term(value: &str, term: &PatternTerm, binding: &mut Binding) -> Option<()> {
1223 match term {
1224 PatternTerm::Variable(var) => {
1225 if let Some(existing) = binding.get(var.as_str()) {
1226 if existing != value {
1227 return None;
1228 }
1229 } else {
1230 binding.insert(var.clone(), value.to_string());
1231 }
1232 Some(())
1233 }
1234 PatternTerm::Iri(iri) => {
1235 if iri == value {
1236 Some(())
1237 } else {
1238 None
1239 }
1240 }
1241 PatternTerm::Literal(lit) => {
1242 let inner = lit.trim_matches('"').trim_matches('\'');
1244 if inner == value || lit == value {
1245 Some(())
1246 } else {
1247 None
1248 }
1249 }
1250 PatternTerm::BlankNode(bn) => {
1251 if bn == value {
1252 Some(())
1253 } else {
1254 None
1255 }
1256 }
1257 }
1258}
1259
1260impl UpdateExecutor {
1261 fn match_patterns(&self, patterns: &[TriplePattern]) -> Vec<Binding> {
1263 match_patterns(&self.triples, patterns)
1264 }
1265}
1266
1267fn instantiate_one(pattern: &TriplePattern, binding: &Binding) -> Option<Triple> {
1270 let s = resolve_term(&pattern.s, binding)?;
1271 let p = resolve_term(&pattern.p, binding)?;
1272 let o = resolve_term(&pattern.o, binding)?;
1273 Some(Triple::new(s, p, o))
1274}
1275
1276fn instantiate_template_triple(
1279 template: Option<&TriplePattern>,
1280 binding: &Binding,
1281) -> Option<Vec<Triple>> {
1282 let tp = template?;
1283 Some(instantiate_one(tp, binding).into_iter().collect())
1284}
1285
1286fn resolve_term(term: &PatternTerm, binding: &Binding) -> Option<String> {
1289 match term {
1290 PatternTerm::Variable(var) => binding.get(var.as_str()).cloned(),
1291 PatternTerm::Iri(iri) => Some(iri.clone()),
1292 PatternTerm::Literal(lit) => Some(lit.trim_matches('"').trim_matches('\'').to_string()),
1293 PatternTerm::BlankNode(bn) => Some(bn.clone()),
1294 }
1295}
1296
1297#[cfg(test)]
1302mod tests {
1303 use super::*;
1304
1305 #[test]
1310 fn test_parse_insert_data_single_triple() {
1311 let input = "INSERT DATA { <http://a> <http://b> <http://c> }";
1312 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1313 match update {
1314 SparqlUpdate::InsertData(triples) => {
1315 assert_eq!(triples.len(), 1);
1316 assert_eq!(triples[0].s, "http://a");
1317 assert_eq!(triples[0].p, "http://b");
1318 assert_eq!(triples[0].o, "http://c");
1319 }
1320 _ => panic!("wrong variant"),
1321 }
1322 }
1323
1324 #[test]
1325 fn test_parse_insert_data_multiple_triples() {
1326 let input = "INSERT DATA { <s1> <p1> <o1> . <s2> <p2> <o2> }";
1327 let result = SparqlUpdateParser::parse_one(input).expect("parse failed");
1328 if let SparqlUpdate::InsertData(triples) = result {
1329 assert_eq!(triples.len(), 2);
1330 } else {
1331 panic!("wrong variant");
1332 }
1333 }
1334
1335 #[test]
1340 fn test_parse_delete_data() {
1341 let input = "DELETE DATA { <http://x> <http://y> <http://z> }";
1342 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1343 match update {
1344 SparqlUpdate::DeleteData(triples) => {
1345 assert_eq!(triples.len(), 1);
1346 assert_eq!(triples[0].s, "http://x");
1347 }
1348 _ => panic!("wrong variant"),
1349 }
1350 }
1351
1352 #[test]
1357 fn test_parse_create_graph() {
1358 let input = "CREATE GRAPH <http://example.org/g>";
1359 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1360 match update {
1361 SparqlUpdate::CreateGraph { iri, silent } => {
1362 assert_eq!(iri, "http://example.org/g");
1363 assert!(!silent);
1364 }
1365 _ => panic!("wrong variant"),
1366 }
1367 }
1368
1369 #[test]
1370 fn test_parse_create_graph_silent() {
1371 let input = "CREATE SILENT GRAPH <http://example.org/g>";
1372 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1373 match update {
1374 SparqlUpdate::CreateGraph { iri: _, silent } => {
1375 assert!(silent);
1376 }
1377 _ => panic!("wrong variant"),
1378 }
1379 }
1380
1381 #[test]
1386 fn test_parse_drop_graph_named() {
1387 let input = "DROP GRAPH <http://g>";
1388 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1389 match update {
1390 SparqlUpdate::DropGraph {
1391 iri,
1392 silent,
1393 drop_type,
1394 } => {
1395 assert_eq!(iri, Some("http://g".to_string()));
1396 assert!(!silent);
1397 assert_eq!(drop_type, DropType::Graph);
1398 }
1399 _ => panic!("wrong variant"),
1400 }
1401 }
1402
1403 #[test]
1404 fn test_parse_drop_all() {
1405 let input = "DROP ALL";
1406 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1407 match update {
1408 SparqlUpdate::DropGraph { drop_type, .. } => {
1409 assert_eq!(drop_type, DropType::All);
1410 }
1411 _ => panic!("wrong variant"),
1412 }
1413 }
1414
1415 #[test]
1420 fn test_parse_clear_default() {
1421 let input = "CLEAR DEFAULT";
1422 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1423 match update {
1424 SparqlUpdate::ClearGraph { clear_type, .. } => {
1425 assert_eq!(clear_type, ClearType::Default);
1426 }
1427 _ => panic!("wrong variant"),
1428 }
1429 }
1430
1431 #[test]
1436 fn test_parse_copy_graph() {
1437 let input = "COPY <http://src> TO <http://dst>";
1438 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1439 match update {
1440 SparqlUpdate::CopyGraph {
1441 source,
1442 target,
1443 silent,
1444 } => {
1445 assert_eq!(source, "http://src");
1446 assert_eq!(target, "http://dst");
1447 assert!(!silent);
1448 }
1449 _ => panic!("wrong variant"),
1450 }
1451 }
1452
1453 #[test]
1454 fn test_parse_move_graph() {
1455 let input = "MOVE <http://old> TO <http://new>";
1456 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1457 match update {
1458 SparqlUpdate::MoveGraph { source, target, .. } => {
1459 assert_eq!(source, "http://old");
1460 assert_eq!(target, "http://new");
1461 }
1462 _ => panic!("wrong variant"),
1463 }
1464 }
1465
1466 #[test]
1467 fn test_parse_add_graph() {
1468 let input = "ADD <http://src> TO <http://dst>";
1469 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1470 match update {
1471 SparqlUpdate::AddGraph { .. } => {}
1472 _ => panic!("wrong variant"),
1473 }
1474 }
1475
1476 #[test]
1481 fn test_parse_load_basic() {
1482 let input = "LOAD <http://data.example.org/data.ttl>";
1483 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1484 match update {
1485 SparqlUpdate::Load { iri, into, silent } => {
1486 assert_eq!(iri, "http://data.example.org/data.ttl");
1487 assert!(into.is_none());
1488 assert!(!silent);
1489 }
1490 _ => panic!("wrong variant"),
1491 }
1492 }
1493
1494 #[test]
1495 fn test_parse_load_into_graph() {
1496 let input = "LOAD <http://src.ttl> INTO GRAPH <http://target>";
1497 let update = SparqlUpdateParser::parse_one(input).expect("parse failed");
1498 match update {
1499 SparqlUpdate::Load { into, .. } => {
1500 assert_eq!(into, Some("http://target".to_string()));
1501 }
1502 _ => panic!("wrong variant"),
1503 }
1504 }
1505
1506 #[test]
1511 fn test_parse_multiple_operations() {
1512 let input = "INSERT DATA { <s1> <p1> <o1> } ; DELETE DATA { <s2> <p2> <o2> }";
1513 let updates = SparqlUpdateParser::parse(input).expect("parse failed");
1514 assert_eq!(updates.len(), 2);
1515 }
1516
1517 #[test]
1518 fn test_parse_empty_input() {
1519 let updates = SparqlUpdateParser::parse("").expect("parse failed");
1520 assert!(updates.is_empty());
1521 }
1522
1523 #[test]
1524 fn test_parse_unknown_keyword_returns_error() {
1525 let result = SparqlUpdateParser::parse_one("FROBULATE { }");
1526 assert!(result.is_err());
1527 }
1528
1529 #[test]
1534 fn test_executor_insert_data() {
1535 let mut exec = UpdateExecutor::new();
1536 let update = SparqlUpdate::InsertData(vec![Triple::new("s", "p", "o")]);
1537 let result = exec.execute(&update).expect("execute failed");
1538 assert_eq!(result.triples_inserted, 1);
1539 assert_eq!(exec.triple_count(), 1);
1540 }
1541
1542 #[test]
1543 fn test_executor_delete_data_existing() {
1544 let mut exec = UpdateExecutor::new();
1545 let t = Triple::new("s", "p", "o");
1546 exec.execute(&SparqlUpdate::InsertData(vec![t.clone()]))
1547 .expect("insert failed");
1548 let result = exec
1549 .execute(&SparqlUpdate::DeleteData(vec![t]))
1550 .expect("delete failed");
1551 assert_eq!(result.triples_deleted, 1);
1552 assert_eq!(exec.triple_count(), 0);
1553 }
1554
1555 #[test]
1556 fn test_executor_delete_data_nonexistent() {
1557 let mut exec = UpdateExecutor::new();
1558 let result = exec
1559 .execute(&SparqlUpdate::DeleteData(vec![Triple::new("x", "y", "z")]))
1560 .expect("delete failed");
1561 assert_eq!(result.triples_deleted, 0);
1562 }
1563
1564 #[test]
1569 fn test_executor_create_graph() {
1570 let mut exec = UpdateExecutor::new();
1571 exec.execute(&SparqlUpdate::CreateGraph {
1572 iri: "http://g".to_string(),
1573 silent: false,
1574 })
1575 .expect("create failed");
1576 assert_eq!(exec.graph_count(), 1);
1577 assert!(exec.get_graph("http://g").is_some());
1578 }
1579
1580 #[test]
1581 fn test_executor_create_duplicate_graph_silent() {
1582 let mut exec = UpdateExecutor::new();
1583 let update = SparqlUpdate::CreateGraph {
1584 iri: "http://g".to_string(),
1585 silent: true,
1586 };
1587 exec.execute(&update).expect("first create failed");
1588 exec.execute(&update)
1589 .expect("second create (silent) should not error");
1590 }
1591
1592 #[test]
1593 fn test_executor_create_duplicate_graph_non_silent_errors() {
1594 let mut exec = UpdateExecutor::new();
1595 let update = SparqlUpdate::CreateGraph {
1596 iri: "http://g".to_string(),
1597 silent: false,
1598 };
1599 exec.execute(&update).expect("first create failed");
1600 let result = exec.execute(&update);
1601 assert!(result.is_err());
1602 }
1603
1604 #[test]
1605 fn test_executor_drop_named_graph() {
1606 let mut exec = UpdateExecutor::new();
1607 exec.execute(&SparqlUpdate::CreateGraph {
1608 iri: "http://g".to_string(),
1609 silent: false,
1610 })
1611 .expect("create failed");
1612 exec.execute(&SparqlUpdate::DropGraph {
1613 iri: Some("http://g".to_string()),
1614 silent: false,
1615 drop_type: DropType::Graph,
1616 })
1617 .expect("drop failed");
1618 assert_eq!(exec.graph_count(), 0);
1619 }
1620
1621 #[test]
1626 fn test_executor_clear_default_graph() {
1627 let mut exec = UpdateExecutor::new();
1628 exec.execute(&SparqlUpdate::InsertData(vec![
1629 Triple::new("s1", "p1", "o1"),
1630 Triple::new("s2", "p2", "o2"),
1631 ]))
1632 .expect("insert failed");
1633 exec.execute(&SparqlUpdate::ClearGraph {
1634 iri: None,
1635 silent: false,
1636 clear_type: ClearType::Default,
1637 })
1638 .expect("clear failed");
1639 assert_eq!(exec.triple_count(), 0);
1640 }
1641
1642 #[test]
1647 fn test_executor_execute_all() {
1648 let mut exec = UpdateExecutor::new();
1649 let updates = vec![
1650 SparqlUpdate::InsertData(vec![Triple::new("a", "b", "c")]),
1651 SparqlUpdate::InsertData(vec![Triple::new("d", "e", "f")]),
1652 ];
1653 let results = exec.execute_all(&updates).expect("execute_all failed");
1654 assert_eq!(results.len(), 2);
1655 assert_eq!(exec.triple_count(), 2);
1656 }
1657
1658 #[test]
1663 fn test_triple_equality() {
1664 let t1 = Triple::new("s", "p", "o");
1665 let t2 = Triple::new("s", "p", "o");
1666 assert_eq!(t1, t2);
1667 }
1668
1669 #[test]
1670 fn test_pattern_term_is_variable() {
1671 let var = PatternTerm::Variable("x".to_string());
1672 assert!(var.is_variable());
1673 let iri = PatternTerm::Iri("http://a".to_string());
1674 assert!(!iri.is_variable());
1675 }
1676
1677 #[test]
1678 fn test_pattern_term_variable_name() {
1679 let var = PatternTerm::Variable("myVar".to_string());
1680 assert_eq!(var.variable_name(), Some("myVar"));
1681 let iri = PatternTerm::Iri("http://a".to_string());
1682 assert_eq!(iri.variable_name(), None);
1683 }
1684
1685 #[test]
1690 fn test_executor_copy_graph() {
1691 let mut exec = UpdateExecutor::new();
1692 exec.execute(&SparqlUpdate::CreateGraph {
1693 iri: "http://src".into(),
1694 silent: false,
1695 })
1696 .expect("create src");
1697 exec.named_graphs
1699 .get_mut("http://src")
1700 .expect("src exists")
1701 .push(Triple::new("a", "b", "c"));
1702
1703 exec.execute(&SparqlUpdate::CopyGraph {
1704 source: "http://src".into(),
1705 target: "http://dst".into(),
1706 silent: false,
1707 })
1708 .expect("copy failed");
1709 assert_eq!(exec.get_graph("http://dst").expect("dst exists").len(), 1);
1710 }
1711
1712 #[test]
1713 fn test_executor_move_graph() {
1714 let mut exec = UpdateExecutor::new();
1715 exec.execute(&SparqlUpdate::CreateGraph {
1716 iri: "http://src".into(),
1717 silent: false,
1718 })
1719 .expect("create src");
1720 exec.named_graphs
1721 .get_mut("http://src")
1722 .expect("src exists")
1723 .push(Triple::new("x", "y", "z"));
1724
1725 exec.execute(&SparqlUpdate::MoveGraph {
1726 source: "http://src".into(),
1727 target: "http://dst".into(),
1728 silent: false,
1729 })
1730 .expect("move failed");
1731
1732 assert!(exec.get_graph("http://src").is_none());
1733 assert_eq!(exec.get_graph("http://dst").expect("dst exists").len(), 1);
1734 }
1735}