Makes most open-word SPARQL tests pass

Do not make pass the one that distinguish simple literals and xsd:string
pull/10/head
Tpt 5 years ago
parent 8e87396d5e
commit 06c0773e5a
  1. 42
      lib/src/model/literal.rs
  2. 204
      lib/src/sparql/eval.rs
  3. 38
      lib/src/store/numeric_encoder.rs
  4. 38
      lib/tests/sparql_test_cases.rs

@ -1,6 +1,7 @@
use crate::model::named_node::NamedNode; use crate::model::named_node::NamedNode;
use crate::model::vocab::rdf; use crate::model::vocab::rdf;
use crate::model::vocab::xsd; use crate::model::vocab::xsd;
use chrono::format::{parse, Parsed, StrftimeItems};
use chrono::prelude::*; use chrono::prelude::*;
use num_traits::identities::Zero; use num_traits::identities::Zero;
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
@ -47,6 +48,7 @@ enum LiteralContent {
Double(OrderedFloat<f64>), Double(OrderedFloat<f64>),
Integer(i128), Integer(i128),
Decimal(Decimal), Decimal(Decimal),
Date(Date<FixedOffset>),
NaiveDate(NaiveDate), NaiveDate(NaiveDate),
NaiveTime(NaiveTime), NaiveTime(NaiveTime),
DateTime(DateTime<FixedOffset>), DateTime(DateTime<FixedOffset>),
@ -106,9 +108,21 @@ impl Literal {
Err(_) => LiteralContent::TypedLiteral { value, datatype }, Err(_) => LiteralContent::TypedLiteral { value, datatype },
} }
} else if datatype == *xsd::DATE { } else if datatype == *xsd::DATE {
match NaiveDate::parse_from_str(&value, "%Y-%m-%d") { let mut parsed = Parsed::new();
Ok(value) => LiteralContent::NaiveDate(value), match parse(&mut parsed, &value, StrftimeItems::new("%Y-%m-%d%:z")).and_then(|_| {
Err(_) => LiteralContent::TypedLiteral { value, datatype }, 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 { } else if datatype == *xsd::TIME {
match NaiveTime::parse_from_str(&value, "%H:%M:%S") { match NaiveTime::parse_from_str(&value, "%H:%M:%S") {
@ -152,6 +166,7 @@ impl Literal {
LiteralContent::Double(value) => Cow::Owned(value.to_string()), LiteralContent::Double(value) => Cow::Owned(value.to_string()),
LiteralContent::Integer(value) => Cow::Owned(value.to_string()), LiteralContent::Integer(value) => Cow::Owned(value.to_string()),
LiteralContent::Decimal(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::NaiveDate(value) => Cow::Owned(value.to_string()),
LiteralContent::NaiveTime(value) => Cow::Owned(value.to_string()), LiteralContent::NaiveTime(value) => Cow::Owned(value.to_string()),
LiteralContent::DateTime(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::Double(_) => &xsd::DOUBLE,
LiteralContent::Integer(_) => &xsd::INTEGER, LiteralContent::Integer(_) => &xsd::INTEGER,
LiteralContent::Decimal(_) => &xsd::DECIMAL, LiteralContent::Decimal(_) => &xsd::DECIMAL,
LiteralContent::NaiveDate(_) => &xsd::DATE, LiteralContent::Date(_) | LiteralContent::NaiveDate(_) => &xsd::DATE,
LiteralContent::NaiveTime(_) => &xsd::TIME, LiteralContent::NaiveTime(_) => &xsd::TIME,
LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => &xsd::DATE_TIME, LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => &xsd::DATE_TIME,
LiteralContent::TypedLiteral { ref datatype, .. } => datatype, 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 /// 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 { pub fn is_date(&self) -> bool {
match self.0 { match self.0 {
LiteralContent::NaiveDate(_) => true, LiteralContent::Date(_) | LiteralContent::NaiveDate(_) => true,
_ => false, _ => false,
} }
} }
@ -351,13 +366,22 @@ impl Literal {
} }
/// Returns the value of this literal as NaiveDate if possible /// Returns the value of this literal as NaiveDate if possible
pub(crate) fn to_date(&self) -> Option<NaiveDate> { pub(crate) fn to_naive_date(&self) -> Option<NaiveDate> {
match self.0 { match self.0 {
LiteralContent::Date(value) => Some(value.naive_utc()),
LiteralContent::NaiveDate(value) => Some(value), LiteralContent::NaiveDate(value) => Some(value),
_ => None, _ => None,
} }
} }
/// Returns the value of this literal as Date if possible
pub(crate) fn to_date(&self) -> Option<Date<FixedOffset>> {
match self.0 {
LiteralContent::Date(value) => Some(value),
_ => None,
}
}
/// Returns the value of this literal as NaiveTime if possible /// Returns the value of this literal as NaiveTime if possible
pub(crate) fn to_time(&self) -> Option<NaiveTime> { pub(crate) fn to_time(&self) -> Option<NaiveTime> {
match self.0 { match self.0 {
@ -498,6 +522,12 @@ impl From<Decimal> for Literal {
} }
} }
impl From<Date<FixedOffset>> for Literal {
fn from(value: Date<FixedOffset>) -> Self {
Literal(LiteralContent::Date(value))
}
}
impl From<NaiveDate> for Literal { impl From<NaiveDate> for Literal {
fn from(value: NaiveDate) -> Self { fn from(value: NaiveDate) -> Self {
Literal(LiteralContent::NaiveDate(value)) Literal(LiteralContent::NaiveDate(value))

@ -319,31 +319,44 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
match expression { match expression {
PlanExpression::Constant(t) => Some(*t), PlanExpression::Constant(t) => Some(*t),
PlanExpression::Variable(v) => get_tuple_value(*v, tuple), PlanExpression::Variable(v) => get_tuple_value(*v, tuple),
PlanExpression::Or(a, b) => match self.to_bool(self.eval_expression(a, tuple)?) { PlanExpression::Or(a, b) => {
Some(true) => Some(true.into()), match self.eval_expression(a, tuple).and_then(|v| self.to_bool(v)) {
Some(false) => self.eval_expression(b, tuple),
None => match self.to_bool(self.eval_expression(b, tuple)?) {
Some(true) => Some(true.into()), Some(true) => Some(true.into()),
_ => None, Some(false) => self.eval_expression(b, tuple),
}, None => {
}, if Some(true)
PlanExpression::And(a, b) => match self.to_bool(self.eval_expression(a, tuple)?) { == 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(true) => self.eval_expression(b, tuple),
Some(false) => Some(false.into()), Some(false) => Some(false.into()),
None => match self.to_bool(self.eval_expression(b, tuple)?) { None => {
Some(false) => Some(false.into()), if Some(false) == self.eval_expression(b, tuple).and_then(|v| self.to_bool(v)) {
_ => None, Some(false.into())
}, } else {
None
}
}
}, },
PlanExpression::Equal(a, b) => { PlanExpression::Equal(a, b) => {
let a = self.eval_expression(a, tuple)?; let a = self.eval_expression(a, tuple)?;
let b = self.eval_expression(b, 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) => { PlanExpression::NotEqual(a, b) => {
let a = self.eval_expression(a, tuple)?; let a = self.eval_expression(a, tuple)?;
let b = self.eval_expression(b, 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( PlanExpression::Greater(a, b) => Some(
(self.partial_cmp_literals( (self.partial_cmp_literals(
@ -384,7 +397,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
let mut error = false; let mut error = false;
for possible in l { for possible in l {
if let Some(possible) = self.eval_expression(possible, tuple) { 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()); return Some(true.into());
} }
} else { } else {
@ -475,18 +488,21 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
None => Some(BlankNode::default().into()), None => Some(BlankNode::default().into()),
}, },
PlanExpression::Year(e) => match self.eval_expression(e, tuple)? { PlanExpression::Year(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::Date(date) => Some(date.year().into()),
EncodedTerm::NaiveDate(date) => Some(date.year().into()), EncodedTerm::NaiveDate(date) => Some(date.year().into()),
EncodedTerm::DateTime(date_time) => Some(date_time.year().into()), EncodedTerm::DateTime(date_time) => Some(date_time.year().into()),
EncodedTerm::NaiveDateTime(date_time) => Some(date_time.year().into()), EncodedTerm::NaiveDateTime(date_time) => Some(date_time.year().into()),
_ => None, _ => None,
}, },
PlanExpression::Month(e) => match self.eval_expression(e, tuple)? { PlanExpression::Month(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::Date(date) => Some(date.year().into()),
EncodedTerm::NaiveDate(date) => Some(date.month().into()), EncodedTerm::NaiveDate(date) => Some(date.month().into()),
EncodedTerm::DateTime(date_time) => Some(date_time.month().into()), EncodedTerm::DateTime(date_time) => Some(date_time.month().into()),
EncodedTerm::NaiveDateTime(date_time) => Some(date_time.month().into()), EncodedTerm::NaiveDateTime(date_time) => Some(date_time.month().into()),
_ => None, _ => None,
}, },
PlanExpression::Day(e) => match self.eval_expression(e, tuple)? { PlanExpression::Day(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::Date(date) => Some(date.year().into()),
EncodedTerm::NaiveDate(date) => Some(date.day().into()), EncodedTerm::NaiveDate(date) => Some(date.day().into()),
EncodedTerm::DateTime(date_time) => Some(date_time.day().into()), EncodedTerm::DateTime(date_time) => Some(date_time.day().into()),
EncodedTerm::NaiveDateTime(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<S> {
EncodedTerm::DoubleLiteral(value) => self.store.insert_str(&value.to_string()).ok(), EncodedTerm::DoubleLiteral(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::IntegerLiteral(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::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::NaiveDate(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::NaiveTime(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(), EncodedTerm::DateTime(value) => self.store.insert_str(&value.to_string()).ok(),
@ -874,12 +891,116 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
) )
} }
fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> bool { #[allow(clippy::float_cmp)]
(a == b || self.partial_cmp_literals(a, b) == Some(Ordering::Equal)) fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> Option<bool> {
} match a {
EncodedTerm::DefaultGraph
fn not_equals(&self, a: EncodedTerm, b: EncodedTerm) -> bool { | EncodedTerm::NamedNode { .. }
(a != b && self.partial_cmp_literals(a, b) != Some(Ordering::Equal)) | 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( fn cmp_according_to_expression(
@ -955,13 +1076,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b), EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b),
_ => None, _ => None,
}, },
EncodedTerm::NaiveDate(a) => { EncodedTerm::Date(a) => match b {
if let EncodedTerm::NaiveDate(ref b) = b { EncodedTerm::Date(ref b) => a.partial_cmp(b),
a.partial_cmp(b) EncodedTerm::NaiveDate(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges
} else { _ => None,
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) => { EncodedTerm::NaiveTime(a) => {
if let EncodedTerm::NaiveTime(ref b) = b { if let EncodedTerm::NaiveTime(ref b) = b {
a.partial_cmp(b) a.partial_cmp(b)
@ -969,20 +1093,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
None None
} }
} }
EncodedTerm::DateTime(a) => { EncodedTerm::DateTime(a) => match b {
if let EncodedTerm::DateTime(ref b) = b { EncodedTerm::DateTime(ref b) => a.partial_cmp(b),
a.partial_cmp(b) EncodedTerm::NaiveDateTime(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges
} else { _ => None,
None },
} EncodedTerm::NaiveDateTime(a) => match b {
} EncodedTerm::NaiveDateTime(ref b) => a.partial_cmp(b),
EncodedTerm::NaiveDateTime(a) => { EncodedTerm::DateTime(ref b) => a.partial_cmp(&b.naive_utc()), //TODO: check edges
if let EncodedTerm::NaiveDateTime(ref b) = b { _ => None,
a.partial_cmp(b) },
} else {
None
}
}
_ => None, _ => None,
} }
} }

@ -126,8 +126,9 @@ const TYPE_INTEGER_LITERAL: u8 = 11;
const TYPE_DECIMAL_LITERAL: u8 = 12; const TYPE_DECIMAL_LITERAL: u8 = 12;
const TYPE_DATE_TIME_LITERAL: u8 = 13; const TYPE_DATE_TIME_LITERAL: u8 = 13;
const TYPE_NAIVE_DATE_TIME_LITERAL: u8 = 14; const TYPE_NAIVE_DATE_TIME_LITERAL: u8 = 14;
const TYPE_NAIVE_DATE_LITERAL: u8 = 15; const TYPE_DATE_LITERAL: u8 = 15;
const TYPE_NAIVE_TIME_LITERAL: u8 = 16; 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_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph;
pub static ENCODED_EMPTY_STRING_LITERAL: EncodedTerm = EncodedTerm::StringLiteral { pub static ENCODED_EMPTY_STRING_LITERAL: EncodedTerm = EncodedTerm::StringLiteral {
@ -177,6 +178,7 @@ pub enum EncodedTerm {
DoubleLiteral(OrderedFloat<f64>), DoubleLiteral(OrderedFloat<f64>),
IntegerLiteral(i128), IntegerLiteral(i128),
DecimalLiteral(Decimal), DecimalLiteral(Decimal),
Date(Date<FixedOffset>),
NaiveDate(NaiveDate), NaiveDate(NaiveDate),
NaiveTime(NaiveTime), NaiveTime(NaiveTime),
DateTime(DateTime<FixedOffset>), DateTime(DateTime<FixedOffset>),
@ -208,6 +210,7 @@ impl EncodedTerm {
| EncodedTerm::DoubleLiteral(_) | EncodedTerm::DoubleLiteral(_)
| EncodedTerm::IntegerLiteral(_) | EncodedTerm::IntegerLiteral(_)
| EncodedTerm::DecimalLiteral(_) | EncodedTerm::DecimalLiteral(_)
| EncodedTerm::Date(_)
| EncodedTerm::NaiveDate(_) | EncodedTerm::NaiveDate(_)
| EncodedTerm::NaiveTime(_) | EncodedTerm::NaiveTime(_)
| EncodedTerm::DateTime(_) | EncodedTerm::DateTime(_)
@ -228,6 +231,7 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE), EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE),
EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE), EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE),
EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_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::NaiveDate(..) => Some(ENCODED_XSD_DATE_NAMED_NODE),
EncodedTerm::NaiveTime(..) => Some(ENCODED_XSD_TIME_NAMED_NODE), EncodedTerm::NaiveTime(..) => Some(ENCODED_XSD_TIME_NAMED_NODE),
EncodedTerm::DateTime(..) | EncodedTerm::NaiveDateTime(..) => { EncodedTerm::DateTime(..) | EncodedTerm::NaiveDateTime(..) => {
@ -251,6 +255,7 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL, EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL,
EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL, EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL,
EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL, EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL,
EncodedTerm::Date(_) => TYPE_DATE_LITERAL,
EncodedTerm::NaiveDate(_) => TYPE_NAIVE_DATE_LITERAL, EncodedTerm::NaiveDate(_) => TYPE_NAIVE_DATE_LITERAL,
EncodedTerm::NaiveTime(_) => TYPE_NAIVE_TIME_LITERAL, EncodedTerm::NaiveTime(_) => TYPE_NAIVE_TIME_LITERAL,
EncodedTerm::DateTime(_) => TYPE_DATE_TIME_LITERAL, EncodedTerm::DateTime(_) => TYPE_DATE_TIME_LITERAL,
@ -301,6 +306,12 @@ impl From<Decimal> for EncodedTerm {
} }
} }
impl From<Date<FixedOffset>> for EncodedTerm {
fn from(value: Date<FixedOffset>) -> Self {
EncodedTerm::Date(value)
}
}
impl From<NaiveDate> for EncodedTerm { impl From<NaiveDate> for EncodedTerm {
fn from(value: NaiveDate) -> Self { fn from(value: NaiveDate) -> Self {
EncodedTerm::NaiveDate(value) EncodedTerm::NaiveDate(value)
@ -401,6 +412,12 @@ impl<R: Read> TermReader for R {
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::deserialize(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::<LittleEndian>()?)
.ok_or_else(|| format_err!("Invalid date serialization"))?,
FixedOffset::east_opt(self.read_i32::<LittleEndian>()?)
.ok_or_else(|| format_err!("Invalid timezone offset"))?,
))),
TYPE_NAIVE_DATE_LITERAL => Ok(EncodedTerm::NaiveDate( TYPE_NAIVE_DATE_LITERAL => Ok(EncodedTerm::NaiveDate(
NaiveDate::from_num_days_from_ce_opt(self.read_i32::<LittleEndian>()?) NaiveDate::from_num_days_from_ce_opt(self.read_i32::<LittleEndian>()?)
.ok_or_else(|| format_err!("Invalid date serialization"))?, .ok_or_else(|| format_err!("Invalid date serialization"))?,
@ -508,6 +525,10 @@ impl<R: Write> TermWriter for R {
EncodedTerm::DoubleLiteral(value) => self.write_f64::<LittleEndian>(*value)?, EncodedTerm::DoubleLiteral(value) => self.write_f64::<LittleEndian>(*value)?,
EncodedTerm::IntegerLiteral(value) => self.write_i128::<LittleEndian>(value)?, EncodedTerm::IntegerLiteral(value) => self.write_i128::<LittleEndian>(value)?,
EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?, EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?,
EncodedTerm::Date(value) => {
self.write_i32::<LittleEndian>(value.num_days_from_ce())?;
self.write_i32::<LittleEndian>(value.timezone().local_minus_utc())?;
}
EncodedTerm::NaiveDate(value) => { EncodedTerm::NaiveDate(value) => {
self.write_i32::<LittleEndian>(value.num_days_from_ce())?; self.write_i32::<LittleEndian>(value.num_days_from_ce())?;
} }
@ -608,10 +629,14 @@ impl<S: StringStore> Encoder<S> {
.ok_or_else(|| format_err!("decimal literal without decimal value"))? .ok_or_else(|| format_err!("decimal literal without decimal value"))?
.into() .into()
} else if literal.is_date() { } else if literal.is_date() {
literal if let Some(date) = literal.to_date() {
.to_date() date.into()
.ok_or_else(|| format_err!("date literal without date value"))? } else {
.into() literal
.to_naive_date()
.ok_or_else(|| format_err!("date literal without date value"))?
.into()
}
} else if literal.is_time() { } else if literal.is_time() {
literal literal
.to_time() .to_time()
@ -808,6 +833,7 @@ impl<S: StringStore> Encoder<S> {
EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()), EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DecimalLiteral(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::NaiveDate(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveTime(value) => Ok(Literal::from(value).into()), EncodedTerm::NaiveTime(value) => Ok(Literal::from(value).into()),
EncodedTerm::DateTime(value) => Ok(Literal::from(value).into()), EncodedTerm::DateTime(value) => Ok(Literal::from(value).into()),

@ -50,7 +50,7 @@ fn sparql_w3c_syntax_testsuite() -> Result<()> {
#[test] #[test]
fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
//TODO: dataset open-world //TODO: dataset
let manifest_10_urls = vec![ 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/algebra/manifest.ttl",
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/ask/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/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/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/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/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/reduced/manifest.ttl",
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/regex/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-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-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/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. //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").unwrap(),
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest#sameTerm-simple").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(), 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 //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(), 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/open-world/manifest#open-eq-08").unwrap(),
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional-filter/manifest#dawg-optional-filter-005-not-simplified").unwrap(), NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#open-eq-10").unwrap(),
//DATATYPE("foo"@en) returns rdf:langString in SPARQL 1.1 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/expr-builtin/manifest#dawg-datatype-2").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 for test_result in manifest_10_urls
.into_iter() .into_iter()
.flat_map(|manifest| TestManifest::new(manifest)) .flat_map(|manifest| TestManifest::new(manifest))
@ -119,17 +125,15 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
.connection()? .connection()?
.prepare_query(&read_file_to_string(&test.query)?, Some(&test.query)) .prepare_query(&read_file_to_string(&test.query)?, Some(&test.query))
{ {
Err(error) => assert!( Err(error) => failed.push(format!(
false,
"Failure to parse query of {} with error: {}", "Failure to parse query of {} with error: {}",
test, error test, error
), )),
Ok(query) => match query.exec() { Ok(query) => match query.exec() {
Err(error) => assert!( Err(error) => failed.push(format!(
false,
"Failure to execute query of {} with error: {}", "Failure to execute query of {} with error: {}",
test, error test, error
), )),
Ok(result) => { Ok(result) => {
let expected_graph = let expected_graph =
load_sparql_query_result_graph(test.result.as_ref().unwrap())?; load_sparql_query_result_graph(test.result.as_ref().unwrap())?;
@ -139,15 +143,13 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
.is_some(); .is_some();
let actual_graph = to_graph(result, with_order)?; let actual_graph = to_graph(result, with_order)?;
if !actual_graph.is_isomorphic(&expected_graph) { if !actual_graph.is_isomorphic(&expected_graph) {
assert!( failed.push(format!("Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n",
false,
"Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n",
test, test,
expected_graph, expected_graph,
actual_graph, actual_graph,
Query::parse(&read_file_to_string(&test.query)?, Some(&test.query)).unwrap(), Query::parse(&read_file_to_string(&test.query)?, Some(&test.query)).unwrap(),
repository_to_string(&repository) repository_to_string(&repository)
) ))
} }
} }
}, },
@ -156,6 +158,12 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
assert!(false, "Not supported test: {}", test); assert!(false, "Not supported test: {}", test);
} }
} }
assert!(
failed.is_empty(),
"{} tests failed:\n{}",
failed.len(),
failed.join("\n")
);
Ok(()) Ok(())
} }

Loading…
Cancel
Save