Skip to main content

spareval/
update.rs

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/// A [`Quad`] to delete or insert.
16#[derive(Eq, PartialEq, Debug, Clone, Hash)]
17pub enum DeleteInsertQuad {
18    Delete(Quad),
19    Insert(Quad),
20}
21
22/// Output of [`PreparedDeleteInsertUpdate::execute`](super::PreparedDeleteInsertUpdate::execute).
23pub 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}