diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 989dcb90..bb1738ca 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -1462,6 +1462,21 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { PlanExpression::StringCast(e) => Some(EncodedTerm::StringLiteral { value_id: self.to_string_id(self.eval_expression(e, tuple, options)?)?, }), + PlanExpression::CustomFunction { name, parameters } => { + println!("name: {:?}", name); + println!("parameters: {:?}", parameters); + println!("tuple: {:?}", tuple); + let parameters = parameters + .iter() + .map(|p| { + self.eval_expression(p, tuple, options) + .and_then(|encoded| self.dataset.decode_term(encoded).ok()) + }) + .collect::>(); + + println!("parameters: {:?}", parameters); + None + } } } diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 9b84a8aa..f675f832 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,3 +1,4 @@ +use crate::model::NamedNode; use crate::sparql::GraphPattern; use crate::sparql::model::Variable; use crate::sparql::eval::StringOrStoreString; @@ -306,6 +307,10 @@ pub enum PlanExpression { TimeCast(Box), DateTimeCast(Box), StringCast(Box), + CustomFunction { + name: NamedNode, + parameters: Vec + }, } impl PlanExpression { @@ -415,6 +420,11 @@ impl PlanExpression { e.add_variables(set); } } + PlanExpression::CustomFunction{ parameters, .. } => { + for p in parameters { + p.add_variables(set); + } + } PlanExpression::Exists(n) => n.add_variables(set), } } diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index e75051e4..f1c64364 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -644,7 +644,8 @@ impl PlanBuilder { "string", )? } else { - return Err(format_err!("Not supported custom function {}", expression)); + let parameters = self.expression_list(parameters, variables, graph_name)?; + PlanExpression::CustomFunction { name: name.clone(), parameters } } } }, diff --git a/lib/tests/custom_functions.rs b/lib/tests/custom_functions.rs new file mode 100644 index 00000000..ac8552ad --- /dev/null +++ b/lib/tests/custom_functions.rs @@ -0,0 +1,69 @@ +use rudf::model::*; +use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result}; +use rudf::sparql::{BindingsIterator, PreparedQuery, QueryOptions, QueryResult}; +use failure::format_err; +use std::io::BufRead; + + + +#[test] +fn simple_custom_function_test() { + + let query = r#" + PREFIX ex: + SELECT ?name ?reverse + WHERE + { + ?s ?name . + BIND(ex:REVERSE(?name) as ?reverse) + } + ORDER BY ?name + "#.to_string(); + + + let options = QueryOptions::default(); + let triples = br#" + "Bob" . + "Alice" . + . + . + "#.as_ref(); + let results = do_query(triples, query, options).unwrap(); + let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); + let solution = vec![ + vec![ Some(literal(String::from("Alice"))), Some(literal(String::from("ecilA"))) ], + vec![ Some(literal(String::from("Bob"))), Some(literal(String::from("boB"))) ], + ]; + assert_eq!(collected, solution); + +} + +fn literal(str: String) -> Term { + Term::Literal(Literal::new_simple_literal(str)) +} + +fn make_repository(reader: impl BufRead) -> Result { + let repository = MemoryRepository::default(); + let mut connection = repository.connection()?; + connection.load_graph(reader, GraphSyntax::NTriples, None, None).unwrap(); + Ok(repository) +} + +fn query_repository<'a>(repository: MemoryRepository, query: String, options: QueryOptions<'a>) -> Result> { + let connection = repository.connection()?; + let prepared_query = connection.prepare_query(&query, None)?; + let result = prepared_query.exec(&options)?; + match result { + QueryResult::Bindings(iterator) => { + let (varaibles, iter) = iterator.destruct(); + let collected = iter.collect::>(); + Ok(BindingsIterator::new(varaibles, Box::new(collected.into_iter()))) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } +} + +fn do_query<'a>(reader: impl BufRead, query: String, options: QueryOptions<'a>) -> Result> { + let repository = make_repository(reader)?; + query_repository(repository, query, options) +}