1#[allow(unused_imports)]
12use crate::algebra::{Algebra, EvaluationContext, Term, TriplePattern, Variable};
13use crate::executor::ExecutionContext;
14use oxirs_core::model::{BlankNode, GraphName, Literal as CoreLiteral, NamedNode, Quad};
15use oxirs_core::OxirsError;
16use oxirs_core::Store;
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub enum UpdateOperation {
23 InsertData { data: Vec<QuadPattern> },
25
26 DeleteData { data: Vec<QuadPattern> },
28
29 DeleteWhere { pattern: Box<Algebra> },
31
32 InsertWhere {
34 pattern: Box<Algebra>,
35 template: Vec<QuadPattern>,
36 },
37
38 DeleteInsertWhere {
40 delete_template: Vec<QuadPattern>,
41 insert_template: Vec<QuadPattern>,
42 pattern: Box<Algebra>,
43 using: Option<Vec<GraphReference>>,
44 },
45
46 Clear { target: GraphTarget, silent: bool },
48
49 Drop { target: GraphTarget, silent: bool },
51
52 Create { graph: GraphReference, silent: bool },
54
55 Copy {
57 from: GraphTarget,
58 to: GraphTarget,
59 silent: bool,
60 },
61
62 Move {
64 from: GraphTarget,
65 to: GraphTarget,
66 silent: bool,
67 },
68
69 Add {
71 from: GraphTarget,
72 to: GraphTarget,
73 silent: bool,
74 },
75
76 Load {
78 source: String,
79 graph: Option<GraphReference>,
80 silent: bool,
81 },
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
86pub struct QuadPattern {
87 pub subject: Term,
88 pub predicate: Term,
89 pub object: Term,
90 pub graph: Option<GraphReference>,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
95pub enum GraphReference {
96 Iri(String),
97 Default,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102pub enum GraphTarget {
103 Graph(GraphReference),
104 All,
105 Named,
106 Default,
107}
108
109#[derive(Debug, Clone, Default)]
111pub struct UpdateResult {
112 pub inserted: usize,
114 pub deleted: usize,
116 pub graphs_created: Vec<String>,
118 pub graphs_dropped: Vec<String>,
120}
121
122pub struct UpdateExecutor<'a> {
124 store: &'a mut dyn Store,
125 #[allow(dead_code)]
126 context: ExecutionContext,
127 transaction_mode: bool,
129 batch_size: usize,
131 stats: UpdateStatistics,
133}
134
135#[derive(Debug, Clone, Default)]
137pub struct UpdateStatistics {
138 pub total_operations: usize,
139 pub total_execution_time: std::time::Duration,
140 pub operations_per_second: f64,
141 pub memory_usage: usize,
142 pub batch_count: usize,
143}
144
145impl<'a> UpdateExecutor<'a> {
146 pub fn new(store: &'a mut dyn Store) -> Self {
148 UpdateExecutor {
149 store,
150 context: ExecutionContext::default(),
151 transaction_mode: false,
152 batch_size: 10000, stats: UpdateStatistics::default(),
154 }
155 }
156
157 pub fn with_transaction(store: &'a mut dyn Store) -> Self {
159 UpdateExecutor {
160 store,
161 context: ExecutionContext::default(),
162 transaction_mode: true,
163 batch_size: 10000,
164 stats: UpdateStatistics::default(),
165 }
166 }
167
168 pub fn with_batch_size(mut self, batch_size: usize) -> Self {
170 self.batch_size = batch_size.max(1);
171 self
172 }
173
174 pub fn is_transactional(&self) -> bool {
182 self.transaction_mode
183 }
184
185 pub fn statistics(&self) -> &UpdateStatistics {
187 &self.stats
188 }
189
190 pub fn reset_statistics(&mut self) {
192 self.stats = UpdateStatistics::default();
193 }
194
195 pub fn execute(&mut self, operation: &UpdateOperation) -> Result<UpdateResult, OxirsError> {
197 let start_time = std::time::Instant::now();
198 self.stats.total_operations += 1;
199
200 let result = match operation {
207 UpdateOperation::InsertData { data } => self.execute_insert_data_enhanced(data),
208 UpdateOperation::DeleteData { data } => self.execute_delete_data_enhanced(data),
209 UpdateOperation::DeleteWhere { pattern } => self.execute_delete_where(pattern),
210 UpdateOperation::InsertWhere { pattern, template } => {
211 self.execute_insert_where(pattern, template)
212 }
213 UpdateOperation::DeleteInsertWhere {
214 delete_template,
215 insert_template,
216 pattern,
217 using,
218 } => self.execute_delete_insert_where(delete_template, insert_template, pattern, using),
219 UpdateOperation::Clear { target, silent } => self.execute_clear(target, *silent),
220 UpdateOperation::Drop { target, silent } => self.execute_drop(target, *silent),
221 UpdateOperation::Create { graph, silent } => self.execute_create(graph, *silent),
222 UpdateOperation::Copy { from, to, silent } => self.execute_copy(from, to, *silent),
223 UpdateOperation::Move { from, to, silent } => self.execute_move(from, to, *silent),
224 UpdateOperation::Add { from, to, silent } => self.execute_add(from, to, *silent),
225 UpdateOperation::Load {
226 source,
227 graph,
228 silent,
229 } => self.execute_load(source, graph.as_ref(), *silent),
230 };
231
232 let execution_time = start_time.elapsed();
238 self.stats.total_execution_time += execution_time;
239 self.stats.operations_per_second =
240 self.stats.total_operations as f64 / self.stats.total_execution_time.as_secs_f64();
241
242 result
243 }
244
245 #[allow(dead_code)]
247 fn execute_insert_data(&mut self, data: &[QuadPattern]) -> Result<UpdateResult, OxirsError> {
248 let mut result = UpdateResult::default();
249
250 for pattern in data {
251 let quad = self.pattern_to_quad(pattern)?;
253
254 self.store.insert(&quad)?;
256 result.inserted += 1;
257 }
258
259 Ok(result)
260 }
261
262 fn execute_insert_data_enhanced(
264 &mut self,
265 data: &[QuadPattern],
266 ) -> Result<UpdateResult, OxirsError> {
267 let mut result = UpdateResult::default();
268
269 if data.is_empty() {
270 return Ok(result);
271 }
272
273 if data.len() > self.batch_size {
275 for chunk in data.chunks(self.batch_size) {
276 let batch_result = self.execute_insert_data_batch(chunk)?;
277 result.inserted += batch_result.inserted;
278 self.stats.batch_count += 1;
279 }
280 } else {
281 for pattern in data {
283 self.validate_quad_pattern(pattern)?;
285
286 let quad = self.pattern_to_quad(pattern)?;
288
289 self.store.insert(&quad)?;
291 result.inserted += 1;
292 }
293 }
294
295 Ok(result)
296 }
297
298 fn execute_insert_data_batch(
300 &mut self,
301 batch: &[QuadPattern],
302 ) -> Result<UpdateResult, OxirsError> {
303 let mut result = UpdateResult::default();
304 let mut quads_to_insert = Vec::with_capacity(batch.len());
305
306 for pattern in batch {
308 self.validate_quad_pattern(pattern)?;
309 let quad = self.pattern_to_quad(pattern)?;
310 quads_to_insert.push(quad);
311 }
312
313 for quad in quads_to_insert {
315 self.store.insert(&quad)?;
316 result.inserted += 1;
317 }
318
319 Ok(result)
320 }
321
322 fn validate_quad_pattern(&self, pattern: &QuadPattern) -> Result<(), OxirsError> {
324 if matches!(pattern.subject, Term::Variable(_)) {
326 return Err(OxirsError::Query(
327 "Variables not allowed in INSERT DATA subject".to_string(),
328 ));
329 }
330 if matches!(pattern.predicate, Term::Variable(_)) {
331 return Err(OxirsError::Query(
332 "Variables not allowed in INSERT DATA predicate".to_string(),
333 ));
334 }
335 if matches!(pattern.object, Term::Variable(_)) {
336 return Err(OxirsError::Query(
337 "Variables not allowed in INSERT DATA object".to_string(),
338 ));
339 }
340
341 if let Term::Iri(iri) = &pattern.subject {
343 if iri.as_str().is_empty() {
344 return Err(OxirsError::Query("Empty IRI in subject".to_string()));
345 }
346 }
347 if let Term::Iri(iri) = &pattern.predicate {
348 if iri.as_str().is_empty() {
349 return Err(OxirsError::Query("Empty IRI in predicate".to_string()));
350 }
351 }
352 if let Term::Iri(iri) = &pattern.object {
353 if iri.as_str().is_empty() {
354 return Err(OxirsError::Query("Empty IRI in object".to_string()));
355 }
356 }
357
358 Ok(())
359 }
360
361 #[allow(dead_code)]
363 fn execute_delete_data(&mut self, data: &[QuadPattern]) -> Result<UpdateResult, OxirsError> {
364 let mut result = UpdateResult::default();
365
366 for pattern in data {
367 let quad = self.pattern_to_quad(pattern)?;
369
370 if self.store.remove(&quad)? {
372 result.deleted += 1;
373 }
374 }
375
376 Ok(result)
377 }
378
379 fn execute_delete_data_enhanced(
381 &mut self,
382 data: &[QuadPattern],
383 ) -> Result<UpdateResult, OxirsError> {
384 let mut result = UpdateResult::default();
385
386 if data.is_empty() {
387 return Ok(result);
388 }
389
390 if data.len() > self.batch_size {
392 for chunk in data.chunks(self.batch_size) {
393 let batch_result = self.execute_delete_data_batch(chunk)?;
394 result.deleted += batch_result.deleted;
395 self.stats.batch_count += 1;
396 }
397 } else {
398 for pattern in data {
400 self.validate_quad_pattern(pattern)?;
402
403 let quad = self.pattern_to_quad(pattern)?;
405
406 if self.store.remove(&quad)? {
408 result.deleted += 1;
409 }
410 }
411 }
412
413 Ok(result)
414 }
415
416 fn execute_delete_data_batch(
418 &mut self,
419 batch: &[QuadPattern],
420 ) -> Result<UpdateResult, OxirsError> {
421 let mut result = UpdateResult::default();
422 let mut quads_to_delete = Vec::with_capacity(batch.len());
423
424 for pattern in batch {
426 self.validate_quad_pattern(pattern)?;
427 let quad = self.pattern_to_quad(pattern)?;
428 quads_to_delete.push(quad);
429 }
430
431 for quad in quads_to_delete {
433 if self.store.remove(&quad)? {
434 result.deleted += 1;
435 }
436 }
437
438 Ok(result)
439 }
440
441 fn execute_delete_where(&mut self, pattern: &Algebra) -> Result<UpdateResult, OxirsError> {
443 let mut result = UpdateResult::default();
444
445 let bindings = self.evaluate_pattern(pattern)?;
447
448 if bindings.is_empty() {
449 return Ok(result); }
451
452 let mut quads_to_delete = Vec::new();
454
455 for binding in &bindings {
457 let quads = self.apply_binding_to_pattern(pattern, binding)?;
458 quads_to_delete.extend(quads);
459 }
460
461 quads_to_delete.sort();
463 quads_to_delete.dedup();
464
465 if quads_to_delete.len() > self.batch_size {
467 for chunk in quads_to_delete.chunks(self.batch_size) {
468 for quad in chunk {
469 if self.store.remove(quad)? {
470 result.deleted += 1;
471 }
472 }
473 self.stats.batch_count += 1;
474 }
475 } else {
476 for quad in quads_to_delete {
478 if self.store.remove(&quad)? {
479 result.deleted += 1;
480 }
481 }
482 }
483
484 Ok(result)
485 }
486
487 fn execute_insert_where(
489 &mut self,
490 pattern: &Algebra,
491 template: &[QuadPattern],
492 ) -> Result<UpdateResult, OxirsError> {
493 let mut result = UpdateResult::default();
494
495 if template.is_empty() {
496 return Ok(result); }
498
499 let bindings = self.evaluate_pattern(pattern)?;
501
502 if bindings.is_empty() {
503 return Ok(result); }
505
506 let mut quads_to_insert = Vec::new();
508
509 for binding in &bindings {
513 for quad_pattern in template {
514 if let Some(quad) = self.instantiate_template(quad_pattern, binding)? {
515 quads_to_insert.push(quad);
516 }
517 }
518 }
519
520 quads_to_insert.sort();
522 quads_to_insert.dedup();
523
524 if quads_to_insert.len() > self.batch_size {
526 for chunk in quads_to_insert.chunks(self.batch_size) {
527 for quad in chunk {
528 self.store.insert(quad)?;
529 result.inserted += 1;
530 }
531 self.stats.batch_count += 1;
532 }
533 } else {
534 for quad in quads_to_insert {
536 self.store.insert(&quad)?;
537 result.inserted += 1;
538 }
539 }
540
541 Ok(result)
542 }
543
544 fn execute_delete_insert_where(
546 &mut self,
547 delete_template: &[QuadPattern],
548 insert_template: &[QuadPattern],
549 pattern: &Algebra,
550 using: &Option<Vec<GraphReference>>,
551 ) -> Result<UpdateResult, OxirsError> {
552 let mut result = UpdateResult::default();
553
554 if delete_template.is_empty() && insert_template.is_empty() {
555 return Ok(result); }
557
558 let bindings = self.evaluate_pattern_with_using(pattern, using.as_deref())?;
561
562 if bindings.is_empty() {
563 return Ok(result); }
565
566 let mut quads_to_delete = Vec::new();
568 if !delete_template.is_empty() {
569 for binding in &bindings {
570 for quad_pattern in delete_template {
571 if let Some(quad) = self.instantiate_template(quad_pattern, binding)? {
573 quads_to_delete.push(quad);
574 }
575 }
576 }
577
578 quads_to_delete.sort();
580 quads_to_delete.dedup();
581 }
582
583 let mut quads_to_insert = Vec::new();
585 if !insert_template.is_empty() {
586 for binding in &bindings {
587 for quad_pattern in insert_template {
588 if let Some(quad) = self.instantiate_template(quad_pattern, binding)? {
590 quads_to_insert.push(quad);
591 }
592 }
593 }
594
595 quads_to_insert.sort();
597 quads_to_insert.dedup();
598 }
599
600 let mut applied_deletes: Vec<&Quad> = Vec::new();
607 let mut applied_inserts: Vec<&Quad> = Vec::new();
608
609 for quad in &quads_to_delete {
611 match self.store.remove(quad) {
612 Ok(true) => {
613 applied_deletes.push(quad);
614 result.deleted += 1;
615 }
616 Ok(false) => {
617 }
619 Err(e) => {
620 Self::rollback_delete_insert(self.store, &applied_deletes, &applied_inserts);
621 return Err(e);
622 }
623 }
624 }
625
626 for quad in &quads_to_insert {
628 match self.store.insert(quad) {
629 Ok(()) => {
630 applied_inserts.push(quad);
631 result.inserted += 1;
632 }
633 Err(e) => {
634 Self::rollback_delete_insert(self.store, &applied_deletes, &applied_inserts);
635 return Err(e);
636 }
637 }
638 }
639
640 if quads_to_delete.len() > self.batch_size || quads_to_insert.len() > self.batch_size {
641 self.stats.batch_count += 1;
642 }
643
644 Ok(result)
645 }
646
647 fn rollback_delete_insert(
655 store: &mut dyn Store,
656 applied_deletes: &[&Quad],
657 applied_inserts: &[&Quad],
658 ) {
659 for quad in applied_inserts.iter().rev() {
660 let _ = store.remove(quad);
661 }
662 for quad in applied_deletes.iter().rev() {
663 let _ = store.insert(quad);
664 }
665 }
666
667 fn execute_clear(
669 &mut self,
670 target: &GraphTarget,
671 silent: bool,
672 ) -> Result<UpdateResult, OxirsError> {
673 let mut result = UpdateResult::default();
674
675 match target {
676 GraphTarget::All => {
677 result.deleted = self.store.clear_all()?;
679 }
680 GraphTarget::Named => {
681 result.deleted = self.store.clear_named_graphs()?;
683 }
684 GraphTarget::Default => {
685 result.deleted = self.store.clear_default_graph()?;
687 }
688 GraphTarget::Graph(graph_ref) => {
689 let graph_name = self.graph_ref_to_named_node(graph_ref)?;
691 match self
692 .store
693 .clear_graph(Some(&GraphName::NamedNode(graph_name)))
694 {
695 Ok(count) => result.deleted = count,
696 Err(e) if !silent => return Err(e),
697 _ => {} }
699 }
700 }
701
702 Ok(result)
703 }
704
705 fn execute_drop(
707 &mut self,
708 target: &GraphTarget,
709 silent: bool,
710 ) -> Result<UpdateResult, OxirsError> {
711 let mut result = UpdateResult::default();
712
713 match target {
714 GraphTarget::All => {
715 let graphs = self.store.graphs()?;
717 for graph in graphs {
718 self.store
719 .drop_graph(Some(&GraphName::NamedNode(graph.clone())))?;
720 result.graphs_dropped.push(graph.as_str().to_string());
721 }
722 }
723 GraphTarget::Named => {
724 let graphs = self.store.named_graphs()?;
726 for graph in graphs {
727 self.store
728 .drop_graph(Some(&GraphName::NamedNode(graph.clone())))?;
729 result.graphs_dropped.push(graph.as_str().to_string());
730 }
731 }
732 GraphTarget::Default => {
733 if !silent {
735 return Err(OxirsError::Query("Cannot DROP DEFAULT graph".to_string()));
736 }
737 }
738 GraphTarget::Graph(graph_ref) => {
739 let graph_name = self.graph_ref_to_named_node(graph_ref)?;
740 match self
741 .store
742 .drop_graph(Some(&GraphName::NamedNode(graph_name.clone())))
743 {
744 Ok(_) => result.graphs_dropped.push(graph_name.as_str().to_string()),
745 Err(e) if !silent => return Err(e),
746 _ => {} }
748 }
749 }
750
751 Ok(result)
752 }
753
754 fn execute_create(
756 &mut self,
757 graph: &GraphReference,
758 silent: bool,
759 ) -> Result<UpdateResult, OxirsError> {
760 let mut result = UpdateResult::default();
761
762 let graph_name = self.graph_ref_to_named_node(graph)?;
763
764 match self.store.create_graph(Some(&graph_name)) {
765 Ok(_) => {
766 result.graphs_created.push(graph_name.as_str().to_string());
767 }
768 Err(e) => {
769 if !silent {
770 return Err(e);
771 }
772 }
773 }
774
775 Ok(result)
776 }
777
778 fn execute_copy(
780 &mut self,
781 from: &GraphTarget,
782 to: &GraphTarget,
783 silent: bool,
784 ) -> Result<UpdateResult, OxirsError> {
785 self.execute_clear(to, silent)?;
787
788 self.execute_add(from, to, silent)
790 }
791
792 fn execute_move(
794 &mut self,
795 from: &GraphTarget,
796 to: &GraphTarget,
797 silent: bool,
798 ) -> Result<UpdateResult, OxirsError> {
799 let result = self.execute_copy(from, to, silent)?;
801
802 self.execute_clear(from, silent)?;
804
805 Ok(result)
806 }
807
808 fn execute_add(
810 &mut self,
811 from: &GraphTarget,
812 to: &GraphTarget,
813 _silent: bool,
814 ) -> Result<UpdateResult, OxirsError> {
815 let mut result = UpdateResult::default();
816
817 let source_quads = self.get_quads_from_target(from)?;
819
820 let target_graph = match to {
822 GraphTarget::Graph(g) => Some(self.graph_ref_to_named_node(g)?),
823 GraphTarget::Default => None,
824 _ => {
825 return Err(OxirsError::Query(
826 "Invalid target for ADD operation".to_string(),
827 ))
828 }
829 };
830
831 for quad in source_quads {
833 let new_quad = if let Some(ref target) = target_graph {
834 Quad::new(
835 quad.subject().clone(),
836 quad.predicate().clone(),
837 quad.object().clone(),
838 GraphName::NamedNode(target.clone()),
839 )
840 } else {
841 Quad::new(
842 quad.subject().clone(),
843 quad.predicate().clone(),
844 quad.object().clone(),
845 GraphName::DefaultGraph,
846 )
847 };
848
849 self.store.insert(&new_quad)?;
850 result.inserted += 1;
851 }
852
853 Ok(result)
854 }
855
856 fn execute_load(
858 &mut self,
859 source: &str,
860 graph: Option<&GraphReference>,
861 silent: bool,
862 ) -> Result<UpdateResult, OxirsError> {
863 let mut result = UpdateResult::default();
864
865 let target_graph = graph.map(|g| self.graph_ref_to_named_node(g)).transpose()?;
867
868 match self.store.load_from_url(source, target_graph.as_ref()) {
870 Ok(count) => {
871 result.inserted = count;
872 }
873 Err(e) if !silent => return Err(e),
874 _ => {} }
876
877 Ok(result)
878 }
879
880 fn pattern_to_quad(&self, pattern: &QuadPattern) -> Result<Quad, OxirsError> {
884 let subject = self.term_to_subject(&pattern.subject)?;
885 let predicate = self.term_to_predicate(&pattern.predicate)?;
886 let object = self.term_to_object(&pattern.object)?;
887 let graph_name = pattern
888 .graph
889 .as_ref()
890 .map(|g| self.graph_ref_to_named_node(g))
891 .transpose()?
892 .map(GraphName::NamedNode)
893 .unwrap_or(GraphName::DefaultGraph);
894
895 Ok(Quad::new(subject, predicate, object, graph_name))
896 }
897
898 fn term_to_subject(&self, term: &Term) -> Result<oxirs_core::model::Subject, OxirsError> {
900 match term {
901 Term::Iri(iri) => Ok(NamedNode::new(iri.as_str())?.into()),
902 Term::BlankNode(id) => Ok(BlankNode::new(id)?.into()),
903 Term::Variable(_) => Err(OxirsError::Query(
904 "Variables not allowed in concrete data".to_string(),
905 )),
906 Term::Literal(_) => Err(OxirsError::Query(
907 "Literals cannot be used as subjects".to_string(),
908 )),
909 Term::QuotedTriple(_) => Err(OxirsError::Query(
910 "Quoted triples not yet supported as subjects in concrete data".to_string(),
911 )),
912 Term::PropertyPath(_) => Err(OxirsError::Query(
913 "Property paths not allowed as subjects in concrete data".to_string(),
914 )),
915 }
916 }
917
918 fn term_to_predicate(&self, term: &Term) -> Result<NamedNode, OxirsError> {
920 match term {
921 Term::Iri(iri) => NamedNode::new(iri.as_str()),
922 Term::Variable(_) => Err(OxirsError::Query(
923 "Variables not allowed in concrete data".to_string(),
924 )),
925 Term::BlankNode(_) => Err(OxirsError::Query(
926 "Blank nodes cannot be used as predicates in most RDF contexts".to_string(),
927 )),
928 Term::Literal(_) => Err(OxirsError::Query(
929 "Literals cannot be used as predicates".to_string(),
930 )),
931 Term::QuotedTriple(_) => Err(OxirsError::Query(
932 "Quoted triples not supported as predicates in concrete data".to_string(),
933 )),
934 Term::PropertyPath(_) => Err(OxirsError::Query(
935 "Property paths not allowed as predicates in concrete data".to_string(),
936 )),
937 }
938 }
939
940 fn term_to_object(&self, term: &Term) -> Result<oxirs_core::model::Object, OxirsError> {
942 match term {
943 Term::Iri(iri) => Ok(NamedNode::new(iri.as_str())?.into()),
944 Term::BlankNode(id) => Ok(BlankNode::new(id)?.into()),
945 Term::Literal(lit) => {
946 let literal = if let Some(lang) = &lit.language {
947 CoreLiteral::new_language_tagged_literal(&lit.value, lang)?
948 } else if let Some(dt) = &lit.datatype {
949 CoreLiteral::new_typed(&lit.value, dt.clone())
950 } else {
951 CoreLiteral::new(&lit.value)
952 };
953 Ok(literal.into())
954 }
955 Term::Variable(_) => Err(OxirsError::Query(
956 "Variables not allowed in concrete data".to_string(),
957 )),
958 Term::QuotedTriple(_) => Err(OxirsError::Query(
959 "Quoted triples not yet supported in concrete data".to_string(),
960 )),
961 Term::PropertyPath(_) => Err(OxirsError::Query(
962 "Property paths not allowed in concrete data".to_string(),
963 )),
964 }
965 }
966
967 fn graph_ref_to_named_node(&self, graph_ref: &GraphReference) -> Result<NamedNode, OxirsError> {
969 match graph_ref {
970 GraphReference::Iri(iri) => NamedNode::new(iri),
971 GraphReference::Default => Err(OxirsError::Query(
972 "DEFAULT is not a valid graph IRI".to_string(),
973 )),
974 }
975 }
976
977 fn evaluate_pattern(
984 &mut self,
985 pattern: &Algebra,
986 ) -> Result<Vec<HashMap<String, oxirs_core::model::Term>>, OxirsError> {
987 self.evaluate_pattern_with_using(pattern, None)
988 }
989
990 fn evaluate_pattern_with_using(
1001 &mut self,
1002 pattern: &Algebra,
1003 using: Option<&[GraphReference]>,
1004 ) -> Result<Vec<HashMap<String, oxirs_core::model::Term>>, OxirsError> {
1005 use crate::executor::dataset::DatasetView;
1006 use crate::executor::{QueryExecutor, StoreRefDataset};
1007
1008 let using_graphs: Vec<NamedNode> = match using {
1011 Some(refs) if !refs.is_empty() => refs
1012 .iter()
1013 .map(|graph_ref| match graph_ref {
1014 GraphReference::Iri(iri) => NamedNode::new(iri).map_err(|e| {
1015 OxirsError::Query(format!("Invalid USING graph IRI `{iri}`: {e}"))
1016 }),
1017 GraphReference::Default => Err(OxirsError::Query(
1018 "USING requires a graph IRI; the default graph cannot be a USING target"
1019 .to_string(),
1020 )),
1021 })
1022 .collect::<Result<Vec<_>, _>>()?,
1023 _ => Vec::new(),
1024 };
1025
1026 let solution = {
1029 let store_ref: &dyn Store = &*self.store;
1030 let base = StoreRefDataset::new(store_ref);
1031 let mut executor = QueryExecutor::new();
1032 if using_graphs.is_empty() {
1033 let (solution, _stats) = executor
1034 .execute(pattern, &base)
1035 .map_err(|e| OxirsError::Query(e.to_string()))?;
1036 solution
1037 } else {
1038 let dataset = DatasetView::new(&base, using_graphs, Vec::new());
1041 let (solution, _stats) = executor
1042 .execute(pattern, &dataset)
1043 .map_err(|e| OxirsError::Query(e.to_string()))?;
1044 solution
1045 }
1046 };
1047
1048 let mut bindings = Vec::new();
1050 for binding in solution {
1051 let mut converted_binding = HashMap::new();
1052 for (var, term) in binding {
1053 let arq_term = crate::term::Term::from_algebra_term(&term);
1055 let core_term = self.convert_term_to_core(&arq_term)?;
1056 converted_binding.insert(var.as_str().to_string(), core_term);
1057 }
1058 bindings.push(converted_binding);
1059 }
1060
1061 Ok(bindings)
1062 }
1063
1064 fn apply_binding_to_pattern(
1066 &self,
1067 pattern: &Algebra,
1068 binding: &HashMap<String, oxirs_core::model::Term>,
1069 ) -> Result<Vec<Quad>, OxirsError> {
1070 let mut quads = Vec::new();
1071
1072 let mut triple_patterns: Vec<(TriplePattern, Option<NamedNode>)> = Vec::new();
1076 self.extract_triple_patterns_with_graph(pattern, None, &mut triple_patterns)?;
1077
1078 for (triple_pattern, graph) in triple_patterns {
1079 let subject_term = self.instantiate_algebra_term(&triple_pattern.subject, binding)?;
1081 let predicate_term =
1082 self.instantiate_algebra_term(&triple_pattern.predicate, binding)?;
1083 let object_term = self.instantiate_algebra_term(&triple_pattern.object, binding)?;
1084
1085 let subject = self.core_term_to_subject(subject_term)?;
1087 let predicate = self.core_term_to_predicate(predicate_term)?;
1088 let object = self.core_term_to_object(object_term)?;
1089
1090 let graph_name = match graph {
1092 Some(node) => GraphName::NamedNode(node),
1093 None => GraphName::DefaultGraph,
1094 };
1095
1096 let quad = Quad::new(subject, predicate, object, graph_name);
1098 quads.push(quad);
1099 }
1100
1101 Ok(quads)
1102 }
1103
1104 fn instantiate_template(
1111 &self,
1112 template: &QuadPattern,
1113 binding: &HashMap<String, oxirs_core::model::Term>,
1114 ) -> Result<Option<Quad>, OxirsError> {
1115 let (Some(subject), Some(predicate), Some(object)) = (
1116 self.instantiate_term(&template.subject, binding)?,
1117 self.instantiate_term(&template.predicate, binding)?,
1118 self.instantiate_term(&template.object, binding)?,
1119 ) else {
1120 return Ok(None);
1122 };
1123
1124 let subject = self.term_to_subject(&subject)?;
1125 let predicate = self.term_to_predicate(&predicate)?;
1126 let object = self.term_to_object(&object)?;
1127
1128 let graph_name = template
1129 .graph
1130 .as_ref()
1131 .map(|g| self.graph_ref_to_named_node(g))
1132 .transpose()?
1133 .map(GraphName::NamedNode)
1134 .unwrap_or(GraphName::DefaultGraph);
1135
1136 Ok(Some(Quad::new(subject, predicate, object, graph_name)))
1137 }
1138
1139 fn instantiate_term(
1146 &self,
1147 term: &Term,
1148 binding: &HashMap<String, oxirs_core::model::Term>,
1149 ) -> Result<Option<Term>, OxirsError> {
1150 match term {
1151 Term::Variable(var) => match binding.get(var.as_str()) {
1152 Some(t) => self.core_term_to_arq_term(t).map(Some),
1153 None => Ok(None),
1154 },
1155 _ => Ok(Some(term.clone())),
1156 }
1157 }
1158
1159 fn core_term_to_arq_term(&self, term: &oxirs_core::model::Term) -> Result<Term, OxirsError> {
1166 use oxirs_core::model::Term as CoreTerm;
1167 match term {
1168 CoreTerm::NamedNode(n) => Ok(Term::Iri(n.clone())),
1169 CoreTerm::BlankNode(b) => Ok(Term::BlankNode(b.as_str().to_string())),
1170 CoreTerm::Literal(l) => {
1171 let lit = crate::algebra::Literal {
1172 value: l.value().to_string(),
1173 language: l.language().map(|s| s.to_string()),
1174 datatype: Some(l.datatype().into()),
1175 };
1176 Ok(Term::Literal(lit))
1177 }
1178 CoreTerm::QuotedTriple(qt) => {
1179 let subject =
1180 self.core_term_to_arq_term(&CoreTerm::from_subject(qt.subject()))?;
1181 let predicate =
1182 self.core_term_to_arq_term(&CoreTerm::from_predicate(qt.predicate()))?;
1183 let object = self.core_term_to_arq_term(&CoreTerm::from_object(qt.object()))?;
1184 Ok(Term::QuotedTriple(Box::new(crate::algebra::TriplePattern {
1185 subject,
1186 predicate,
1187 object,
1188 })))
1189 }
1190 CoreTerm::Variable(v) => Err(OxirsError::Query(format!(
1191 "Variable term `{v}` is not valid in stored RDF data for update template instantiation"
1192 ))),
1193 }
1194 }
1195
1196 fn get_quads_from_target(&self, target: &GraphTarget) -> Result<Vec<Quad>, OxirsError> {
1198 match target {
1199 GraphTarget::All => self.store.quads(),
1200 GraphTarget::Named => self.store.named_graph_quads(),
1201 GraphTarget::Default => self.store.default_graph_quads(),
1202 GraphTarget::Graph(graph_ref) => {
1203 let graph = self.graph_ref_to_named_node(graph_ref)?;
1204 self.store.graph_quads(Some(&graph))
1205 }
1206 }
1207 }
1208
1209 fn convert_term_to_core(
1211 &self,
1212 term: &crate::term::Term,
1213 ) -> Result<oxirs_core::model::Term, OxirsError> {
1214 use oxirs_core::model::Term as CoreTerm;
1215
1216 match term {
1217 crate::term::Term::Iri(iri) => Ok(CoreTerm::NamedNode(NamedNode::new(iri)?)),
1218 crate::term::Term::BlankNode(id) => Ok(CoreTerm::BlankNode(BlankNode::new(id)?)),
1219 crate::term::Term::Literal(lit) => {
1220 let core_literal = if let Some(lang) = &lit.language_tag {
1221 CoreLiteral::new_language_tagged_literal(&lit.lexical_form, lang)?
1222 } else if lit.datatype != "http://www.w3.org/2001/XMLSchema#string" {
1223 CoreLiteral::new_typed(&lit.lexical_form, NamedNode::new(&lit.datatype)?)
1224 } else {
1225 CoreLiteral::new_simple_literal(&lit.lexical_form)
1226 };
1227 Ok(CoreTerm::Literal(core_literal))
1228 }
1229 crate::term::Term::Variable(_) => Err(OxirsError::Query(
1230 "Cannot convert variable to concrete term".to_string(),
1231 )),
1232 crate::term::Term::QuotedTriple(_) => Err(OxirsError::Query(
1233 "Cannot convert quoted triple to concrete term".to_string(),
1234 )),
1235 crate::term::Term::PropertyPath(_) => Err(OxirsError::Query(
1236 "Cannot convert property path to concrete term".to_string(),
1237 )),
1238 }
1239 }
1240
1241 #[allow(clippy::only_used_in_recursion)]
1243 #[allow(clippy::only_used_in_recursion)]
1249 fn extract_triple_patterns_with_graph(
1250 &self,
1251 algebra: &Algebra,
1252 current_graph: Option<&NamedNode>,
1253 out: &mut Vec<(TriplePattern, Option<NamedNode>)>,
1254 ) -> Result<(), OxirsError> {
1255 match algebra {
1256 Algebra::Bgp(bgp_patterns) => {
1257 for p in bgp_patterns {
1258 out.push((p.clone(), current_graph.cloned()));
1259 }
1260 }
1261 Algebra::Graph { graph, pattern } => match graph {
1262 Term::Iri(node) => {
1263 self.extract_triple_patterns_with_graph(pattern, Some(node), out)?;
1264 }
1265 _ => {
1266 self.extract_triple_patterns_with_graph(pattern, current_graph, out)?;
1267 }
1268 },
1269 Algebra::Join { left, right }
1270 | Algebra::Union { left, right }
1271 | Algebra::Minus { left, right } => {
1272 self.extract_triple_patterns_with_graph(left, current_graph, out)?;
1273 self.extract_triple_patterns_with_graph(right, current_graph, out)?;
1274 }
1275 Algebra::LeftJoin { left, right, .. } => {
1276 self.extract_triple_patterns_with_graph(left, current_graph, out)?;
1277 self.extract_triple_patterns_with_graph(right, current_graph, out)?;
1278 }
1279 Algebra::Filter { pattern, .. }
1280 | Algebra::Extend { pattern, .. }
1281 | Algebra::Service { pattern, .. } => {
1282 self.extract_triple_patterns_with_graph(pattern, current_graph, out)?;
1283 }
1284 _ => {}
1285 }
1286 Ok(())
1287 }
1288
1289 fn instantiate_algebra_term(
1291 &self,
1292 term: &Term,
1293 binding: &HashMap<String, oxirs_core::model::Term>,
1294 ) -> Result<oxirs_core::model::Term, OxirsError> {
1295 match term {
1296 Term::Variable(var) => binding
1297 .get(var.as_str())
1298 .cloned()
1299 .ok_or_else(|| OxirsError::Query(format!("Unbound variable: {var}"))),
1300 _ => {
1301 let arq_term = crate::term::Term::from_algebra_term(term);
1303 self.convert_term_to_core(&arq_term)
1304 }
1305 }
1306 }
1307
1308 fn core_term_to_subject(
1310 &self,
1311 term: oxirs_core::model::Term,
1312 ) -> Result<oxirs_core::Subject, OxirsError> {
1313 match term {
1314 oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Subject::NamedNode(node)),
1315 oxirs_core::model::Term::BlankNode(node) => Ok(oxirs_core::Subject::BlankNode(node)),
1316 oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Subject::Variable(var)),
1317 _ => Err(OxirsError::Query("Invalid subject term type".to_string())),
1318 }
1319 }
1320
1321 fn core_term_to_predicate(
1323 &self,
1324 term: oxirs_core::model::Term,
1325 ) -> Result<oxirs_core::Predicate, OxirsError> {
1326 match term {
1327 oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Predicate::NamedNode(node)),
1328 oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Predicate::Variable(var)),
1329 _ => Err(OxirsError::Query("Invalid predicate term type".to_string())),
1330 }
1331 }
1332
1333 fn core_term_to_object(
1335 &self,
1336 term: oxirs_core::model::Term,
1337 ) -> Result<oxirs_core::Object, OxirsError> {
1338 match term {
1339 oxirs_core::model::Term::NamedNode(node) => Ok(oxirs_core::Object::NamedNode(node)),
1340 oxirs_core::model::Term::BlankNode(node) => Ok(oxirs_core::Object::BlankNode(node)),
1341 oxirs_core::model::Term::Literal(lit) => Ok(oxirs_core::Object::Literal(lit)),
1342 oxirs_core::model::Term::Variable(var) => Ok(oxirs_core::Object::Variable(var)),
1343 oxirs_core::model::Term::QuotedTriple(qt) => Ok(oxirs_core::Object::QuotedTriple(qt)),
1344 }
1345 }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350 use super::*;
1351
1352 #[test]
1353 fn test_update_result_default() {
1354 let result = UpdateResult::default();
1355 assert_eq!(result.inserted, 0);
1356 assert_eq!(result.deleted, 0);
1357 assert!(result.graphs_created.is_empty());
1358 assert!(result.graphs_dropped.is_empty());
1359 }
1360
1361 #[test]
1362 fn test_graph_reference() {
1363 let iri_ref = GraphReference::Iri("http://example.org/graph".to_string());
1364 let default_ref = GraphReference::Default;
1365
1366 assert_ne!(iri_ref, default_ref);
1367 }
1368
1369 #[test]
1370 fn test_quad_pattern() {
1371 let pattern = QuadPattern {
1372 subject: Term::Variable(Variable::new("s").unwrap()),
1373 predicate: Term::Iri(NamedNode::new("http://example.org/pred").unwrap()),
1374 object: Term::Literal(crate::algebra::Literal {
1375 value: "test".to_string(),
1376 language: None,
1377 datatype: None,
1378 }),
1379 graph: None,
1380 };
1381
1382 assert_eq!(pattern.subject, Term::Variable(Variable::new("s").unwrap()));
1383 }
1384
1385 #[test]
1386 fn test_enhanced_update_operations() {
1387 let insert_data = UpdateOperation::InsertData {
1392 data: vec![QuadPattern {
1393 subject: Term::Iri(NamedNode::new("http://example.org/subject").unwrap()),
1394 predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
1395 object: Term::Literal(crate::algebra::Literal {
1396 value: "test_value".to_string(),
1397 language: None,
1398 datatype: None,
1399 }),
1400 graph: None,
1401 }],
1402 };
1403
1404 let delete_data = UpdateOperation::DeleteData {
1405 data: vec![QuadPattern {
1406 subject: Term::Iri(NamedNode::new("http://example.org/subject").unwrap()),
1407 predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
1408 object: Term::Literal(crate::algebra::Literal {
1409 value: "test_value".to_string(),
1410 language: None,
1411 datatype: None,
1412 }),
1413 graph: None,
1414 }],
1415 };
1416
1417 match insert_data {
1419 UpdateOperation::InsertData { data } => {
1420 assert_eq!(data.len(), 1);
1421 }
1422 _ => panic!("Expected InsertData operation"),
1423 }
1424
1425 match delete_data {
1426 UpdateOperation::DeleteData { data } => {
1427 assert_eq!(data.len(), 1);
1428 }
1429 _ => panic!("Expected DeleteData operation"),
1430 }
1431 }
1432
1433 #[test]
1434 fn test_update_statistics() {
1435 let stats = UpdateStatistics::default();
1436 assert_eq!(stats.total_operations, 0);
1437 assert_eq!(stats.batch_count, 0);
1438 assert_eq!(stats.operations_per_second, 0.0);
1439 }
1440
1441 #[test]
1442 fn delete_where_roundtrip_on_real_store() {
1443 use oxirs_core::model::{Literal as CoreLiteral, NamedNode, Predicate, Quad};
1444 use oxirs_core::rdf_store::{ConcreteStore, Store};
1445
1446 let mut store = ConcreteStore::new().expect("create store");
1447
1448 let s1 = NamedNode::new("http://example.org/s1").expect("iri");
1449 let s2 = NamedNode::new("http://example.org/s2").expect("iri");
1450 let p = NamedNode::new("http://example.org/p").expect("iri");
1451 let q = NamedNode::new("http://example.org/q").expect("iri");
1452
1453 store
1454 .insert(&Quad::new(
1455 s1.clone(),
1456 p.clone(),
1457 CoreLiteral::new("v1"),
1458 GraphName::DefaultGraph,
1459 ))
1460 .expect("insert s1");
1461 store
1462 .insert(&Quad::new(
1463 s2.clone(),
1464 q.clone(),
1465 CoreLiteral::new("v2"),
1466 GraphName::DefaultGraph,
1467 ))
1468 .expect("insert s2");
1469
1470 let pattern = Algebra::Bgp(vec![TriplePattern {
1474 subject: Term::Variable(Variable::new("s").expect("var")),
1475 predicate: Term::Iri(p.clone()),
1476 object: Term::Variable(Variable::new("o").expect("var")),
1477 }]);
1478 let op = UpdateOperation::DeleteWhere {
1479 pattern: Box::new(pattern),
1480 };
1481
1482 let deleted = {
1483 let mut executor = UpdateExecutor::new(&mut store);
1484 executor.execute(&op).expect("delete where").deleted
1485 };
1486 assert_eq!(deleted, 1, "exactly one matching triple must be deleted");
1487
1488 let remaining_p = store
1489 .find_quads(None, Some(&Predicate::NamedNode(p)), None, None)
1490 .expect("find p");
1491 assert!(remaining_p.is_empty(), "the (s1 p v1) triple must be gone");
1492 let remaining_q = store
1493 .find_quads(None, Some(&Predicate::NamedNode(q)), None, None)
1494 .expect("find q");
1495 assert_eq!(remaining_q.len(), 1, "the (s2 q v2) triple must survive");
1496 }
1497
1498 #[test]
1499 fn delete_where_matches_typed_literal_on_real_store() {
1500 use oxirs_core::model::{Literal as CoreLiteral, NamedNode, Predicate, Quad};
1501 use oxirs_core::rdf_store::{ConcreteStore, Store};
1502
1503 let mut store = ConcreteStore::new().expect("create store");
1504
1505 let s = NamedNode::new("http://example.org/s").expect("iri");
1506 let age = NamedNode::new("http://example.org/age").expect("iri");
1507 let xsd_int = NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").expect("iri");
1508
1509 store
1511 .insert(&Quad::new(
1512 s.clone(),
1513 age.clone(),
1514 CoreLiteral::new_typed("25", xsd_int.clone()),
1515 GraphName::DefaultGraph,
1516 ))
1517 .expect("insert 25");
1518 store
1519 .insert(&Quad::new(
1520 s.clone(),
1521 age.clone(),
1522 CoreLiteral::new_typed("30", xsd_int.clone()),
1523 GraphName::DefaultGraph,
1524 ))
1525 .expect("insert 30");
1526
1527 let pattern = Algebra::Bgp(vec![TriplePattern {
1529 subject: Term::Variable(Variable::new("s").expect("var")),
1530 predicate: Term::Iri(age.clone()),
1531 object: Term::Literal(crate::algebra::Literal {
1532 value: "25".to_string(),
1533 language: None,
1534 datatype: Some(xsd_int.clone()),
1535 }),
1536 }]);
1537 let op = UpdateOperation::DeleteWhere {
1538 pattern: Box::new(pattern),
1539 };
1540
1541 let deleted = {
1542 let mut executor = UpdateExecutor::new(&mut store);
1543 executor.execute(&op).expect("delete where").deleted
1544 };
1545 assert_eq!(
1546 deleted, 1,
1547 "typed-literal pattern must match exactly one triple"
1548 );
1549
1550 let remaining = store
1551 .find_quads(None, Some(&Predicate::NamedNode(age)), None, None)
1552 .expect("find age");
1553 assert_eq!(remaining.len(), 1, "only the age=30 triple must survive");
1554 }
1555
1556 #[test]
1557 fn test_validation_errors() {
1558 let _invalid_pattern = QuadPattern {
1559 subject: Term::Variable(Variable::new("s").unwrap()),
1560 predicate: Term::Iri(NamedNode::new("http://example.org/predicate").unwrap()),
1561 object: Term::Literal(crate::algebra::Literal {
1562 value: "test".to_string(),
1563 language: None,
1564 datatype: None,
1565 }),
1566 graph: None,
1567 };
1568
1569 let validation_result = std::panic::catch_unwind(|| {
1572 });
1575
1576 assert!(validation_result.is_ok());
1578 }
1579
1580 #[test]
1581 fn regression_core_term_to_arq_term_quoted_triple_no_panic() {
1582 use oxirs_core::model::star::QuotedTriple;
1583 use oxirs_core::model::{Literal as CoreLiteral, NamedNode as CoreNamedNode, Triple};
1584 use oxirs_core::rdf_store::ConcreteStore;
1585
1586 let mut store = ConcreteStore::new().expect("create store");
1587 let executor = UpdateExecutor::new(&mut store);
1588
1589 let inner = Triple::new(
1591 CoreNamedNode::new("http://ex/s").expect("s"),
1592 CoreNamedNode::new("http://ex/p").expect("p"),
1593 CoreLiteral::new("o"),
1594 );
1595 let quoted = oxirs_core::model::Term::QuotedTriple(Box::new(QuotedTriple::new(inner)));
1596 let converted = executor
1597 .core_term_to_arq_term("ed)
1598 .expect("quoted triple must convert without panicking");
1599 assert!(matches!(converted, Term::QuotedTriple(_)));
1600
1601 let var_term =
1603 oxirs_core::model::Term::Variable(oxirs_core::model::Variable::new("x").expect("var"));
1604 assert!(executor.core_term_to_arq_term(&var_term).is_err());
1605 }
1606
1607 #[test]
1608 fn regression_delete_insert_where_honors_using_clause() {
1609 use oxirs_core::model::{Literal as CoreLiteral, NamedNode, Quad};
1610 use oxirs_core::rdf_store::{ConcreteStore, Store};
1611
1612 let mut store = ConcreteStore::new().expect("create store");
1613
1614 let s1 = NamedNode::new("http://ex/s1").expect("iri");
1615 let s2 = NamedNode::new("http://ex/s2").expect("iri");
1616 let p = NamedNode::new("http://ex/p").expect("iri");
1617 let g1 = NamedNode::new("http://ex/g1").expect("iri");
1618 let g2 = NamedNode::new("http://ex/g2").expect("iri");
1619
1620 store
1621 .insert(&Quad::new(
1622 s1.clone(),
1623 p.clone(),
1624 CoreLiteral::new("v1"),
1625 GraphName::NamedNode(g1.clone()),
1626 ))
1627 .expect("insert g1");
1628 store
1629 .insert(&Quad::new(
1630 s2.clone(),
1631 p.clone(),
1632 CoreLiteral::new("v2"),
1633 GraphName::NamedNode(g2.clone()),
1634 ))
1635 .expect("insert g2");
1636
1637 let pattern = Algebra::Bgp(vec![TriplePattern {
1638 subject: Term::Variable(Variable::new("s").expect("var")),
1639 predicate: Term::Iri(p.clone()),
1640 object: Term::Variable(Variable::new("o").expect("var")),
1641 }]);
1642
1643 let mut executor = UpdateExecutor::new(&mut store);
1644
1645 let using_g1 = [GraphReference::Iri("http://ex/g1".to_string())];
1647 let bindings = executor
1648 .evaluate_pattern_with_using(&pattern, Some(&using_g1))
1649 .expect("using g1");
1650 assert_eq!(bindings.len(), 1, "USING <g1> must scope WHERE to g1 only");
1651
1652 let using_g2 = [GraphReference::Iri("http://ex/g2".to_string())];
1654 let bindings = executor
1655 .evaluate_pattern_with_using(&pattern, Some(&using_g2))
1656 .expect("using g2");
1657 assert_eq!(bindings.len(), 1, "USING <g2> must scope WHERE to g2 only");
1658
1659 let bindings = executor
1661 .evaluate_pattern_with_using(&pattern, None)
1662 .expect("no using");
1663 assert!(
1664 bindings.is_empty(),
1665 "without USING the default graph is empty here"
1666 );
1667
1668 let using_default = [GraphReference::Default];
1670 assert!(executor
1671 .evaluate_pattern_with_using(&pattern, Some(&using_default))
1672 .is_err());
1673 }
1674}