rdf_fusion_execution/sparql/algebra.rs
1//! [SPARQL 1.1 Query Algebra](https://www.w3.org/TR/sparql11-query/#sparqlQuery)
2//!
3//! The root type for SPARQL queries is [`Query`] and the root type for updates is [`Update`].
4
5use rdf_fusion_model::{GraphName, NamedOrBlankNode};
6use spargebra::{GraphUpdateOperation, SparqlSyntaxError};
7use std::fmt;
8use std::str::FromStr;
9
10/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
11///
12/// ```
13/// use rdf_fusion_execution::sparql::Query;
14/// use rdf_fusion_model::NamedNode;
15///
16/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
17/// let mut query = Query::parse(query_str, None)?;
18///
19/// assert_eq!(query.to_string(), query_str);
20///
21/// // We edit the query dataset specification
22/// let default = vec![NamedNode::new("http://example.com")?.into()];
23/// query.dataset_mut().set_default_graph(default.clone());
24/// assert_eq!(
25/// query.dataset().default_graph_graphs(),
26/// Some(default.as_slice())
27/// );
28/// # Ok::<_, Box<dyn std::error::Error>>(())
29/// ```
30#[allow(clippy::field_scoped_visibility_modifiers)]
31#[derive(Eq, PartialEq, Debug, Clone, Hash)]
32pub struct Query {
33 pub(super) inner: spargebra::Query,
34 pub(super) dataset: QueryDataset,
35}
36
37impl Query {
38 /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
39 pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, SparqlSyntaxError> {
40 #[allow(deprecated, reason = "Converting to SparqlSyntaxError")]
41 let query = Self::from(spargebra::Query::parse(query, base_iri)?);
42 Ok(Self {
43 dataset: query.dataset,
44 inner: query.inner,
45 })
46 }
47
48 /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
49 pub fn dataset(&self) -> &QueryDataset {
50 &self.dataset
51 }
52
53 /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
54 pub fn dataset_mut(&mut self) -> &mut QueryDataset {
55 &mut self.dataset
56 }
57}
58
59impl fmt::Display for Query {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 self.inner.fmt(f) // TODO: override
62 }
63}
64
65impl FromStr for Query {
66 type Err = SparqlSyntaxError;
67
68 fn from_str(query: &str) -> Result<Self, Self::Err> {
69 Self::parse(query, None)
70 }
71}
72
73impl TryFrom<&str> for Query {
74 type Error = SparqlSyntaxError;
75
76 fn try_from(query: &str) -> Result<Self, Self::Error> {
77 Self::from_str(query)
78 }
79}
80
81impl TryFrom<&String> for Query {
82 type Error = SparqlSyntaxError;
83
84 fn try_from(query: &String) -> Result<Self, Self::Error> {
85 Self::from_str(query)
86 }
87}
88
89impl From<spargebra::Query> for Query {
90 fn from(query: spargebra::Query) -> Self {
91 Self {
92 dataset: QueryDataset::from_algebra(match &query {
93 spargebra::Query::Select { dataset, .. }
94 | spargebra::Query::Construct { dataset, .. }
95 | spargebra::Query::Describe { dataset, .. }
96 | spargebra::Query::Ask { dataset, .. } => dataset,
97 }),
98 inner: query,
99 }
100 }
101}
102
103/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
104///
105/// ```
106/// use spargebra::SparqlSyntaxError;
107/// use rdf_fusion_execution::sparql::Update;
108///
109/// let update_str = "CLEAR ALL ;";
110/// let update = Update::parse(update_str, None)?;
111///
112/// assert_eq!(update.to_string().trim(), update_str);
113/// # Ok::<_, SparqlSyntaxError>(())
114/// ```
115#[allow(clippy::field_scoped_visibility_modifiers)]
116#[derive(Eq, PartialEq, Debug, Clone, Hash)]
117pub struct Update {
118 pub(super) inner: spargebra::Update,
119 pub(super) using_datasets: Vec<Option<QueryDataset>>,
120}
121
122impl Update {
123 /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
124 pub fn parse(
125 update: &str,
126 base_iri: Option<&str>,
127 ) -> Result<Self, SparqlSyntaxError> {
128 #[allow(deprecated, reason = "Converting to SparqlSyntaxError")]
129 Ok(spargebra::Update::parse(update, base_iri)?.into())
130 }
131
132 /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) in [DELETE/INSERT operations](https://www.w3.org/TR/sparql11-update/#deleteInsert).
133 pub fn using_datasets(&self) -> impl Iterator<Item = &QueryDataset> {
134 self.using_datasets.iter().filter_map(Option::as_ref)
135 }
136
137 /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) in [DELETE/INSERT operations](https://www.w3.org/TR/sparql11-update/#deleteInsert).
138 pub fn using_datasets_mut(&mut self) -> impl Iterator<Item = &mut QueryDataset> {
139 self.using_datasets.iter_mut().filter_map(Option::as_mut)
140 }
141}
142
143impl fmt::Display for Update {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 self.inner.fmt(f)
146 }
147}
148
149impl FromStr for Update {
150 type Err = SparqlSyntaxError;
151
152 fn from_str(update: &str) -> Result<Self, Self::Err> {
153 Self::parse(update, None)
154 }
155}
156
157impl TryFrom<&str> for Update {
158 type Error = SparqlSyntaxError;
159
160 fn try_from(update: &str) -> Result<Self, Self::Error> {
161 Self::from_str(update)
162 }
163}
164
165impl TryFrom<&String> for Update {
166 type Error = SparqlSyntaxError;
167
168 fn try_from(update: &String) -> Result<Self, Self::Error> {
169 Self::from_str(update)
170 }
171}
172
173impl From<spargebra::Update> for Update {
174 fn from(update: spargebra::Update) -> Self {
175 Self {
176 using_datasets: update
177 .operations
178 .iter()
179 .map(|operation| {
180 if let GraphUpdateOperation::DeleteInsert { using, .. } = operation {
181 Some(QueryDataset::from_algebra(using))
182 } else {
183 None
184 }
185 })
186 .collect(),
187 inner: update,
188 }
189 }
190}
191
192/// A SPARQL query [dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
193#[derive(Eq, PartialEq, Debug, Clone, Hash)]
194pub struct QueryDataset {
195 default: Option<Vec<GraphName>>,
196 named: Option<Vec<NamedOrBlankNode>>,
197}
198
199impl QueryDataset {
200 fn from_algebra(inner: &Option<spargebra::algebra::QueryDataset>) -> Self {
201 if let Some(inner) = inner {
202 Self {
203 default: Some(inner.default.iter().map(|g| g.clone().into()).collect()),
204 named: inner
205 .named
206 .as_ref()
207 .map(|named| named.iter().map(|g| g.clone().into()).collect()),
208 }
209 } else {
210 Self {
211 default: Some(vec![GraphName::DefaultGraph]),
212 named: None,
213 }
214 }
215 }
216
217 /// Checks if this dataset specification is the default one
218 /// (i.e. the default graph is the store default graph and all the store named graphs are available)
219 ///
220 /// ```
221 /// use rdf_fusion_execution::sparql::Query;
222 ///
223 /// assert!(Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?
224 /// .dataset()
225 /// .is_default_dataset());
226 /// assert!(!Query::parse(
227 /// "SELECT ?s ?p ?o FROM <http://example.com> WHERE { ?s ?p ?o . }",
228 /// None
229 /// )?
230 /// .dataset()
231 /// .is_default_dataset());
232 ///
233 /// # Ok::<_, Box<dyn std::error::Error>>(())
234 /// ```
235 pub fn is_default_dataset(&self) -> bool {
236 self.default
237 .as_ref()
238 .is_some_and(|t| t == &[GraphName::DefaultGraph])
239 && self.named.is_none()
240 }
241
242 /// Returns the list of the store graphs that are available to the query as the default graph or `None` if the union of all graphs is used as the default graph
243 /// This list is by default only the store default graph
244 pub fn default_graph_graphs(&self) -> Option<&[GraphName]> {
245 self.default.as_deref()
246 }
247
248 /// Sets if the default graph for the query should be the union of all the graphs in the queried store
249 pub fn set_default_graph_as_union(&mut self) {
250 self.default = None;
251 }
252
253 /// Sets the list of graphs the query should consider as being part of the default graph.
254 ///
255 /// By default only the store default graph is considered.
256 /// ```
257 /// use rdf_fusion_execution::sparql::Query;
258 /// use rdf_fusion_model::NamedNode;
259 ///
260 /// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
261 /// let default = vec![NamedNode::new("http://example.com")?.into()];
262 /// query.dataset_mut().set_default_graph(default.clone());
263 /// assert_eq!(
264 /// query.dataset().default_graph_graphs(),
265 /// Some(default.as_slice())
266 /// );
267 ///
268 /// # Ok::<_, Box<dyn std::error::Error>>(())
269 /// ```
270 pub fn set_default_graph(&mut self, graphs: Vec<GraphName>) {
271 self.default = Some(graphs)
272 }
273
274 /// Returns the list of the available named graphs for the query or `None` if all graphs are available
275 pub fn available_named_graphs(&self) -> Option<&[NamedOrBlankNode]> {
276 self.named.as_deref()
277 }
278
279 /// Sets the list of allowed named graphs in the query.
280 ///
281 /// ```
282 /// use rdf_fusion_execution::sparql::Query;
283 /// use rdf_fusion_model::NamedNode;
284 ///
285 /// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
286 /// let named = vec![NamedNode::new("http://example.com")?.into()];
287 /// query
288 /// .dataset_mut()
289 /// .set_available_named_graphs(named.clone());
290 /// assert_eq!(
291 /// query.dataset().available_named_graphs(),
292 /// Some(named.as_slice())
293 /// );
294 ///
295 /// # Ok::<_, Box<dyn std::error::Error>>(())
296 /// ```
297 pub fn set_available_named_graphs(&mut self, named_graphs: Vec<NamedOrBlankNode>) {
298 self.named = Some(named_graphs);
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_send_sync() {
308 fn is_send_sync<T: Send + Sync>() {}
309 is_send_sync::<Query>();
310 is_send_sync::<Update>();
311 }
312}