oxigraph/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 crate::model::*;
6use spargebra::GraphUpdateOperation;
7use std::fmt;
8use std::str::FromStr;
9
10/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
11///
12/// ```
13/// use oxigraph::model::NamedNode;
14/// use oxigraph::sparql::Query;
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(
40        query: &str,
41        base_iri: Option<&str>,
42    ) -> Result<Self, spargebra::SparqlSyntaxError> {
43        let query = Self::from(spargebra::Query::parse(query, base_iri)?);
44        Ok(Self {
45            dataset: query.dataset,
46            inner: query.inner,
47        })
48    }
49
50    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
51    pub fn dataset(&self) -> &QueryDataset {
52        &self.dataset
53    }
54
55    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
56    pub fn dataset_mut(&mut self) -> &mut QueryDataset {
57        &mut self.dataset
58    }
59}
60
61impl fmt::Display for Query {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        self.inner.fmt(f) // TODO: override
64    }
65}
66
67impl FromStr for Query {
68    type Err = spargebra::SparqlSyntaxError;
69
70    fn from_str(query: &str) -> Result<Self, Self::Err> {
71        Self::parse(query, None)
72    }
73}
74
75impl TryFrom<&str> for Query {
76    type Error = spargebra::SparqlSyntaxError;
77
78    fn try_from(query: &str) -> Result<Self, Self::Error> {
79        Self::from_str(query)
80    }
81}
82
83impl TryFrom<&String> for Query {
84    type Error = spargebra::SparqlSyntaxError;
85
86    fn try_from(query: &String) -> Result<Self, Self::Error> {
87        Self::from_str(query)
88    }
89}
90
91impl From<spargebra::Query> for Query {
92    fn from(query: spargebra::Query) -> Self {
93        Self {
94            dataset: QueryDataset::from_algebra(match &query {
95                spargebra::Query::Select { dataset, .. }
96                | spargebra::Query::Construct { dataset, .. }
97                | spargebra::Query::Describe { dataset, .. }
98                | spargebra::Query::Ask { dataset, .. } => dataset,
99            }),
100            inner: query,
101        }
102    }
103}
104
105/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
106///
107/// ```
108/// use oxigraph::sparql::Update;
109///
110/// let update_str = "CLEAR ALL ;";
111/// let update = Update::parse(update_str, None)?;
112///
113/// assert_eq!(update.to_string().trim(), update_str);
114/// # Ok::<_, oxigraph::sparql::SparqlSyntaxError>(())
115/// ```
116#[allow(clippy::field_scoped_visibility_modifiers)]
117#[derive(Eq, PartialEq, Debug, Clone, Hash)]
118pub struct Update {
119    pub(super) inner: spargebra::Update,
120    pub(super) using_datasets: Vec<Option<QueryDataset>>,
121}
122
123impl Update {
124    /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
125    pub fn parse(
126        update: &str,
127        base_iri: Option<&str>,
128    ) -> Result<Self, spargebra::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 = spargebra::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 = spargebra::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 = spargebra::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    pub(crate) fn new() -> Self {
201        Self {
202            default: None,
203            named: None,
204        }
205    }
206
207    fn from_algebra(inner: &Option<spargebra::algebra::QueryDataset>) -> Self {
208        if let Some(inner) = inner {
209            Self {
210                default: Some(inner.default.iter().map(|g| g.clone().into()).collect()),
211                named: inner
212                    .named
213                    .as_ref()
214                    .map(|named| named.iter().map(|g| g.clone().into()).collect()),
215            }
216        } else {
217            Self {
218                default: Some(vec![GraphName::DefaultGraph]),
219                named: None,
220            }
221        }
222    }
223
224    /// Checks if this dataset specification is the default one
225    /// (i.e. the default graph is the store default graph and all the store named graphs are available)
226    ///
227    /// ```
228    /// use oxigraph::sparql::Query;
229    ///
230    /// assert!(Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?
231    ///     .dataset()
232    ///     .is_default_dataset());
233    /// assert!(!Query::parse(
234    ///     "SELECT ?s ?p ?o FROM <http://example.com> WHERE { ?s ?p ?o . }",
235    ///     None
236    /// )?
237    /// .dataset()
238    /// .is_default_dataset());
239    ///
240    /// # Ok::<_, Box<dyn std::error::Error>>(())
241    /// ```
242    pub fn is_default_dataset(&self) -> bool {
243        self.default
244            .as_ref()
245            .is_some_and(|t| t == &[GraphName::DefaultGraph])
246            && self.named.is_none()
247    }
248
249    /// 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
250    /// This list is by default only the store default graph
251    pub fn default_graph_graphs(&self) -> Option<&[GraphName]> {
252        self.default.as_deref()
253    }
254
255    /// Sets if the default graph for the query should be the union of all the graphs in the queried store
256    pub fn set_default_graph_as_union(&mut self) {
257        self.default = None;
258    }
259
260    /// Sets the list of graphs the query should consider as being part of the default graph.
261    ///
262    /// By default only the store default graph is considered.
263    /// ```
264    /// use oxigraph::model::NamedNode;
265    /// use oxigraph::sparql::Query;
266    ///
267    /// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
268    /// let default = vec![NamedNode::new("http://example.com")?.into()];
269    /// query.dataset_mut().set_default_graph(default.clone());
270    /// assert_eq!(
271    ///     query.dataset().default_graph_graphs(),
272    ///     Some(default.as_slice())
273    /// );
274    ///
275    /// # Ok::<_, Box<dyn std::error::Error>>(())
276    /// ```
277    pub fn set_default_graph(&mut self, graphs: Vec<GraphName>) {
278        self.default = Some(graphs)
279    }
280
281    /// Returns the list of the available named graphs for the query or `None` if all graphs are available
282    pub fn available_named_graphs(&self) -> Option<&[NamedOrBlankNode]> {
283        self.named.as_deref()
284    }
285
286    /// Sets the list of allowed named graphs in the query.
287    ///
288    /// ```
289    /// use oxigraph::model::NamedNode;
290    /// use oxigraph::sparql::Query;
291    ///
292    /// let mut query = Query::parse("SELECT ?s ?p ?o WHERE { ?s ?p ?o . }", None)?;
293    /// let named = vec![NamedNode::new("http://example.com")?.into()];
294    /// query
295    ///     .dataset_mut()
296    ///     .set_available_named_graphs(named.clone());
297    /// assert_eq!(
298    ///     query.dataset().available_named_graphs(),
299    ///     Some(named.as_slice())
300    /// );
301    ///
302    /// # Ok::<_, Box<dyn std::error::Error>>(())
303    /// ```
304    pub fn set_available_named_graphs(&mut self, named_graphs: Vec<NamedOrBlankNode>) {
305        self.named = Some(named_graphs);
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    #[test]
314    fn test_send_sync() {
315        fn is_send_sync<T: Send + Sync>() {}
316        is_send_sync::<Query>();
317        is_send_sync::<Update>();
318    }
319}