oxigraph/
store.rs

1//! API to access an on-disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
2//!
3//! The entry point of the module is the [`Store`] struct.
4//!
5//! Usage example:
6//! ```
7//! use oxigraph::model::*;
8//! use oxigraph::sparql::QueryResults;
9//! use oxigraph::store::Store;
10//!
11//! let store = Store::new()?;
12//!
13//! // insertion
14//! let ex = NamedNode::new("http://example.com")?;
15//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
16//! store.insert(&quad)?;
17//!
18//! // quad filter
19//! let results: Result<Vec<Quad>, _> = store.quads_for_pattern(None, None, None, None).collect();
20//! assert_eq!(vec![quad], results?);
21//!
22//! // SPARQL query
23//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
24//!     assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
25//! };
26//! # Result::<_, Box<dyn std::error::Error>>::Ok(())
27//! ```
28use crate::io::{RdfFormat, RdfParseError, RdfParser, RdfSerializer};
29use crate::model::*;
30use crate::sparql::{
31    evaluate_query, evaluate_update, EvaluationError, Query, QueryExplanation, QueryOptions,
32    QueryResults, Update, UpdateOptions,
33};
34use crate::storage::numeric_encoder::{Decoder, EncodedQuad, EncodedTerm};
35pub use crate::storage::{CorruptionError, LoaderError, SerializerError, StorageError};
36use crate::storage::{
37    DecodingGraphIterator, DecodingQuadIterator, Storage, StorageBulkLoader, StorageReader,
38    StorageWriter,
39};
40use std::error::Error;
41use std::io::{Read, Write};
42#[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
43use std::path::Path;
44use std::{fmt, str};
45
46/// An on-disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
47/// Allows to query and update it using SPARQL.
48/// It is based on the [RocksDB](https://rocksdb.org/) key-value store.
49///
50/// This store ensures the "repeatable read" isolation level: the store only exposes changes that have
51/// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
52/// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
53///
54/// Usage example:
55/// ```
56/// use oxigraph::model::*;
57/// use oxigraph::sparql::QueryResults;
58/// use oxigraph::store::Store;
59/// # use std::fs::remove_dir_all;
60///
61/// # {
62/// let store = Store::open("example.db")?;
63///
64/// // insertion
65/// let ex = NamedNode::new("http://example.com")?;
66/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
67/// store.insert(&quad)?;
68///
69/// // quad filter
70/// let results: Result<Vec<Quad>, _> = store.quads_for_pattern(None, None, None, None).collect();
71/// assert_eq!(vec![quad], results?);
72///
73/// // SPARQL query
74/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
75///     assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
76/// };
77/// #
78/// # };
79/// # remove_dir_all("example.db")?;
80/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
81/// ```
82#[derive(Clone)]
83pub struct Store {
84    storage: Storage,
85}
86
87impl Store {
88    /// New in-memory [`Store`] without RocksDB.
89    pub fn new() -> Result<Self, StorageError> {
90        Ok(Self {
91            storage: Storage::new()?,
92        })
93    }
94
95    /// Opens a read-write [`Store`] and creates it if it does not exist yet.
96    ///
97    /// Only one read-write [`Store`] can exist at the same time.
98    /// If you want to have extra [`Store`] instance opened on the same data
99    /// use [`Store::open_read_only`].
100    #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
101    pub fn open(path: impl AsRef<Path>) -> Result<Self, StorageError> {
102        Ok(Self {
103            storage: Storage::open(path.as_ref())?,
104        })
105    }
106
107    /// Opens a read-only [`Store`] from disk.
108    ///
109    /// Opening as read-only while having an other process writing the database is undefined behavior.
110    #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
111    pub fn open_read_only(path: impl AsRef<Path>) -> Result<Self, StorageError> {
112        Ok(Self {
113            storage: Storage::open_read_only(path.as_ref())?,
114        })
115    }
116
117    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
118    ///
119    /// Usage example:
120    /// ```
121    /// use oxigraph::model::*;
122    /// use oxigraph::sparql::QueryResults;
123    /// use oxigraph::store::Store;
124    ///
125    /// let store = Store::new()?;
126    ///
127    /// // insertions
128    /// let ex = NamedNodeRef::new("http://example.com")?;
129    /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
130    ///
131    /// // SPARQL query
132    /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
133    ///     assert_eq!(
134    ///         solutions.next().unwrap()?.get("s"),
135    ///         Some(&ex.into_owned().into())
136    ///     );
137    /// }
138    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
139    /// ```
140    pub fn query(
141        &self,
142        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
143    ) -> Result<QueryResults, EvaluationError> {
144        self.query_opt(query, QueryOptions::default())
145    }
146
147    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
148    ///
149    /// Usage example with a custom function serializing terms to N-Triples:
150    /// ```
151    /// use oxigraph::model::*;
152    /// use oxigraph::sparql::{QueryOptions, QueryResults};
153    /// use oxigraph::store::Store;
154    ///
155    /// let store = Store::new()?;
156    /// if let QueryResults::Solutions(mut solutions) = store.query_opt(
157    ///     "SELECT (<http://www.w3.org/ns/formats/N-Triples>(1) AS ?nt) WHERE {}",
158    ///     QueryOptions::default().with_custom_function(
159    ///         NamedNode::new("http://www.w3.org/ns/formats/N-Triples")?,
160    ///         |args| args.get(0).map(|t| Literal::from(t.to_string()).into()),
161    ///     ),
162    /// )? {
163    ///     assert_eq!(
164    ///         solutions.next().unwrap()?.get("nt"),
165    ///         Some(&Literal::from("\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>").into())
166    ///     );
167    /// }
168    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
169    /// ```
170    pub fn query_opt(
171        &self,
172        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
173        options: QueryOptions,
174    ) -> Result<QueryResults, EvaluationError> {
175        let (results, _) = self.explain_query_opt(query, options, false)?;
176        results
177    }
178
179    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options while substituting some variables with the given values.
180    ///
181    /// Substitution follows [RDF-dev SEP-0007](https://github.com/w3c/sparql-dev/blob/main/SEP/SEP-0007/sep-0007.md).
182    ///
183    /// Usage example with a custom function serializing terms to N-Triples:
184    /// ```
185    /// use oxigraph::model::{Literal, Variable};
186    /// use oxigraph::sparql::{QueryOptions, QueryResults};
187    /// use oxigraph::store::Store;
188    ///
189    /// let store = Store::new()?;
190    /// if let QueryResults::Solutions(mut solutions) = store.query_opt_with_substituted_variables(
191    ///     "SELECT ?v WHERE {}",
192    ///     QueryOptions::default(),
193    ///     [(Variable::new("v")?, Literal::from(1).into())],
194    /// )? {
195    ///     assert_eq!(
196    ///         solutions.next().unwrap()?.get("v"),
197    ///         Some(&Literal::from(1).into())
198    ///     );
199    /// }
200    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
201    /// ```
202    pub fn query_opt_with_substituted_variables(
203        &self,
204        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
205        options: QueryOptions,
206        substitutions: impl IntoIterator<Item = (Variable, Term)>,
207    ) -> Result<QueryResults, EvaluationError> {
208        let (results, _) = self.explain_query_opt_with_substituted_variables(
209            query,
210            options,
211            false,
212            substitutions,
213        )?;
214        results
215    }
216
217    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options and
218    /// returns a query explanation with some statistics (if enabled with the `with_stats` parameter).
219    ///
220    /// <div class="warning">If you want to compute statistics you need to exhaust the results iterator before having a look at them.</div>
221    ///
222    /// Usage example serialising the explanation with statistics in JSON:
223    /// ```
224    /// use oxigraph::sparql::{QueryOptions, QueryResults};
225    /// use oxigraph::store::Store;
226    ///
227    /// let store = Store::new()?;
228    /// if let (Ok(QueryResults::Solutions(solutions)), explanation) = store.explain_query_opt(
229    ///     "SELECT ?s WHERE { VALUES ?s { 1 2 3 } }",
230    ///     QueryOptions::default(),
231    ///     true,
232    /// )? {
233    ///     // We make sure to have read all the solutions
234    ///     for _ in solutions {}
235    ///     let mut buf = Vec::new();
236    ///     explanation.write_in_json(&mut buf)?;
237    /// }
238    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
239    /// ```
240    pub fn explain_query_opt(
241        &self,
242        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
243        options: QueryOptions,
244        with_stats: bool,
245    ) -> Result<(Result<QueryResults, EvaluationError>, QueryExplanation), EvaluationError> {
246        self.explain_query_opt_with_substituted_variables(query, options, with_stats, [])
247    }
248
249    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options and
250    /// returns a query explanation with some statistics (if enabled with the `with_stats` parameter).
251    ///
252    /// <div class="warning">If you want to compute statistics you need to exhaust the results iterator before having a look at them.</div>
253    ///
254    /// Usage example serialising the explanation with statistics in JSON:
255    /// ```
256    /// use oxigraph::sparql::{QueryOptions, QueryResults};
257    /// use oxigraph::store::Store;
258    /// use oxrdf::{Literal, Variable};
259    ///
260    /// let store = Store::new()?;
261    /// if let (Ok(QueryResults::Solutions(solutions)), explanation) = store
262    ///     .explain_query_opt_with_substituted_variables(
263    ///         "SELECT ?s WHERE {}",
264    ///         QueryOptions::default(),
265    ///         true,
266    ///         [(Variable::new("s")?, Literal::from(1).into())],
267    ///     )?
268    /// {
269    ///     // We make sure to have read all the solutions
270    ///     for _ in solutions {}
271    ///     let mut buf = Vec::new();
272    ///     explanation.write_in_json(&mut buf)?;
273    /// }
274    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
275    /// ```
276    pub fn explain_query_opt_with_substituted_variables(
277        &self,
278        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
279        options: QueryOptions,
280        with_stats: bool,
281        substitutions: impl IntoIterator<Item = (Variable, Term)>,
282    ) -> Result<(Result<QueryResults, EvaluationError>, QueryExplanation), EvaluationError> {
283        evaluate_query(
284            self.storage.snapshot(),
285            query,
286            options,
287            with_stats,
288            substitutions,
289        )
290    }
291
292    /// Retrieves quads with a filter on each quad component
293    ///
294    /// Usage example:
295    /// ```
296    /// use oxigraph::model::*;
297    /// use oxigraph::store::Store;
298    ///
299    /// let store = Store::new()?;
300    ///
301    /// // insertion
302    /// let ex = NamedNode::new("http://example.com")?;
303    /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
304    /// store.insert(&quad)?;
305    ///
306    /// // quad filter by object
307    /// let results = store
308    ///     .quads_for_pattern(None, None, Some((&ex).into()), None)
309    ///     .collect::<Result<Vec<_>, _>>()?;
310    /// assert_eq!(vec![quad], results);
311    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
312    /// ```
313    pub fn quads_for_pattern(
314        &self,
315        subject: Option<SubjectRef<'_>>,
316        predicate: Option<NamedNodeRef<'_>>,
317        object: Option<TermRef<'_>>,
318        graph_name: Option<GraphNameRef<'_>>,
319    ) -> QuadIter {
320        let reader = self.storage.snapshot();
321        QuadIter {
322            iter: reader.quads_for_pattern(
323                subject.map(EncodedTerm::from).as_ref(),
324                predicate.map(EncodedTerm::from).as_ref(),
325                object.map(EncodedTerm::from).as_ref(),
326                graph_name.map(EncodedTerm::from).as_ref(),
327            ),
328            reader,
329        }
330    }
331
332    /// Returns all the quads contained in the store.
333    ///
334    /// Usage example:
335    /// ```
336    /// use oxigraph::model::*;
337    /// use oxigraph::store::Store;
338    ///
339    /// let store = Store::new()?;
340    ///
341    /// // insertion
342    /// let ex = NamedNode::new("http://example.com")?;
343    /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
344    /// store.insert(&quad)?;
345    ///
346    /// // quad filter by object
347    /// let results = store.iter().collect::<Result<Vec<_>, _>>()?;
348    /// assert_eq!(vec![quad], results);
349    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
350    /// ```
351    pub fn iter(&self) -> QuadIter {
352        self.quads_for_pattern(None, None, None, None)
353    }
354
355    /// Checks if this store contains a given quad.
356    ///
357    /// Usage example:
358    /// ```
359    /// use oxigraph::model::*;
360    /// use oxigraph::store::Store;
361    ///
362    /// let ex = NamedNodeRef::new("http://example.com")?;
363    /// let quad = QuadRef::new(ex, ex, ex, ex);
364    ///
365    /// let store = Store::new()?;
366    /// assert!(!store.contains(quad)?);
367    ///
368    /// store.insert(quad)?;
369    /// assert!(store.contains(quad)?);
370    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
371    /// ```
372    pub fn contains<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
373        let quad = EncodedQuad::from(quad.into());
374        self.storage.snapshot().contains(&quad)
375    }
376
377    /// Returns the number of quads in the store.
378    ///
379    /// <div class="warning">This function executes a full scan.</div>
380    ///
381    /// Usage example:
382    /// ```
383    /// use oxigraph::model::*;
384    /// use oxigraph::store::Store;
385    ///
386    /// let ex = NamedNodeRef::new("http://example.com")?;
387    /// let store = Store::new()?;
388    /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
389    /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
390    /// assert_eq!(2, store.len()?);
391    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
392    /// ```
393    pub fn len(&self) -> Result<usize, StorageError> {
394        self.storage.snapshot().len()
395    }
396
397    /// Returns if the store is empty.
398    ///
399    /// Usage example:
400    /// ```
401    /// use oxigraph::model::*;
402    /// use oxigraph::store::Store;
403    ///
404    /// let store = Store::new()?;
405    /// assert!(store.is_empty()?);
406    ///
407    /// let ex = NamedNodeRef::new("http://example.com")?;
408    /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
409    /// assert!(!store.is_empty()?);
410    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
411    /// ```
412    pub fn is_empty(&self) -> Result<bool, StorageError> {
413        self.storage.snapshot().is_empty()
414    }
415
416    /// Executes a transaction.
417    ///
418    /// Transactions ensure the "repeatable read" isolation level: the store only exposes changes that have
419    /// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
420    /// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
421    ///
422    /// Usage example:
423    /// ```
424    /// use oxigraph::model::*;
425    /// use oxigraph::store::{StorageError, Store};
426    ///
427    /// let store = Store::new()?;
428    /// let a = NamedNodeRef::new("http://example.com/a")?;
429    /// let b = NamedNodeRef::new("http://example.com/b")?;
430    ///
431    /// // Copy all triples about ex:a to triples about ex:b
432    /// store.transaction(|mut transaction| {
433    ///     for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
434    ///         let q = q?;
435    ///         transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
436    ///     }
437    ///     Result::<_, StorageError>::Ok(())
438    /// })?;
439    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
440    /// ```
441    pub fn transaction<T, E: Error + 'static + From<StorageError>>(
442        &self,
443        f: impl for<'a> Fn(Transaction<'a>) -> Result<T, E>,
444    ) -> Result<T, E> {
445        self.storage.transaction(|writer| f(Transaction { writer }))
446    }
447
448    /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
449    ///
450    /// Usage example:
451    /// ```
452    /// use oxigraph::model::*;
453    /// use oxigraph::store::Store;
454    ///
455    /// let store = Store::new()?;
456    ///
457    /// // insertion
458    /// store
459    ///     .update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
460    ///
461    /// // we inspect the store contents
462    /// let ex = NamedNodeRef::new("http://example.com")?;
463    /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
464    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
465    /// ```
466    pub fn update(
467        &self,
468        update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
469    ) -> Result<(), EvaluationError> {
470        self.update_opt(update, UpdateOptions::default())
471    }
472
473    /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
474    ///
475    /// ```
476    /// use oxigraph::store::Store;
477    /// use oxigraph::model::*;
478    /// use oxigraph::sparql::QueryOptions;
479    ///
480    /// let store = Store::new()?;
481    /// store.update_opt(
482    ///     "INSERT { ?s <http://example.com/n-triples-representation> ?n } WHERE { ?s ?p ?o BIND(<http://www.w3.org/ns/formats/N-Triples>(?s) AS ?nt) }",
483    ///     QueryOptions::default().with_custom_function(
484    ///         NamedNode::new("http://www.w3.org/ns/formats/N-Triples")?,
485    ///         |args| args.get(0).map(|t| Literal::from(t.to_string()).into())
486    ///     )
487    /// )?;
488    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
489    /// ```
490    pub fn update_opt(
491        &self,
492        update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
493        options: impl Into<UpdateOptions>,
494    ) -> Result<(), EvaluationError> {
495        let update = update.try_into().map_err(Into::into)?;
496        let options = options.into();
497        self.storage
498            .transaction(|mut t| evaluate_update(&mut t, &update, &options))
499    }
500
501    /// Loads a RDF file under into the store.
502    ///
503    /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
504    ///
505    /// Usage example:
506    /// ```
507    /// use oxigraph::store::Store;
508    /// use oxigraph::io::RdfFormat;
509    /// use oxigraph::model::*;
510    /// use oxrdfio::RdfParser;
511    ///
512    /// let store = Store::new()?;
513    ///
514    /// // insert a dataset file (former load_dataset method)
515    /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
516    /// store.load_from_reader(RdfFormat::NQuads, file.as_ref())?;
517    ///
518    /// // insert a graph file (former load_graph method)
519    /// let file = b"<> <> <> .";
520    /// store.load_from_reader(
521    ///     RdfParser::from_format(RdfFormat::Turtle)
522    ///         .with_base_iri("http://example.com")?
523    ///         .without_named_graphs() // No named graphs allowed in the input
524    ///         .with_default_graph(NamedNodeRef::new("http://example.com/g2")?), // we put the file default graph inside of a named graph
525    ///     file.as_ref()
526    /// )?;
527    ///
528    /// // we inspect the store contents
529    /// let ex = NamedNodeRef::new("http://example.com")?;
530    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
531    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
532    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
533    /// ```
534    pub fn load_from_reader(
535        &self,
536        parser: impl Into<RdfParser>,
537        reader: impl Read,
538    ) -> Result<(), LoaderError> {
539        let quads = parser
540            .into()
541            .rename_blank_nodes()
542            .for_reader(reader)
543            .collect::<Result<Vec<_>, _>>()?;
544        self.storage.transaction(move |mut t| {
545            for quad in &quads {
546                t.insert(quad.as_ref())?;
547            }
548            Ok(())
549        })
550    }
551
552    /// Loads a graph file (i.e. triples) into the store.
553    ///
554    /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
555    ///
556    /// Usage example:
557    /// ```
558    /// use oxigraph::io::RdfFormat;
559    /// use oxigraph::model::*;
560    /// use oxigraph::store::Store;
561    ///
562    /// let store = Store::new()?;
563    ///
564    /// // insertion
565    /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
566    /// store.load_graph(
567    ///     file.as_ref(),
568    ///     RdfFormat::NTriples,
569    ///     GraphName::DefaultGraph,
570    ///     None,
571    /// )?;
572    ///
573    /// // we inspect the store contents
574    /// let ex = NamedNodeRef::new("http://example.com")?;
575    /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
576    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
577    /// ```
578    #[deprecated(note = "use Store.load_from_reader instead", since = "0.4.0")]
579    pub fn load_graph(
580        &self,
581        reader: impl Read,
582        format: impl Into<RdfFormat>,
583        to_graph_name: impl Into<GraphName>,
584        base_iri: Option<&str>,
585    ) -> Result<(), LoaderError> {
586        let mut parser = RdfParser::from_format(format.into())
587            .without_named_graphs()
588            .with_default_graph(to_graph_name);
589        if let Some(base_iri) = base_iri {
590            parser = parser
591                .with_base_iri(base_iri)
592                .map_err(|e| LoaderError::InvalidBaseIri {
593                    iri: base_iri.into(),
594                    error: e,
595                })?;
596        }
597        self.load_from_reader(parser, reader)
598    }
599
600    /// Loads a dataset file (i.e. quads) into the store.
601    ///
602    /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
603    ///
604    /// Usage example:
605    /// ```
606    /// use oxigraph::io::RdfFormat;
607    /// use oxigraph::model::*;
608    /// use oxigraph::store::Store;
609    ///
610    /// let store = Store::new()?;
611    ///
612    /// // insertion
613    /// let file =
614    ///     b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
615    /// store.load_dataset(file.as_ref(), RdfFormat::NQuads, None)?;
616    ///
617    /// // we inspect the store contents
618    /// let ex = NamedNodeRef::new("http://example.com")?;
619    /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
620    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
621    /// ```
622    #[deprecated(note = "use Store.load_from_reader instead", since = "0.4.0")]
623    pub fn load_dataset(
624        &self,
625        reader: impl Read,
626        format: impl Into<RdfFormat>,
627        base_iri: Option<&str>,
628    ) -> Result<(), LoaderError> {
629        let mut parser = RdfParser::from_format(format.into());
630        if let Some(base_iri) = base_iri {
631            parser = parser
632                .with_base_iri(base_iri)
633                .map_err(|e| LoaderError::InvalidBaseIri {
634                    iri: base_iri.into(),
635                    error: e,
636                })?;
637        }
638        self.load_from_reader(parser, reader)
639    }
640
641    /// Adds a quad to this store.
642    ///
643    /// Returns `true` if the quad was not already in the store.
644    ///
645    /// Usage example:
646    /// ```
647    /// use oxigraph::model::*;
648    /// use oxigraph::store::Store;
649    ///
650    /// let ex = NamedNodeRef::new("http://example.com")?;
651    /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
652    ///
653    /// let store = Store::new()?;
654    /// assert!(store.insert(quad)?);
655    /// assert!(!store.insert(quad)?);
656    ///
657    /// assert!(store.contains(quad)?);
658    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
659    /// ```
660    pub fn insert<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
661        let quad = quad.into();
662        self.transaction(|mut t| t.insert(quad))
663    }
664
665    /// Adds atomically a set of quads to this store.
666    ///
667    /// <div class="warning">
668    ///
669    /// This operation uses a memory heavy transaction internally, use the [`bulk_loader`](Store::bulk_loader) if you plan to add ten of millions of triples.</div>
670    pub fn extend(
671        &self,
672        quads: impl IntoIterator<Item = impl Into<Quad>>,
673    ) -> Result<(), StorageError> {
674        let quads = quads.into_iter().map(Into::into).collect::<Vec<_>>();
675        self.transaction(move |mut t| t.extend(&quads))
676    }
677
678    /// Removes a quad from this store.
679    ///
680    /// Returns `true` if the quad was in the store and has been removed.
681    ///
682    /// Usage example:
683    /// ```
684    /// use oxigraph::model::*;
685    /// use oxigraph::store::Store;
686    ///
687    /// let ex = NamedNodeRef::new("http://example.com")?;
688    /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
689    ///
690    /// let store = Store::new()?;
691    /// store.insert(quad)?;
692    /// assert!(store.remove(quad)?);
693    /// assert!(!store.remove(quad)?);
694    ///
695    /// assert!(!store.contains(quad)?);
696    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
697    /// ```
698    pub fn remove<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
699        let quad = quad.into();
700        self.transaction(move |mut t| t.remove(quad))
701    }
702
703    /// Dumps the store into a file.
704    ///    
705    /// ```
706    /// use oxigraph::io::RdfFormat;
707    /// use oxigraph::store::Store;
708    ///
709    /// let file =
710    ///     "<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n"
711    ///         .as_bytes();
712    ///
713    /// let store = Store::new()?;
714    /// store.load_from_reader(RdfFormat::NQuads, file)?;
715    ///
716    /// let buffer = store.dump_to_writer(RdfFormat::NQuads, Vec::new())?;
717    /// assert_eq!(file, buffer.as_slice());
718    /// # std::io::Result::Ok(())
719    /// ```
720    pub fn dump_to_writer<W: Write>(
721        &self,
722        serializer: impl Into<RdfSerializer>,
723        writer: W,
724    ) -> Result<W, SerializerError> {
725        let serializer = serializer.into();
726        if !serializer.format().supports_datasets() {
727            return Err(SerializerError::DatasetFormatExpected(serializer.format()));
728        }
729        let mut serializer = serializer.for_writer(writer);
730        for quad in self {
731            serializer.serialize_quad(&quad?)?;
732        }
733        Ok(serializer.finish()?)
734    }
735
736    /// Dumps a store graph into a file.
737    ///    
738    /// Usage example:
739    /// ```
740    /// use oxigraph::io::RdfFormat;
741    /// use oxigraph::model::*;
742    /// use oxigraph::store::Store;
743    ///
744    /// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
745    ///
746    /// let store = Store::new()?;
747    /// store.load_graph(file, RdfFormat::NTriples, GraphName::DefaultGraph, None)?;
748    ///
749    /// let mut buffer = Vec::new();
750    /// store.dump_graph_to_writer(GraphNameRef::DefaultGraph, RdfFormat::NTriples, &mut buffer)?;
751    /// assert_eq!(file, buffer.as_slice());
752    /// # std::io::Result::Ok(())
753    /// ```
754    pub fn dump_graph_to_writer<'a, W: Write>(
755        &self,
756        from_graph_name: impl Into<GraphNameRef<'a>>,
757        serializer: impl Into<RdfSerializer>,
758        writer: W,
759    ) -> Result<W, SerializerError> {
760        let mut serializer = serializer.into().for_writer(writer);
761        for quad in self.quads_for_pattern(None, None, None, Some(from_graph_name.into())) {
762            serializer.serialize_triple(quad?.as_ref())?;
763        }
764        Ok(serializer.finish()?)
765    }
766
767    /// Dumps a store graph into a file.
768    ///    
769    /// Usage example:
770    /// ```
771    /// use oxigraph::io::RdfFormat;
772    /// use oxigraph::model::*;
773    /// use oxigraph::store::Store;
774    ///
775    /// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
776    ///
777    /// let store = Store::new()?;
778    /// store.load_graph(file, RdfFormat::NTriples, GraphName::DefaultGraph, None)?;
779    ///
780    /// let mut buffer = Vec::new();
781    /// store.dump_graph(&mut buffer, RdfFormat::NTriples, GraphNameRef::DefaultGraph)?;
782    /// assert_eq!(file, buffer.as_slice());
783    /// # std::io::Result::Ok(())
784    /// ```
785    #[deprecated(note = "use Store.dump_graph_to_writer instead", since = "0.4.0")]
786    pub fn dump_graph<'a, W: Write>(
787        &self,
788        writer: W,
789        format: impl Into<RdfFormat>,
790        from_graph_name: impl Into<GraphNameRef<'a>>,
791    ) -> Result<W, SerializerError> {
792        self.dump_graph_to_writer(from_graph_name, format.into(), writer)
793    }
794
795    /// Dumps the store into a file.
796    ///    
797    /// ```
798    /// use oxigraph::io::RdfFormat;
799    /// use oxigraph::store::Store;
800    ///
801    /// let file =
802    ///     "<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n"
803    ///         .as_bytes();
804    ///
805    /// let store = Store::new()?;
806    /// store.load_from_reader(RdfFormat::NQuads, file)?;
807    ///
808    /// let buffer = store.dump_dataset(Vec::new(), RdfFormat::NQuads)?;
809    /// assert_eq!(file, buffer.as_slice());
810    /// # std::io::Result::Ok(())
811    /// ```
812    #[deprecated(note = "use Store.dump_to_writer instead", since = "0.4.0")]
813    pub fn dump_dataset<W: Write>(
814        &self,
815        writer: W,
816        format: impl Into<RdfFormat>,
817    ) -> Result<W, SerializerError> {
818        self.dump_to_writer(format.into(), writer)
819    }
820
821    /// Returns all the store named graphs.
822    ///
823    /// Usage example:
824    /// ```
825    /// use oxigraph::model::*;
826    /// use oxigraph::store::Store;
827    ///
828    /// let ex = NamedNode::new("http://example.com")?;
829    /// let store = Store::new()?;
830    /// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
831    /// store.insert(QuadRef::new(&ex, &ex, &ex, GraphNameRef::DefaultGraph))?;
832    /// assert_eq!(
833    ///     vec![NamedOrBlankNode::from(ex)],
834    ///     store.named_graphs().collect::<Result<Vec<_>, _>>()?
835    /// );
836    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
837    /// ```
838    pub fn named_graphs(&self) -> GraphNameIter {
839        let reader = self.storage.snapshot();
840        GraphNameIter {
841            iter: reader.named_graphs(),
842            reader,
843        }
844    }
845
846    /// Checks if the store contains a given graph
847    ///
848    /// Usage example:
849    /// ```
850    /// use oxigraph::model::{NamedNode, QuadRef};
851    /// use oxigraph::store::Store;
852    ///
853    /// let ex = NamedNode::new("http://example.com")?;
854    /// let store = Store::new()?;
855    /// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
856    /// assert!(store.contains_named_graph(&ex)?);
857    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
858    /// ```
859    pub fn contains_named_graph<'a>(
860        &self,
861        graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
862    ) -> Result<bool, StorageError> {
863        let graph_name = EncodedTerm::from(graph_name.into());
864        self.storage.snapshot().contains_named_graph(&graph_name)
865    }
866
867    /// Inserts a graph into this store.
868    ///
869    /// Returns `true` if the graph was not already in the store.
870    ///
871    /// Usage example:
872    /// ```
873    /// use oxigraph::model::NamedNodeRef;
874    /// use oxigraph::store::Store;
875    ///
876    /// let ex = NamedNodeRef::new("http://example.com")?;
877    /// let store = Store::new()?;
878    /// store.insert_named_graph(ex)?;
879    ///
880    /// assert_eq!(
881    ///     store.named_graphs().collect::<Result<Vec<_>, _>>()?,
882    ///     vec![ex.into_owned().into()]
883    /// );
884    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
885    /// ```
886    pub fn insert_named_graph<'a>(
887        &self,
888        graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
889    ) -> Result<bool, StorageError> {
890        let graph_name = graph_name.into();
891        self.transaction(|mut t| t.insert_named_graph(graph_name))
892    }
893
894    /// Clears a graph from this store.
895    ///
896    /// Usage example:
897    /// ```
898    /// use oxigraph::model::{NamedNodeRef, QuadRef};
899    /// use oxigraph::store::Store;
900    ///
901    /// let ex = NamedNodeRef::new("http://example.com")?;
902    /// let quad = QuadRef::new(ex, ex, ex, ex);
903    /// let store = Store::new()?;
904    /// store.insert(quad)?;
905    /// assert_eq!(1, store.len()?);
906    ///
907    /// store.clear_graph(ex)?;
908    /// assert!(store.is_empty()?);
909    /// assert_eq!(1, store.named_graphs().count());
910    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
911    /// ```
912    pub fn clear_graph<'a>(
913        &self,
914        graph_name: impl Into<GraphNameRef<'a>>,
915    ) -> Result<(), StorageError> {
916        let graph_name = graph_name.into();
917        self.transaction(|mut t| t.clear_graph(graph_name))
918    }
919
920    /// Removes a graph from this store.
921    ///
922    /// Returns `true` if the graph was in the store and has been removed.
923    ///
924    /// Usage example:
925    /// ```
926    /// use oxigraph::model::{NamedNodeRef, QuadRef};
927    /// use oxigraph::store::Store;
928    ///
929    /// let ex = NamedNodeRef::new("http://example.com")?;
930    /// let quad = QuadRef::new(ex, ex, ex, ex);
931    /// let store = Store::new()?;
932    /// store.insert(quad)?;
933    /// assert_eq!(1, store.len()?);
934    ///
935    /// assert!(store.remove_named_graph(ex)?);
936    /// assert!(store.is_empty()?);
937    /// assert_eq!(0, store.named_graphs().count());
938    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
939    /// ```
940    pub fn remove_named_graph<'a>(
941        &self,
942        graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
943    ) -> Result<bool, StorageError> {
944        let graph_name = graph_name.into();
945        self.transaction(|mut t| t.remove_named_graph(graph_name))
946    }
947
948    /// Clears the store.
949    ///
950    /// Usage example:
951    /// ```
952    /// use oxigraph::model::*;
953    /// use oxigraph::store::Store;
954    ///
955    /// let ex = NamedNodeRef::new("http://example.com")?;
956    /// let store = Store::new()?;
957    /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
958    /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
959    /// assert_eq!(2, store.len()?);
960    ///
961    /// store.clear()?;
962    /// assert!(store.is_empty()?);
963    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
964    /// ```
965    pub fn clear(&self) -> Result<(), StorageError> {
966        self.transaction(|mut t| t.clear())
967    }
968
969    /// Flushes all buffers and ensures that all writes are saved on disk.
970    ///
971    /// Flushes are automatically done using background threads but might lag a little bit.
972    #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
973    pub fn flush(&self) -> Result<(), StorageError> {
974        self.storage.flush()
975    }
976
977    /// Optimizes the database for future workload.
978    ///
979    /// Useful to call after a batch upload or another similar operation.
980    ///
981    /// <div class="warning">Can take hours on huge databases.</div>
982    #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
983    pub fn optimize(&self) -> Result<(), StorageError> {
984        self.storage.compact()
985    }
986
987    /// Creates database backup into the `target_directory`.
988    ///
989    /// After its creation, the backup is usable using [`Store::open`]
990    /// like a regular Oxigraph database and operates independently from the original database.
991    ///
992    /// <div class="warning">
993    ///
994    /// Backups are only possible for on-disk databases created using [`Store::open`].</div>
995    /// Temporary in-memory databases created using [`Store::new`] are not compatible with RocksDB backup system.
996    ///
997    /// <div class="warning">An error is raised if the `target_directory` already exists.</div>
998    ///
999    /// If the target directory is in the same file system as the current database,
1000    /// the database content will not be fully copied
1001    /// but hard links will be used to point to the original database immutable snapshots.
1002    /// This allows cheap regular backups.
1003    ///
1004    /// If you want to move your data to another RDF storage system, you should have a look at the [`Store::dump_to_writer`] function instead.
1005    #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
1006    pub fn backup(&self, target_directory: impl AsRef<Path>) -> Result<(), StorageError> {
1007        self.storage.backup(target_directory.as_ref())
1008    }
1009
1010    /// Creates a bulk loader allowing to load at lot of data quickly into the store.
1011    ///
1012    /// Usage example:
1013    /// ```
1014    /// use oxigraph::io::RdfFormat;
1015    /// use oxigraph::model::*;
1016    /// use oxigraph::store::Store;
1017    ///
1018    /// let store = Store::new()?;
1019    ///
1020    /// // quads file insertion
1021    /// let file =
1022    ///     b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1023    /// store
1024    ///     .bulk_loader()
1025    ///     .load_from_reader(RdfFormat::NQuads, file.as_ref())?;
1026    ///
1027    /// // we inspect the store contents
1028    /// let ex = NamedNodeRef::new("http://example.com")?;
1029    /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1030    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1031    /// ```
1032    pub fn bulk_loader(&self) -> BulkLoader {
1033        BulkLoader {
1034            storage: self.storage.bulk_loader(),
1035            on_parse_error: None,
1036        }
1037    }
1038
1039    /// Validates that all the store invariants held in the data
1040    #[doc(hidden)]
1041    pub fn validate(&self) -> Result<(), StorageError> {
1042        self.storage.snapshot().validate()
1043    }
1044}
1045
1046impl fmt::Display for Store {
1047    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048        for t in self {
1049            writeln!(f, "{} .", t.map_err(|_| fmt::Error)?)?;
1050        }
1051        Ok(())
1052    }
1053}
1054
1055impl IntoIterator for &Store {
1056    type IntoIter = QuadIter;
1057    type Item = Result<Quad, StorageError>;
1058
1059    #[inline]
1060    fn into_iter(self) -> Self::IntoIter {
1061        self.iter()
1062    }
1063}
1064
1065/// An object to do operations during a transaction.
1066///
1067/// See [`Store::transaction`] for a more detailed description.
1068pub struct Transaction<'a> {
1069    writer: StorageWriter<'a>,
1070}
1071
1072impl Transaction<'_> {
1073    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
1074    ///
1075    /// Usage example:
1076    /// ```
1077    /// use oxigraph::model::*;
1078    /// use oxigraph::sparql::{EvaluationError, QueryResults};
1079    /// use oxigraph::store::Store;
1080    ///
1081    /// let store = Store::new()?;
1082    /// store.transaction(|mut transaction| {
1083    ///     if let QueryResults::Solutions(solutions) =
1084    ///         transaction.query("SELECT ?s WHERE { ?s ?p ?o }")?
1085    ///     {
1086    ///         for solution in solutions {
1087    ///             if let Some(Term::NamedNode(s)) = solution?.get("s") {
1088    ///                 transaction.insert(QuadRef::new(
1089    ///                     s,
1090    ///                     vocab::rdf::TYPE,
1091    ///                     NamedNodeRef::new_unchecked("http://example.com"),
1092    ///                     GraphNameRef::DefaultGraph,
1093    ///                 ))?;
1094    ///             }
1095    ///         }
1096    ///     }
1097    ///     Result::<_, EvaluationError>::Ok(())
1098    /// })?;
1099    /// # Result::<_, EvaluationError>::Ok(())
1100    /// ```
1101    pub fn query(
1102        &self,
1103        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
1104    ) -> Result<QueryResults, EvaluationError> {
1105        self.query_opt(query, QueryOptions::default())
1106    }
1107
1108    /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
1109    ///
1110    /// Usage example with a custom function serializing terms to N-Triples:
1111    /// ```
1112    /// use oxigraph::model::*;
1113    /// use oxigraph::sparql::{EvaluationError, QueryOptions, QueryResults};
1114    /// use oxigraph::store::Store;
1115    ///
1116    /// let store = Store::new()?;
1117    /// store.transaction(|mut transaction| {
1118    ///     if let QueryResults::Solutions(solutions) = transaction.query_opt(
1119    ///         "SELECT ?s (<http://www.w3.org/ns/formats/N-Triples>(?s) AS ?nt) WHERE { ?s ?p ?o }",
1120    ///         QueryOptions::default().with_custom_function(
1121    ///             NamedNode::new_unchecked("http://www.w3.org/ns/formats/N-Triples"),
1122    ///             |args| args.get(0).map(|t| Literal::from(t.to_string()).into()),
1123    ///         ),
1124    ///     )? {
1125    ///         for solution in solutions {
1126    ///             let solution = solution?;
1127    ///             if let (Some(Term::NamedNode(s)), Some(nt)) =
1128    ///                 (solution.get("s"), solution.get("nt"))
1129    ///             {
1130    ///                 transaction.insert(QuadRef::new(
1131    ///                     s,
1132    ///                     NamedNodeRef::new_unchecked("http://example.com/n-triples-representation"),
1133    ///                     nt,
1134    ///                     GraphNameRef::DefaultGraph,
1135    ///                 ))?;
1136    ///             }
1137    ///         }
1138    ///     }
1139    ///     Result::<_, EvaluationError>::Ok(())
1140    /// })?;
1141    /// # Result::<_, EvaluationError>::Ok(())
1142    /// ```
1143    pub fn query_opt(
1144        &self,
1145        query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
1146        options: QueryOptions,
1147    ) -> Result<QueryResults, EvaluationError> {
1148        let (results, _) = evaluate_query(self.writer.reader(), query, options, false, [])?;
1149        results
1150    }
1151
1152    /// Retrieves quads with a filter on each quad component.
1153    ///
1154    /// Usage example:
1155    /// ```
1156    /// use oxigraph::model::*;
1157    /// use oxigraph::store::{StorageError, Store};
1158    ///
1159    /// let store = Store::new()?;
1160    /// let a = NamedNodeRef::new("http://example.com/a")?;
1161    /// let b = NamedNodeRef::new("http://example.com/b")?;
1162    ///
1163    /// // Copy all triples about ex:a to triples about ex:b
1164    /// store.transaction(|mut transaction| {
1165    ///     for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
1166    ///         let q = q?;
1167    ///         transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
1168    ///     }
1169    ///     Result::<_, StorageError>::Ok(())
1170    /// })?;
1171    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1172    /// ```
1173    pub fn quads_for_pattern(
1174        &self,
1175        subject: Option<SubjectRef<'_>>,
1176        predicate: Option<NamedNodeRef<'_>>,
1177        object: Option<TermRef<'_>>,
1178        graph_name: Option<GraphNameRef<'_>>,
1179    ) -> QuadIter {
1180        let reader = self.writer.reader();
1181        QuadIter {
1182            iter: reader.quads_for_pattern(
1183                subject.map(EncodedTerm::from).as_ref(),
1184                predicate.map(EncodedTerm::from).as_ref(),
1185                object.map(EncodedTerm::from).as_ref(),
1186                graph_name.map(EncodedTerm::from).as_ref(),
1187            ),
1188            reader,
1189        }
1190    }
1191
1192    /// Returns all the quads contained in the store.
1193    pub fn iter(&self) -> QuadIter {
1194        self.quads_for_pattern(None, None, None, None)
1195    }
1196
1197    /// Checks if this store contains a given quad.
1198    pub fn contains<'b>(&self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1199        let quad = EncodedQuad::from(quad.into());
1200        self.writer.reader().contains(&quad)
1201    }
1202
1203    /// Returns the number of quads in the store.
1204    ///
1205    /// <div class="warning">this function executes a full scan.</div>
1206    pub fn len(&self) -> Result<usize, StorageError> {
1207        self.writer.reader().len()
1208    }
1209
1210    /// Returns if the store is empty.
1211    pub fn is_empty(&self) -> Result<bool, StorageError> {
1212        self.writer.reader().is_empty()
1213    }
1214
1215    /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
1216    ///
1217    /// Usage example:
1218    /// ```
1219    /// use oxigraph::model::*;
1220    /// use oxigraph::sparql::EvaluationError;
1221    /// use oxigraph::store::Store;
1222    ///
1223    /// let store = Store::new()?;
1224    /// store.transaction(|mut transaction| {
1225    ///     // insertion
1226    ///     transaction.update(
1227    ///         "INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }",
1228    ///     )?;
1229    ///
1230    ///     // we inspect the store contents
1231    ///     let ex = NamedNodeRef::new_unchecked("http://example.com");
1232    ///     assert!(transaction.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1233    ///     Result::<_, EvaluationError>::Ok(())
1234    /// })?;
1235    /// # Result::<_, EvaluationError>::Ok(())
1236    /// ```
1237    pub fn update(
1238        &mut self,
1239        update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
1240    ) -> Result<(), EvaluationError> {
1241        self.update_opt(update, UpdateOptions::default())
1242    }
1243
1244    /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
1245    pub fn update_opt(
1246        &mut self,
1247        update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
1248        options: impl Into<UpdateOptions>,
1249    ) -> Result<(), EvaluationError> {
1250        evaluate_update(
1251            &mut self.writer,
1252            &update.try_into().map_err(Into::into)?,
1253            &options.into(),
1254        )
1255    }
1256
1257    /// Loads a RDF file into the store.
1258    ///
1259    /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
1260    ///
1261    /// Usage example:
1262    /// ```
1263    /// use oxigraph::store::Store;
1264    /// use oxigraph::io::RdfFormat;
1265    /// use oxigraph::model::*;
1266    /// use oxrdfio::RdfParser;
1267    ///
1268    /// let store = Store::new()?;
1269    ///
1270    /// // insert a dataset file (former load_dataset method)
1271    /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
1272    /// store.transaction(|mut t| t.load_from_reader(RdfFormat::NQuads, file.as_ref()))?;
1273    ///
1274    /// // insert a graph file (former load_graph method)
1275    /// let file = b"<> <> <> .";
1276    /// store.transaction(|mut t|
1277    ///     t.load_from_reader(
1278    ///         RdfParser::from_format(RdfFormat::Turtle)
1279    ///             .with_base_iri("http://example.com")
1280    ///             .unwrap()
1281    ///             .without_named_graphs() // No named graphs allowed in the input
1282    ///             .with_default_graph(NamedNodeRef::new("http://example.com/g2").unwrap()), // we put the file default graph inside of a named graph
1283    ///         file.as_ref()
1284    ///     )
1285    /// )?;
1286    ///
1287    /// // we inspect the store contents
1288    /// let ex = NamedNodeRef::new("http://example.com")?;
1289    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
1290    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
1291    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1292    /// ```
1293    pub fn load_from_reader(
1294        &mut self,
1295        parser: impl Into<RdfParser>,
1296        reader: impl Read,
1297    ) -> Result<(), LoaderError> {
1298        for quad in parser.into().rename_blank_nodes().for_reader(reader) {
1299            self.insert(quad?.as_ref())?;
1300        }
1301        Ok(())
1302    }
1303
1304    /// Loads a graph file (i.e. triples) into the store.
1305    ///
1306    /// Usage example:
1307    /// ```
1308    /// use oxigraph::io::RdfFormat;
1309    /// use oxigraph::model::*;
1310    /// use oxigraph::store::Store;
1311    ///
1312    /// let store = Store::new()?;
1313    ///
1314    /// // insertion
1315    /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
1316    /// store.transaction(|mut transaction| {
1317    ///     transaction.load_graph(
1318    ///         file.as_ref(),
1319    ///         RdfFormat::NTriples,
1320    ///         GraphName::DefaultGraph,
1321    ///         None,
1322    ///     )
1323    /// })?;
1324    ///
1325    /// // we inspect the store contents
1326    /// let ex = NamedNodeRef::new("http://example.com")?;
1327    /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1328    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1329    /// ```
1330    #[deprecated(note = "use Transaction.load_from_reader instead", since = "0.4.0")]
1331    pub fn load_graph(
1332        &mut self,
1333        reader: impl Read,
1334        format: impl Into<RdfFormat>,
1335        to_graph_name: impl Into<GraphName>,
1336        base_iri: Option<&str>,
1337    ) -> Result<(), LoaderError> {
1338        let mut parser = RdfParser::from_format(format.into())
1339            .without_named_graphs()
1340            .with_default_graph(to_graph_name);
1341        if let Some(base_iri) = base_iri {
1342            parser = parser
1343                .with_base_iri(base_iri)
1344                .map_err(|e| LoaderError::InvalidBaseIri {
1345                    iri: base_iri.into(),
1346                    error: e,
1347                })?;
1348        }
1349        self.load_from_reader(parser, reader)
1350    }
1351
1352    /// Loads a dataset file (i.e. quads) into the store.
1353    ///
1354    /// Usage example:
1355    /// ```
1356    /// use oxigraph::io::RdfFormat;
1357    /// use oxigraph::model::*;
1358    /// use oxigraph::store::Store;
1359    ///
1360    /// let store = Store::new()?;
1361    ///
1362    /// // insertion
1363    /// let file =
1364    ///     b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1365    /// store.transaction(|mut transaction| {
1366    ///     transaction.load_dataset(file.as_ref(), RdfFormat::NQuads, None)
1367    /// })?;
1368    ///
1369    /// // we inspect the store contents
1370    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1371    /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1372    /// # Result::<_,oxigraph::store::LoaderError>::Ok(())
1373    /// ```
1374    #[deprecated(note = "use Transaction.load_from_reader instead", since = "0.4.0")]
1375    pub fn load_dataset(
1376        &mut self,
1377        reader: impl Read,
1378        format: impl Into<RdfFormat>,
1379        base_iri: Option<&str>,
1380    ) -> Result<(), LoaderError> {
1381        let mut parser = RdfParser::from_format(format.into());
1382        if let Some(base_iri) = base_iri {
1383            parser = parser
1384                .with_base_iri(base_iri)
1385                .map_err(|e| LoaderError::InvalidBaseIri {
1386                    iri: base_iri.into(),
1387                    error: e,
1388                })?;
1389        }
1390        self.load_from_reader(parser, reader)
1391    }
1392
1393    /// Adds a quad to this store.
1394    ///
1395    /// Returns `true` if the quad was not already in the store.
1396    ///
1397    /// Usage example:
1398    /// ```
1399    /// use oxigraph::model::*;
1400    /// use oxigraph::store::Store;
1401    ///
1402    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1403    /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
1404    ///
1405    /// let store = Store::new()?;
1406    /// store.transaction(|mut transaction| transaction.insert(quad))?;
1407    /// assert!(store.contains(quad)?);
1408    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1409    /// ```
1410    pub fn insert<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1411        self.writer.insert(quad.into())
1412    }
1413
1414    /// Adds a set of quads to this store.
1415    pub fn extend<'b>(
1416        &mut self,
1417        quads: impl IntoIterator<Item = impl Into<QuadRef<'b>>>,
1418    ) -> Result<(), StorageError> {
1419        for quad in quads {
1420            self.writer.insert(quad.into())?;
1421        }
1422        Ok(())
1423    }
1424
1425    /// Removes a quad from this store.
1426    ///
1427    /// Returns `true` if the quad was in the store and has been removed.
1428    ///
1429    /// Usage example:
1430    /// ```
1431    /// use oxigraph::model::*;
1432    /// use oxigraph::store::Store;
1433    ///
1434    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1435    /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
1436    /// let store = Store::new()?;
1437    /// store.transaction(|mut transaction| {
1438    ///     transaction.insert(quad)?;
1439    ///     transaction.remove(quad)
1440    /// })?;
1441    /// assert!(!store.contains(quad)?);
1442    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1443    /// ```
1444    pub fn remove<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1445        self.writer.remove(quad.into())
1446    }
1447
1448    /// Returns all the store named graphs.
1449    pub fn named_graphs(&self) -> GraphNameIter {
1450        let reader = self.writer.reader();
1451        GraphNameIter {
1452            iter: reader.named_graphs(),
1453            reader,
1454        }
1455    }
1456
1457    /// Checks if the store contains a given graph.
1458    pub fn contains_named_graph<'b>(
1459        &self,
1460        graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1461    ) -> Result<bool, StorageError> {
1462        self.writer
1463            .reader()
1464            .contains_named_graph(&EncodedTerm::from(graph_name.into()))
1465    }
1466
1467    /// Inserts a graph into this store.
1468    ///
1469    /// Returns `true` if the graph was not already in the store.
1470    ///
1471    /// Usage example:
1472    /// ```
1473    /// use oxigraph::model::NamedNodeRef;
1474    /// use oxigraph::store::Store;
1475    ///
1476    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1477    /// let store = Store::new()?;
1478    /// store.transaction(|mut transaction| transaction.insert_named_graph(ex))?;
1479    /// assert_eq!(
1480    ///     store.named_graphs().collect::<Result<Vec<_>, _>>()?,
1481    ///     vec![ex.into_owned().into()]
1482    /// );
1483    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1484    /// ```
1485    pub fn insert_named_graph<'b>(
1486        &mut self,
1487        graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1488    ) -> Result<bool, StorageError> {
1489        self.writer.insert_named_graph(graph_name.into())
1490    }
1491
1492    /// Clears a graph from this store.
1493    ///
1494    /// Usage example:
1495    /// ```
1496    /// use oxigraph::model::{NamedNodeRef, QuadRef};
1497    /// use oxigraph::store::Store;
1498    ///
1499    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1500    /// let quad = QuadRef::new(ex, ex, ex, ex);
1501    /// let store = Store::new()?;
1502    /// store.transaction(|mut transaction| {
1503    ///     transaction.insert(quad)?;
1504    ///     transaction.clear_graph(ex)
1505    /// })?;
1506    /// assert!(store.is_empty()?);
1507    /// assert_eq!(1, store.named_graphs().count());
1508    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1509    /// ```
1510    pub fn clear_graph<'b>(
1511        &mut self,
1512        graph_name: impl Into<GraphNameRef<'b>>,
1513    ) -> Result<(), StorageError> {
1514        self.writer.clear_graph(graph_name.into())
1515    }
1516
1517    /// Removes a graph from this store.
1518    ///
1519    /// Returns `true` if the graph was in the store and has been removed.
1520    ///
1521    /// Usage example:
1522    /// ```
1523    /// use oxigraph::model::{NamedNodeRef, QuadRef};
1524    /// use oxigraph::store::Store;
1525    ///
1526    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1527    /// let quad = QuadRef::new(ex, ex, ex, ex);
1528    /// let store = Store::new()?;
1529    /// store.transaction(|mut transaction| {
1530    ///     transaction.insert(quad)?;
1531    ///     transaction.remove_named_graph(ex)
1532    /// })?;
1533    /// assert!(store.is_empty()?);
1534    /// assert_eq!(0, store.named_graphs().count());
1535    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1536    /// ```
1537    pub fn remove_named_graph<'b>(
1538        &mut self,
1539        graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1540    ) -> Result<bool, StorageError> {
1541        self.writer.remove_named_graph(graph_name.into())
1542    }
1543
1544    /// Clears the store.
1545    ///
1546    /// Usage example:
1547    /// ```
1548    /// use oxigraph::model::*;
1549    /// use oxigraph::store::Store;
1550    ///
1551    /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1552    /// let store = Store::new()?;
1553    /// store.transaction(|mut transaction| {
1554    ///     transaction.insert(QuadRef::new(ex, ex, ex, ex))?;
1555    ///     transaction.clear()
1556    /// })?;
1557    /// assert!(store.is_empty()?);
1558    /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1559    /// ```
1560    pub fn clear(&mut self) -> Result<(), StorageError> {
1561        self.writer.clear()
1562    }
1563}
1564
1565impl IntoIterator for &Transaction<'_> {
1566    type IntoIter = QuadIter;
1567    type Item = Result<Quad, StorageError>;
1568
1569    #[inline]
1570    fn into_iter(self) -> Self::IntoIter {
1571        self.iter()
1572    }
1573}
1574
1575/// An iterator returning the quads contained in a [`Store`].
1576pub struct QuadIter {
1577    iter: DecodingQuadIterator,
1578    reader: StorageReader,
1579}
1580
1581impl Iterator for QuadIter {
1582    type Item = Result<Quad, StorageError>;
1583
1584    fn next(&mut self) -> Option<Self::Item> {
1585        Some(match self.iter.next()? {
1586            Ok(quad) => self.reader.decode_quad(&quad),
1587            Err(error) => Err(error),
1588        })
1589    }
1590}
1591
1592/// An iterator returning the graph names contained in a [`Store`].
1593pub struct GraphNameIter {
1594    iter: DecodingGraphIterator,
1595    reader: StorageReader,
1596}
1597
1598impl Iterator for GraphNameIter {
1599    type Item = Result<NamedOrBlankNode, StorageError>;
1600
1601    fn next(&mut self) -> Option<Self::Item> {
1602        Some(
1603            self.iter
1604                .next()?
1605                .and_then(|graph_name| self.reader.decode_named_or_blank_node(&graph_name)),
1606        )
1607    }
1608
1609    fn size_hint(&self) -> (usize, Option<usize>) {
1610        self.iter.size_hint()
1611    }
1612}
1613
1614/// A bulk loader allowing to load at lot of data quickly into the store.
1615///
1616/// <div class="warning">The operations provided here are not atomic.
1617/// If the operation fails in the middle, only a part of the data may be written to the store.
1618/// Results might get weird if you delete data during the loading process.</div>
1619///
1620/// Memory usage is configurable using [`with_max_memory_size_in_megabytes`](Self::with_max_memory_size_in_megabytes)
1621/// and the number of used threads with [`with_num_threads`](Self::with_num_threads).
1622/// By default the memory consumption target (excluding the system and RocksDB internal consumption)
1623/// is around 2GB per thread and 2 threads.
1624/// These targets are considered per loaded file.
1625///
1626/// Usage example with loading a dataset:
1627/// ```
1628/// use oxigraph::io::RdfFormat;
1629/// use oxigraph::model::*;
1630/// use oxigraph::store::Store;
1631///
1632/// let store = Store::new()?;
1633///
1634/// // quads file insertion
1635/// let file =
1636///     b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1637/// store
1638///     .bulk_loader()
1639///     .load_from_reader(RdfFormat::NQuads, file.as_ref())?;
1640///
1641/// // we inspect the store contents
1642/// let ex = NamedNodeRef::new("http://example.com")?;
1643/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1644/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1645/// ```
1646#[must_use]
1647pub struct BulkLoader {
1648    storage: StorageBulkLoader,
1649    on_parse_error: Option<Box<dyn Fn(RdfParseError) -> Result<(), RdfParseError>>>,
1650}
1651
1652impl BulkLoader {
1653    /// Sets the maximal number of threads to be used by the bulk loader per operation.
1654    ///
1655    /// This number must be at last 2 (one for parsing and one for loading).
1656    ///
1657    /// The default value is 2.
1658    pub fn with_num_threads(mut self, num_threads: usize) -> Self {
1659        self.storage = self.storage.with_num_threads(num_threads);
1660        self
1661    }
1662
1663    #[doc(hidden)]
1664    #[deprecated(note = "Use with_num_threads", since = "0.4.0")]
1665    pub fn set_num_threads(self, num_threads: usize) -> Self {
1666        self.with_num_threads(num_threads)
1667    }
1668
1669    /// Sets a rough idea of the maximal amount of memory to be used by this operation.
1670    ///
1671    /// This number must be at last a few megabytes per thread.
1672    ///
1673    /// Memory used by RocksDB and the system is not taken into account in this limit.
1674    /// Note that depending on the system behavior this amount might never be reached or be blown up
1675    /// (for example if the data contains very long IRIs or literals).
1676    ///
1677    /// By default, a target 2GB per used thread is used.
1678    pub fn with_max_memory_size_in_megabytes(mut self, max_memory_size: usize) -> Self {
1679        self.storage = self
1680            .storage
1681            .with_max_memory_size_in_megabytes(max_memory_size);
1682        self
1683    }
1684
1685    #[doc(hidden)]
1686    #[deprecated(note = "Use with_max_memory_size_in_megabytes", since = "0.4.0")]
1687    pub fn set_max_memory_size_in_megabytes(self, max_memory_size: usize) -> Self {
1688        self.with_max_memory_size_in_megabytes(max_memory_size)
1689    }
1690
1691    /// Adds a `callback` evaluated from time to time with the number of loaded triples.
1692    pub fn on_progress(mut self, callback: impl Fn(u64) + 'static) -> Self {
1693        self.storage = self.storage.on_progress(callback);
1694        self
1695    }
1696
1697    /// Adds a `callback` catching all parse errors and choosing if the parsing should continue
1698    /// by returning `Ok` or fail by returning `Err`.
1699    ///
1700    /// By default the parsing fails.
1701    pub fn on_parse_error(
1702        mut self,
1703        callback: impl Fn(RdfParseError) -> Result<(), RdfParseError> + 'static,
1704    ) -> Self {
1705        self.on_parse_error = Some(Box::new(callback));
1706        self
1707    }
1708
1709    /// Loads a file using the bulk loader.
1710    ///
1711    /// This function is optimized for large dataset loading speed. For small files, [`Store::load_from_reader`] might be more convenient.
1712    ///
1713    /// <div class="warning">This method is not atomic.
1714    /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1715    /// Results might get weird if you delete data during the loading process.</div>
1716    ///
1717    /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1718    ///
1719    /// To get better speed on valid datasets, consider enabling [`RdfParser::unchecked`] option to skip some validations.
1720    ///
1721    /// Usage example:
1722    /// ```
1723    /// use oxigraph::store::Store;
1724    /// use oxigraph::io::{RdfParser, RdfFormat};
1725    /// use oxigraph::model::*;
1726    ///
1727    /// let store = Store::new()?;
1728    ///
1729    /// // insert a dataset file (former load_dataset method)
1730    /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
1731    /// store.bulk_loader().load_from_reader(
1732    ///     RdfParser::from_format(RdfFormat::NQuads).unchecked(), // we inject a custom parser with options
1733    ///     file.as_ref()
1734    /// )?;
1735    ///
1736    /// // insert a graph file (former load_graph method)
1737    /// let file = b"<> <> <> .";
1738    /// store.bulk_loader().load_from_reader(
1739    ///     RdfParser::from_format(RdfFormat::Turtle)
1740    ///         .with_base_iri("http://example.com")?
1741    ///         .without_named_graphs() // No named graphs allowed in the input
1742    ///         .with_default_graph(NamedNodeRef::new("http://example.com/g2")?), // we put the file default graph inside of a named graph
1743    ///     file.as_ref()
1744    /// )?;
1745    ///
1746    /// // we inspect the store contents
1747    /// let ex = NamedNodeRef::new("http://example.com")?;
1748    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
1749    /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
1750    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1751    /// ```
1752    pub fn load_from_reader(
1753        &self,
1754        parser: impl Into<RdfParser>,
1755        reader: impl Read,
1756    ) -> Result<(), LoaderError> {
1757        self.load_ok_quads(
1758            parser
1759                .into()
1760                .rename_blank_nodes()
1761                .for_reader(reader)
1762                .filter_map(|r| match r {
1763                    Ok(q) => Some(Ok(q)),
1764                    Err(e) => {
1765                        if let Some(callback) = &self.on_parse_error {
1766                            if let Err(e) = callback(e) {
1767                                Some(Err(e))
1768                            } else {
1769                                None
1770                            }
1771                        } else {
1772                            Some(Err(e))
1773                        }
1774                    }
1775                }),
1776        )
1777    }
1778
1779    /// Loads a dataset file using the bulk loader.
1780    ///
1781    /// This function is optimized for large dataset loading speed. For small files, [`Store::load_dataset`] might be more convenient.
1782    ///
1783    /// <div class="warning">This method is not atomic.
1784    /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1785    /// Results might get weird if you delete data during the loading process.</div>
1786    ///
1787    /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1788    ///
1789    /// Usage example:
1790    /// ```
1791    /// use oxigraph::io::RdfFormat;
1792    /// use oxigraph::model::*;
1793    /// use oxigraph::store::Store;
1794    ///
1795    /// let store = Store::new()?;
1796    ///
1797    /// // insertion
1798    /// let file =
1799    ///     b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1800    /// store
1801    ///     .bulk_loader()
1802    ///     .load_dataset(file.as_ref(), RdfFormat::NQuads, None)?;
1803    ///
1804    /// // we inspect the store contents
1805    /// let ex = NamedNodeRef::new("http://example.com")?;
1806    /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1807    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1808    /// ```
1809    #[deprecated(note = "use BulkLoader.load_from_reader instead", since = "0.4.0")]
1810    pub fn load_dataset(
1811        &self,
1812        reader: impl Read,
1813        format: impl Into<RdfFormat>,
1814        base_iri: Option<&str>,
1815    ) -> Result<(), LoaderError> {
1816        let mut parser = RdfParser::from_format(format.into()).rename_blank_nodes();
1817        if let Some(base_iri) = base_iri {
1818            parser = parser
1819                .with_base_iri(base_iri)
1820                .map_err(|e| LoaderError::InvalidBaseIri {
1821                    iri: base_iri.into(),
1822                    error: e,
1823                })?;
1824        }
1825        self.load_ok_quads(parser.for_reader(reader).filter_map(|r| match r {
1826            Ok(q) => Some(Ok(q)),
1827            Err(e) => {
1828                if let Some(callback) = &self.on_parse_error {
1829                    if let Err(e) = callback(e) {
1830                        Some(Err(e))
1831                    } else {
1832                        None
1833                    }
1834                } else {
1835                    Some(Err(e))
1836                }
1837            }
1838        }))
1839    }
1840
1841    /// Loads a graph file using the bulk loader.
1842    ///
1843    /// This function is optimized for large graph loading speed. For small files, [`Store::load_graph`] might be more convenient.   
1844    ///
1845    /// <div class="warning">This method is not atomic.
1846    /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1847    /// Results might get weird if you delete data during the loading process.</div>
1848    ///
1849    /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1850    ///
1851    /// Usage example:
1852    /// ```
1853    /// use oxigraph::io::RdfFormat;
1854    /// use oxigraph::model::*;
1855    /// use oxigraph::store::Store;
1856    ///
1857    /// let store = Store::new()?;
1858    ///
1859    /// // insertion
1860    /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
1861    /// store.bulk_loader().load_graph(
1862    ///     file.as_ref(),
1863    ///     RdfFormat::NTriples,
1864    ///     GraphName::DefaultGraph,
1865    ///     None,
1866    /// )?;
1867    ///
1868    /// // we inspect the store contents
1869    /// let ex = NamedNodeRef::new("http://example.com")?;
1870    /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1871    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1872    /// ```
1873    #[deprecated(note = "use BulkLoader.load_from_reader instead", since = "0.4.0")]
1874    pub fn load_graph(
1875        &self,
1876        reader: impl Read,
1877        format: impl Into<RdfFormat>,
1878        to_graph_name: impl Into<GraphName>,
1879        base_iri: Option<&str>,
1880    ) -> Result<(), LoaderError> {
1881        let mut parser = RdfParser::from_format(format.into())
1882            .without_named_graphs()
1883            .with_default_graph(to_graph_name)
1884            .rename_blank_nodes();
1885        if let Some(base_iri) = base_iri {
1886            parser = parser
1887                .with_base_iri(base_iri)
1888                .map_err(|e| LoaderError::InvalidBaseIri {
1889                    iri: base_iri.into(),
1890                    error: e,
1891                })?;
1892        }
1893        self.load_ok_quads(parser.for_reader(reader).filter_map(|r| match r {
1894            Ok(q) => Some(Ok(q)),
1895            Err(e) => {
1896                if let Some(callback) = &self.on_parse_error {
1897                    if let Err(e) = callback(e) {
1898                        Some(Err(e))
1899                    } else {
1900                        None
1901                    }
1902                } else {
1903                    Some(Err(e))
1904                }
1905            }
1906        }))
1907    }
1908
1909    /// Adds a set of quads using the bulk loader.
1910    ///
1911    /// <div class="warning">This method is not atomic.
1912    /// If the process fails in the middle of the file, only a part of the data may be written to the store.
1913    /// Results might get weird if you delete data during the loading process.</div>
1914    ///
1915    /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1916    pub fn load_quads(
1917        &self,
1918        quads: impl IntoIterator<Item = impl Into<Quad>>,
1919    ) -> Result<(), StorageError> {
1920        self.load_ok_quads(quads.into_iter().map(Ok::<_, StorageError>))
1921    }
1922
1923    /// Adds a set of quads using the bulk loader while breaking in the middle of the process in case of error.
1924    ///
1925    /// <div class="warning">This method is not atomic.
1926    /// If the process fails in the middle of the file, only a part of the data may be written to the store.
1927    /// Results might get weird if you delete data during the loading process.</div>
1928    ///
1929    /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1930    pub fn load_ok_quads<EI, EO: From<StorageError> + From<EI>>(
1931        &self,
1932        quads: impl IntoIterator<Item = Result<impl Into<Quad>, EI>>,
1933    ) -> Result<(), EO> {
1934        self.storage
1935            .load(quads.into_iter().map(|q| q.map(Into::into)))
1936    }
1937}
1938
1939#[cfg(test)]
1940#[allow(clippy::panic_in_result_fn)]
1941mod tests {
1942    use super::*;
1943
1944    #[test]
1945    fn test_send_sync() {
1946        fn is_send_sync<T: Send + Sync>() {}
1947        is_send_sync::<Store>();
1948    }
1949
1950    #[test]
1951    fn store() -> Result<(), StorageError> {
1952        use crate::model::*;
1953
1954        let main_s = Subject::from(BlankNode::default());
1955        let main_p = NamedNode::new("http://example.com").unwrap();
1956        let main_o = Term::from(Literal::from(1));
1957        let main_g = GraphName::from(BlankNode::default());
1958
1959        let default_quad = Quad::new(
1960            main_s.clone(),
1961            main_p.clone(),
1962            main_o.clone(),
1963            GraphName::DefaultGraph,
1964        );
1965        let named_quad = Quad::new(
1966            main_s.clone(),
1967            main_p.clone(),
1968            main_o.clone(),
1969            main_g.clone(),
1970        );
1971        let mut default_quads = vec![
1972            Quad::new(
1973                main_s.clone(),
1974                main_p.clone(),
1975                Literal::from(0),
1976                GraphName::DefaultGraph,
1977            ),
1978            default_quad.clone(),
1979            Quad::new(
1980                main_s.clone(),
1981                main_p.clone(),
1982                Literal::from(200_000_000),
1983                GraphName::DefaultGraph,
1984            ),
1985        ];
1986        let all_quads = vec![
1987            named_quad.clone(),
1988            Quad::new(
1989                main_s.clone(),
1990                main_p.clone(),
1991                Literal::from(200_000_000),
1992                GraphName::DefaultGraph,
1993            ),
1994            default_quad.clone(),
1995            Quad::new(
1996                main_s.clone(),
1997                main_p.clone(),
1998                Literal::from(0),
1999                GraphName::DefaultGraph,
2000            ),
2001        ];
2002
2003        let store = Store::new()?;
2004        for t in &default_quads {
2005            assert!(store.insert(t)?);
2006        }
2007        assert!(!store.insert(&default_quad)?);
2008
2009        assert!(store.remove(&default_quad)?);
2010        assert!(!store.remove(&default_quad)?);
2011        assert!(store.insert(&named_quad)?);
2012        assert!(!store.insert(&named_quad)?);
2013        assert!(store.insert(&default_quad)?);
2014        assert!(!store.insert(&default_quad)?);
2015        store.validate()?;
2016
2017        assert_eq!(store.len()?, 4);
2018        assert_eq!(store.iter().collect::<Result<Vec<_>, _>>()?, all_quads);
2019        assert_eq!(
2020            store
2021                .quads_for_pattern(Some(main_s.as_ref()), None, None, None)
2022                .collect::<Result<Vec<_>, _>>()?,
2023            all_quads
2024        );
2025        assert_eq!(
2026            store
2027                .quads_for_pattern(Some(main_s.as_ref()), Some(main_p.as_ref()), None, None)
2028                .collect::<Result<Vec<_>, _>>()?,
2029            all_quads
2030        );
2031        assert_eq!(
2032            store
2033                .quads_for_pattern(
2034                    Some(main_s.as_ref()),
2035                    Some(main_p.as_ref()),
2036                    Some(main_o.as_ref()),
2037                    None
2038                )
2039                .collect::<Result<Vec<_>, _>>()?,
2040            vec![named_quad.clone(), default_quad.clone()]
2041        );
2042        assert_eq!(
2043            store
2044                .quads_for_pattern(
2045                    Some(main_s.as_ref()),
2046                    Some(main_p.as_ref()),
2047                    Some(main_o.as_ref()),
2048                    Some(GraphNameRef::DefaultGraph)
2049                )
2050                .collect::<Result<Vec<_>, _>>()?,
2051            vec![default_quad.clone()]
2052        );
2053        assert_eq!(
2054            store
2055                .quads_for_pattern(
2056                    Some(main_s.as_ref()),
2057                    Some(main_p.as_ref()),
2058                    Some(main_o.as_ref()),
2059                    Some(main_g.as_ref())
2060                )
2061                .collect::<Result<Vec<_>, _>>()?,
2062            vec![named_quad.clone()]
2063        );
2064        default_quads.reverse();
2065        assert_eq!(
2066            store
2067                .quads_for_pattern(
2068                    Some(main_s.as_ref()),
2069                    Some(main_p.as_ref()),
2070                    None,
2071                    Some(GraphNameRef::DefaultGraph)
2072                )
2073                .collect::<Result<Vec<_>, _>>()?,
2074            default_quads
2075        );
2076        assert_eq!(
2077            store
2078                .quads_for_pattern(Some(main_s.as_ref()), None, Some(main_o.as_ref()), None)
2079                .collect::<Result<Vec<_>, _>>()?,
2080            vec![named_quad.clone(), default_quad.clone()]
2081        );
2082        assert_eq!(
2083            store
2084                .quads_for_pattern(
2085                    Some(main_s.as_ref()),
2086                    None,
2087                    Some(main_o.as_ref()),
2088                    Some(GraphNameRef::DefaultGraph)
2089                )
2090                .collect::<Result<Vec<_>, _>>()?,
2091            vec![default_quad.clone()]
2092        );
2093        assert_eq!(
2094            store
2095                .quads_for_pattern(
2096                    Some(main_s.as_ref()),
2097                    None,
2098                    Some(main_o.as_ref()),
2099                    Some(main_g.as_ref())
2100                )
2101                .collect::<Result<Vec<_>, _>>()?,
2102            vec![named_quad.clone()]
2103        );
2104        assert_eq!(
2105            store
2106                .quads_for_pattern(
2107                    Some(main_s.as_ref()),
2108                    None,
2109                    None,
2110                    Some(GraphNameRef::DefaultGraph)
2111                )
2112                .collect::<Result<Vec<_>, _>>()?,
2113            default_quads
2114        );
2115        assert_eq!(
2116            store
2117                .quads_for_pattern(None, Some(main_p.as_ref()), None, None)
2118                .collect::<Result<Vec<_>, _>>()?,
2119            all_quads
2120        );
2121        assert_eq!(
2122            store
2123                .quads_for_pattern(None, Some(main_p.as_ref()), Some(main_o.as_ref()), None)
2124                .collect::<Result<Vec<_>, _>>()?,
2125            vec![named_quad.clone(), default_quad.clone()]
2126        );
2127        assert_eq!(
2128            store
2129                .quads_for_pattern(None, None, Some(main_o.as_ref()), None)
2130                .collect::<Result<Vec<_>, _>>()?,
2131            vec![named_quad.clone(), default_quad.clone()]
2132        );
2133        assert_eq!(
2134            store
2135                .quads_for_pattern(None, None, None, Some(GraphNameRef::DefaultGraph))
2136                .collect::<Result<Vec<_>, _>>()?,
2137            default_quads
2138        );
2139        assert_eq!(
2140            store
2141                .quads_for_pattern(
2142                    None,
2143                    Some(main_p.as_ref()),
2144                    Some(main_o.as_ref()),
2145                    Some(GraphNameRef::DefaultGraph)
2146                )
2147                .collect::<Result<Vec<_>, _>>()?,
2148            vec![default_quad]
2149        );
2150        assert_eq!(
2151            store
2152                .quads_for_pattern(
2153                    None,
2154                    Some(main_p.as_ref()),
2155                    Some(main_o.as_ref()),
2156                    Some(main_g.as_ref())
2157                )
2158                .collect::<Result<Vec<_>, _>>()?,
2159            vec![named_quad]
2160        );
2161
2162        Ok(())
2163    }
2164}