Adds native xsd:duration xsd:dateTime xsd:date and xsd:time structures

pull/22/head
Tpt 5 years ago
parent 05b8a5ac55
commit 10b1fa68f3
  1. 8
      lib/Cargo.toml
  2. 34
      lib/src/model/literal.rs
  3. 1613
      lib/src/model/xsd/date_time.rs
  4. 232
      lib/src/model/xsd/decimal.rs
  5. 382
      lib/src/model/xsd/duration.rs
  6. 8
      lib/src/model/xsd/mod.rs
  7. 462
      lib/src/model/xsd/parser.rs
  8. 329
      lib/src/sparql/eval.rs
  9. 1
      lib/src/sparql/plan.rs
  10. 8
      lib/src/sparql/plan_builder.rs
  11. 1
      lib/src/store/mod.rs
  12. 214
      lib/src/store/numeric_encoder.rs
  13. 2
      lib/tests/sparql_test_cases.rs

@ -18,7 +18,6 @@ rocksdb = { version = "0.13", optional = true }
byteorder = { version = "1", features = ["i128"] }
quick-xml = "0.17"
num-traits = "0.2"
chrono = "0.4"
rand = "0.7"
md-5 = "0.8"
sha-1 = "0.8"
@ -26,10 +25,11 @@ sha2 = "0.8"
digest = "0.8"
failure = "0.1"
regex = "1"
rio_api = "0.4"
rio_turtle = "0.4"
rio_xml = "0.4"
rio_api = "0.3"
rio_turtle = "0.3"
rio_xml = "0.3"
hex = "0.4"
nom = "5"
[dev-dependencies]
rayon = "1"

