1use crate::{QueryEvaluationError, QuerySolutionIter};
2#[cfg(feature = "sparql-12")]
3use oxrdf::Triple;
4use oxrdf::{BlankNode, GraphName, NamedNode, Quad, Term};
5use rustc_hash::FxHashMap;
6use sparesults::QuerySolution;
7use spargebra::term::{
8 GraphNamePattern, GroundQuadPattern, GroundTermPattern, NamedNodePattern, QuadPattern,
9 TermPattern,
10};
11#[cfg(feature = "sparql-12")]
12use spargebra::term::{GroundTriplePattern, TriplePattern};
13use std::collections::VecDeque;
14
15#[derive(Eq, PartialEq, Debug, Clone, Hash)]
17pub enum DeleteInsertQuad {
18 Delete(Quad),
19 Insert(Quad),
20}
21
22pub struct DeleteInsertIter<'a> {
24 solutions: QuerySolutionIter<'a>,
25 delete: Vec<GroundQuadPattern>,
26 insert: Vec<QuadPattern>,
27 buffer: VecDeque<DeleteInsertQuad>,
28 bnodes: FxHashMap<BlankNode, BlankNode>,
29}
30
31impl<'a> DeleteInsertIter<'a> {
32 pub(crate) fn new(
33 solutions: QuerySolutionIter<'a>,
34 delete: Vec<GroundQuadPattern>,
35 insert: Vec<QuadPattern>,
36 ) -> Self {
37 Self {
38 solutions,
39 delete,
40 insert,
41 buffer: VecDeque::new(),
42 bnodes: FxHashMap::default(),
43 }
44 }
45}
46
47impl Iterator for DeleteInsertIter<'_> {
48 type Item = Result<DeleteInsertQuad, QueryEvaluationError>;
49
50 fn next(&mut self) -> Option<Result<DeleteInsertQuad, QueryEvaluationError>> {
51 loop {
52 if let Some(quad) = self.buffer.pop_front() {
53 return Some(Ok(quad));
54 }
55 let solution = match self.solutions.next()? {
56 Ok(solution) => solution,
57 Err(e) => return Some(Err(e)),
58 };
59 for quad in &self.delete {
60 if let Some(quad) = fill_ground_quad_pattern(quad, &solution) {
61 self.buffer.push_back(DeleteInsertQuad::Delete(quad));
62 }
63 }
64 for quad in &self.insert {
65 if let Some(quad) = fill_quad_pattern(quad, &solution, &mut self.bnodes) {
66 self.buffer.push_back(DeleteInsertQuad::Insert(quad));
67 }
68 }
69 self.bnodes.clear();
70 }
71 }
72}
73
74fn fill_quad_pattern(
75 quad: &QuadPattern,
76 solution: &QuerySolution,
77 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
78) -> Option<Quad> {
79 Some(Quad {
80 subject: match fill_term_or_var(&quad.subject, solution, bnodes)? {
81 Term::NamedNode(node) => node.into(),
82 Term::BlankNode(node) => node.into(),
83 #[cfg(feature = "sparql-12")]
84 Term::Triple(_) => return None,
85 Term::Literal(_) => return None,
86 },
87 predicate: fill_named_node_or_var(&quad.predicate, solution)?,
88 object: fill_term_or_var(&quad.object, solution, bnodes)?,
89 graph_name: fill_graph_name_or_var(&quad.graph_name, solution)?,
90 })
91}
92
93fn fill_term_or_var(
94 term: &TermPattern,
95 solution: &QuerySolution,
96 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
97) -> Option<Term> {
98 Some(match term {
99 TermPattern::NamedNode(term) => term.clone().into(),
100 TermPattern::BlankNode(bnode) => convert_blank_node(bnode, bnodes).into(),
101 TermPattern::Literal(term) => term.clone().into(),
102 #[cfg(feature = "sparql-12")]
103 TermPattern::Triple(triple) => fill_triple_pattern(triple, solution, bnodes)?.into(),
104 TermPattern::Variable(v) => solution.get(v)?.clone(),
105 })
106}
107
108fn fill_named_node_or_var(term: &NamedNodePattern, solution: &QuerySolution) -> Option<NamedNode> {
109 Some(match term {
110 NamedNodePattern::NamedNode(term) => term.clone(),
111 NamedNodePattern::Variable(v) => {
112 if let Term::NamedNode(s) = solution.get(v)? {
113 s.clone()
114 } else {
115 return None;
116 }
117 }
118 })
119}
120
121fn fill_graph_name_or_var(term: &GraphNamePattern, solution: &QuerySolution) -> Option<GraphName> {
122 Some(match term {
123 GraphNamePattern::NamedNode(term) => term.clone().into(),
124 GraphNamePattern::DefaultGraph => GraphName::DefaultGraph,
125 GraphNamePattern::Variable(v) => match solution.get(v)? {
126 Term::NamedNode(node) => node.clone().into(),
127 Term::BlankNode(node) => node.clone().into(),
128 Term::Literal(_) => return None,
129 #[cfg(feature = "sparql-12")]
130 Term::Triple(_) => return None,
131 },
132 })
133}
134
135#[cfg(feature = "sparql-12")]
136fn fill_triple_pattern(
137 triple: &TriplePattern,
138 solution: &QuerySolution,
139 bnodes: &mut FxHashMap<BlankNode, BlankNode>,
140) -> Option<Triple> {
141 Some(Triple {
142 subject: match fill_term_or_var(&triple.subject, solution, bnodes)? {
143 Term::NamedNode(node) => node.into(),
144 Term::BlankNode(node) => node.into(),
145 #[cfg(feature = "sparql-12")]
146 Term::Triple(_) => return None,
147 Term::Literal(_) => return None,
148 },
149 predicate: fill_named_node_or_var(&triple.predicate, solution)?,
150 object: fill_term_or_var(&triple.object, solution, bnodes)?,
151 })
152}
153fn fill_ground_quad_pattern(quad: &GroundQuadPattern, solution: &QuerySolution) -> Option<Quad> {
154 Some(Quad {
155 subject: match fill_ground_term_or_var(&quad.subject, solution)? {
156 Term::NamedNode(node) => node.into(),
157 Term::BlankNode(node) => node.into(),
158 #[cfg(feature = "sparql-12")]
159 Term::Triple(_) => return None,
160 Term::Literal(_) => return None,
161 },
162 predicate: fill_named_node_or_var(&quad.predicate, solution)?,
163 object: fill_ground_term_or_var(&quad.object, solution)?,
164 graph_name: fill_graph_name_or_var(&quad.graph_name, solution)?,
165 })
166}
167
168fn fill_ground_term_or_var(term: &GroundTermPattern, solution: &QuerySolution) -> Option<Term> {
169 Some(match term {
170 GroundTermPattern::NamedNode(term) => term.clone().into(),
171 GroundTermPattern::Literal(term) => term.clone().into(),
172 #[cfg(feature = "sparql-12")]
173 GroundTermPattern::Triple(triple) => fill_ground_triple_pattern(triple, solution)?.into(),
174 GroundTermPattern::Variable(v) => solution.get(v)?.clone(),
175 })
176}
177
178#[cfg(feature = "sparql-12")]
179fn fill_ground_triple_pattern(
180 triple: &GroundTriplePattern,
181 solution: &QuerySolution,
182) -> Option<Triple> {
183 Some(Triple {
184 subject: match fill_ground_term_or_var(&triple.subject, solution)? {
185 Term::NamedNode(node) => node.into(),
186 Term::BlankNode(node) => node.into(),
187 #[cfg(feature = "sparql-12")]
188 Term::Triple(_) => return None,
189 Term::Literal(_) => return None,
190 },
191 predicate: fill_named_node_or_var(&triple.predicate, solution)?,
192 object: fill_ground_term_or_var(&triple.object, solution)?,
193 })
194}
195
196fn convert_blank_node(node: &BlankNode, bnodes: &mut FxHashMap<BlankNode, BlankNode>) -> BlankNode {
197 bnodes.entry(node.clone()).or_default().clone()
198}