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}