spareval/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(test(attr(deny(warnings))))]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![doc(html_favicon_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
5#![doc(html_logo_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
6
7mod dataset;
8mod error;
9mod eval;
10mod expression;
11mod model;
12mod service;
13mod update;
14
15#[cfg(feature = "sparql-12")]
16pub use crate::dataset::ExpressionTriple;
17pub use crate::dataset::{ExpressionTerm, InternalQuad, QueryableDataset};
18pub use crate::error::QueryEvaluationError;
19pub use crate::eval::CancellationToken;
20use crate::eval::{EvalNodeWithStats, SimpleEvaluator, Timer};
21use crate::expression::{
22    CustomFunctionRegistry, ExpressionEvaluatorContext, build_expression_evaluator,
23};
24pub use crate::model::{QueryResults, QuerySolution, QuerySolutionIter, QueryTripleIter};
25use crate::service::ServiceHandlerRegistry;
26pub use crate::service::{DefaultServiceHandler, ServiceHandler};
27pub use crate::update::{DeleteInsertIter, DeleteInsertQuad};
28use json_event_parser::{JsonEvent, WriterJsonSerializer};
29use oxiri::Iri;
30use oxrdf::{GraphName, Literal, NamedNode, NamedOrBlankNode, Term, Variable};
31use oxsdatatypes::{DateTime, DayTimeDuration, Float};
32use spargebra::Query;
33use spargebra::algebra::QueryDataset;
34use spargebra::term::{GroundQuadPattern, QuadPattern};
35use sparopt::Optimizer;
36use sparopt::algebra::GraphPattern;
37use std::collections::HashMap;
38use std::rc::Rc;
39use std::sync::Arc;
40use std::{fmt, io};
41
42/// Evaluates a query against a given [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset)
43///
44/// To adapt this software to work on your own RDF dataset, you need to implement the [`QueryableDataset`] trait.
45///
46/// ```
47/// use oxrdf::{Dataset, GraphName, NamedNode, Quad};
48/// use spareval::{QueryEvaluator, QueryResults};
49/// use spargebra::SparqlParser;
50///
51/// let ex = NamedNode::new("http://example.com")?;
52/// let dataset = Dataset::from_iter([Quad::new(
53///     ex.clone(),
54///     ex.clone(),
55///     ex.clone(),
56///     GraphName::DefaultGraph,
57/// )]);
58/// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
59/// let evaluator = QueryEvaluator::new();
60/// let results = evaluator.prepare(&query).execute(&dataset)?;
61/// if let QueryResults::Solutions(solutions) = results {
62///     let solutions = solutions.collect::<Result<Vec<_>, _>>()?;
63///     assert_eq!(solutions.len(), 1);
64///     assert_eq!(solutions[0]["s"], ex.into());
65/// }
66/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
67/// ```
68#[derive(Clone, Default)]
69pub struct QueryEvaluator {
70    service_handler: ServiceHandlerRegistry,
71    custom_functions: CustomFunctionRegistry,
72    custom_aggregate_functions: CustomAggregateFunctionRegistry,
73    without_optimizations: bool,
74    run_stats: bool,
75    cancellation_token: Option<CancellationToken>,
76}
77
78impl QueryEvaluator {
79    #[must_use]
80    #[inline]
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// Prepare the SPARQL query to be executed.
86    pub fn prepare<'a>(&'a self, query: &'a Query) -> PreparedQuery<'a> {
87        let dataset = query.dataset().cloned().map(Into::into).unwrap_or_default();
88        PreparedQuery {
89            evaluator: self,
90            query,
91            dataset,
92            substitutions: HashMap::new(),
93        }
94    }
95
96    /// Execute the SPARQL query against the given dataset.
97    ///
98    /// Note that this evaluator does not handle the `FROM` and `FROM NAMED` part of the query.
99    /// You must select the proper dataset before using this struct.
100    #[deprecated(since = "0.2.1", note = "Use prepare instead")]
101    #[expect(deprecated)]
102    pub fn execute<'a>(
103        &self,
104        dataset: impl QueryableDataset<'a>,
105        query: &Query,
106    ) -> Result<QueryResults<'a>, QueryEvaluationError> {
107        self.execute_with_substituted_variables(dataset, query, [])
108    }
109
110    /// Executes a SPARQL query while substituting some variables with the given values.
111    ///
112    /// Substitution follows [RDF-dev SEP-0007](https://github.com/w3c/sparql-dev/blob/main/SEP/SEP-0007/sep-0007.md).
113    ///
114    /// ```
115    /// # #![expect(deprecated)]
116    /// use oxrdf::{Dataset, GraphName, NamedNode, Quad, Variable};
117    /// use spareval::{QueryEvaluator, QueryResults};
118    /// use spargebra::SparqlParser;
119    ///
120    /// let ex = NamedNode::new("http://example.com")?;
121    /// let dataset = Dataset::from_iter([Quad::new(
122    ///     ex.clone(),
123    ///     ex.clone(),
124    ///     ex.clone(),
125    ///     GraphName::DefaultGraph,
126    /// )]);
127    /// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
128    /// let results = QueryEvaluator::new().execute_with_substituted_variables(
129    ///     &dataset,
130    ///     &query,
131    ///     [(Variable::new("s")?, ex.clone().into())],
132    /// );
133    /// if let QueryResults::Solutions(solutions) = results? {
134    ///     let solutions = solutions.collect::<Result<Vec<_>, _>>()?;
135    ///     assert_eq!(solutions.len(), 1);
136    ///     assert_eq!(solutions[0]["s"], ex.into());
137    /// }
138    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
139    /// ```
140    #[deprecated(since = "0.2.1", note = "Use prepare instead")]
141    #[expect(deprecated)]
142    pub fn execute_with_substituted_variables<'a>(
143        &self,
144        dataset: impl QueryableDataset<'a>,
145        query: &Query,
146        substitutions: impl IntoIterator<Item = (Variable, Term)>,
147    ) -> Result<QueryResults<'a>, QueryEvaluationError> {
148        self.explain_with_substituted_variables(dataset, query, substitutions)
149            .0
150    }
151
152    #[deprecated(since = "0.2.1", note = "Use prepare instead")]
153    #[expect(deprecated)]
154    pub fn explain<'a>(
155        &self,
156        dataset: impl QueryableDataset<'a>,
157        query: &Query,
158    ) -> (
159        Result<QueryResults<'a>, QueryEvaluationError>,
160        QueryExplanation,
161    ) {
162        self.explain_with_substituted_variables(dataset, query, [])
163    }
164
165    #[deprecated(since = "0.2.1", note = "Use prepare instead")]
166    pub fn explain_with_substituted_variables<'a>(
167        &self,
168        dataset: impl QueryableDataset<'a>,
169        query: &Query,
170        substitutions: impl IntoIterator<Item = (Variable, Term)>,
171    ) -> (
172        Result<QueryResults<'a>, QueryEvaluationError>,
173        QueryExplanation,
174    ) {
175        let mut prepared = PreparedQuery {
176            evaluator: self,
177            query,
178            dataset: QueryDatasetSpecification::new(),
179            substitutions: HashMap::new(),
180        };
181        for (variable, term) in substitutions {
182            prepared = prepared.substitute_variable(variable, term);
183        }
184        prepared.explain(dataset)
185    }
186
187    /// Use a given [`ServiceHandler`] to execute [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICE calls.
188    ///
189    /// See [`ServiceHandler`] for an example.
190    #[inline]
191    #[must_use]
192    pub fn with_service_handler(
193        mut self,
194        service_name: impl Into<NamedNode>,
195        handler: impl ServiceHandler + 'static,
196    ) -> Self {
197        self.service_handler = self
198            .service_handler
199            .with_handler(service_name.into(), handler);
200        self
201    }
202
203    /// Use a given [`DefaultServiceHandler`] to execute [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) SERVICE calls if no explicit service handler is defined for the service.
204    ///
205    /// See [`DefaultServiceHandler`] for an example.
206    #[inline]
207    #[must_use]
208    pub fn with_default_service_handler(
209        mut self,
210        handler: impl DefaultServiceHandler + 'static,
211    ) -> Self {
212        self.service_handler = self.service_handler.with_default_handler(handler);
213        self
214    }
215
216    #[inline]
217    #[must_use]
218    pub fn has_default_service_handler(&self) -> bool {
219        self.service_handler.has_default_handler()
220    }
221
222    /// Adds a custom SPARQL evaluation function.
223    ///
224    /// Example with a function serializing terms to N-Triples:
225    /// ```
226    /// use oxrdf::{Dataset, Literal, NamedNode};
227    /// use spareval::{QueryEvaluator, QueryResults};
228    /// use spargebra::SparqlParser;
229    ///
230    /// let evaluator = QueryEvaluator::new().with_custom_function(
231    ///     NamedNode::new("http://www.w3.org/ns/formats/N-Triples")?,
232    ///     |args| args.get(0).map(|t| Literal::from(t.to_string()).into()),
233    /// );
234    /// let query = SparqlParser::new()
235    ///     .parse_query("SELECT (<http://www.w3.org/ns/formats/N-Triples>(1) AS ?nt) WHERE {}")?;
236    /// if let QueryResults::Solutions(mut solutions) =
237    ///     evaluator.prepare(&query).execute(&Dataset::new())?
238    /// {
239    ///     assert_eq!(
240    ///         solutions.next().unwrap()?.get("nt"),
241    ///         Some(&Literal::from("\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>").into())
242    ///     );
243    /// }
244    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
245    /// ```
246    #[inline]
247    #[must_use]
248    pub fn with_custom_function(
249        mut self,
250        name: NamedNode,
251        evaluator: impl Fn(&[Term]) -> Option<Term> + Send + Sync + 'static,
252    ) -> Self {
253        self.custom_functions.insert(name, Arc::new(evaluator));
254        self
255    }
256
257    /// Adds a custom SPARQL evaluation aggregate function.
258    ///
259    /// Note that it must also be given to the SPARQL parser using [`SparqlParser::with_custom_aggregate_function`](spargebra::SparqlParser::with_custom_aggregate_function).
260    ///
261    /// Example with a function doing concatenation:
262    /// ```
263    /// use oxrdf::{Dataset, Literal, NamedNode, Term};
264    /// use spareval::{AggregateFunctionAccumulator, QueryEvaluator, QueryResults};
265    /// use spargebra::SparqlParser;
266    /// use std::mem::take;
267    ///
268    /// struct ConcatAccumulator {
269    ///     value: String,
270    /// }
271    ///
272    /// impl AggregateFunctionAccumulator for ConcatAccumulator {
273    ///     fn accumulate(&mut self, element: Term) {
274    ///         if let Term::Literal(v) = element {
275    ///             if !self.value.is_empty() {
276    ///                 self.value.push(' ');
277    ///             }
278    ///             self.value.push_str(v.value());
279    ///         }
280    ///     }
281    ///
282    ///     fn finish(&mut self) -> Option<Term> {
283    ///         Some(Literal::new_simple_literal(take(&mut self.value)).into())
284    ///     }
285    /// }
286    ///
287    /// let evaluator = QueryEvaluator::new().with_custom_aggregate_function(
288    ///     NamedNode::new("http://example.com/concat")?,
289    ///     || {
290    ///         Box::new(ConcatAccumulator {
291    ///             value: String::new(),
292    ///         })
293    ///     },
294    /// );
295    /// let query = SparqlParser::new()
296    ///     .with_custom_aggregate_function(NamedNode::new("http://example.com/concat")?)
297    ///     .parse_query(
298    ///         "SELECT (<http://example.com/concat>(?v) AS ?r) WHERE { VALUES ?v { 1 2 3 } }",
299    ///     )?;
300    /// if let QueryResults::Solutions(mut solutions) =
301    ///     evaluator.prepare(&query).execute(&Dataset::new())?
302    /// {
303    ///     assert_eq!(
304    ///         solutions.next().unwrap()?.get("r"),
305    ///         Some(&Literal::new_simple_literal("1 2 3").into())
306    ///     );
307    /// }
308    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
309    /// ```
310    #[inline]
311    #[must_use]
312    pub fn with_custom_aggregate_function(
313        mut self,
314        name: NamedNode,
315        evaluator: impl Fn() -> Box<dyn AggregateFunctionAccumulator + Send + Sync>
316        + Send
317        + Sync
318        + 'static,
319    ) -> Self {
320        self.custom_aggregate_functions
321            .insert(name, Arc::new(evaluator));
322        self
323    }
324
325    /// Disables query optimizations and runs the query as it is.
326    #[inline]
327    #[must_use]
328    pub fn without_optimizations(mut self) -> Self {
329        self.without_optimizations = true;
330        self
331    }
332
333    /// Compute statistics during evaluation and fills them in the explanation tree.
334    #[inline]
335    #[must_use]
336    pub fn compute_statistics(mut self) -> Self {
337        self.run_stats = true;
338        self
339    }
340
341    /// Inject a cancellation token to the SPARQL evaluation.
342    ///
343    /// Might be used to abort a query cleanly.
344    ///
345    /// ```
346    /// use oxrdf::{Dataset, GraphName, NamedNode, Quad};
347    /// use spareval::{CancellationToken, QueryEvaluationError, QueryEvaluator, QueryResults};
348    /// use spargebra::SparqlParser;
349    ///
350    /// let ex = NamedNode::new("http://example.com")?;
351    /// let dataset = Dataset::from_iter([Quad::new(
352    ///     ex.clone(),
353    ///     ex.clone(),
354    ///     ex.clone(),
355    ///     GraphName::DefaultGraph,
356    /// )]);
357    /// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
358    /// let cancellation_token = CancellationToken::new();
359    /// let evaluator = QueryEvaluator::new().with_cancellation_token(cancellation_token.clone());
360    /// let results = evaluator.prepare(&query).execute(&dataset)?;
361    /// if let QueryResults::Solutions(mut solutions) = results {
362    ///     cancellation_token.cancel(); // We cancel
363    ///     assert!(matches!(
364    ///         solutions.next().unwrap().unwrap_err(), // It's cancelled
365    ///         QueryEvaluationError::Cancelled
366    ///     ));
367    /// }
368    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
369    /// ```
370    #[must_use]
371    pub fn with_cancellation_token(mut self, cancellation_token: CancellationToken) -> Self {
372        self.cancellation_token = Some(cancellation_token);
373        self
374    }
375
376    // Internal helper: evaluates a SPARQL expression to an ExpressionTerm against an empty dataset
377    fn eval_expression_term_with_substitutions<'a>(
378        &self,
379        expression: &sparopt::algebra::Expression,
380        substitutions: impl IntoIterator<Item = (&'a Variable, Term)>,
381    ) -> Option<ExpressionTerm> {
382        struct Context<'a> {
383            now: Option<DateTime>,
384            custom_functions: &'a CustomFunctionRegistry,
385        }
386
387        impl<'a> ExpressionEvaluatorContext<'a> for Context<'a> {
388            type Term = Term;
389            type Tuple = HashMap<&'a Variable, Term>;
390            type Error = QueryEvaluationError;
391
392            fn build_variable_lookup(
393                &mut self,
394                variable: &Variable,
395            ) -> impl Fn(&HashMap<&'a Variable, Term>) -> Option<Term> + 'a {
396                let variable = variable.clone();
397                move |tuple| tuple.get(&variable).cloned()
398            }
399
400            fn build_is_variable_bound(
401                &mut self,
402                variable: &Variable,
403            ) -> impl Fn(&HashMap<&'a Variable, Term>) -> bool + 'a {
404                let variable = variable.clone();
405                move |tuple| tuple.contains_key(&variable)
406            }
407
408            fn build_exists(
409                &mut self,
410                _: &GraphPattern,
411            ) -> Result<impl Fn(&HashMap<&'a Variable, Term>) -> bool + 'a, QueryEvaluationError>
412            {
413                Err::<fn(&HashMap<&'a Variable, Term>) -> bool, _>(
414                    QueryEvaluationError::Unexpected(
415                        "EXISTS is not supported by the SPARQL expression evaluator".into(),
416                    ),
417                )
418            }
419
420            fn internalize_named_node(
421                &mut self,
422                term: &NamedNode,
423            ) -> Result<Term, QueryEvaluationError> {
424                Ok(term.clone().into())
425            }
426
427            fn internalize_literal(
428                &mut self,
429                term: &Literal,
430            ) -> Result<Term, QueryEvaluationError> {
431                Ok(term.clone().into())
432            }
433
434            fn build_internalize_expression_term(
435                &mut self,
436            ) -> impl Fn(ExpressionTerm) -> Option<Term> + 'a {
437                |t| Some(t.into())
438            }
439
440            fn build_externalize_expression_term(
441                &mut self,
442            ) -> impl Fn(Term) -> Option<ExpressionTerm> + 'a {
443                |t| Some(t.into())
444            }
445
446            fn now(&mut self) -> DateTime {
447                *self.now.get_or_insert_with(DateTime::now)
448            }
449
450            fn base_iri(&mut self) -> Option<Arc<Iri<String>>> {
451                None
452            }
453
454            fn custom_functions(&mut self) -> &CustomFunctionRegistry {
455                self.custom_functions
456            }
457        }
458
459        build_expression_evaluator(
460            expression,
461            &mut Context {
462                now: None,
463                custom_functions: &self.custom_functions,
464            },
465        )
466        .ok()?(&substitutions.into_iter().collect::<HashMap<_, _>>())
467    }
468
469    /// Evaluates a SPARQL expression against an empty dataset with optional variable substitutions.
470    ///
471    /// Returns the computed term or `None` if an error occurs or the expression is invalid.
472    pub fn evaluate_expression<'a>(
473        &self,
474        expression: &sparopt::algebra::Expression,
475        substitutions: impl IntoIterator<Item = (&'a Variable, Term)>,
476    ) -> Option<Term> {
477        self.eval_expression_term_with_substitutions(expression, substitutions)
478            .map(Into::into)
479    }
480
481    /// Evaluates a SPARQL expression effective boolean value (EBV) against an empty dataset
482    /// with optional variable substitutions.
483    ///
484    /// Returns the EBV or `None` if an error occurs or EBV is undefined for the result type.
485    pub fn evaluate_effective_boolean_value_expression<'a>(
486        &self,
487        expression: &sparopt::algebra::Expression,
488        substitutions: impl IntoIterator<Item = (&'a Variable, Term)>,
489    ) -> Option<bool> {
490        self.eval_expression_term_with_substitutions(expression, substitutions)?
491            .effective_boolean_value()
492    }
493
494    /// Evaluates a SPARQL UPDATE DELETE/INSERT operation.
495    ///
496    /// Returns the list of quads to delete or insert.
497    ///
498    /// ```
499    /// use oxrdf::{Dataset, GraphName, Literal, NamedNode, Quad};
500    /// use spareval::{DeleteInsertQuad, QueryEvaluator};
501    /// use spargebra::{GraphUpdateOperation, SparqlParser};
502    ///
503    /// let ex = NamedNode::new("http://example.com")?;
504    /// let dataset = Dataset::from_iter([Quad::new(
505    ///     ex.clone(),
506    ///     ex.clone(),
507    ///     Literal::from(0),
508    ///     GraphName::DefaultGraph,
509    /// )]);
510    /// let update = SparqlParser::new().parse_update(
511    ///     "DELETE { ?s ?p ?o } INSERT { ?s ?p ?o2 } WHERE { ?s ?p ?o BIND(?o +1 AS ?o2) }",
512    /// )?;
513    /// let GraphUpdateOperation::DeleteInsert {
514    ///     delete,
515    ///     insert,
516    ///     using: _,
517    ///     pattern,
518    /// } = &update.operations[0]
519    /// else {
520    ///     unreachable!()
521    /// };
522    /// let results = QueryEvaluator::new()
523    ///     .prepare_delete_insert(delete.clone(), insert.clone(), None, None, pattern)
524    ///     .execute(&dataset)?
525    ///     .collect::<Result<Vec<_>, _>>()?;
526    /// assert_eq!(
527    ///     results,
528    ///     vec![
529    ///         DeleteInsertQuad::Delete(Quad::new(
530    ///             ex.clone(),
531    ///             ex.clone(),
532    ///             Literal::from(0),
533    ///             GraphName::DefaultGraph,
534    ///         )),
535    ///         DeleteInsertQuad::Insert(Quad::new(
536    ///             ex.clone(),
537    ///             ex.clone(),
538    ///             Literal::from(1),
539    ///             GraphName::DefaultGraph,
540    ///         ))
541    ///     ]
542    /// );
543    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
544    /// ```
545    pub fn prepare_delete_insert<'a>(
546        &'a self,
547        delete: Vec<GroundQuadPattern>,
548        insert: Vec<QuadPattern>,
549        base_iri: Option<Iri<String>>,
550        using: Option<QueryDataset>,
551        pattern: &'a spargebra::algebra::GraphPattern,
552    ) -> PreparedDeleteInsertUpdate<'a> {
553        PreparedDeleteInsertUpdate {
554            evaluator: self,
555            pattern,
556            delete,
557            insert,
558            base_iri,
559            dataset: using.map(Into::into).unwrap_or_default(),
560        }
561    }
562
563    fn simple_evaluator<'a, D: QueryableDataset<'a>>(
564        &self,
565        dataset: D,
566        dataset_spec: QueryDatasetSpecification,
567        base_iri: &Option<Iri<String>>,
568    ) -> Result<SimpleEvaluator<'a, D>, QueryEvaluationError> {
569        SimpleEvaluator::new(
570            dataset,
571            base_iri.clone().map(Arc::new),
572            Rc::new(self.service_handler.clone()),
573            Rc::new(self.custom_functions.clone()),
574            Rc::new(self.custom_aggregate_functions.clone()),
575            self.cancellation_token.clone().unwrap_or_default(),
576            dataset_spec,
577            self.run_stats,
578        )
579    }
580}
581
582/// A prepared SPARQL query.
583///
584/// Allows customizing things like the evaluation dataset and substituting variables.
585///
586/// Usage example:
587/// ```
588/// use oxrdf::{Dataset, Literal, Variable};
589/// use spareval::{QueryEvaluator, QueryResults};
590/// use spargebra::SparqlParser;
591///
592/// let query = SparqlParser::new().parse_query("SELECT ?v WHERE {}")?;
593/// let evaluator = QueryEvaluator::new();
594/// let prepared_query = evaluator
595///     .prepare(&query)
596///     .substitute_variable(Variable::new("v")?, Literal::from(1));
597///
598/// if let QueryResults::Solutions(mut solutions) = prepared_query.execute(&Dataset::new())? {
599///     assert_eq!(
600///         solutions.next().unwrap()?.get("v"),
601///         Some(&Literal::from(1).into())
602///     );
603/// }
604/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
605/// ```
606#[derive(Clone)]
607#[must_use]
608pub struct PreparedQuery<'a> {
609    evaluator: &'a QueryEvaluator,
610    query: &'a Query,
611    dataset: QueryDatasetSpecification,
612    substitutions: HashMap<Variable, Term>,
613}
614
615impl PreparedQuery<'_> {
616    /// Substitute a variable with a given RDF term in the SPARQL query.
617    ///
618    /// Usage example:
619    /// ```
620    /// use oxrdf::{Dataset, Literal, Variable};
621    /// use spareval::{QueryEvaluator, QueryResults};
622    /// use spargebra::SparqlParser;
623    ///
624    /// let query = SparqlParser::new().parse_query("SELECT ?v WHERE {}")?;
625    /// let evaluator = QueryEvaluator::new();
626    /// let prepared_query = evaluator
627    ///     .prepare(&query)
628    ///     .substitute_variable(Variable::new("v")?, Literal::from(1));
629    ///
630    /// if let QueryResults::Solutions(mut solutions) = prepared_query.execute(&Dataset::new())? {
631    ///     assert_eq!(
632    ///         solutions.next().unwrap()?.get("v"),
633    ///         Some(&Literal::from(1).into())
634    ///     );
635    /// }
636    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
637    /// ```
638    #[inline]
639    pub fn substitute_variable(
640        mut self,
641        variable: impl Into<Variable>,
642        term: impl Into<Term>,
643    ) -> Self {
644        self.substitutions.insert(variable.into(), term.into());
645        self
646    }
647
648    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) of this prepared query.
649    #[inline]
650    pub fn dataset(&self) -> &QueryDatasetSpecification {
651        &self.dataset
652    }
653    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) of this prepared query.
654    #[inline]
655    pub fn dataset_mut(&mut self) -> &mut QueryDatasetSpecification {
656        &mut self.dataset
657    }
658
659    /// Execute the SPARQL query against the given [`QueryableDataset`].
660    pub fn execute<'b>(
661        self,
662        dataset: impl QueryableDataset<'b>,
663    ) -> Result<QueryResults<'b>, QueryEvaluationError> {
664        self.explain(dataset).0
665    }
666
667    pub fn explain<'b>(
668        self,
669        dataset: impl QueryableDataset<'b>,
670    ) -> (
671        Result<QueryResults<'b>, QueryEvaluationError>,
672        QueryExplanation,
673    ) {
674        let start_planning = Timer::now();
675        let (results, plan_node_with_stats, planning_duration) = match self.query {
676            Query::Select {
677                pattern, base_iri, ..
678            } => {
679                let mut pattern = GraphPattern::from(pattern);
680                if !self.evaluator.without_optimizations {
681                    pattern = Optimizer::optimize_graph_pattern(pattern);
682                }
683                let planning_duration = start_planning.elapsed();
684                let (results, explanation) =
685                    match self
686                        .evaluator
687                        .simple_evaluator(dataset, self.dataset, base_iri)
688                    {
689                        Ok(evaluator) => evaluator.evaluate_select(&pattern, self.substitutions),
690                        Err(e) => (Err(e), Rc::new(EvalNodeWithStats::empty())),
691                    };
692                (
693                    results.map(QueryResults::Solutions),
694                    explanation,
695                    planning_duration,
696                )
697            }
698            Query::Ask {
699                pattern, base_iri, ..
700            } => {
701                let mut pattern = GraphPattern::from(pattern);
702                if !self.evaluator.without_optimizations {
703                    pattern = Optimizer::optimize_graph_pattern(pattern);
704                }
705                let planning_duration = start_planning.elapsed();
706                let (results, explanation) =
707                    match self
708                        .evaluator
709                        .simple_evaluator(dataset, self.dataset, base_iri)
710                    {
711                        Ok(evaluator) => evaluator.evaluate_ask(&pattern, self.substitutions),
712                        Err(e) => (Err(e), Rc::new(EvalNodeWithStats::empty())),
713                    };
714                (
715                    results.map(QueryResults::Boolean),
716                    explanation,
717                    planning_duration,
718                )
719            }
720            Query::Construct {
721                template,
722                pattern,
723                base_iri,
724                ..
725            } => {
726                let mut pattern = GraphPattern::from(pattern);
727                if !self.evaluator.without_optimizations {
728                    pattern = Optimizer::optimize_graph_pattern(pattern);
729                }
730                let planning_duration = start_planning.elapsed();
731                let (results, explanation) =
732                    match self
733                        .evaluator
734                        .simple_evaluator(dataset, self.dataset, base_iri)
735                    {
736                        Ok(evaluator) => {
737                            evaluator.evaluate_construct(&pattern, template, self.substitutions)
738                        }
739                        Err(e) => (Err(e), Rc::new(EvalNodeWithStats::empty())),
740                    };
741                (
742                    results.map(QueryResults::Graph),
743                    explanation,
744                    planning_duration,
745                )
746            }
747            Query::Describe {
748                pattern, base_iri, ..
749            } => {
750                let mut pattern = GraphPattern::from(pattern);
751                if !self.evaluator.without_optimizations {
752                    pattern = Optimizer::optimize_graph_pattern(pattern);
753                }
754                let planning_duration = start_planning.elapsed();
755                let (results, explanation) =
756                    match self
757                        .evaluator
758                        .simple_evaluator(dataset, self.dataset, base_iri)
759                    {
760                        Ok(evaluator) => evaluator.evaluate_describe(&pattern, self.substitutions),
761                        Err(e) => (Err(e), Rc::new(EvalNodeWithStats::empty())),
762                    };
763                (
764                    results.map(QueryResults::Graph),
765                    explanation,
766                    planning_duration,
767                )
768            }
769        };
770        let explanation = QueryExplanation {
771            inner: plan_node_with_stats,
772            with_stats: self.evaluator.run_stats,
773            planning_duration,
774        };
775        (results, explanation)
776    }
777}
778
779/// A prepared SPARQL query.
780///
781/// Allows customizing things like the evaluation dataset and substituting variables.
782///
783/// Usage example:
784/// ```
785/// use oxrdf::{Dataset, GraphName, Literal, NamedNode, Quad};
786/// use spareval::{DeleteInsertQuad, QueryEvaluator};
787/// use spargebra::{GraphUpdateOperation, SparqlParser};
788///
789/// let ex = NamedNode::new("http://example.com")?;
790/// let dataset = Dataset::from_iter([Quad::new(
791///     ex.clone(),
792///     ex.clone(),
793///     Literal::from(0),
794///     GraphName::DefaultGraph,
795/// )]);
796/// let update = SparqlParser::new().parse_update(
797///     "DELETE { ?s ?p ?o } INSERT { ?s ?p ?o2 } WHERE { ?s ?p ?o BIND(?o +1 AS ?o2) }",
798/// )?;
799/// let GraphUpdateOperation::DeleteInsert {
800///     delete,
801///     insert,
802///     using: _,
803///     pattern,
804/// } = &update.operations[0]
805/// else {
806///     unreachable!()
807/// };
808/// let results = QueryEvaluator::new()
809///     .prepare_delete_insert(delete.clone(), insert.clone(), None, None, pattern)
810///     .execute(&dataset)?
811///     .collect::<Result<Vec<_>, _>>()?;
812/// assert_eq!(
813///     results,
814///     vec![
815///         DeleteInsertQuad::Delete(Quad::new(
816///             ex.clone(),
817///             ex.clone(),
818///             Literal::from(0),
819///             GraphName::DefaultGraph,
820///         )),
821///         DeleteInsertQuad::Insert(Quad::new(
822///             ex.clone(),
823///             ex.clone(),
824///             Literal::from(1),
825///             GraphName::DefaultGraph,
826///         ))
827///     ]
828/// );
829/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
830/// ```
831#[derive(Clone)]
832#[must_use]
833pub struct PreparedDeleteInsertUpdate<'a> {
834    evaluator: &'a QueryEvaluator,
835    pattern: &'a spargebra::algebra::GraphPattern,
836    delete: Vec<GroundQuadPattern>,
837    insert: Vec<QuadPattern>,
838    base_iri: Option<Iri<String>>,
839    dataset: QueryDatasetSpecification,
840}
841
842impl PreparedDeleteInsertUpdate<'_> {
843    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) of this prepared update.
844    #[inline]
845    pub fn dataset(&self) -> &QueryDatasetSpecification {
846        &self.dataset
847    }
848    /// Returns [the query dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset) of this prepared update.
849    #[inline]
850    pub fn dataset_mut(&mut self) -> &mut QueryDatasetSpecification {
851        &mut self.dataset
852    }
853
854    /// Execute the SPARQL query against the given [`QueryableDataset`].
855    pub fn execute<'b>(
856        self,
857        dataset: impl QueryableDataset<'b>,
858    ) -> Result<DeleteInsertIter<'b>, QueryEvaluationError> {
859        let mut pattern = GraphPattern::from(self.pattern);
860        if !self.evaluator.without_optimizations {
861            pattern = Optimizer::optimize_graph_pattern(pattern);
862        }
863        let (solutions, _) = self
864            .evaluator
865            .simple_evaluator(dataset, self.dataset, &self.base_iri)?
866            .evaluate_select(&pattern, []);
867        Ok(DeleteInsertIter::new(solutions?, self.delete, self.insert))
868    }
869}
870
871pub(crate) type CustomAggregateFunctionRegistry = HashMap<
872    NamedNode,
873    Arc<dyn (Fn() -> Box<dyn AggregateFunctionAccumulator + Send + Sync>) + Send + Sync>,
874>;
875
876/// A trait for custom aggregate function implementation.
877///
878/// The accumulator accumulates values using the [`accumulate`](Self::accumulate) method
879/// and returns a final aggregated value (or an error) using [`finish`](Self::finish).
880///
881/// See [`QueryEvaluator::with_custom_aggregate_function`] for an example.
882pub trait AggregateFunctionAccumulator {
883    fn accumulate(&mut self, element: Term);
884    fn finish(&mut self) -> Option<Term>;
885}
886
887/// An extended SPARQL query [dataset specification](https://www.w3.org/TR/sparql11-query/#specifyingDataset).
888///
889/// Allows setting blank node graph names and that the default graph is the union of all named graphs.
890#[derive(Eq, PartialEq, Debug, Clone, Hash)]
891pub struct QueryDatasetSpecification {
892    default: Option<Vec<GraphName>>,
893    named: Option<Vec<NamedOrBlankNode>>,
894}
895
896impl QueryDatasetSpecification {
897    pub fn new() -> Self {
898        Self {
899            default: Some(vec![GraphName::DefaultGraph]),
900            named: None,
901        }
902    }
903
904    /// Checks if this dataset specification is the default one
905    /// (i.e., the default graph is the store default graph, and all named graphs included in the queried store are available)
906    pub fn is_default_dataset(&self) -> bool {
907        // TODO: rename to is_default?
908        self.default
909            .as_ref()
910            .is_some_and(|t| t == &[GraphName::DefaultGraph])
911            && self.named.is_none()
912    }
913
914    /// 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.
915    /// This list is by default only the store default graph.
916    pub fn default_graph_graphs(&self) -> Option<&[GraphName]> {
917        self.default.as_deref()
918    }
919
920    /// Sets the default graph of the query to be the union of all the graphs in the queried store.
921    ///
922    /// ```
923    /// use oxrdf::{Dataset, NamedNode, Quad};
924    /// use spareval::{QueryEvaluator, QueryResults};
925    /// use spargebra::SparqlParser;
926    ///
927    /// let dataset = Dataset::from_iter([Quad::new(
928    ///     NamedNode::new("http://example.com/s")?,
929    ///     NamedNode::new("http://example.com/p")?,
930    ///     NamedNode::new("http://example.com/o")?,
931    ///     NamedNode::new("http://example.com/g")?,
932    /// )]);
933    /// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
934    /// let evaluator = QueryEvaluator::new();
935    /// let mut prepared = evaluator.prepare(&query);
936    /// prepared
937    ///     .dataset_mut()
938    ///     .set_default_graph(vec![NamedNode::new("http://example.com/g")?.into()]);
939    /// if let QueryResults::Solutions(mut solutions) = prepared.execute(&dataset)? {
940    ///     assert_eq!(
941    ///         solutions.next().unwrap()?.get("s"),
942    ///         Some(&NamedNode::new("http://example.com/s")?.into())
943    ///     );
944    /// }
945    ///
946    /// # Ok::<_, Box<dyn std::error::Error>>(())
947    /// ```
948    pub fn set_default_graph_as_union(&mut self) {
949        self.default = None;
950    }
951
952    /// Sets the list of graphs the query should consider as being part of the default graph.
953    ///
954    /// By default, only the store default graph is considered.
955    /// ```
956    /// use oxrdf::{Dataset, NamedNode, Quad};
957    /// use spareval::{QueryEvaluator, QueryResults};
958    /// use spargebra::SparqlParser;
959    ///
960    /// let dataset = Dataset::from_iter([Quad::new(
961    ///     NamedNode::new("http://example.com/s")?,
962    ///     NamedNode::new("http://example.com/p")?,
963    ///     NamedNode::new("http://example.com/o")?,
964    ///     NamedNode::new("http://example.com/g")?,
965    /// )]);
966    /// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
967    /// let evaluator = QueryEvaluator::new();
968    /// let mut prepared = evaluator.prepare(&query);
969    /// prepared
970    ///     .dataset_mut()
971    ///     .set_default_graph(vec![NamedNode::new("http://example.com/g")?.into()]);
972    /// if let QueryResults::Solutions(mut solutions) = prepared.execute(&dataset)? {
973    ///     assert_eq!(
974    ///         solutions.next().unwrap()?.get("s"),
975    ///         Some(&NamedNode::new("http://example.com/s")?.into())
976    ///     );
977    /// }
978    ///
979    /// # Ok::<_, Box<dyn std::error::Error>>(())
980    /// ```
981    pub fn set_default_graph(&mut self, graphs: Vec<GraphName>) {
982        self.default = Some(graphs)
983    }
984
985    /// Returns the list of the available named graphs for the query or `None` if all graphs are available
986    pub fn available_named_graphs(&self) -> Option<&[NamedOrBlankNode]> {
987        self.named.as_deref()
988    }
989
990    /// Sets the list of allowed named graphs in the query.
991    ///
992    /// ```
993    /// use oxrdf::{Dataset, NamedNode, Quad};
994    /// use spareval::{QueryEvaluator, QueryResults};
995    /// use spargebra::SparqlParser;
996    ///
997    /// let dataset = Dataset::from_iter([Quad::new(
998    ///     NamedNode::new("http://example.com/s")?,
999    ///     NamedNode::new("http://example.com/p")?,
1000    ///     NamedNode::new("http://example.com/o")?,
1001    ///     NamedNode::new("http://example.com/g")?,
1002    /// )]);
1003    /// let query = SparqlParser::new().parse_query("SELECT * WHERE { ?s ?p ?o }")?;
1004    /// let evaluator = QueryEvaluator::new();
1005    /// let mut prepared = evaluator.prepare(&query);
1006    /// prepared
1007    ///     .dataset_mut()
1008    ///     .set_available_named_graphs(Vec::new());
1009    /// if let QueryResults::Solutions(mut solutions) = prepared.execute(&dataset)? {
1010    ///     assert!(solutions.next().is_none(),);
1011    /// }
1012    ///
1013    /// # Ok::<_, Box<dyn std::error::Error>>(())
1014    /// ```
1015    pub fn set_available_named_graphs(&mut self, named_graphs: Vec<NamedOrBlankNode>) {
1016        self.named = Some(named_graphs);
1017    }
1018}
1019
1020impl Default for QueryDatasetSpecification {
1021    fn default() -> Self {
1022        Self::new()
1023    }
1024}
1025
1026impl From<QueryDataset> for QueryDatasetSpecification {
1027    fn from(dataset: QueryDataset) -> Self {
1028        Self {
1029            default: Some(dataset.default.into_iter().map(Into::into).collect()),
1030            named: dataset
1031                .named
1032                .map(|named| named.into_iter().map(Into::into).collect()),
1033        }
1034    }
1035}
1036
1037/// The explanation of a query.
1038#[derive(Clone)]
1039pub struct QueryExplanation {
1040    inner: Rc<EvalNodeWithStats>,
1041    with_stats: bool,
1042    planning_duration: Option<DayTimeDuration>,
1043}
1044
1045impl QueryExplanation {
1046    /// Writes the explanation as JSON.
1047    pub fn write_in_json(&self, writer: impl io::Write) -> io::Result<()> {
1048        let mut serializer = WriterJsonSerializer::new(writer);
1049        serializer.serialize_event(JsonEvent::StartObject)?;
1050        if let Some(planning_duration) = self.planning_duration {
1051            serializer
1052                .serialize_event(JsonEvent::ObjectKey("planning duration in seconds".into()))?;
1053            serializer.serialize_event(JsonEvent::Number(
1054                planning_duration.as_seconds().to_string().into(),
1055            ))?;
1056        }
1057        serializer.serialize_event(JsonEvent::ObjectKey("plan".into()))?;
1058        self.inner.json_node(&mut serializer, self.with_stats)?;
1059        serializer.serialize_event(JsonEvent::EndObject)
1060    }
1061}
1062
1063impl fmt::Debug for QueryExplanation {
1064    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1065        let mut obj = f.debug_struct("QueryExplanation");
1066        if let Some(planning_duration) = self.planning_duration {
1067            obj.field(
1068                "planning duration in seconds",
1069                &f32::from(Float::from(planning_duration.as_seconds())),
1070            );
1071        }
1072        obj.field("tree", &self.inner);
1073        obj.finish_non_exhaustive()
1074    }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079    use super::*;
1080    use oxrdf::vocab::xsd;
1081    use oxrdf::{Literal, Term};
1082    use sparopt::algebra::{Expression, GraphPattern};
1083
1084    #[test]
1085    fn evaluate_expression_literal_and_arithmetic() {
1086        let evaluator = QueryEvaluator::new();
1087
1088        // Simple literal
1089        let expr = Expression::from(Literal::from(3_i32));
1090        let term = evaluator.evaluate_expression(&expr, std::iter::empty());
1091        assert_eq!(term, Some(Term::from(Literal::from(3_i32))));
1092
1093        // 1 + 2 = 3
1094        let add = Expression::Add(
1095            Box::new(Expression::from(Literal::from(1_i32))),
1096            Box::new(Expression::from(Literal::from(2_i32))),
1097        );
1098        let term = evaluator.evaluate_expression(&add, std::iter::empty());
1099        assert_eq!(term, Some(Term::from(Literal::from(3_i32))));
1100    }
1101
1102    #[test]
1103    fn evaluate_expression_with_variable_substitution() {
1104        let evaluator = QueryEvaluator::new();
1105        let x = Variable::new("x").unwrap();
1106
1107        // ?x + 2 with ?x = 1 => 3
1108        let expr = Expression::Add(
1109            Box::new(Expression::from(x.clone())),
1110            Box::new(Expression::from(Literal::from(2_i32))),
1111        );
1112        let one: Term = Literal::from(1_i32).into();
1113        let result = evaluator.evaluate_expression(&expr, [(&x, one)]);
1114        assert_eq!(result, Some(Term::from(Literal::from(3_i32))));
1115    }
1116
1117    #[test]
1118    fn evaluate_expression_with_unbound_variable_returns_none() {
1119        let evaluator = QueryEvaluator::new();
1120        let x = Variable::new("x").unwrap();
1121        let expr = Expression::from(x);
1122        let result = evaluator.evaluate_expression(&expr, std::iter::empty());
1123        assert!(result.is_none());
1124    }
1125
1126    #[test]
1127    fn evaluate_effective_boolean_value_expression_basic() {
1128        let evaluator = QueryEvaluator::new();
1129
1130        // Numeric EBV: 0 -> false, non-zero -> true
1131        let zero = Expression::from(Literal::from(0_i32));
1132        let five = Expression::from(Literal::from(5_i32));
1133        assert_eq!(
1134            evaluator.evaluate_effective_boolean_value_expression(&zero, std::iter::empty()),
1135            Some(false)
1136        );
1137        assert_eq!(
1138            evaluator.evaluate_effective_boolean_value_expression(&five, std::iter::empty()),
1139            Some(true)
1140        );
1141
1142        // String EBV: empty -> false, non-empty -> true
1143        let empty_str = Expression::from(Literal::from(""));
1144        let non_empty_str = Expression::from(Literal::from("a"));
1145        assert_eq!(
1146            evaluator.evaluate_effective_boolean_value_expression(&empty_str, std::iter::empty()),
1147            Some(false)
1148        );
1149        assert_eq!(
1150            evaluator
1151                .evaluate_effective_boolean_value_expression(&non_empty_str, std::iter::empty()),
1152            Some(true)
1153        );
1154    }
1155
1156    #[test]
1157    fn evaluate_effective_boolean_value_expression_exists() {
1158        let evaluator = QueryEvaluator::new();
1159
1160        // EXISTS {} (empty) -> false
1161        let exists_empty = Expression::exists(GraphPattern::empty());
1162        assert_eq!(
1163            evaluator
1164                .evaluate_effective_boolean_value_expression(&exists_empty, std::iter::empty()),
1165            Some(false)
1166        );
1167
1168        // EXISTS { VALUES () {} } (empty singleton) -> true
1169        let exists_unit = Expression::exists(GraphPattern::empty_singleton());
1170        assert_eq!(
1171            evaluator.evaluate_effective_boolean_value_expression(&exists_unit, std::iter::empty()),
1172            Some(true)
1173        );
1174    }
1175
1176    #[test]
1177    fn evaluate_effective_boolean_value_expression_non_boolean_term() {
1178        let evaluator = QueryEvaluator::new();
1179
1180        // NamedNode has no EBV
1181        let iri = NamedNode::new("http://example.com/").unwrap();
1182        let nn = Expression::from(iri.clone());
1183        assert_eq!(
1184            evaluator.evaluate_effective_boolean_value_expression(&nn, std::iter::empty()),
1185            None
1186        );
1187
1188        // dateTime literal has no EBV
1189        let dt = Literal::new_typed_literal("2020-01-01T00:00:00Z", xsd::DATE_TIME);
1190        let expr = Expression::from(dt);
1191        assert_eq!(
1192            evaluator.evaluate_effective_boolean_value_expression(&expr, std::iter::empty()),
1193            None
1194        );
1195    }
1196
1197    #[test]
1198    fn evaluate_effective_boolean_value_expression_boolean_lexical_forms() {
1199        let evaluator = QueryEvaluator::new();
1200        let one = Expression::from(Literal::new_typed_literal("1", xsd::BOOLEAN));
1201        let zero = Expression::from(Literal::new_typed_literal("0", xsd::BOOLEAN));
1202        assert_eq!(
1203            evaluator.evaluate_effective_boolean_value_expression(&one, std::iter::empty()),
1204            Some(true)
1205        );
1206        assert_eq!(
1207            evaluator.evaluate_effective_boolean_value_expression(&zero, std::iter::empty()),
1208            Some(false)
1209        );
1210    }
1211
1212    #[test]
1213    fn evaluate_effective_boolean_value_expression_logic_with_errors() {
1214        let evaluator = QueryEvaluator::new();
1215
1216        // OR(error, false) => error (None)
1217        let errorish = Expression::from(NamedNode::new("http://e/iri").unwrap());
1218        let or_expr = Expression::or_all([errorish, Expression::from(Literal::from(false))]);
1219        assert_eq!(
1220            evaluator.evaluate_effective_boolean_value_expression(&or_expr, std::iter::empty()),
1221            None
1222        );
1223
1224        // AND(false, error) => false
1225        let errorish = Expression::from(NamedNode::new("http://e/iri2").unwrap());
1226        let and_expr = Expression::and_all([Expression::from(Literal::from(false)), errorish]);
1227        assert_eq!(
1228            evaluator.evaluate_effective_boolean_value_expression(&and_expr, std::iter::empty()),
1229            Some(false)
1230        );
1231
1232        // AND(true, error) => error (None)
1233        let errorish = Expression::from(NamedNode::new("http://e/iri3").unwrap());
1234        let and_expr = Expression::and_all([Expression::from(Literal::from(true)), errorish]);
1235        assert_eq!(
1236            evaluator.evaluate_effective_boolean_value_expression(&and_expr, std::iter::empty()),
1237            None
1238        );
1239    }
1240
1241    #[test]
1242    fn evaluate_expression_equality_returns_boolean_literal() {
1243        let evaluator = QueryEvaluator::new();
1244        let eq = Expression::equal(
1245            Expression::from(Literal::from(1_i32)),
1246            Expression::from(Literal::from(1_i32)),
1247        );
1248        let term = evaluator.evaluate_expression(&eq, std::iter::empty());
1249        assert_eq!(term, Some(Term::from(Literal::from(true))));
1250    }
1251
1252    #[test]
1253    fn evaluate_expression_arithmetic_with_unbound_variable_is_none() {
1254        let evaluator = QueryEvaluator::new();
1255        let x = Variable::new("x").unwrap();
1256        let expr = Expression::Add(
1257            Box::new(Expression::from(Literal::from(2_i32))),
1258            Box::new(Expression::from(x)),
1259        );
1260        let result = evaluator.evaluate_expression(&expr, std::iter::empty());
1261        assert!(result.is_none());
1262    }
1263}