Skip to main content

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
5#![expect(deprecated)]
6
7use spareval::QueryDatasetSpecification;
8use spargebra::GraphUpdateOperation;
9use std::fmt;
10use std::str::FromStr;
11
12/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/).
13///
14/// ```
15/// use oxigraph::model::NamedNode;
16/// use oxigraph::sparql::Query;
17///
18/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
19/// let mut query = Query::parse(query_str, None)?;
20///
21/// assert_eq!(query.to_string(), query_str);
22///
23/// // We edit the query dataset specification
24/// let default = vec![NamedNode::new("http://example.com")?.into()];
25/// query.dataset_mut().set_default_graph(default.clone());
26/// assert_eq!(
27///     query.dataset().default_graph_graphs(),
28///     Some(default.as_slice())
29/// );
30/// # Ok::<_, Box<dyn std::error::Error>>(())
31/// ```
32#[expect(clippy::field_scoped_visibility_modifiers)]
33#[derive(Eq, PartialEq, Debug, Clone, Hash)]
34#[deprecated(
35    note = "Use SparqlEvaluator instead to parse the query with options or directly the spargebra::Query type",
36    since = "0.5.0"
37)]
38pub struct Query {
39    pub(super) inner: spargebra::Query,
40    pub(super) dataset: QueryDatasetSpecification,
41}
42
43impl Query {
44    /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
45    pub fn parse(
46        query: &str,
47        base_iri: Option<&str>,
48    ) -> Result<Self, spargebra::SparqlSyntaxError> {
49        #[expect(deprecated)]
50        Ok(spargebra::Query::parse(query, base_iri)?.into())
51    }
52
53    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
54    pub fn dataset(&self) -> &QueryDatasetSpecification {
55        &self.dataset
56    }
57
58    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset)
59    pub fn dataset_mut(&mut self) -> &mut QueryDatasetSpecification {
60        &mut self.dataset
61    }
62}
63
64impl fmt::Display for Query {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        self.inner.fmt(f) // TODO: override
67    }
68}
69
70impl FromStr for Query {
71    type Err = spargebra::SparqlSyntaxError;
72
73    fn from_str(query: &str) -> Result<Self, Self::Err> {
74        Self::parse(query, None)
75    }
76}
77
78impl TryFrom<&str> for Query {
79    type Error = spargebra::SparqlSyntaxError;
80
81    fn try_from(query: &str) -> Result<Self, Self::Error> {
82        Self::from_str(query)
83    }
84}
85
86impl TryFrom<&String> for Query {
87    type Error = spargebra::SparqlSyntaxError;
88
89    fn try_from(query: &String) -> Result<Self, Self::Error> {
90        Self::from_str(query)
91    }
92}
93
94impl From<spargebra::Query> for Query {
95    fn from(query: spargebra::Query) -> Self {
96        Self {
97            dataset: query.dataset().cloned().map(Into::into).unwrap_or_default(),
98            inner: query,
99        }
100    }
101}
102
103/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
104///
105/// ```
106/// use oxigraph::sparql::Update;
107///
108/// let update_str = "CLEAR ALL ;";
109/// let update = Update::parse(update_str, None)?;
110///
111/// assert_eq!(update.to_string().trim(), update_str);
112/// # Ok::<_, oxigraph::sparql::SparqlSyntaxError>(())
113/// ```
114#[expect(clippy::field_scoped_visibility_modifiers)]
115#[derive(Eq, PartialEq, Debug, Clone, Hash)]
116#[deprecated(
117    note = "Use SparqlEvaluator instead to parse the update with options or directly the spargebra::Update type",
118    since = "0.5.0"
119)]
120pub struct Update {
121    pub(super) inner: spargebra::Update,
122    pub(super) using_datasets: Vec<Option<QueryDatasetSpecification>>,
123}
124
125impl Update {
126    /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
127    pub fn parse(
128        update: &str,
129        base_iri: Option<&str>,
130    ) -> Result<Self, spargebra::SparqlSyntaxError> {
131        #[expect(deprecated)]
132        Ok(spargebra::Update::parse(update, base_iri)?.into())
133    }
134
135    /// 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).
136    pub fn using_datasets(&self) -> impl Iterator<Item = &QueryDatasetSpecification> {
137        self.using_datasets.iter().filter_map(Option::as_ref)
138    }
139
140    /// 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).
141    pub fn using_datasets_mut(&mut self) -> impl Iterator<Item = &mut QueryDatasetSpecification> {
142        self.using_datasets.iter_mut().filter_map(Option::as_mut)
143    }
144}
145
146impl fmt::Display for Update {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        self.inner.fmt(f)
149    }
150}
151
152impl FromStr for Update {
153    type Err = spargebra::SparqlSyntaxError;
154
155    fn from_str(update: &str) -> Result<Self, Self::Err> {
156        Self::parse(update, None)
157    }
158}
159
160impl TryFrom<&str> for Update {
161    type Error = spargebra::SparqlSyntaxError;
162
163    fn try_from(update: &str) -> Result<Self, Self::Error> {
164        Self::from_str(update)
165    }
166}
167
168impl TryFrom<&String> for Update {
169    type Error = spargebra::SparqlSyntaxError;
170
171    fn try_from(update: &String) -> Result<Self, Self::Error> {
172        Self::from_str(update)
173    }
174}
175
176impl From<spargebra::Update> for Update {
177    fn from(update: spargebra::Update) -> Self {
178        Self {
179            using_datasets: update
180                .operations
181                .iter()
182                .map(|operation| {
183                    if let GraphUpdateOperation::DeleteInsert { using, .. } = operation {
184                        Some(using.clone().map(Into::into).unwrap_or_default())
185                    } else {
186                        None
187                    }
188                })
189                .collect(),
190            inner: update,
191        }
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn test_send_sync() {
201        fn is_send_sync<T: Send + Sync>() {}
202        is_send_sync::<Query>();
203        is_send_sync::<Update>();
204    }
205}