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}