@ -1,8 +1,7 @@
use crate::model::named_node::NamedNode;
use crate::model::vocab::rdf;
use crate::model::vocab::xsd;
use crate::model::xsd::Decimal;
use chrono::prelude::*;
use crate::model::xsd::*;
use rio_api::model as rio;
use std::borrow::Cow;
use std::fmt;
@ -237,8 +236,8 @@ impl From<Decimal> for Literal {
}
}
impl From<Date<FixedOffset>> for Literal {
fn from(value: Date<FixedOffset>) -> Self {
impl From<Date> for Literal {
fn from(value: Date) -> Self {
Literal(LiteralContent::TypedLiteral {
value: value.to_string(),
datatype: xsd::DATE.clone(),
@ -246,17 +245,8 @@ impl From<Date<FixedOffset>> for Literal {
}
}
impl From<NaiveDate> for Literal {
fn from(value: NaiveDate) -> Self {
Literal(LiteralContent::TypedLiteral {
value: value.to_string(),
datatype: xsd::DATE.clone(),
})
}
}
impl From<NaiveTime> for Literal {
fn from(value: NaiveTime) -> Self {
impl From<Time> for Literal {
fn from(value: Time) -> Self {
Literal(LiteralContent::TypedLiteral {
value: value.to_string(),
datatype: xsd::TIME.clone(),
@ -264,20 +254,20 @@ impl From<NaiveTime> for Literal {
}
}
impl From<DateTime<FixedOffset>> for Literal {
fn from(value: DateTime<FixedOffset>) -> Self {
impl From<DateTime> for Literal {
fn from(value: DateTime) -> Self {
Literal(LiteralContent::TypedLiteral {
value: value.to_rfc3339(),
value: value.to_string(),
datatype: xsd::DATE_TIME.clone(),
})
}
}
impl From<NaiveDateTime> for Literal {
fn from(value: NaiveDateTime) -> Self {
impl From<Duration> for Literal {
fn from(value: Duration) -> Self {
Literal(LiteralContent::TypedLiteral {
value: value.format("%Y-%m-%dT%H:%M:%S%.f").to_string(),
datatype: xsd::DATE_TIME.clone(),
value: value.to_string(),
datatype: xsd::DURATION.clone(),
})
}
}

File diff suppressed because it is too large Load Diff

@ -2,6 +2,7 @@ use std::convert::{TryFrom, TryInto};
use std::error::Error;
use std::fmt;
use std::fmt::Write;
use std::i128;
use std::ops::Neg;
use std::str::FromStr;
@ -10,6 +11,13 @@ const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000;
const DECIMAL_PART_POW_MINUS_ONE: i128 = 100_000_000_000_000_000;
const DECIMAL_PART_HALF_POW: i128 = 1_000_000_000;
#[cfg(test)]
pub(super) const MIN: Decimal = Decimal { value: i128::MIN };
#[cfg(test)]
pub(super) const MAX: Decimal = Decimal { value: i128::MAX };
#[cfg(test)]
pub(super) const STEP: Decimal = Decimal { value: 1 };
/// [XML Schema `decimal` datatype](https://www.w3.org/TR/xmlschema11-2/#decimal) implementation.
///
/// It stores the decimal in a fix point encoding allowing nearly 18 digits before and 18 digits after ".".
@ -19,6 +27,17 @@ pub struct Decimal {
}
impl Decimal {
/// Constructs the decimal i / 10^n
pub fn new(i: i128, n: u32) -> Result<Self, DecimalOverflowError> {
if n > DECIMAL_PART_DIGITS as u32 {
//TODO: check if end with zeros?
return Err(DecimalOverflowError);
}
Ok(Self {
value: i.checked_div(10i128.pow(n)).ok_or(DecimalOverflowError)?,
})
}
#[inline]
pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
Self {
@ -27,15 +46,88 @@ impl Decimal {
}
}
impl<I: Into<i64>> From<I> for Decimal {
impl From<i8> for Decimal {
#[inline]
fn from(value: I) -> Self {
let value: i64 = value.into();
fn from(value: i8) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i16> for Decimal {
fn from(value: i16) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i32> for Decimal {
fn from(value: i32) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<i64> for Decimal {
fn from(value: i64) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u8> for Decimal {
fn from(value: u8) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u16> for Decimal {
fn from(value: u16) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u32> for Decimal {
fn from(value: u32) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl From<u64> for Decimal {
fn from(value: u64) -> Self {
Self {
value: i128::from(value) * DECIMAL_PART_POW,
}
}
}
impl TryFrom<i128> for Decimal {
type Error = DecimalOverflowError;
fn try_from(value: i128) -> Result<Self, DecimalOverflowError> {
Ok(Self {
value: value
.checked_mul(DECIMAL_PART_POW)
.ok_or(DecimalOverflowError)?,
})
}
}
impl TryFrom<u128> for Decimal {
type Error = DecimalOverflowError;
fn try_from(value: u128) -> Result<Self, DecimalOverflowError> {
Ok(Self {
value: i128::try_from(value)
.map_err(|_| DecimalOverflowError)?
.checked_mul(DECIMAL_PART_POW)
.ok_or(DecimalOverflowError)?,
})
}
}
impl FromStr for Decimal {
type Err = ParseDecimalError;
@ -146,11 +238,41 @@ impl fmt::Display for ParseDecimalError {
impl Error for ParseDecimalError {}
impl From<DecimalOverflowError> for ParseDecimalError {
fn from(_: DecimalOverflowError) -> Self {
Self {
kind: ParseDecimalErrorKind::Overflow,
}
}
}
#[derive(Debug, Clone)]
pub struct DecimalOverflowError;
impl fmt::Display for DecimalOverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Value overflow")
}
}
impl Error for DecimalOverflowError {}
impl fmt::Display for Decimal {
/// Formats the decimal following its canonical representation
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.value == 0 {
return if let Some(width) = f.width() {
for _ in 0..width {
f.write_char('0')?;
}
Ok(())
} else {
f.write_char('0')
};
}
let mut value = self.value;
if value < 0 {
if self.value.is_negative() {
f.write_char('-')?;
}
@ -162,10 +284,6 @@ impl fmt::Display for Decimal {
i += 1;
}
if i == 0 {
return f.write_char('0');
}
let last_non_zero = i - 1;
let first_non_zero = digits
.iter()
@ -176,7 +294,19 @@ impl fmt::Display for Decimal {
.unwrap_or(40);
if last_non_zero >= DECIMAL_PART_DIGITS {
for c in digits[DECIMAL_PART_DIGITS..=last_non_zero].iter().rev() {
let end = if let Some(mut width) = f.width() {
if self.value.is_negative() {
width -= 1;
}
if last_non_zero - DECIMAL_PART_DIGITS + 1 < width {
DECIMAL_PART_DIGITS + width
} else {
last_non_zero + 1
}
} else {
last_non_zero + 1
};
for c in digits[DECIMAL_PART_DIGITS..end].iter().rev() {
f.write_char(char::from(*c))?;
}
} else {
@ -184,7 +314,16 @@ impl fmt::Display for Decimal {
}
if DECIMAL_PART_DIGITS > first_non_zero {
f.write_char('.')?;
for c in digits[first_non_zero..DECIMAL_PART_DIGITS].iter().rev() {
let start = if let Some(precision) = f.precision() {
if DECIMAL_PART_DIGITS - first_non_zero > precision {
DECIMAL_PART_DIGITS - precision
} else {
first_non_zero
}
} else {
first_non_zero
};
for c in digits[start..DECIMAL_PART_DIGITS].iter().rev() {
f.write_char(char::from(*c))?;
}
}
@ -205,56 +344,64 @@ impl Neg for Decimal {
}
impl Decimal {
/*pub fn trunc(self) -> i64 {
(self.value / DECIMAL_PART_POW) as i64
}*/
#[inline]
pub fn to_le_bytes(&self) -> [u8; 16] {
self.value.to_le_bytes()
}
/// [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add)
#[inline]
pub fn checked_add(&self, rhs: Self) -> Option<Self> {
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_add(rhs.value)?,
value: self.value.checked_add(rhs.into().value)?,
})
}
/// [op:numeric-subtract](https://www.w3.org/TR/xpath-functions/#func-numeric-subtract)
#[inline]
pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_sub(rhs.value)?,
value: self.value.checked_sub(rhs.into().value)?,
})
}
/// [op:numeric-multiply](https://www.w3.org/TR/xpath-functions/#func-numeric-multiply)
#[inline]
pub fn checked_mul(&self, rhs: Self) -> Option<Self> {
pub fn checked_mul(&self, rhs: impl Into<Self>) -> Option<Self> {
//TODO: better algorithm to keep precision
Some(Self {
value: self
.value
.checked_div(DECIMAL_PART_HALF_POW)?
.checked_mul(rhs.value.checked_div(DECIMAL_PART_HALF_POW)?)?,
.checked_mul(rhs.into().value.checked_div(DECIMAL_PART_HALF_POW)?)?,
})
}
/// [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide)
#[inline]
pub fn checked_div(&self, rhs: Self) -> Option<Self> {
pub fn checked_div(&self, rhs: impl Into<Self>) -> Option<Self> {
//TODO: better algorithm to keep precision
Some(Self {
value: self
.value
.checked_mul(DECIMAL_PART_HALF_POW)?
.checked_div(rhs.value)?
.checked_div(rhs.into().value)?
.checked_mul(DECIMAL_PART_HALF_POW)?,
})
}
/// TODO: XSD? is well defined for not integer
pub fn checked_rem(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_rem(rhs.into().value)?,
})
}
pub fn checked_rem_euclid(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_rem_euclid(rhs.into().value)?,
})
}
/// [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs)
#[inline]
pub fn abs(&self) -> Decimal {
@ -300,9 +447,17 @@ impl Decimal {
}
}
pub fn is_negative(&self) -> bool {
self.value < 0
}
pub fn is_positive(&self) -> bool {
self.value > 0
}
/// Creates a `Decimal` from a `f32` without taking care of precision
#[inline]
pub fn from_f32(v: f32) -> Self {
pub(crate) fn from_f32(v: f32) -> Self {
Self {
value: (v * (DECIMAL_PART_POW as f32)) as i128,
}
@ -316,7 +471,7 @@ impl Decimal {
/// Creates a `Decimal` from a `f64` without taking care of precision
#[inline]
pub fn from_f64(v: f64) -> Self {
pub(crate) fn from_f64(v: f64) -> Self {
Self {
value: (v * (DECIMAL_PART_POW as f64)) as i128,
}
@ -327,31 +482,30 @@ impl Decimal {
pub fn to_f64(&self) -> f64 {
(self.value as f64) / (DECIMAL_PART_POW as f64)
}
pub(super) fn as_i128(&self) -> i128 {
self.value / DECIMAL_PART_POW
}
}
impl TryFrom<Decimal> for i64 {
type Error = ();
type Error = DecimalOverflowError;
fn try_from(value: Decimal) -> Result<i64, ()> {
fn try_from(value: Decimal) -> Result<i64, DecimalOverflowError> {
value
.value
.checked_div(DECIMAL_PART_POW)
.ok_or(())?
.ok_or(DecimalOverflowError)?
.try_into()
.map_err(|_| ())
.map_err(|_| DecimalOverflowError)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::i128;
use std::i64;
const MIN: Decimal = Decimal { value: i128::MIN };
const MAX: Decimal = Decimal { value: i128::MAX };
const STEP: Decimal = Decimal { value: 1 };
#[test]
fn from_str() {
assert_eq!(Decimal::from_str("210").unwrap().to_string(), "210");
@ -374,6 +528,16 @@ mod tests {
);
}
#[test]
fn format() {
assert_eq!(format!("{:02}", Decimal::from(0)), "00");
assert_eq!(format!("{:02}", Decimal::from(1)), "01");
assert_eq!(format!("{:02}", Decimal::from(10)), "10");
assert_eq!(format!("{:02}", Decimal::from(100)), "100");
assert_eq!(format!("{:02}", Decimal::from(-1)), "-1");
assert_eq!(format!("{:02}", Decimal::from(-10)), "-10");
}
#[test]
fn add() {
assert!(MIN.checked_add(STEP).is_some());

@ -0,0 +1,382 @@
use super::parser::duration_lexical_rep;
use super::parser::parse_value;
use super::*;
use crate::model::xsd::decimal::DecimalOverflowError;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::fmt;
use std::i64;
use std::ops::Neg;
use std::str::FromStr;
use std::time::Duration as StdDuration;
/// [XML Schema `duration` datatype](https://www.w3.org/TR/xmlschema11-2/#duration) implementation.
///
/// It stores the duration using the two components model suggested by the specification:
/// - a number of months encoded using a `i64`
/// - a number of seconds encoded using a `Decimal`
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Default)]
pub struct Duration {
months: i64,
seconds: Decimal,
}
impl Duration {
pub fn new(months: impl Into<i64>, seconds: impl Into<Decimal>) -> Self {
Self {
months: months.into(),
seconds: seconds.into(),
}
}
pub fn from_le_bytes(bytes: [u8; 24]) -> Self {
let mut months = [0; 8];
months.copy_from_slice(&bytes[0..8]);
let mut seconds = [8; 16];
seconds.copy_from_slice(&bytes[8..24]);
Self {
months: i64::from_le_bytes(months),
seconds: Decimal::from_le_bytes(seconds),
}
}
}
impl TryFrom<StdDuration> for Duration {
type Error = DecimalOverflowError;
fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> {
Ok(Self {
months: 0,
seconds: Decimal::new(
i128::try_from(value.as_nanos()).map_err(|_| DecimalOverflowError)?,
9,
)?,
})
}
}
impl FromStr for Duration {
type Err = XsdParseError;
fn from_str(input: &str) -> Result<Self, XsdParseError> {
parse_value(duration_lexical_rep, input)
}
}
impl fmt::Display for Duration {
#[allow(clippy::many_single_char_names)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ym = self.months;
let mut ss = self.seconds;
if ym < 0 || ss < 0.into() {
write!(f, "-")?;
ym = -ym;
ss = -ss;
}
write!(f, "P")?;
if ym == 0 && ss == 0.into() {
return write!(f, "T0S");
}
{
let y = ym / 12;
let m = ym % 12;
if y != 0 {
if m != 0 {
write!(f, "{}Y{}M", y, m)?;
} else {
write!(f, "{}Y", y)?;
}
} else if m != 0 || ss == 0.into() {
write!(f, "{}M", m)?;
}
}
{
let s_int = ss.as_i128();
let d = s_int / 86400;
let h = (s_int % 86400) / 3600;
let m = (s_int % 3600) / 60;
let s = ss
.checked_sub(Decimal::try_from(d * 86400 + h * 3600 + m * 60).unwrap())
.unwrap(); //could not fail
if d != 0 {
write!(f, "{}D", d)?;
}
if h != 0 || m != 0 || s != 0.into() {
write!(f, "T")?;
if h != 0 {
write!(f, "{}H", h)?;
}
if m != 0 {
write!(f, "{}M", m)?;
}
if s != 0.into() {
write!(f, "{}S", s)?;
}
}
}
Ok(())
}
}
impl PartialOrd for Duration {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let first = DateTime::new(1969, 9, 1, 0, 0, 0.into(), None).ok()?;
let first_result = first
.checked_add_duration(*self)?
.partial_cmp(&first.checked_add_duration(*other)?);
let second = DateTime::new(1697, 2, 1, 0, 0, 0.into(), None).ok()?;
let second_result = second
.checked_add_duration(*self)?
.partial_cmp(&second.checked_add_duration(*other)?);
let third = DateTime::new(1903, 3, 1, 0, 0, 0.into(), None).ok()?;
let third_result = third
.checked_add_duration(*self)?
.partial_cmp(&third.checked_add_duration(*other)?);
let fourth = DateTime::new(1903, 7, 1, 0, 0, 0.into(), None).ok()?;
let fourth_result = fourth
.checked_add_duration(*self)?
.partial_cmp(&fourth.checked_add_duration(*other)?);
if first_result == second_result
&& second_result == third_result
&& third_result == fourth_result
{
first_result
} else {
None
}
}
}
impl Duration {
/// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration)
pub fn years(&self) -> i64 {
self.months / 12
}
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
pub fn months(&self) -> i64 {
self.months % 12
}
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration)
pub fn days(&self) -> i64 {
(self.seconds.as_i128() / 86400) as i64
}
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
pub fn hours(&self) -> i64 {
((self.seconds.as_i128() % 86400) / 3600) as i64
}
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
pub fn minutes(&self) -> i64 {
((self.seconds.as_i128() % 3600) / 60) as i64
}
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
pub fn seconds(&self) -> Decimal {
self.seconds.checked_rem(60).unwrap()
}
pub(super) fn all_months(&self) -> i64 {
self.months
}
pub(super) fn all_seconds(&self) -> Decimal {
self.seconds
}
pub fn to_le_bytes(&self) -> [u8; 24] {
let mut bytes = [0; 24];
bytes[0..8].copy_from_slice(&self.months.to_le_bytes());
bytes[8..24].copy_from_slice(&self.seconds.to_le_bytes());
bytes
}
/// [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations) and [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations)
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into();
Some(Self {
months: self.months.checked_add(rhs.months)?,
seconds: self.seconds.checked_add(rhs.seconds)?,
})
}
/// [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations) and [op:subtract-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations)
pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into();
Some(Self {
months: self.months.checked_sub(rhs.months)?,
seconds: self.seconds.checked_sub(rhs.seconds)?,
})
}
}
impl Neg for Duration {
type Output = Self;
fn neg(self) -> Self {
Self {
months: self.months.neg(),
seconds: self.seconds.neg(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str() {
let min = Duration::new(
i64::MIN + 1,
decimal::MIN.checked_add(decimal::STEP).unwrap(),
);
let max = Duration::new(i64::MAX, decimal::MAX);
assert_eq!(Duration::from_str("P1Y").unwrap().to_string(), "P1Y");
assert_eq!(Duration::from_str("P1M").unwrap().to_string(), "P1M");
assert_eq!(Duration::from_str("P1D").unwrap().to_string(), "P1D");
assert_eq!(Duration::from_str("PT1H").unwrap().to_string(), "PT1H");
assert_eq!(Duration::from_str("PT1M").unwrap().to_string(), "PT1M");
assert_eq!(Duration::from_str("PT1.1S").unwrap().to_string(), "PT1.1S");
assert_eq!(Duration::from_str("-P1Y").unwrap().to_string(), "-P1Y");
assert_eq!(Duration::from_str("-P1M").unwrap().to_string(), "-P1M");
assert_eq!(Duration::from_str("-P1D").unwrap().to_string(), "-P1D");
assert_eq!(Duration::from_str("-PT1H").unwrap().to_string(), "-PT1H");
assert_eq!(Duration::from_str("-PT1M").unwrap().to_string(), "-PT1M");
assert_eq!(
Duration::from_str("-PT1.1S").unwrap().to_string(),
"-PT1.1S"
);
assert_eq!(Duration::from_str(&max.to_string()).unwrap(), max);
assert_eq!(Duration::from_str(&min.to_string()).unwrap(), min);
}
#[test]
fn equals() {
assert_eq!(
Duration::from_str("P1Y").unwrap(),
Duration::from_str("P12M").unwrap()
);
assert_eq!(
Duration::from_str("PT24H").unwrap(),
Duration::from_str("P1D").unwrap()
);
assert_ne!(
Duration::from_str("P1Y").unwrap(),
Duration::from_str("P365D").unwrap()
);
assert_eq!(
Duration::from_str("P0Y").unwrap(),
Duration::from_str("P0D").unwrap()
);
assert_ne!(
Duration::from_str("P1Y").unwrap(),
Duration::from_str("P365D").unwrap()
);
assert_eq!(
Duration::from_str("P2Y").unwrap(),
Duration::from_str("P24M").unwrap()
);
assert_eq!(
Duration::from_str("P10D").unwrap(),
Duration::from_str("PT240H").unwrap()
);
assert_eq!(
Duration::from_str("P2Y0M0DT0H0M0S").unwrap(),
Duration::from_str("P24M").unwrap()
);
assert_eq!(
Duration::from_str("P0Y0M10D").unwrap(),
Duration::from_str("PT240H").unwrap()
);
}
#[test]
fn years() {
assert_eq!(Duration::from_str("P20Y15M").unwrap().years(), 21);
assert_eq!(Duration::from_str("-P15M").unwrap().years(), -1);
assert_eq!(Duration::from_str("-P2DT15H").unwrap().years(), 0);
}
#[test]
fn months() {
assert_eq!(Duration::from_str("P20Y15M").unwrap().months(), 3);
assert_eq!(Duration::from_str("-P20Y18M").unwrap().months(), -6);
assert_eq!(Duration::from_str("-P2DT15H0M0S").unwrap().months(), 0);
}
#[test]
fn days() {
assert_eq!(Duration::from_str("P3DT10H").unwrap().days(), 3);
assert_eq!(Duration::from_str("P3DT55H").unwrap().days(), 5);
assert_eq!(Duration::from_str("P3Y5M").unwrap().days(), 0);
}
#[test]
fn hours() {
assert_eq!(Duration::from_str("P3DT10H").unwrap().hours(), 10);
assert_eq!(Duration::from_str("P3DT12H32M12S").unwrap().hours(), 12);
assert_eq!(Duration::from_str("PT123H").unwrap().hours(), 3);
assert_eq!(Duration::from_str("-P3DT10H").unwrap().hours(), -10);
}
#[test]
fn minutes() {
assert_eq!(Duration::from_str("P3DT10H").unwrap().minutes(), 0);
assert_eq!(Duration::from_str("-P5DT12H30M").unwrap().minutes(), -30);
}
#[test]
fn seconds() {
assert_eq!(
Duration::from_str("P3DT10H12.5S").unwrap().seconds(),
Decimal::from_str("12.5").unwrap()
);
assert_eq!(
Duration::from_str("-PT256S").unwrap().seconds(),
Decimal::from_str("-16.0").unwrap()
);
}
#[test]
fn add() {
assert_eq!(
Duration::from_str("P2Y11M")
.unwrap()
.checked_add(Duration::from_str("P3Y3M").unwrap()),
Some(Duration::from_str("P6Y2M").unwrap())
);
assert_eq!(
Duration::from_str("P2DT12H5M")
.unwrap()
.checked_add(Duration::from_str("P5DT12H").unwrap()),
Some(Duration::from_str("P8DT5M").unwrap())
);
}
#[test]
fn sub() {
assert_eq!(
Duration::from_str("P2Y11M")
.unwrap()
.checked_sub(Duration::from_str("P3Y3M").unwrap()),
Some(Duration::from_str("-P4M").unwrap())
);
assert_eq!(
Duration::from_str("P2DT12H")
.unwrap()
.checked_sub(Duration::from_str("P1DT10H30M").unwrap()),
Some(Duration::from_str("P1DT1H30M").unwrap())
);
}
}

@ -1,3 +1,9 @@
mod decimal;
pub mod date_time;
pub mod decimal;
mod duration;
mod parser;
pub use self::date_time::{Date, DateTime, Time};
pub use self::decimal::Decimal;
pub use self::duration::Duration;
pub use self::parser::XsdParseError;

@ -0,0 +1,462 @@
use super::*;
use nom::branch::alt;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{char, digit0, digit1};
use nom::combinator::{map, opt, recognize};
use nom::error::{ErrorKind, ParseError};
use nom::multi::many1;
use nom::sequence::{preceded, terminated, tuple};
use nom::Err;
use nom::{IResult, Needed};
use std::str::FromStr;
use super::date_time::DateTimeError;
use super::decimal::ParseDecimalError;
use crate::model::xsd::date_time::TimezoneOffset;
use nom::bytes::streaming::take_while_m_n;
use rand::distributions::weighted::alias_method::Weight;
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug, Clone)]
pub struct XsdParseError {
kind: XsdParseErrorKind,
}
#[derive(Debug, Clone)]
enum XsdParseErrorKind {
NomKind(ErrorKind),
NomChar(char),
MissingData(Needed),
TooMuchData { count: usize },
Overflow,
ParseInt(ParseIntError),
ParseDecimal(ParseDecimalError),
OutOfIntegerRange { value: u8, min: u8, max: u8 },
DateTime(DateTimeError),
}
impl fmt::Display for XsdParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
XsdParseErrorKind::NomKind(kind) => {
write!(f, "Invalid XML Schema value: {}", kind.description())
}
XsdParseErrorKind::NomChar(c) => {
write!(f, "Unexpected character in XML Schema value: '{}'", c)
}
XsdParseErrorKind::MissingData(Needed::Unknown) => {
write!(f, "Too small XML Schema value")
}
XsdParseErrorKind::MissingData(Needed::Size(size)) => {
write!(f, "Too small XML Schema value: missing {} chars", size)
}
XsdParseErrorKind::TooMuchData { count } => {
write!(f, "Too long XML Schema value: {} extra chars", count)
}
XsdParseErrorKind::Overflow => write!(f, "Computation overflow or undeflow"),
XsdParseErrorKind::ParseInt(error) => {
write!(f, "Error while parsing integer: {}", error)
}
XsdParseErrorKind::ParseDecimal(error) => {
write!(f, "Error while parsing decimal: {}", error)
}
XsdParseErrorKind::OutOfIntegerRange { value, min, max } => write!(
f,
"The integer {} is not between {} and {}",
value, min, max
),
XsdParseErrorKind::DateTime(error) => error.fmt(f),
}
}
}
impl Error for XsdParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.kind {
XsdParseErrorKind::ParseInt(error) => Some(error),
XsdParseErrorKind::ParseDecimal(error) => Some(error),
XsdParseErrorKind::DateTime(error) => Some(error),
_ => None,
}
}
}
impl ParseError<&str> for XsdParseError {
fn from_error_kind(_input: &str, kind: ErrorKind) -> Self {
Self {
kind: XsdParseErrorKind::NomKind(kind),
}
}
fn append(_input: &str, _kind: ErrorKind, other: Self) -> Self {
other
}
fn from_char(_input: &str, c: char) -> Self {
Self {
kind: XsdParseErrorKind::NomChar(c),
}
}
fn or(self, other: Self) -> Self {
other
}
fn add_context(_input: &str, _ctx: &'static str, other: Self) -> Self {
other
}
}
impl From<ParseIntError> for XsdParseError {
fn from(error: ParseIntError) -> Self {
XsdParseError {
kind: XsdParseErrorKind::ParseInt(error),
}
}
}
impl From<ParseDecimalError> for XsdParseError {
fn from(error: ParseDecimalError) -> Self {
XsdParseError {
kind: XsdParseErrorKind::ParseDecimal(error),
}
}
}
impl From<DateTimeError> for XsdParseError {
fn from(error: DateTimeError) -> Self {
XsdParseError {
kind: XsdParseErrorKind::DateTime(error),
}
}
}
impl From<Err<XsdParseError>> for XsdParseError {
fn from(err: Err<XsdParseError>) -> Self {
match err {
Err::Incomplete(needed) => XsdParseError {
kind: XsdParseErrorKind::MissingData(needed),
},
Err::Error(e) | Err::Failure(e) => e,
}
}
}
type XsdResult<'a, T> = IResult<&'a str, T, XsdParseError>;
const OVERFLOW_ERROR: XsdParseError = XsdParseError {
kind: XsdParseErrorKind::Overflow,
};
pub fn parse_value<'a, T>(
f: impl Fn(&'a str) -> XsdResult<'a, T>,
input: &'a str,
) -> Result<T, XsdParseError> {
let (left, result) = f(input)?;
if left.is_empty() {
Ok(result)
} else {
Err(XsdParseError {
kind: XsdParseErrorKind::TooMuchData { count: left.len() },
})
}
}
//TODO: check every computation
// [6] duYearFrag ::= unsignedNoDecimalPtNumeral 'Y'
fn du_year_frag(input: &str) -> XsdResult<i64> {
terminated(unsigned_no_decimal_pt_numeral, char('Y'))(input)
}
// [7] duMonthFrag ::= unsignedNoDecimalPtNumeral 'M'
fn du_month_frag(input: &str) -> XsdResult<i64> {
terminated(unsigned_no_decimal_pt_numeral, char('M'))(input)
}
// [8] duDayFrag ::= unsignedNoDecimalPtNumeral 'D'
fn du_day_frag(input: &str) -> XsdResult<i64> {
terminated(unsigned_no_decimal_pt_numeral, char('D'))(input)
}
// [9] duHourFrag ::= unsignedNoDecimalPtNumeral 'H'
fn du_hour_frag(input: &str) -> XsdResult<i64> {
terminated(unsigned_no_decimal_pt_numeral, char('H'))(input)
}
// [10] duMinuteFrag ::= unsignedNoDecimalPtNumeral 'M'
fn du_minute_frag(input: &str) -> XsdResult<i64> {
terminated(unsigned_no_decimal_pt_numeral, char('M'))(input)
}
// [11] duSecondFrag ::= (unsignedNoDecimalPtNumeral | unsignedDecimalPtNumeral) 'S'
fn du_second_frag(input: &str) -> XsdResult<Decimal> {
terminated(
map_res(
recognize(tuple((digit0, opt(preceded(char('.'), digit0))))),
Decimal::from_str,
),
char('S'),
)(input)
}
// [12] duYearMonthFrag ::= (duYearFrag duMonthFrag?) | duMonthFrag
fn du_year_month_frag(input: &str) -> XsdResult<i64> {
alt((
map(tuple((du_year_frag, opt(du_month_frag))), |(y, m)| {
12 * y + m.unwrap_or(0)
}),
du_month_frag,
))(input)
}
// [13] duTimeFrag ::= 'T' ((duHourFrag duMinuteFrag? duSecondFrag?) | (duMinuteFrag duSecondFrag?) | duSecondFrag)
fn du_time_frag(input: &str) -> XsdResult<Decimal> {
preceded(
char('T'),
alt((
map_res(
tuple((du_hour_frag, opt(du_minute_frag), opt(du_second_frag))),
|(h, m, s)| {
Decimal::from(3600 * h + 60 * m.unwrap_or(0))
.checked_add(s.unwrap_or_else(Decimal::default))
.ok_or(OVERFLOW_ERROR)
},
),
map_res(tuple((du_minute_frag, opt(du_second_frag))), |(m, s)| {
Decimal::from(m * 60)
.checked_add(s.unwrap_or_else(Decimal::default))
.ok_or(OVERFLOW_ERROR)
}),
du_second_frag,
)),
)(input)
}
// [14] duDayTimeFrag ::= (duDayFrag duTimeFrag?) | duTimeFrag
fn du_day_time_frag(input: &str) -> XsdResult<Decimal> {
alt((
map_res(tuple((du_day_frag, opt(du_time_frag))), |(d, t)| {
Decimal::from(d)
.checked_mul(Decimal::from(86400))
.ok_or(OVERFLOW_ERROR)?
.checked_add(t.unwrap_or_else(Decimal::default))
.ok_or(OVERFLOW_ERROR)
}),
du_time_frag,
))(input)
}
// [15] durationLexicalRep ::= '-'? 'P' ((duYearMonthFrag duDayTimeFrag?) | duDayTimeFrag)
pub fn duration_lexical_rep(input: &str) -> XsdResult<Duration> {
map(
tuple((
opt(char('-')),
preceded(
char('P'),
alt((
map(
tuple((du_year_month_frag, opt(du_day_time_frag))),
|(y, d)| Duration::new(y, d.unwrap_or_else(Decimal::default)),
),
map(du_day_time_frag, |d| Duration::new(0, d)),
)),
),
)),
|(sign, duration)| {
if sign == Some('-') {
-duration
} else {
duration
}
},
)(input)
}
// [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?
pub fn date_time_lexical_rep(input: &str) -> XsdResult<DateTime> {
map_res(
tuple((
year_frag,
char('-'),
month_frag,
char('-'),
day_frag,
char('T'),
alt((
map(
tuple((hour_frag, char(':'), minute_frag, char(':'), second_frag)),
|(h, _, m, _, s)| (h, m, s),
),
end_of_day_frag,
)),
opt(timezone_frag),
)),
|(year, _, month, _, day, _, (hours, minutes, seconds), timezone)| {
DateTime::new(year, month, day, hours, minutes, seconds, timezone)
},
)(input)
}
// [17] timeLexicalRep ::= ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?
pub fn time_lexical_rep(input: &str) -> XsdResult<Time> {
map_res(
tuple((
alt((
map(
tuple((hour_frag, char(':'), minute_frag, char(':'), second_frag)),
|(h, _, m, _, s)| (h, m, s),
),
end_of_day_frag,
)),
opt(timezone_frag),
)),
|((hours, minutes, seconds), timezone)| Time::new(hours, minutes, seconds, timezone),
)(input)
}
// [18] dateLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag timezoneFrag? Constraint: Day-of-month Representations
pub fn date_lexical_rep(input: &str) -> XsdResult<Date> {
map_res(
tuple((
year_frag,
char('-'),
month_frag,
char('-'),
day_frag,
opt(timezone_frag),
)),
|(year, _, month, _, day, timezone)| Date::new(year, month, day, timezone),
)(input)
}
// [46] unsignedNoDecimalPtNumeral ::= digit+
fn unsigned_no_decimal_pt_numeral(input: &str) -> XsdResult<i64> {
map_res(digit1, |i| i64::from_str(i))(input)
}
// [56] yearFrag ::= '-'? (([1-9] digit digit digit+)) | ('0' digit digit digit))
fn year_frag(input: &str) -> XsdResult<i64> {
map_res(
recognize(tuple((
opt(char('-')),
take_while_m_n(4, usize::MAX, |c: char| c.is_ascii_digit()),
))),
i64::from_str,
)(input)
}
// [57] monthFrag ::= ('0' [1-9]) | ('1' [0-2])
fn month_frag(input: &str) -> XsdResult<u8> {
map_res(take_while_m_n(2, 2, |c: char| c.is_ascii_digit()), |v| {
parsed_u8_range(v, 1, 12)
})(input)
}
// [58] dayFrag ::= ('0' [1-9]) | ([12] digit) | ('3' [01])
fn day_frag(input: &str) -> XsdResult<u8> {
map_res(take_while_m_n(2, 2, |c: char| c.is_ascii_digit()), |v| {
parsed_u8_range(v, 1, 31)
})(input)
}
// [59] hourFrag ::= ([01] digit) | ('2' [0-3])
fn hour_frag(input: &str) -> XsdResult<u8> {
map_res(take_while_m_n(2, 2, |c: char| c.is_ascii_digit()), |v| {
parsed_u8_range(v, 0, 23)
})(input)
}
// [60] minuteFrag ::= [0-5] digit
fn minute_frag(input: &str) -> XsdResult<u8> {
map_res(take_while_m_n(2, 2, |c: char| c.is_ascii_digit()), |v| {
parsed_u8_range(v, 0, 59)
})(input)
}
// [61] secondFrag ::= ([0-5] digit) ('.' digit+)?
fn second_frag(input: &str) -> XsdResult<Decimal> {
map_res(
recognize(tuple((
take_while_m_n(2, 2, |c: char| c.is_ascii_digit()),
opt(preceded(
char('.'),
take_while(|c: char| c.is_ascii_digit()),
)),
))),
|v| {
let value = Decimal::from_str(v)?;
if Decimal::from(0) <= value && value < Decimal::from(60) {
Ok(value)
} else {
Err(XsdParseError {
kind: XsdParseErrorKind::OutOfIntegerRange {
value: value.as_i128() as u8,
min: 0,
max: 60,
},
})
}
},
)(input)
}
// [62] endOfDayFrag ::= '24:00:00' ('.' '0'+)?
fn end_of_day_frag(input: &str) -> XsdResult<(u8, u8, Decimal)> {
map(
recognize(tuple((
tag("24:00:00"),
opt(preceded(char('.'), many1(char('0')))),
))),
|_| (24, 0, 0.into()),
)(input)
}
// [63] timezoneFrag ::= 'Z' | ('+' | '-') (('0' digit | '1' [0-3]) ':' minuteFrag | '14:00')
fn timezone_frag(input: &str) -> XsdResult<TimezoneOffset> {
alt((
map(char('Z'), |_| TimezoneOffset::utc()),
map(
tuple((
alt((map(char('+'), |_| 1), map(char('-'), |_| -1))),
alt((
map(
tuple((
map_res(take_while_m_n(2, 2, |c: char| c.is_ascii_digit()), |v| {
parsed_u8_range(v, 0, 13)
}),
char(':'),
minute_frag,
)),
|(hours, _, minutes)| i16::from(hours) * 60 + i16::from(minutes),
),
map(tag("14:00"), |_| 14 * 60),
)),
)),
|(sign, value)| TimezoneOffset::new(sign * value),
),
))(input)
}
fn parsed_u8_range(input: &str, min: u8, max: u8) -> Result<u8, XsdParseError> {
let value = u8::from_str(input)?;
if min <= value && value <= max {
Ok(value)
} else {
Err(XsdParseError {
kind: XsdParseErrorKind::OutOfIntegerRange { value, min, max },
})
}
}
pub fn map_res<'a, O1, O2, E2: Into<XsdParseError>>(
first: impl Fn(&'a str) -> XsdResult<'a, O1>,
second: impl Fn(O1) -> Result<O2, E2>,
) -> impl Fn(&'a str) -> XsdResult<'a, O2> {
move |input| {
let (input, o1) = first(input)?;
Ok((input, second(o1).map_err(|e| Err::Error(e.into()))?))
}
}

@ -1,4 +1,4 @@
use crate::model::xsd::Decimal;
use crate::model::xsd::*;
use crate::model::BlankNode;
use crate::model::Triple;
use crate::sparql::algebra::GraphPattern;
@ -8,7 +8,6 @@ use crate::sparql::ServiceHandler;
use crate::store::numeric_encoder::*;
use crate::store::StoreConnection;
use crate::Result;
use chrono::prelude::*;
use digest::Digest;
use failure::format_err;
use md5::Md5;
@ -22,7 +21,6 @@ use sha2::{Sha256, Sha384, Sha512};
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::convert::{TryFrom, TryInto};
use std::fmt::Write;
use std::hash::Hash;
use std::iter::Iterator;
use std::iter::{empty, once};
@ -37,7 +35,7 @@ pub struct SimpleEvaluator<S: StoreConnection> {
dataset: DatasetView<S>,
base_iri: Option<Iri<String>>,
bnodes_map: Mutex<BTreeMap<u128, u128>>,
now: DateTime<FixedOffset>,
now: DateTime,
service_handler: Box<dyn ServiceHandler>,
}
@ -51,7 +49,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
dataset,
bnodes_map: Mutex::new(BTreeMap::default()),
base_iri,
now: Utc::now().with_timezone(&FixedOffset::east(0)),
now: DateTime::now().unwrap(),
service_handler,
}
}
@ -813,37 +811,60 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
Some(false.into())
}
}
PlanExpression::Add(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => (v1 + v2).into(),
NumericBinaryOperands::Double(v1, v2) => (v1 + v2).into(),
NumericBinaryOperands::Integer(v1, v2) => v1.checked_add(v2)?.into(),
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_add(v2)?.into(),
}),
PlanExpression::Add(a, b) => match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => Some((v1 + v2).into()),
NumericBinaryOperands::Double(v1, v2) => Some((v1 + v2).into()),
NumericBinaryOperands::Integer(v1, v2) => Some(v1.checked_add(v2)?.into()),
NumericBinaryOperands::Decimal(v1, v2) => Some(v1.checked_add(v2)?.into()),
NumericBinaryOperands::Duration(v1, v2) => Some(v1.checked_add(v2)?.into()),
NumericBinaryOperands::DateTimeDuration(v1, v2) => {
Some(v1.checked_add_duration(v2)?.into())
}
NumericBinaryOperands::DateDuration(v1, v2) => {
Some(v1.checked_add_duration(v2)?.into())
}
NumericBinaryOperands::TimeDuration(v1, v2) => {
Some(v1.checked_add_duration(v2)?.into())
}
_ => None,
},
PlanExpression::Sub(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => (v1 - v2).into(),
NumericBinaryOperands::Double(v1, v2) => (v1 - v2).into(),
NumericBinaryOperands::Integer(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::Duration(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::DateTime(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::Date(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::Time(v1, v2) => v1.checked_sub(v2)?.into(),
NumericBinaryOperands::DateTimeDuration(v1, v2) => {
v1.checked_sub_duration(v2)?.into()
}
NumericBinaryOperands::DateDuration(v1, v2) => v1.checked_sub_duration(v2)?.into(),
NumericBinaryOperands::TimeDuration(v1, v2) => v1.checked_sub_duration(v2)?.into(),
}),
PlanExpression::Mul(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => (v1 * v2).into(),
NumericBinaryOperands::Double(v1, v2) => (v1 * v2).into(),
NumericBinaryOperands::Integer(v1, v2) => v1.checked_mul(v2)?.into(),
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_mul(v2)?.into(),
}),
PlanExpression::Div(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => (v1 / v2).into(),
NumericBinaryOperands::Double(v1, v2) => (v1 / v2).into(),
PlanExpression::Mul(a, b) => match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => Some((v1 * v2).into()),
NumericBinaryOperands::Double(v1, v2) => Some((v1 * v2).into()),
NumericBinaryOperands::Integer(v1, v2) => Some(v1.checked_mul(v2)?.into()),
NumericBinaryOperands::Decimal(v1, v2) => Some(v1.checked_mul(v2)?.into()),
_ => None,
},
PlanExpression::Div(a, b) => match self.parse_numeric_operands(a, b, tuple)? {
NumericBinaryOperands::Float(v1, v2) => Some((v1 / v2).into()),
NumericBinaryOperands::Double(v1, v2) => Some((v1 / v2).into()),
NumericBinaryOperands::Integer(v1, v2) => {
Decimal::from(v1).checked_div(Decimal::from(v2))?.into()
Some(Decimal::from(v1).checked_div(v2)?.into())
}
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_div(v2)?.into(),
}),
NumericBinaryOperands::Decimal(v1, v2) => Some(v1.checked_div(v2)?.into()),
_ => None,
},
PlanExpression::UnaryPlus(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::FloatLiteral(value) => Some(value.into()),
EncodedTerm::DoubleLiteral(value) => Some(value.into()),
EncodedTerm::IntegerLiteral(value) => Some(value.into()),
EncodedTerm::DecimalLiteral(value) => Some(value.into()),
EncodedTerm::DurationLiteral(value) => Some(value.into()),
_ => None,
},
PlanExpression::UnaryMinus(e) => match self.eval_expression(e, tuple)? {
@ -851,6 +872,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
EncodedTerm::DoubleLiteral(value) => Some((-value).into()),
EncodedTerm::IntegerLiteral(value) => Some((-value).into()),
EncodedTerm::DecimalLiteral(value) => Some((-value).into()),
EncodedTerm::DurationLiteral(value) => Some((-value).into()),
_ => None,
},
PlanExpression::UnaryNot(e) => self
@ -1119,111 +1141,55 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
}
PlanExpression::Year(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => Some(date.year().into()),
EncodedTerm::NaiveDateLiteral(date) => Some(date.year().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.year().into()),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.year().into()),
_ => None,
},
PlanExpression::Month(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => Some(date.year().into()),
EncodedTerm::NaiveDateLiteral(date) => Some(date.month().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.month().into()),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.month().into()),
_ => None,
},
PlanExpression::Day(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => Some(date.year().into()),
EncodedTerm::NaiveDateLiteral(date) => Some(date.day().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.day().into()),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.day().into()),
_ => None,
},
PlanExpression::Hours(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveTimeLiteral(time) => Some(time.hour().into()),
EncodedTerm::TimeLiteral(time) => Some(time.hour().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.hour().into()),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.hour().into()),
_ => None,
},
PlanExpression::Minutes(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveTimeLiteral(time) => Some(time.minute().into()),
EncodedTerm::TimeLiteral(time) => Some(time.minute().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.minute().into()),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.minute().into()),
_ => None,
},
PlanExpression::Seconds(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveTimeLiteral(time) => Some(
Decimal::from(time.nanosecond())
.checked_div(Decimal::from(1_000_000_000))?
.checked_add(Decimal::from(time.second()))?
.into(),
),
EncodedTerm::DateTimeLiteral(date_time) => Some(
Decimal::from(date_time.nanosecond())
.checked_div(Decimal::from(1_000_000_000))?
.checked_add(Decimal::from(date_time.second()))?
.into(),
),
EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(
Decimal::from(date_time.nanosecond())
.checked_div(Decimal::from(1_000_000_000))?
.checked_add(Decimal::from(date_time.second()))?
.into(),
),
EncodedTerm::TimeLiteral(time) => Some(time.second().into()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.second().into()),
_ => None,
},
PlanExpression::Timezone(e) => {
let timezone = match self.eval_expression(e, tuple)? {
PlanExpression::Timezone(e) => Some(
match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => date.timezone(),
EncodedTerm::TimeLiteral(time) => time.timezone(),
EncodedTerm::DateTimeLiteral(date_time) => date_time.timezone(),
_ => return None,
};
let mut result = String::with_capacity(9);
let mut shift = timezone.local_minus_utc();
if shift < 0 {
write!(&mut result, "-").ok()?;
shift = -shift
};
write!(&mut result, "PT").ok()?;
let hours = shift / 3600;
if hours > 0 {
write!(&mut result, "{}H", hours).ok()?;
}
let minutes = (shift / 60) % 60;
if minutes > 0 {
write!(&mut result, "{}M", minutes).ok()?;
}
let seconds = shift % 60;
if seconds > 0 || shift == 0 {
write!(&mut result, "{}S", seconds).ok()?;
}
Some(EncodedTerm::TypedLiteral {
value_id: self.build_string_id(&result)?,
datatype_id: self
.build_string_id("http://www.w3.org/2001/XMLSchema#dayTimeDuration")?,
})
}
_ => None,
}?
.into(),
),
PlanExpression::Tz(e) => {
let timezone = match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => Some(date.timezone()),
EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.timezone()),
EncodedTerm::NaiveDateLiteral(_)
| EncodedTerm::NaiveTimeLiteral(_)
| EncodedTerm::NaiveDateTimeLiteral(_) => None,
let timezone_offset = match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(date) => date.timezone_offset(),
EncodedTerm::TimeLiteral(time) => time.timezone_offset(),
EncodedTerm::DateTimeLiteral(date_time) => date_time.timezone_offset(),
_ => return None,
};
Some(if let Some(timezone) = timezone {
EncodedTerm::StringLiteral {
value_id: if timezone.local_minus_utc() == 0 {
self.build_string_id("Z")?
} else {
self.build_string_id(&timezone.to_string())?
},
Some(match timezone_offset {
Some(timezone_offset) => {
self.build_string_literal(&timezone_offset.to_string())?
}
} else {
ENCODED_EMPTY_STRING_LITERAL
None => ENCODED_EMPTY_STRING_LITERAL,
})
}
PlanExpression::Now => Some(self.now.into()),
@ -1376,18 +1342,15 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
},
PlanExpression::DateCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateLiteral(value) => Some(value.into()),
EncodedTerm::NaiveDateLiteral(value) => Some(value.into()),
EncodedTerm::DateTimeLiteral(value) => Some(value.date().into()),
EncodedTerm::NaiveDateTimeLiteral(value) => Some(value.date().into()),
EncodedTerm::DateTimeLiteral(value) => Some(Date::from(value).into()),
EncodedTerm::StringLiteral { value_id } => {
parse_date_str(&*self.dataset.get_str(value_id).ok()??)
}
_ => None,
},
PlanExpression::TimeCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveTimeLiteral(value) => Some(value.into()),
EncodedTerm::DateTimeLiteral(value) => Some(value.time().into()),
EncodedTerm::NaiveDateTimeLiteral(value) => Some(value.time().into()),
EncodedTerm::TimeLiteral(value) => Some(value.into()),
EncodedTerm::DateTimeLiteral(value) => Some(Time::from(value).into()),
EncodedTerm::StringLiteral { value_id } => {
parse_time_str(&*self.dataset.get_str(value_id).ok()??)
}
@ -1395,12 +1358,19 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
},
PlanExpression::DateTimeCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateTimeLiteral(value) => Some(value.into()),
EncodedTerm::NaiveDateTimeLiteral(value) => Some(value.into()),
EncodedTerm::DateLiteral(value) => Some(DateTime::from(value).into()),
EncodedTerm::StringLiteral { value_id } => {
parse_date_time_str(&*self.dataset.get_str(value_id).ok()??)
}
_ => None,
},
PlanExpression::DurationCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DurationLiteral(value) => Some(value.into()),
EncodedTerm::StringLiteral { value_id } => {
parse_duration_str(&*self.dataset.get_str(value_id).ok()??)
}
_ => None,
},
PlanExpression::StringCast(e) => Some(EncodedTerm::StringLiteral {
value_id: self.to_string_id(self.eval_expression(e, tuple)?)?,
}),
@ -1414,7 +1384,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
EncodedTerm::FloatLiteral(value) => Some(value != 0f32),
EncodedTerm::DoubleLiteral(value) => Some(value != 0f64),
EncodedTerm::IntegerLiteral(value) => Some(value != 0),
EncodedTerm::DecimalLiteral(value) => Some(value != Decimal::from(0)),
EncodedTerm::DecimalLiteral(value) => Some(value != Decimal::default()),
_ => None,
}
}
@ -1435,10 +1405,9 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
EncodedTerm::IntegerLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::DecimalLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::DateLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::NaiveDateLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::NaiveTimeLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::TimeLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::DateTimeLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::NaiveDateTimeLiteral(value) => self.build_string_id(&value.to_string()),
EncodedTerm::DurationLiteral(value) => self.build_string_id(&value.to_string()),
}
}
@ -1690,54 +1659,21 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
},
EncodedTerm::DateLiteral(a) => match b {
EncodedTerm::DateLiteral(b) => Some(a == b),
EncodedTerm::NaiveDateLiteral(b) => {
if a.naive_utc() == b {
None
} else {
Some(false)
}
}
EncodedTerm::TypedLiteral { .. } => None,
_ => Some(false),
},
EncodedTerm::NaiveDateLiteral(a) => match b {
EncodedTerm::NaiveDateLiteral(b) => Some(a == b),
EncodedTerm::DateLiteral(b) => {
if a == b.naive_utc() {
None
} else {
Some(false)
}
}
EncodedTerm::TypedLiteral { .. } => None,
_ => Some(false),
},
EncodedTerm::NaiveTimeLiteral(a) => match b {
EncodedTerm::NaiveTimeLiteral(b) => Some(a == b),
EncodedTerm::TimeLiteral(a) => match b {
EncodedTerm::TimeLiteral(b) => Some(a == b),
EncodedTerm::TypedLiteral { .. } => None,
_ => Some(false),
},
EncodedTerm::DateTimeLiteral(a) => match b {
EncodedTerm::DateTimeLiteral(b) => Some(a == b),
EncodedTerm::NaiveDateTimeLiteral(b) => {
if a.naive_utc() == b {
None
} else {
Some(false)
}
}
EncodedTerm::TypedLiteral { .. } => None,
_ => Some(false),
},
EncodedTerm::NaiveDateTimeLiteral(a) => match b {
EncodedTerm::NaiveDateTimeLiteral(b) => Some(a == b),
EncodedTerm::DateTimeLiteral(b) => {
if a == b.naive_utc() {
None
} else {
Some(false)
}
}
EncodedTerm::DurationLiteral(a) => match b {
EncodedTerm::DurationLiteral(b) => Some(a == b),
EncodedTerm::TypedLiteral { .. } => None,
_ => Some(false),
},
@ -1796,60 +1732,61 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
}
}
EncodedTerm::FloatLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(&b),
EncodedTerm::DoubleLiteral(b) => a.to_f64()?.partial_cmp(&b),
EncodedTerm::FloatLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::DoubleLiteral(ref b) => a.to_f64()?.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&b.to_f32()?),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_f32()),
_ => None,
},
EncodedTerm::DoubleLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(&b.to_f64()?),
EncodedTerm::DoubleLiteral(b) => a.partial_cmp(&b),
EncodedTerm::DoubleLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&b.to_f64()?),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_f64()),
_ => None,
},
EncodedTerm::IntegerLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.to_f32()?.partial_cmp(&b),
EncodedTerm::DoubleLiteral(b) => a.to_f64()?.partial_cmp(&b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&b),
EncodedTerm::FloatLiteral(ref b) => a.to_f32()?.partial_cmp(b),
EncodedTerm::DoubleLiteral(ref b) => a.to_f64()?.partial_cmp(b),
EncodedTerm::IntegerLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::DecimalLiteral(b) => Decimal::from(a).partial_cmp(&b),
_ => None,
},
EncodedTerm::DecimalLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.to_f32().partial_cmp(&b),
EncodedTerm::DoubleLiteral(b) => a.to_f64().partial_cmp(&b),
EncodedTerm::FloatLiteral(ref b) => a.to_f32().partial_cmp(b),
EncodedTerm::DoubleLiteral(ref b) => a.to_f64().partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Decimal::from(b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b),
EncodedTerm::DecimalLiteral(ref b) => a.partial_cmp(b),
_ => None,
},
EncodedTerm::DateLiteral(a) => match b {
EncodedTerm::DateLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::NaiveDateLiteral(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges
_ => None,
},
EncodedTerm::NaiveDateLiteral(a) => match b {
EncodedTerm::NaiveDateLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::DateLiteral(ref b) => a.partial_cmp(&b.naive_utc()), //TODO: check edges
_ => None,
},
EncodedTerm::NaiveTimeLiteral(a) => {
if let EncodedTerm::NaiveTimeLiteral(ref b) = b {
EncodedTerm::DateLiteral(a) => {
if let EncodedTerm::DateLiteral(ref b) = b {
a.partial_cmp(b)
} else {
None
}
}
EncodedTerm::TimeLiteral(a) => {
if let EncodedTerm::TimeLiteral(ref b) = b {
a.partial_cmp(b)
} else {
None
}
}
EncodedTerm::DateTimeLiteral(a) => {
if let EncodedTerm::DateTimeLiteral(ref b) = b {
a.partial_cmp(b)
} else {
None
}
}
EncodedTerm::DurationLiteral(a) => {
if let EncodedTerm::DurationLiteral(ref b) = b {
a.partial_cmp(b)
} else {
None
}
}
EncodedTerm::DateTimeLiteral(a) => match b {
EncodedTerm::DateTimeLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::NaiveDateTimeLiteral(ref b) => a.naive_utc().partial_cmp(b), //TODO: check edges
_ => None,
},
EncodedTerm::NaiveDateTimeLiteral(a) => match b {
EncodedTerm::NaiveDateTimeLiteral(ref b) => a.partial_cmp(b),
EncodedTerm::DateTimeLiteral(ref b) => a.partial_cmp(&b.naive_utc()), //TODO: check edges
_ => None,
},
_ => None,
}
}
@ -1879,6 +1816,13 @@ enum NumericBinaryOperands {
Double(f64, f64),
Integer(i64, i64),
Decimal(Decimal, Decimal),
Duration(Duration, Duration),
DateTime(DateTime, DateTime),
Time(Time, Time),
Date(Date, Date),
DateTimeDuration(DateTime, Duration),
TimeDuration(Time, Duration),
DateDuration(Date, Duration),
}
impl NumericBinaryOperands {
@ -1932,6 +1876,27 @@ impl NumericBinaryOperands {
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(NumericBinaryOperands::Decimal(v1, v2))
}
(EncodedTerm::DurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(NumericBinaryOperands::Duration(v1, v2))
}
(EncodedTerm::DateTimeLiteral(v1), EncodedTerm::DateTimeLiteral(v2)) => {
Some(NumericBinaryOperands::DateTime(v1, v2))
}
(EncodedTerm::DateLiteral(v1), EncodedTerm::DateLiteral(v2)) => {
Some(NumericBinaryOperands::Date(v1, v2))
}
(EncodedTerm::TimeLiteral(v1), EncodedTerm::TimeLiteral(v2)) => {
Some(NumericBinaryOperands::Time(v1, v2))
}
(EncodedTerm::DateTimeLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(NumericBinaryOperands::DateTimeDuration(v1, v2))
}
(EncodedTerm::DateLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(NumericBinaryOperands::DateDuration(v1, v2))
}
(EncodedTerm::TimeLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(NumericBinaryOperands::TimeDuration(v1, v2))
}
_ => None,
}
}
@ -2452,6 +2417,8 @@ impl Accumulator for SumAccumulator {
NumericBinaryOperands::Double(v1, v2) => Some((v1 + v2).into()),
NumericBinaryOperands::Integer(v1, v2) => v1.checked_add(v2).map(|v| v.into()),
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_add(v2).map(|v| v.into()),
NumericBinaryOperands::Duration(v1, v2) => v1.checked_add(v2).map(|v| v.into()),
_ => None,
};
} else {
self.sum = None;
@ -2483,13 +2450,15 @@ impl Accumulator for AvgAccumulator {
Some(0.into())
} else {
//TODO: deduplicate?
//TODO: duration?
match NumericBinaryOperands::new(sum, count)? {
NumericBinaryOperands::Float(v1, v2) => Some((v1 / v2).into()),
NumericBinaryOperands::Double(v1, v2) => Some((v1 / v2).into()),
NumericBinaryOperands::Integer(v1, v2) => Decimal::from(v1)
.checked_div(Decimal::from(v2))
.map(|v| v.into()),
NumericBinaryOperands::Integer(v1, v2) => {
Decimal::from(v1).checked_div(v2).map(|v| v.into())
}
NumericBinaryOperands::Decimal(v1, v2) => v1.checked_div(v2).map(|v| v.into()),
_ => None,
}
}
}

