Adds native support of xsd:date and xsd:time without duration

pull/10/head
Tpt 6 years ago
parent 33c5534c84
commit 1499c2c96c
  1. 64
      lib/src/model/literal.rs
  2. 47
      lib/src/sparql/eval.rs
  3. 10
      lib/src/sparql/plan.rs
  4. 65
      lib/src/store/numeric_encoder.rs

@ -2,9 +2,7 @@ use crate::model::named_node::NamedNode;
use crate::model::vocab::rdf;
use crate::model::vocab::xsd;
use crate::utils::Escaper;
use chrono::DateTime;
use chrono::FixedOffset;
use chrono::NaiveDateTime;
use chrono::prelude::*;
use num_traits::identities::Zero;
use num_traits::FromPrimitive;
use num_traits::One;
@ -50,6 +48,8 @@ enum LiteralContent {
Double(OrderedFloat<f64>),
Integer(i128),
Decimal(Decimal),
NaiveDate(NaiveDate),
NaiveTime(NaiveTime),
DateTime(DateTime<FixedOffset>),
NaiveDateTime(NaiveDateTime),
TypedLiteral { value: String, datatype: NamedNode },
@ -106,6 +106,16 @@ impl Literal {
Ok(value) => LiteralContent::Decimal(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype },
}
} else if datatype == *xsd::DATE {
match NaiveDate::parse_from_str(&value, "%Y-%m-%d") {
Ok(value) => LiteralContent::NaiveDate(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype },
}
} else if datatype == *xsd::TIME {
match NaiveTime::parse_from_str(&value, "%H:%M:%S") {
Ok(value) => LiteralContent::NaiveTime(value),
Err(_) => LiteralContent::TypedLiteral { value, datatype },
}
} else if datatype == *xsd::DATE_TIME || datatype == *xsd::DATE_TIME_STAMP {
match DateTime::parse_from_rfc3339(&value) {
Ok(value) => LiteralContent::DateTime(value),
@ -142,6 +152,8 @@ impl Literal {
LiteralContent::Double(value) => Cow::Owned(value.to_string()),
LiteralContent::Integer(value) => Cow::Owned(value.to_string()),
LiteralContent::Decimal(value) => Cow::Owned(value.to_string()),
LiteralContent::NaiveDate(value) => Cow::Owned(value.to_string()),
LiteralContent::NaiveTime(value) => Cow::Owned(value.to_string()),
LiteralContent::DateTime(value) => Cow::Owned(value.to_string()),
LiteralContent::NaiveDateTime(value) => Cow::Owned(value.to_string()),
}
@ -169,6 +181,8 @@ impl Literal {
LiteralContent::Double(_) => &xsd::DOUBLE,
LiteralContent::Integer(_) => &xsd::INTEGER,
LiteralContent::Decimal(_) => &xsd::DECIMAL,
LiteralContent::NaiveDate(_) => &xsd::DATE,
LiteralContent::NaiveTime(_) => &xsd::TIME,
LiteralContent::DateTime(_) | LiteralContent::NaiveDateTime(_) => &xsd::DATE_TIME,
LiteralContent::TypedLiteral { ref datatype, .. } => datatype,
}
@ -233,6 +247,22 @@ impl Literal {
}
}
/// Checks if the literal has the datatype [xsd:date](http://www.w3.org/2001/XMLSchema#date) and is valid
pub fn is_date(&self) -> bool {
match self.0 {
LiteralContent::NaiveDate(_) => true,
_ => false,
}
}
/// Checks if the literal has the datatype [xsd:date](http://www.w3.org/2001/XMLSchema#time) and is valid
pub fn is_time(&self) -> bool {
match self.0 {
LiteralContent::NaiveTime(_) => true,
_ => false,
}
}
/// Checks if the literal has the datatype [xsd:dateTime](http://www.w3.org/2001/XMLSchema#dateTime) or one of its sub datatype and is valid
pub fn is_date_time(&self) -> bool {
match self.0 {
@ -328,6 +358,22 @@ impl Literal {
}
}
/// Returns the value of this literal as NaiveDate if possible
pub(crate) fn to_date(&self) -> Option<NaiveDate> {
match self.0 {
LiteralContent::NaiveDate(value) => Some(value),
_ => None,
}
}
/// Returns the value of this literal as NaiveTime if possible
pub(crate) fn to_time(&self) -> Option<NaiveTime> {
match self.0 {
LiteralContent::NaiveTime(value) => Some(value),
_ => None,
}
}
/// Returns the value of this literal as NaiveDateTime if possible
pub(crate) fn to_date_time(&self) -> Option<NaiveDateTime> {
match self.0 {
@ -437,6 +483,18 @@ impl From<Decimal> for Literal {
}
}
impl From<NaiveDate> for Literal {
fn from(value: NaiveDate) -> Self {
Literal(LiteralContent::NaiveDate(value))
}
}
impl From<NaiveTime> for Literal {
fn from(value: NaiveTime) -> Self {
Literal(LiteralContent::NaiveTime(value))
}
}
impl From<DateTime<FixedOffset>> for Literal {
fn from(value: DateTime<FixedOffset>) -> Self {
Literal(LiteralContent::DateTime(value))

@ -5,8 +5,7 @@ use crate::sparql::plan::*;
use crate::store::encoded::EncodedQuadsStore;
use crate::store::numeric_encoder::*;
use crate::Result;
use chrono::DateTime;
use chrono::NaiveDateTime;
use chrono::prelude::*;
use language_tags::LanguageTag;
use num_traits::identities::Zero;
use num_traits::FromPrimitive;
@ -658,6 +657,28 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
)),
_ => None,
},
PlanExpression::DateCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveDate(value) => Some(value.into()),
EncodedTerm::DateTime(value) => Some(value.date().naive_utc().into()), //TODO: use date with timezone
EncodedTerm::NaiveDateTime(value) => Some(value.date().into()),
EncodedTerm::SimpleLiteral { value_id }
| EncodedTerm::StringLiteral { value_id } => {
let value = self.store.get_str(value_id).ok()?;
Some(NaiveDate::parse_from_str(&value, "%Y-%m-%d").ok()?.into())
}
_ => None,
},
PlanExpression::TimeCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NaiveTime(value) => Some(value.into()),
EncodedTerm::DateTime(value) => Some(value.time().into()),
EncodedTerm::NaiveDateTime(value) => Some(value.time().into()),
EncodedTerm::SimpleLiteral { value_id }
| EncodedTerm::StringLiteral { value_id } => {
let value = self.store.get_str(value_id).ok()?;
Some(NaiveTime::parse_from_str(&value, "%H:%M:%S").ok()?.into())
}
_ => None,
},
PlanExpression::DateTimeCast(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::DateTime(value) => Some(value.into()),
EncodedTerm::NaiveDateTime(value) => Some(value.into()),
@ -709,6 +730,8 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
EncodedTerm::DoubleLiteral(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::IntegerLiteral(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::DecimalLiteral(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::NaiveDate(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::NaiveTime(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::DateTime(value) => self.store.insert_str(&value.to_string()).ok(),
EncodedTerm::NaiveDateTime(value) => self.store.insert_str(&value.to_string()).ok(),
}
@ -904,6 +927,26 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b),
_ => None,
},
EncodedTerm::NaiveDate(a) => if let EncodedTerm::NaiveDate(ref b) = b {
a.partial_cmp(b)
} else {
None
},
EncodedTerm::NaiveTime(a) => if let EncodedTerm::NaiveTime(ref b) = b {
a.partial_cmp(b)
} else {
None
},
EncodedTerm::DateTime(a) => if let EncodedTerm::DateTime(ref b) = b {
a.partial_cmp(b)
} else {
None
},
EncodedTerm::NaiveDateTime(a) => if let EncodedTerm::NaiveDateTime(ref b) = b {
a.partial_cmp(b)
} else {
None
},
_ => None,
}
}

@ -245,6 +245,8 @@ pub enum PlanExpression {
FloatCast(Box<PlanExpression>),
DecimalCast(Box<PlanExpression>),
IntegerCast(Box<PlanExpression>),
DateCast(Box<PlanExpression>),
TimeCast(Box<PlanExpression>),
DateTimeCast(Box<PlanExpression>),
StringCast(Box<PlanExpression>),
}
@ -295,6 +297,8 @@ impl PlanExpression {
| PlanExpression::FloatCast(e)
| PlanExpression::IntegerCast(e)
| PlanExpression::DecimalCast(e)
| PlanExpression::DateCast(e)
| PlanExpression::TimeCast(e)
| PlanExpression::DateTimeCast(e)
| PlanExpression::StringCast(e) => {
e.add_variables(set);
@ -690,7 +694,11 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> {
variables,
"integer",
)?
} else if *name == *xsd::DATE_TIME {
} else if *name == *xsd::DATE {
self.build_cast(parameters, PlanExpression::DateCast, variables, "date")?
} else if *name == *xsd::TIME {
self.build_cast(parameters, PlanExpression::TimeCast, variables, "time")?
} else if *name == *xsd::DATE_TIME {
self.build_cast(
parameters,
PlanExpression::DateTimeCast,

@ -4,9 +4,7 @@ use crate::model::*;
use crate::utils::MutexPoisonError;
use crate::Result;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use chrono::DateTime;
use chrono::FixedOffset;
use chrono::NaiveDateTime;
use chrono::prelude::*;
use failure::format_err;
use ordered_float::OrderedFloat;
use rust_decimal::Decimal;
@ -28,6 +26,8 @@ const XSD_DOUBLE_ID: u64 = 5;
const XSD_INTEGER_ID: u64 = 6;
const XSD_DECIMAL_ID: u64 = 7;
const XSD_DATE_TIME_ID: u64 = 8;
const XSD_DATE_ID: u64 = 9;
const XSD_TIME_ID: u64 = 10;
pub trait StringStore {
fn insert_str(&self, value: &str) -> Result<u64>;
@ -45,6 +45,8 @@ pub trait StringStore {
&& XSD_INTEGER_ID == self.insert_str(xsd::INTEGER.as_str())?
&& XSD_DECIMAL_ID == self.insert_str(xsd::DECIMAL.as_str())?
&& XSD_DATE_TIME_ID == self.insert_str(xsd::DATE_TIME.as_str())?
&& XSD_DATE_ID == self.insert_str(xsd::DATE.as_str())?
&& XSD_TIME_ID == self.insert_str(xsd::TIME.as_str())?
{
Ok(())
} else {
@ -131,6 +133,8 @@ const TYPE_INTEGER_LITERAL: u8 = 11;
const TYPE_DECIMAL_LITERAL: u8 = 12;
const TYPE_DATE_TIME_LITERAL: u8 = 13;
const TYPE_NAIVE_DATE_TIME_LITERAL: u8 = 14;
const TYPE_NAIVE_DATE_LITERAL: u8 = 15;
const TYPE_NAIVE_TIME_LITERAL: u8 = 16;
pub static ENCODED_DEFAULT_GRAPH: EncodedTerm = EncodedTerm::DefaultGraph {};
pub static ENCODED_EMPTY_SIMPLE_LITERAL: EncodedTerm = EncodedTerm::SimpleLiteral {
@ -160,6 +164,12 @@ pub static ENCODED_XSD_INTEGER_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode
pub static ENCODED_XSD_DECIMAL_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_DECIMAL_ID,
};
pub static ENCODED_XSD_DATE_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_DATE_ID,
};
pub static ENCODED_XSD_TIME_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_TIME_ID,
};
pub static ENCODED_XSD_DATE_TIME_NAMED_NODE: EncodedTerm = EncodedTerm::NamedNode {
iri_id: XSD_DATE_TIME_ID,
};
@ -178,6 +188,8 @@ pub enum EncodedTerm {
DoubleLiteral(OrderedFloat<f64>),
IntegerLiteral(i128),
DecimalLiteral(Decimal),
NaiveDate(NaiveDate),
NaiveTime(NaiveTime),
DateTime(DateTime<FixedOffset>),
NaiveDateTime(NaiveDateTime),
}
@ -208,6 +220,8 @@ impl EncodedTerm {
| EncodedTerm::DoubleLiteral(_)
| EncodedTerm::IntegerLiteral(_)
| EncodedTerm::DecimalLiteral(_)
| EncodedTerm::NaiveDate(_)
| EncodedTerm::NaiveTime(_)
| EncodedTerm::DateTime(_)
| EncodedTerm::NaiveDateTime(_) => true,
_ => false,
@ -228,6 +242,8 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(..) => Some(ENCODED_XSD_DOUBLE_NAMED_NODE),
EncodedTerm::IntegerLiteral(..) => Some(ENCODED_XSD_INTEGER_NAMED_NODE),
EncodedTerm::DecimalLiteral(..) => Some(ENCODED_XSD_DECIMAL_NAMED_NODE),
EncodedTerm::NaiveDate(..) => Some(ENCODED_XSD_DATE_NAMED_NODE),
EncodedTerm::NaiveTime(..) => Some(ENCODED_XSD_TIME_NAMED_NODE),
EncodedTerm::DateTime(..) | EncodedTerm::NaiveDateTime(..) => {
Some(ENCODED_XSD_DATE_TIME_NAMED_NODE)
}
@ -250,6 +266,8 @@ impl EncodedTerm {
EncodedTerm::DoubleLiteral(_) => TYPE_DOUBLE_LITERAL,
EncodedTerm::IntegerLiteral(_) => TYPE_INTEGER_LITERAL,
EncodedTerm::DecimalLiteral(_) => TYPE_DECIMAL_LITERAL,
EncodedTerm::NaiveDate(_) => TYPE_NAIVE_DATE_LITERAL,
EncodedTerm::NaiveTime(_) => TYPE_NAIVE_TIME_LITERAL,
EncodedTerm::DateTime(_) => TYPE_DATE_TIME_LITERAL,
EncodedTerm::NaiveDateTime(_) => TYPE_NAIVE_DATE_TIME_LITERAL,
}
@ -286,6 +304,18 @@ impl From<Decimal> for EncodedTerm {
}
}
impl From<NaiveDate> for EncodedTerm {
fn from(value: NaiveDate) -> Self {
EncodedTerm::NaiveDate(value)
}
}
impl From<NaiveTime> for EncodedTerm {
fn from(value: NaiveTime) -> Self {
EncodedTerm::NaiveTime(value)
}
}
impl From<DateTime<FixedOffset>> for EncodedTerm {
fn from(value: DateTime<FixedOffset>) -> Self {
EncodedTerm::DateTime(value)
@ -377,6 +407,16 @@ impl<R: Read> TermReader for R {
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::deserialize(buffer)))
}
TYPE_NAIVE_DATE_LITERAL => Ok(EncodedTerm::NaiveDate(
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::NaiveTime(
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::DateTime(DateTime::from_utc(
NaiveDateTime::from_timestamp_opt(
self.read_i64::<LittleEndian>()?,
@ -473,6 +513,13 @@ impl<R: Write> TermWriter for R {
EncodedTerm::DoubleLiteral(value) => self.write_f64::<LittleEndian>(*value)?,
EncodedTerm::IntegerLiteral(value) => self.write_i128::<LittleEndian>(value)?,
EncodedTerm::DecimalLiteral(value) => self.write_all(&value.serialize())?,
EncodedTerm::NaiveDate(value) => {
self.write_i32::<LittleEndian>(value.num_days_from_ce())?;
}
EncodedTerm::NaiveTime(value) => {
self.write_u32::<LittleEndian>(value.num_seconds_from_midnight())?;
self.write_u32::<LittleEndian>(value.nanosecond())?;
}
EncodedTerm::DateTime(value) => {
self.write_i64::<LittleEndian>(value.timestamp())?;
self.write_u32::<LittleEndian>(value.timestamp_subsec_nanos())?;
@ -571,6 +618,16 @@ impl<S: StringStore> Encoder<S> {
.to_decimal()
.ok_or_else(|| format_err!("decimal literal without decimal value"))?
.into()
} else if literal.is_date() {
literal
.to_date()
.ok_or_else(|| format_err!("date literal without date value"))?
.into()
} else if literal.is_time() {
literal
.to_time()
.ok_or_else(|| format_err!("time literal without time value"))?
.into()
} else if literal.is_date_time_stamp() {
literal
.to_date_time_stamp()
@ -665,6 +722,8 @@ impl<S: StringStore> Encoder<S> {
EncodedTerm::DoubleLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::IntegerLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveDate(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveTime(value) => Ok(Literal::from(value).into()),
EncodedTerm::DateTime(value) => Ok(Literal::from(value).into()),
EncodedTerm::NaiveDateTime(value) => Ok(Literal::from(value).into()),
}

Loading…
Cancel
Save