diff --git a/lib/src/model/literal.rs b/lib/src/model/literal.rs index 9616561a..ac76cb80 100644 --- a/lib/src/model/literal.rs +++ b/lib/src/model/literal.rs @@ -1,6 +1,7 @@ use crate::model::named_node::NamedNode; use crate::model::vocab::rdf; use crate::model::vocab::xsd; +use chrono::format::{parse, Parsed, StrftimeItems}; use chrono::prelude::*; use num_traits::identities::Zero; use num_traits::FromPrimitive; @@ -47,6 +48,7 @@ enum LiteralContent { Double(OrderedFloat), Integer(i128), Decimal(Decimal), + Date(Date), NaiveDate(NaiveDate), NaiveTime(NaiveTime), DateTime(DateTime), @@ -106,9 +108,21 @@ impl Literal { Err(_) => LiteralContent::TypedLiteral { value, datatype }, } } else if datatype == *xsd::DATE { - match NaiveDate::parse_from_str(&value, "%Y-%m-%d") { - Ok(value) => LiteralContent::NaiveDate(value), - Err(_) => LiteralContent::TypedLiteral { value, datatype }, + let mut parsed = Parsed::new(); + match parse(&mut parsed, &value, StrftimeItems::new("%Y-%m-%d%:z")).and_then(|_| { + Ok(Date::from_utc( + parsed.to_naive_date()?, + parsed.to_fixed_offset()?, + )) + }) { + Ok(value) => LiteralContent::Date(value), + Err(_) => match NaiveDate::parse_from_str(&value, "%Y-%m-%dZ") { + Ok(value) => LiteralContent::Date(Date::from_utc(value, FixedOffset::east(0))), + Err(_) => match NaiveDate::parse_from_str(&value, "%Y-%m-%d") { + Ok(value) => LiteralContent::NaiveDate(value), + Err(_) => LiteralContent::TypedLiteral { value, datatype }, + }, + }, } } else if datatype == *xsd::TIME { match NaiveTime::parse_from_str(&value, "%H:%M:%S") { @@ -152,6 +166,7 @@ impl Literal { LiteralContent::Double(value) => Cow::Owned(value.to_string()), LiteralContent::Integer(value) => Cow::Owned(value.to_string()), LiteralContent::Decimal(value) => Cow::Owned(value.to_string()), + LiteralContent::Date(value) => Cow::Owned(value.to_string()), LiteralContent::NaiveDate(value) => Cow::Owned(value.to_string()), LiteralContent::NaiveTime(value) => Cow::Owned(value.to_string()), LiteralContent::DateTime(value) => Cow::Owned(value.to_string()), @@ -183,7 +198,7 @@ impl Literal { LiteralContent::Double(_) => &xsd::DOUBLE, LiteralContent::Integer(_) => &xsd::INTEGER, LiteralContent::Decimal(_) => &xsd::DECIMAL, - LiteralContent::NaiveDate(_) => &xsd::DATE, + LiteralContent::Date(_) | LiteralContent::NaiveDate(_) => &xsd::DATE, LiteralContent::NaiveTime(_) => &xsd::TIME, LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => &xsd::DATE_TIME, LiteralContent::TypedLiteral { ref datatype, .. } => datatype, @@ -252,7 +267,7 @@ impl Literal { /// Checks if the literal has the datatype [xsd:date](http://www.w3.org/2001/XMLSchema#date) and is valid pub fn is_date(&self) -> bool { match self.0 { - LiteralContent::NaiveDate(_) => true, + LiteralContent::Date(_) | LiteralContent::NaiveDate(_) => true, _ => false, } } @@ -351,13 +366,22 @@ impl Literal { } /// Returns the value of this literal as NaiveDate if possible - pub(crate) fn to_date(&self) -> Option { + pub(crate) fn to_naive_date(&self) -> Option { match self.0 { + LiteralContent::Date(value) => Some(value.naive_utc()), LiteralContent::NaiveDate(value) => Some(value), _ => None, } } + /// Returns the value of this literal as Date if possible + pub(crate) fn to_date(&self) -> Option> { + match self.0 { + LiteralContent::Date(value) => Some(value), + _ => None, + } + } + /// Returns the value of this literal as NaiveTime if possible pub(crate) fn to_time(&self) -> Option { match self.0 { @@ -498,6 +522,12 @@ impl From for Literal { } } +impl From> for Literal { + fn from(value: Date) -> Self { + Literal(LiteralContent::Date(value)) + } +} + impl From for Literal { fn from(value: NaiveDate) -> Self { Literal(LiteralContent::NaiveDate(value)) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 31e0aff6..2d545d8a 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -319,31 +319,44 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { match expression { PlanExpression::Constant(t) => Some(*t), PlanExpression::Variable(v) => get_tuple_value(*v, tuple), - PlanExpression::Or(a, b) => match self.to_bool(self.eval_expression(a, tuple)?) { - Some(true) => Some(true.into()), - Some(false) => self.eval_expression(b, tuple), - None => match self.to_bool(self.eval_expression(b, tuple)?) { + PlanExpression::Or(a, b) => { + match self.eval_expression(a, tuple).and_then(|v| self.to_bool(v)) { Some(true) => Some(true.into()), - _ => None, - }, - }, - PlanExpression::And(a, b) => match self.to_bool(self.eval_expression(a, tuple)?) { + Some(false) => self.eval_expression(b, tuple), + None => { + if Some(true) + == self.eval_expression(b, tuple).and_then(|v| self.to_bool(v)) + { + Some(true.into()) + } else { + None + } + } + } + } + PlanExpression::And(a, b) => match self + .eval_expression(a, tuple) + .and_then(|v| self.to_bool(v)) + { Some(true) => self.eval_expression(b, tuple), Some(false) => Some(false.into()), - None => match self.to_bool(self.eval_expression(b, tuple)?) { - Some(false) => Some(false.into()), - _ => None, - }, + None => { + if Some(false) == self.eval_expression(b, tuple).and_then(|v| self.to_bool(v)) { + Some(false.into()) + } else { + None + } + } }, PlanExpression::Equal(a, b) => { let a = self.eval_expression(a, tuple)?; let b = self.eval_expression(b, tuple)?; - Some(self.equals(a, b).into()) + self.equals(a, b).map(|v| v.into()) } PlanExpression::NotEqual(a, b) => { let a = self.eval_expression(a, tuple)?; let b = self.eval_expression(b, tuple)?; - Some(self.not_equals(a, b).into()) + self.equals(a, b).map(|v| (!v).into()) } PlanExpression::Greater(a, b) => Some( (self.partial_cmp_literals( @@ -384,7 +397,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { let mut error = false; for possible in l { if let Some(possible) = self.eval_expression(possible, tuple) { - if self.equals(needed, possible) { + if Some(true) == self.equals(needed, possible) { return Some(true.into()); } } else { @@ -475,18 +488,21 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { None => Some(BlankNode::default().into()), }, PlanExpression::Year(e) => match self.eval_expression(e, tuple)? { + EncodedTerm::Date(date) => Some(date.year().into()), EncodedTerm::NaiveDate(date) => Some(date.year().into()), EncodedTerm::DateTime(date_time) => Some(date_time.year().into()), EncodedTerm::NaiveDateTime(date_time) => Some(date_time.year().into()), _ => None, }, PlanExpression::Month(e) => match self.eval_expression(e, tuple)? { + EncodedTerm::Date(date) => Some(date.year().into()), EncodedTerm::NaiveDate(date) => Some(date.month().into()), EncodedTerm::DateTime(date_time) => Some(date_time.month().into()), EncodedTerm::NaiveDateTime(date_time) => Some(date_time.month().into()), _ => None, }, PlanExpression::Day(e) => match self.eval_expression(e, tuple)? { + EncodedTerm::Date(date) => Some(date.year().into()), EncodedTerm::NaiveDate(date) => Some(date.day().into()), EncodedTerm::DateTime(date_time) => Some(date_time.day().into()), EncodedTerm::NaiveDateTime(date_time) => Some(date_time.day().into()), @@ -753,6 +769,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { EncodedTerm::DoubleLiteral(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::IntegerLiteral(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::DecimalLiteral(value) => self.store.insert_str(&value.to_string()).ok(), + EncodedTerm::Date(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::NaiveDate(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::NaiveTime(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::DateTime(value) => self.store.insert_str(&value.to_string()).ok(), @@ -874,12 +891,116 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) } - fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> bool { - (a == b || self.partial_cmp_literals(a, b) == Some(Ordering::Equal)) - } - - fn not_equals(&self, a: EncodedTerm, b: EncodedTerm) -> bool { - (a != b && self.partial_cmp_literals(a, b) != Some(Ordering::Equal)) + #[allow(clippy::float_cmp)] + fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> Option { + match a { + EncodedTerm::DefaultGraph + | EncodedTerm::NamedNode { .. } + | EncodedTerm::BlankNode(_) + | EncodedTerm::LangStringLiteral { .. } => Some(a == b), + EncodedTerm::StringLiteral { value_id: a } => match b { + EncodedTerm::StringLiteral { value_id: b } => Some(a == b), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::BooleanLiteral(a) => match b { + EncodedTerm::BooleanLiteral(b) => Some(a == b), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::FloatLiteral(a) => match b { + EncodedTerm::FloatLiteral(b) => Some(a == b), + EncodedTerm::DoubleLiteral(b) => Some(a.to_f64()? == *b), + EncodedTerm::IntegerLiteral(b) => Some(*a == b.to_f32()?), + EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_f32()?), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::DoubleLiteral(a) => match b { + EncodedTerm::FloatLiteral(b) => Some(*a == b.to_f64()?), + EncodedTerm::DoubleLiteral(b) => Some(a == b), + EncodedTerm::IntegerLiteral(b) => Some(*a == b.to_f64()?), + EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_f64()?), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::IntegerLiteral(a) => match b { + EncodedTerm::FloatLiteral(b) => Some(a.to_f32()? == *b), + EncodedTerm::DoubleLiteral(b) => Some(a.to_f64()? == *b), + EncodedTerm::IntegerLiteral(b) => Some(a == b), + EncodedTerm::DecimalLiteral(b) => Some(Decimal::from_i128(a)? == b), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::DecimalLiteral(a) => match b { + EncodedTerm::FloatLiteral(b) => Some(a.to_f32()? == *b), + EncodedTerm::DoubleLiteral(b) => Some(a.to_f64()? == *b), + EncodedTerm::IntegerLiteral(b) => Some(a == Decimal::from_i128(b)?), + EncodedTerm::DecimalLiteral(b) => Some(a == b), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::TypedLiteral { .. } => match b { + EncodedTerm::TypedLiteral { .. } if a == b => Some(true), + EncodedTerm::NamedNode { .. } + | EncodedTerm::BlankNode { .. } + | EncodedTerm::LangStringLiteral { .. } => Some(false), + _ => None, + }, + EncodedTerm::Date(a) => match b { + EncodedTerm::Date(b) => Some(a == b), + EncodedTerm::NaiveDate(b) => { + if a.naive_utc() == b { + None + } else { + Some(false) + } + } + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::NaiveDate(a) => match b { + EncodedTerm::NaiveDate(b) => Some(a == b), + EncodedTerm::Date(b) => { + if a == b.naive_utc() { + None + } else { + Some(false) + } + } + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::NaiveTime(a) => match b { + EncodedTerm::NaiveTime(b) => Some(a == b), + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::DateTime(a) => match b { + EncodedTerm::DateTime(b) => Some(a == b), + EncodedTerm::NaiveDateTime(b) => { + if a.naive_utc() == b { + None + } else { + Some(false) + } + } + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + EncodedTerm::NaiveDateTime(a) => match b { + EncodedTerm::NaiveDateTime(b) => Some(a == b), + EncodedTerm::DateTime(b) => { + if a == b.naive_utc() { + None + } else { + Some(false) + } + } + EncodedTerm::TypedLiteral { .. } => None, + _ => Some(false), + }, + } } fn cmp_according_to_expression( @@ -955,13 +1076,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b), _ => None, }, - EncodedTerm::NaiveDate(a) => { - if let EncodedTerm::NaiveDate(ref b) = b { - a.partial_cmp(b) - } else { - None - } - } + EncodedTerm::Date(a) => match b { + EncodedTerm::Date(ref b) => a.partial_cmp(b), + EncodedTerm::NaiveDate(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges + _ => None, + }, + EncodedTerm::NaiveDate(a) => match b { + EncodedTerm::NaiveDate(ref b) => a.partial_cmp(b), + EncodedTerm::Date(ref b) => a.partial_cmp(&b.naive_utc()), //TODO: check edges + _ => None, + }, EncodedTerm::NaiveTime(a) => { if let EncodedTerm::NaiveTime(ref b) = b { a.partial_cmp(b) @@ -969,20 +1093,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { None } } - EncodedTerm::DateTime(a) => { - if let EncodedTerm::DateTime(ref b) = b { - a.partial_cmp(b) - } else { - None - } - } - EncodedTerm::NaiveDateTime(a) => { - if let EncodedTerm::NaiveDateTime(ref b) = b { - a.partial_cmp(b) - } else { - None - } - } + EncodedTerm::DateTime(a) => match b { + EncodedTerm::DateTime(ref b) => a.partial_cmp(b), + EncodedTerm::NaiveDateTime(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges + _ => None, + }, + EncodedTerm::NaiveDateTime(a) => match b { + EncodedTerm::NaiveDateTime(ref b) => a.partial_cmp(b), + EncodedTerm::DateTime(ref b) => a.partial_cmp(&b.naive_utc()), //TODO: check edges + _ => None, + }, _ => None, } } diff --git a/lib/src/store/numeric_encoder.rs b/lib/src/store/numeric_encoder.rs index cea6ef61..fca8289f 100644 --- a/lib/src/store/numeric_encoder.rs +++ b/lib/src/store/numeric_encoder.rs @@ -126,8 +126,9 @@ const TYPE_INTEGER_LITERAL: u8 = 11; const TYPE_DECIMAL_LITERAL: u8 = 12; const TYPE_DATE_TIME_LITERAL: u8 = 13; const TYPE_NAIVE_DATE_TIME_LITERAL: u8 = 14; -const TYPE_NAIVE_DATE_LITERAL: u8 = 15; -const TYPE_NAIVE_TIME_LITERAL: u8 = 16; +const TYPE_DATE_LITERAL: u8 = 15; +const TYPE_NAIVE_DATE_LITERAL: u8 = 16; +const TYPE_NAIVE_TIME_LITERAL: u8 = 17; pub static ENCODED_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph; pub static ENCODED_EMPTY_STRING_LITERAL: EncodedTerm = EncodedTerm::StringLiteral { @@ -177,6 +178,7 @@ pub enum EncodedTerm { DoubleLiteral(OrderedFloat), IntegerLiteral(i128), DecimalLiteral(Decimal), + Date(Date), NaiveDate(NaiveDate), NaiveTime(NaiveTime), DateTime(DateTime), @@ -208,6 +210,7 @@ impl EncodedTerm { | EncodedTerm::DoubleLiteral(_) | EncodedTerm::IntegerLiteral(_) | EncodedTerm::DecimalLiteral(_) + | EncodedTerm::Date(_) | EncodedTerm::NaiveDate(_) | EncodedTerm::NaiveTime(_) | EncodedTerm::DateTime(_) @@ -228,6 +231,7 @@ impl EncodedTerm { EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE), EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE), EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_NAMED_NODE), + EncodedTerm::Date(..) => Some(ENCODED_XSD_DATE_NAMED_NODE), EncodedTerm::NaiveDate(..) => Some(ENCODED_XSD_DATE_NAMED_NODE), EncodedTerm::NaiveTime(..) => Some(ENCODED_XSD_TIME_NAMED_NODE), EncodedTerm::DateTime(..) | EncodedTerm::NaiveDateTime(..) => { @@ -251,6 +255,7 @@ impl EncodedTerm { EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL, EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL, EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL, + EncodedTerm::Date(_) => TYPE_DATE_LITERAL, EncodedTerm::NaiveDate(_) => TYPE_NAIVE_DATE_LITERAL, EncodedTerm::NaiveTime(_) => TYPE_NAIVE_TIME_LITERAL, EncodedTerm::DateTime(_) => TYPE_DATE_TIME_LITERAL, @@ -301,6 +306,12 @@ impl From for EncodedTerm { } } +impl From> for EncodedTerm { + fn from(value: Date) -> Self { + EncodedTerm::Date(value) + } +} + impl From for EncodedTerm { fn from(value: NaiveDate) -> Self { EncodedTerm::NaiveDate(value) @@ -401,6 +412,12 @@ impl TermReader for R { self.read_exact(&mut buffer)?; Ok(EncodedTerm::DecimalLiteral(Decimal::deserialize(buffer))) } + TYPE_DATE_LITERAL => Ok(EncodedTerm::Date(Date::from_utc( + NaiveDate::from_num_days_from_ce_opt(self.read_i32::()?) + .ok_or_else(|| format_err!("Invalid date serialization"))?, + FixedOffset::east_opt(self.read_i32::()?) + .ok_or_else(|| format_err!("Invalid timezone offset"))?, + ))), TYPE_NAIVE_DATE_LITERAL => Ok(EncodedTerm::NaiveDate( NaiveDate::from_num_days_from_ce_opt(self.read_i32::()?) .ok_or_else(|| format_err!("Invalid date serialization"))?, @@ -508,6 +525,10 @@ impl TermWriter for R { EncodedTerm::DoubleLiteral(value) => self.write_f64::(*value)?, EncodedTerm::IntegerLiteral(value) => self.write_i128::(value)?, EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?, + EncodedTerm::Date(value) => { + self.write_i32::(value.num_days_from_ce())?; + self.write_i32::(value.timezone().local_minus_utc())?; + } EncodedTerm::NaiveDate(value) => { self.write_i32::(value.num_days_from_ce())?; } @@ -608,10 +629,14 @@ impl Encoder { .ok_or_else(|| format_err!("decimal literal without decimal value"))? .into() } else if literal.is_date() { - literal - .to_date() - .ok_or_else(|| format_err!("date literal without date value"))? - .into() + if let Some(date) = literal.to_date() { + date.into() + } else { + literal + .to_naive_date() + .ok_or_else(|| format_err!("date literal without date value"))? + .into() + } } else if literal.is_time() { literal .to_time() @@ -808,6 +833,7 @@ impl Encoder { EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()), EncodedTerm::DecimalLiteral(value) => Ok(Literal::from(value).into()), + EncodedTerm::Date(value) => Ok(Literal::from(value).into()), EncodedTerm::NaiveDate(value) => Ok(Literal::from(value).into()), EncodedTerm::NaiveTime(value) => Ok(Literal::from(value).into()), EncodedTerm::DateTime(value) => Ok(Literal::from(value).into()), diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index 1c4bf7f2..44bacff3 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -50,7 +50,7 @@ fn sparql_w3c_syntax_testsuite() -> Result<()> { #[test] fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { - //TODO: dataset open-world + //TODO: dataset let manifest_10_urls = vec![ "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/algebra/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/ask/manifest.ttl", @@ -66,6 +66,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-ops/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/graph/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/i18n/manifest.ttl", + "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/reduced/manifest.ttl", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/regex/manifest.ttl", @@ -82,6 +83,8 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#dawg-str-2").unwrap(), NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-equals/manifest#eq-graph-1").unwrap(), NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-equals/manifest#eq-graph-2").unwrap(), + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-01").unwrap(), + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-04").unwrap(), //Multiple writing of the same xsd:double. Our system does strong normalization. NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#sameTerm").unwrap(), NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#sameTerm-simple").unwrap(), @@ -89,12 +92,15 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#sameTerm-not-eq").unwrap(), //Simple literal vs xsd:string. We apply RDF 1.1 NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-2").unwrap(), - //Test on curly brace scoping with OPTIONAL filter - NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional-filter/manifest#dawg-optional-filter-005-not-simplified").unwrap(), - //DATATYPE("foo"@en) returns rdf:langString in SPARQL 1.1 - NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#dawg-datatype-2").unwrap() + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-08").unwrap(), + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-10").unwrap(), + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-11").unwrap(), + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-12").unwrap(), + //DATATYPE("foo"@en) returns rdf:langString in RDF 1.1 + NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#dawg-datatype-2").unwrap(), ]; + let mut failed = Vec::default(); for test_result in manifest_10_urls .into_iter() .flat_map(|manifest| TestManifest::new(manifest)) @@ -119,17 +125,15 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { .connection()? .prepare_query(&read_file_to_string(&test.query)?, Some(&test.query)) { - Err(error) => assert!( - false, + Err(error) => failed.push(format!( "Failure to parse query of {} with error: {}", test, error - ), + )), Ok(query) => match query.exec() { - Err(error) => assert!( - false, + Err(error) => failed.push(format!( "Failure to execute query of {} with error: {}", test, error - ), + )), Ok(result) => { let expected_graph = load_sparql_query_result_graph(test.result.as_ref().unwrap())?; @@ -139,15 +143,13 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { .is_some(); let actual_graph = to_graph(result, with_order)?; if !actual_graph.is_isomorphic(&expected_graph) { - assert!( - false, - "Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", + failed.push(format!("Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", test, expected_graph, actual_graph, Query::parse(&read_file_to_string(&test.query)?, Some(&test.query)).unwrap(), repository_to_string(&repository) - ) + )) } } }, @@ -156,6 +158,12 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { assert!(false, "Not supported test: {}", test); } } + assert!( + failed.is_empty(), + "{} tests failed:\n{}", + failed.len(), + failed.join("\n") + ); Ok(()) }