@ -303,6 +303,7 @@ pub enum PlanExpression {
DateCast(Box<PlanExpression>),
TimeCast(Box<PlanExpression>),
DateTimeCast(Box<PlanExpression>),
DurationCast(Box<PlanExpression>),
StringCast(Box<PlanExpression>),
}

@ -641,6 +641,14 @@ impl<E: Encoder> PlanBuilder<E> {
graph_name,
"dateTime",
)?
} else if *name == *xsd::DURATION {
self.build_cast(
parameters,
PlanExpression::DurationCast,
variables,
graph_name,
"duration",
)?
} else if *name == *xsd::STRING {
self.build_cast(
parameters,

@ -121,7 +121,6 @@ impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> {
self.inner.contains(&quad.into())
}
#[must_use]
fn transaction(&self, f: impl FnOnce(&mut Self::Transaction) -> Result<()>) -> Result<()> {
let mut transaction = StoreRepositoryTransaction {
inner: self.inner.transaction(),

@ -1,11 +1,9 @@
use crate::model::vocab::rdf;
use crate::model::vocab::xsd;
use crate::model::xsd::Decimal;
use crate::model::xsd::*;
use crate::model::*;
use crate::Result;
use byteorder::{LittleEndian, ReadBytesExt};
use chrono::format::{parse, Parsed, StrftimeItems};
use chrono::prelude::*;
use failure::format_err;
use md5::digest::Digest;
use md5::Md5;
@ -30,6 +28,7 @@ const XSD_DECIMAL_ID: u128 = 0x3ca7_b56d_a746_719a_6800_081f_bb59_ea33;
const XSD_DATE_TIME_ID: u128 = 0xc206_6749_e0e5_015e_f7ee_33b7_b28c_c010;
const XSD_DATE_ID: u128 = 0xcaae_3cc4_f23f_4c5a_7717_dd19_e30a_84b8;
const XSD_TIME_ID: u128 = 0x7af4_6a16_1b02_35d7_9a79_07ba_3da9_48bb;
const XSD_DURATION_ID: u128 = 0x78ab_8431_984b_6b06_c42d_6271_b82e_487d;
pub fn get_str_id(value: &str) -> u128 {
let mut id = [0 as u8; 16];
@ -50,10 +49,9 @@ const TYPE_DOUBLE_LITERAL: u8 = 10;
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_DATE_LITERAL: u8 = 15;
const TYPE_NAIVE_DATE_LITERAL: u8 = 16;
const TYPE_NAIVE_TIME_LITERAL: u8 = 17;
const TYPE_DATE_LITERAL: u8 = 14;
const TYPE_TIME_LITERAL: u8 = 15;
const TYPE_DURATION_LITERAL: u8 = 16;
pub const ENCODED_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph;
pub const ENCODED_EMPTY_STRING_LITERAL: EncodedTerm = EncodedTerm::StringLiteral {
@ -89,6 +87,9 @@ pub const ENCODED_XSD_TIME_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
pub const ENCODED_XSD_DATE_TIME_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_DATE_TIME_ID,
};
pub const ENCODED_XSD_DURATION_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_DURATION_ID,
};
#[derive(Debug, Clone, Copy)]
pub enum EncodedTerm {
@ -103,11 +104,10 @@ pub enum EncodedTerm {
DoubleLiteral(f64),
IntegerLiteral(i64),
DecimalLiteral(Decimal),
DateLiteral(Date<FixedOffset>),
NaiveDateLiteral(NaiveDate),
NaiveTimeLiteral(NaiveTime),
DateTimeLiteral(DateTime<FixedOffset>),
NaiveDateTimeLiteral(NaiveDateTime),
DateLiteral(Date),
TimeLiteral(Time),
DateTimeLiteral(DateTime),
DurationLiteral(Duration),
}
impl PartialEq for EncodedTerm {
@ -167,10 +167,9 @@ impl PartialEq for EncodedTerm {
(EncodedTerm::IntegerLiteral(a), EncodedTerm::IntegerLiteral(b)) => a == b,
(EncodedTerm::DecimalLiteral(a), EncodedTerm::DecimalLiteral(b)) => a == b,
(EncodedTerm::DateLiteral(a), EncodedTerm::DateLiteral(b)) => a == b,
(EncodedTerm::NaiveDateLiteral(a), EncodedTerm::NaiveDateLiteral(b)) => a == b,
(EncodedTerm::NaiveTimeLiteral(a), EncodedTerm::NaiveTimeLiteral(b)) => a == b,
(EncodedTerm::TimeLiteral(a), EncodedTerm::TimeLiteral(b)) => a == b,
(EncodedTerm::DateTimeLiteral(a), EncodedTerm::DateTimeLiteral(b)) => a == b,
(EncodedTerm::NaiveDateTimeLiteral(a), EncodedTerm::NaiveDateTimeLiteral(b)) => a == b,
(EncodedTerm::DurationLiteral(a), EncodedTerm::DurationLiteral(b)) => a == b,
(_, _) => false,
}
}
@ -205,10 +204,9 @@ impl Hash for EncodedTerm {
EncodedTerm::IntegerLiteral(value) => value.hash(state),
EncodedTerm::DecimalLiteral(value) => value.hash(state),
EncodedTerm::DateLiteral(value) => value.hash(state),
EncodedTerm::NaiveDateLiteral(value) => value.hash(state),
EncodedTerm::NaiveTimeLiteral(value) => value.hash(state),
EncodedTerm::TimeLiteral(value) => value.hash(state),
EncodedTerm::DateTimeLiteral(value) => value.hash(state),
EncodedTerm::NaiveDateTimeLiteral(value) => value.hash(state),
EncodedTerm::DurationLiteral(value) => value.hash(state),
}
}
}
@ -239,10 +237,9 @@ impl EncodedTerm {
| EncodedTerm::IntegerLiteral(_)
| EncodedTerm::DecimalLiteral(_)
| EncodedTerm::DateLiteral(_)
| EncodedTerm::NaiveDateLiteral(_)
| EncodedTerm::NaiveTimeLiteral(_)
| EncodedTerm::TimeLiteral(_)
| EncodedTerm::DateTimeLiteral(_)
| EncodedTerm::NaiveDateTimeLiteral(_) => true,
| EncodedTerm::DurationLiteral(_) => true,
_ => false,
}
}
@ -260,11 +257,9 @@ impl EncodedTerm {
EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE),
EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_NAMED_NODE),
EncodedTerm::DateLiteral(..) => Some(ENCODED_XSD_DATE_NAMED_NODE),
EncodedTerm::NaiveDateLiteral(..) => Some(ENCODED_XSD_DATE_NAMED_NODE),
EncodedTerm::NaiveTimeLiteral(..) => Some(ENCODED_XSD_TIME_NAMED_NODE),
EncodedTerm::DateTimeLiteral(..) | EncodedTerm::NaiveDateTimeLiteral(..) => {
Some(ENCODED_XSD_DATE_TIME_NAMED_NODE)
}
EncodedTerm::TimeLiteral(..) => Some(ENCODED_XSD_TIME_NAMED_NODE),
EncodedTerm::DateTimeLiteral(..) => Some(ENCODED_XSD_DATE_TIME_NAMED_NODE),
EncodedTerm::DurationLiteral(..) => Some(ENCODED_XSD_DURATION_NAMED_NODE),
_ => None,
}
}
@ -284,10 +279,9 @@ impl EncodedTerm {
EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL,
EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL,
EncodedTerm::DateLiteral(_) => TYPE_DATE_LITERAL,
EncodedTerm::NaiveDateLiteral(_) => TYPE_NAIVE_DATE_LITERAL,
EncodedTerm::NaiveTimeLiteral(_) => TYPE_NAIVE_TIME_LITERAL,
EncodedTerm::TimeLiteral(_) => TYPE_TIME_LITERAL,
EncodedTerm::DateTimeLiteral(_) => TYPE_DATE_TIME_LITERAL,
EncodedTerm::NaiveDateTimeLiteral(_) => TYPE_NAIVE_DATE_TIME_LITERAL,
EncodedTerm::DurationLiteral(_) => TYPE_DURATION_LITERAL,
}
}
}
@ -316,6 +310,11 @@ impl From<u32> for EncodedTerm {
}
}
impl From<u8> for EncodedTerm {
fn from(value: u8) -> Self {
EncodedTerm::IntegerLiteral(value.into())
}
}
impl From<f32> for EncodedTerm {
fn from(value: f32) -> Self {
EncodedTerm::FloatLiteral(value)
@ -334,33 +333,27 @@ impl From<Decimal> for EncodedTerm {
}
}
impl From<Date<FixedOffset>> for EncodedTerm {
fn from(value: Date<FixedOffset>) -> Self {
impl From<Date> for EncodedTerm {
fn from(value: Date) -> Self {
EncodedTerm::DateLiteral(value)
}
}
impl From<NaiveDate> for EncodedTerm {
fn from(value: NaiveDate) -> Self {
EncodedTerm::NaiveDateLiteral(value)
impl From<Time> for EncodedTerm {
fn from(value: Time) -> Self {
EncodedTerm::TimeLiteral(value)
}
}
impl From<NaiveTime> for EncodedTerm {
fn from(value: NaiveTime) -> Self {
EncodedTerm::NaiveTimeLiteral(value)
}
}
impl From<DateTime<FixedOffset>> for EncodedTerm {
fn from(value: DateTime<FixedOffset>) -> Self {
impl From<DateTime> for EncodedTerm {
fn from(value: DateTime) -> Self {
EncodedTerm::DateTimeLiteral(value)
}
}
impl From<NaiveDateTime> for EncodedTerm {
fn from(value: NaiveDateTime) -> Self {
EncodedTerm::NaiveDateTimeLiteral(value)
impl From<Duration> for EncodedTerm {
fn from(value: Duration) -> Self {
EncodedTerm::DurationLiteral(value)
}
}
@ -436,6 +429,11 @@ impl<'a> From<rio::Literal<'a>> for EncodedTerm {
| "http://www.w3.org/2001/XMLSchema#dateTimeStamp" => {
parse_date_time_str(value)
}
"http://www.w3.org/2001/XMLSchema#duration"
| "http://www.w3.org/2001/XMLSchema#yearMonthDuration"
| "http://www.w3.org/2001/XMLSchema#dayTimeDuration" => {
parse_duration_str(value)
}
_ => None,
} {
Some(v) => v,
@ -545,43 +543,34 @@ impl<R: Read> TermReader for R {
self.read_i64::<LittleEndian>()?,
)),
TYPE_DECIMAL_LITERAL => {
let mut buffer = [0 as u8; 16];
let mut buffer = [0; 16];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::from_le_bytes(buffer)))
}
TYPE_DATE_LITERAL => Ok(EncodedTerm::DateLiteral(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::NaiveDateLiteral(
NaiveDate::from_num_days_from_ce_opt(self.read_i32::<LittleEndian>()?)
.ok_or_else(|| format_err!("Invalid date serialization"))?,
)),
TYPE_NAIVE_TIME_LITERAL => Ok(EncodedTerm::NaiveTimeLiteral(
NaiveTime::from_num_seconds_from_midnight_opt(
self.read_u32::<LittleEndian>()?,
self.read_u32::<LittleEndian>()?,
)
.ok_or_else(|| format_err!("Invalid time serialization"))?,
)),
TYPE_DATE_TIME_LITERAL => Ok(EncodedTerm::DateTimeLiteral(DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(
self.read_i64::<LittleEndian>()?,
self.read_u32::<LittleEndian>()?,
)
.ok_or_else(|| format_err!("Invalid date time serialization"))?,
FixedOffset::east_opt(self.read_i32::<LittleEndian>()?)
.ok_or_else(|| format_err!("Invalid timezone offset"))?,
))),
TYPE_NAIVE_DATE_TIME_LITERAL => Ok(EncodedTerm::NaiveDateTimeLiteral(
NaiveDateTime::from_timestamp_opt(
self.read_i64::<LittleEndian>()?,
self.read_u32::<LittleEndian>()?,
)
.ok_or_else(|| format_err!("Invalid date time serialization"))?,
)),
TYPE_DATE_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DateLiteral(Date::from_le_bytes(buffer)))
}
TYPE_TIME_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::TimeLiteral(Time::from_le_bytes(buffer)))
}
TYPE_DATE_TIME_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DateTimeLiteral(DateTime::from_le_bytes(
buffer,
)))
}
TYPE_DURATION_LITERAL => {
let mut buffer = [0; 24];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DurationLiteral(Duration::from_le_bytes(
buffer,
)))
}
_ => Err(format_err!("the term buffer has an invalid type id")),
}
}
@ -704,26 +693,10 @@ impl<W: Write> TermWriter for W {
EncodedTerm::DoubleLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::IntegerLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::DecimalLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::DateLiteral(value) => {
self.write_all(&value.num_days_from_ce().to_le_bytes())?;
self.write_all(&value.timezone().local_minus_utc().to_le_bytes())?;
}
EncodedTerm::NaiveDateLiteral(value) => {
self.write_all(&value.num_days_from_ce().to_le_bytes())?
}
EncodedTerm::NaiveTimeLiteral(value) => {
self.write_all(&value.num_seconds_from_midnight().to_le_bytes())?;
self.write_all(&value.nanosecond().to_le_bytes())?;
}
EncodedTerm::DateTimeLiteral(value) => {
self.write_all(&value.timestamp().to_le_bytes())?;
self.write_all(&value.timestamp_subsec_nanos().to_le_bytes())?;
self.write_all(&value.timezone().local_minus_utc().to_le_bytes())?;
}
EncodedTerm::NaiveDateTimeLiteral(value) => {
self.write_all(&value.timestamp().to_le_bytes())?;
self.write_all(&value.timestamp_subsec_nanos().to_le_bytes())?;
}
EncodedTerm::DateLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::TimeLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::DateTimeLiteral(value) => self.write_all(&value.to_le_bytes())?,
EncodedTerm::DurationLiteral(value) => self.write_all(&value.to_le_bytes())?,
}
Ok(())
}
@ -797,6 +770,7 @@ pub trait StrContainer {
self.insert_str(XSD_DATE_TIME_ID, xsd::DATE_TIME.as_str())?;
self.insert_str(XSD_DATE_ID, xsd::DATE.as_str())?;
self.insert_str(XSD_TIME_ID, xsd::TIME.as_str())?;
self.insert_str(XSD_DURATION_ID, xsd::DURATION.as_str())?;
Ok(())
}
}
@ -1028,6 +1002,11 @@ impl<S: StrContainer> Encoder for S {
| "http://www.w3.org/2001/XMLSchema#dateTimeStamp" => {
parse_date_time_str(value)
}
"http://www.w3.org/2001/XMLSchema#duration"
| "http://www.w3.org/2001/XMLSchema#yearMonthDuration"
| "http://www.w3.org/2001/XMLSchema#dayTimeDuration" => {
parse_duration_str(value)
}
_ => None,
} {
Some(v) => v,
@ -1072,39 +1051,19 @@ pub fn parse_decimal_str(value: &str) -> Option<EncodedTerm> {
}
pub fn parse_date_str(value: &str) -> Option<EncodedTerm> {
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) => Some(EncodedTerm::DateLiteral(value)),
Err(_) => match NaiveDate::parse_from_str(&value, "%Y-%m-%dZ") {
Ok(value) => Some(EncodedTerm::DateLiteral(Date::from_utc(
value,
FixedOffset::east(0),
))),
Err(_) => NaiveDate::parse_from_str(&value, "%Y-%m-%d")
.map(EncodedTerm::NaiveDateLiteral)
.ok(),
},
}
value.parse().map(EncodedTerm::DateLiteral).ok()
}
pub fn parse_time_str(value: &str) -> Option<EncodedTerm> {
NaiveTime::parse_from_str(&value, "%H:%M:%S")
.map(EncodedTerm::NaiveTimeLiteral)
.ok()
value.parse().map(EncodedTerm::TimeLiteral).ok()
}
pub fn parse_date_time_str(value: &str) -> Option<EncodedTerm> {
match DateTime::parse_from_rfc3339(&value) {
Ok(value) => Some(EncodedTerm::DateTimeLiteral(value)),
Err(_) => NaiveDateTime::parse_from_str(&value, "%Y-%m-%dT%H:%M:%S")
.map(EncodedTerm::NaiveDateTimeLiteral)
.ok(),
value.parse().map(EncodedTerm::DateTimeLiteral).ok()
}
pub fn parse_duration_str(value: &str) -> Option<EncodedTerm> {
value.parse().map(EncodedTerm::DurationLiteral).ok()
}
pub trait Decoder {
@ -1188,10 +1147,9 @@ impl<S: StrLookup> Decoder for S {
EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DateLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveDateLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveTimeLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::TimeLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DateTimeLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveDateTimeLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DurationLiteral(value) => Ok(Literal::from(value).into()),
}
}
}

@ -133,6 +133,8 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/property-path/manifest#pp35").unwrap(),
//SERVICE name from a BGP
NamedNode::parse("http://www.w3.org/2009/sparql/docs/tests/data-sparql11/service/manifest#service5").unwrap(),
// We use XSD 1.1 equality on dates
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/open-world/manifest#date-2").unwrap(),
];

Loading…
Cancel
Save