Allows BlankNode struct to store any valid blank node identifier

Closes #34
pull/41/head
Tpt 4 years ago
parent 3926fdc219
commit 71aa5a6c79
  1. 19
      js/src/model.rs
  2. 14
      lib/src/error.rs
  3. 2
      lib/src/lib.rs
  4. 189
      lib/src/model/blank_node.rs
  5. 3
      lib/src/model/mod.rs
  6. 9
      lib/src/model/named_node.rs
  7. 56
      lib/src/sparql/eval.rs
  8. 18
      lib/src/sparql/parser.rs
  9. 2
      lib/src/sparql/plan_builder.rs
  10. 14
      lib/src/sparql/xml_results.rs
  11. 72
      lib/src/store/memory.rs
  12. 58
      lib/src/store/numeric_encoder.rs
  13. 4
      lib/src/store/rocksdb.rs
  14. 4
      lib/src/store/sled.rs
  15. 8
      lib/tests/service_test_cases.rs
  16. 2
      lib/tests/wasm.rs
  17. 16
      testsuite/src/manifest.rs
  18. 28
      testsuite/src/sparql_evaluator.rs
  19. 42
      testsuite/src/vocab.rs
  20. 4
      wikibase/src/loader.rs

@ -15,7 +15,7 @@ pub struct JsDataFactory {
impl JsDataFactory { impl JsDataFactory {
#[wasm_bindgen(js_name = namedNode)] #[wasm_bindgen(js_name = namedNode)]
pub fn named_node(&self, value: String) -> Result<JsNamedNode, JsValue> { pub fn named_node(&self, value: String) -> Result<JsNamedNode, JsValue> {
NamedNode::parse(value) NamedNode::new(value)
.map(|v| v.into()) .map(|v| v.into())
.map_err(|v| UriError::new(&v.to_string()).into()) .map_err(|v| UriError::new(&v.to_string()).into())
} }
@ -23,9 +23,7 @@ impl JsDataFactory {
#[wasm_bindgen(js_name = blankNode)] #[wasm_bindgen(js_name = blankNode)]
pub fn blank_node(&self, value: Option<String>) -> Result<JsBlankNode, JsValue> { pub fn blank_node(&self, value: Option<String>) -> Result<JsBlankNode, JsValue> {
Ok(if let Some(value) = value { Ok(if let Some(value) = value {
BlankNode::new_from_unique_id(u128::from_str_radix(&value, 16).map_err(|_| { BlankNode::new(value).map_err(to_err)?
format_err!("Oxigraph only supports BlankNode created with Oxigraph DataFactory")
})?)
} else { } else {
BlankNode::default() BlankNode::default()
} }
@ -511,26 +509,19 @@ impl FromJsConverter {
let term_type = Reflect::get(&value, &self.term_type)?; let term_type = Reflect::get(&value, &self.term_type)?;
if let Some(term_type) = term_type.as_string() { if let Some(term_type) = term_type.as_string() {
match term_type.as_str() { match term_type.as_str() {
"NamedNode" => Ok(NamedNode::parse( "NamedNode" => Ok(NamedNode::new(
Reflect::get(&value, &self.value)? Reflect::get(&value, &self.value)?
.as_string() .as_string()
.ok_or_else(|| format_err!("NamedNode should have a string value"))?, .ok_or_else(|| format_err!("NamedNode should have a string value"))?,
) )
.map_err(|v| UriError::new(&v.to_string()))? .map_err(|v| UriError::new(&v.to_string()))?
.into()), .into()),
"BlankNode" => Ok(BlankNode::new_from_unique_id( "BlankNode" => Ok(BlankNode::new(
u128::from_str_radix(
&Reflect::get(&value, &self.value)? &Reflect::get(&value, &self.value)?
.as_string() .as_string()
.ok_or_else(|| format_err!("BlankNode should have a string value"))?, .ok_or_else(|| format_err!("BlankNode should have a string value"))?,
16,
)
.map_err(|_| {
format_err!(
"Oxigraph only supports BlankNode created with Oxigraph DataFactory"
)
})?,
) )
.map_err(to_err)?
.into()), .into()),
"Literal" => { "Literal" => {
if let JsTerm::NamedNode(datatype) = if let JsTerm::NamedNode(datatype) =

@ -1,6 +1,5 @@
use crate::model::{BlankNodeIdParseError, IriParseError, LanguageTagParseError};
use crate::sparql::SparqlParseError; use crate::sparql::SparqlParseError;
use oxilangtag::LanguageTagParseError;
use oxiri::IriParseError;
use rio_turtle::TurtleError; use rio_turtle::TurtleError;
use rio_xml::RdfXmlError; use rio_xml::RdfXmlError;
use std::error; use std::error;
@ -24,6 +23,7 @@ impl fmt::Display for Error {
ErrorKind::Io(e) => e.fmt(f), ErrorKind::Io(e) => e.fmt(f),
ErrorKind::FromUtf8(e) => e.fmt(f), ErrorKind::FromUtf8(e) => e.fmt(f),
ErrorKind::Iri(e) => e.fmt(f), ErrorKind::Iri(e) => e.fmt(f),
ErrorKind::BlankNode(e) => e.fmt(f),
ErrorKind::LanguageTag(e) => e.fmt(f), ErrorKind::LanguageTag(e) => e.fmt(f),
ErrorKind::Other(e) => e.fmt(f), ErrorKind::Other(e) => e.fmt(f),
} }
@ -37,6 +37,7 @@ impl error::Error for Error {
ErrorKind::Io(e) => Some(e), ErrorKind::Io(e) => Some(e),
ErrorKind::FromUtf8(e) => Some(e), ErrorKind::FromUtf8(e) => Some(e),
ErrorKind::Iri(e) => Some(e), ErrorKind::Iri(e) => Some(e),
ErrorKind::BlankNode(e) => Some(e),
ErrorKind::LanguageTag(e) => Some(e), ErrorKind::LanguageTag(e) => Some(e),
ErrorKind::Other(e) => Some(e.as_ref()), ErrorKind::Other(e) => Some(e.as_ref()),
} }
@ -65,6 +66,7 @@ enum ErrorKind {
Io(io::Error), Io(io::Error),
FromUtf8(FromUtf8Error), FromUtf8(FromUtf8Error),
Iri(IriParseError), Iri(IriParseError),
BlankNode(BlankNodeIdParseError),
LanguageTag(LanguageTagParseError), LanguageTag(LanguageTagParseError),
Other(Box<dyn error::Error + Send + Sync + 'static>), Other(Box<dyn error::Error + Send + Sync + 'static>),
} }
@ -93,6 +95,14 @@ impl From<IriParseError> for Error {
} }
} }
impl From<BlankNodeIdParseError> for Error {
fn from(error: BlankNodeIdParseError) -> Self {
Self {
inner: ErrorKind::BlankNode(error),
}
}
}
impl From<LanguageTagParseError> for Error { impl From<LanguageTagParseError> for Error {
fn from(error: LanguageTagParseError) -> Self { fn from(error: LanguageTagParseError) -> Self {
Self { Self {

@ -23,7 +23,7 @@
//! let store = MemoryStore::new(); //! let store = MemoryStore::new();
//! //!
//! // insertion //! // insertion
//! let ex = NamedNode::parse("http://example.com")?; //! let ex = NamedNode::new("http://example.com")?;
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); //! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
//! store.insert(quad.clone()); //! store.insert(quad.clone());
//! //!

@ -1,48 +1,100 @@
use rand::random; use rand::random;
use rio_api::model as rio; use rio_api::model as rio;
use std::error::Error;
use std::fmt; use std::fmt;
use std::io::Write; use std::io::Write;
use std::str; use std::str;
/// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
/// ///
/// This implementation enforces that the blank node id is a uniquely generated ID to easily ensure /// The common way to create a new blank node is to use the `BlankNode::default` trait method.
/// that it is not possible for two blank nodes to share an id.
/// ///
/// The common way to create a new blank node is to use the `Default::default` trait method. /// It is also possible to create a blank node from a blank node identifier using the `BlankNode::new` method.
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation. /// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation.
/// `BlankNode::default().to_string()` should return something like `_:00112233445566778899aabbccddeeff` /// `BlankNode::default().to_string()` should return something like `_:00112233445566778899aabbccddeeff`
/// ///
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct BlankNode { pub struct BlankNode(BlankNodeContent);
id: u128,
str: [u8; 32], #[derive(PartialEq, Eq, Debug, Clone, Hash)]
enum BlankNodeContent {
Named(String),
Anonymous { id: u128, str: [u8; 32] },
} }
impl BlankNode { impl BlankNode {
/// Creates a blank node from a unique id /// Creates a blank node from a unique identifier.
///
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
///
/// In most cases, it is much more convenient to create a blank node using `BlankNode::default()`.
/// `BlankNode::default()` creates a random ID that could be easily inlined by Oxigraph stores.
pub fn new(id: impl Into<String>) -> Result<Self, BlankNodeIdParseError> {
let id = id.into();
validate_blank_node_identifier(&id)?;
Ok(Self::new_unchecked(id))
}
/// Creates a blank node from a unique identifier without validation.
/// ///
/// In most cases, you **should not*** create a blank node this way, /// It is the caller's responsibility to ensure that `id` is a valid blank node identifier
/// but should use `Default::default` instead. /// according to N-Triples, Turtle and SPARQL grammars.
///
/// Except if you really know what you do, you should use [`new`](#method.new).
pub fn new_unchecked(id: impl Into<String>) -> Self {
let id = id.into();
if let Ok(numerical_id) = u128::from_str_radix(&id, 16) {
let result = Self::new_from_unique_id(numerical_id);
if result.as_str() == id {
result
} else {
Self(BlankNodeContent::Named(id))
}
} else {
Self(BlankNodeContent::Named(id))
}
}
/// Creates a blank node from a unique numerical id
/// ///
/// This method is only exposed for low-level library, /// In most cases, it is much more convenient to create a blank node using `BlankNode::default()`.
/// in particular bindings to other languages or APIs. pub fn new_from_unique_id(id: impl Into<u128>) -> Self {
pub fn new_from_unique_id(id: u128) -> Self { let id = id.into();
let mut str = [0; 32]; let mut str = [0; 32];
write!(&mut str[..], "{:x}", id).unwrap(); write!(&mut str[..], "{:x}", id).unwrap();
Self { id, str } Self(BlankNodeContent::Anonymous { id, str })
} }
/// Returns the underlying ID of this blank node /// Returns the underlying ID of this blank node
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
let len = self.str.iter().position(|x| x == &0).unwrap_or(32); match &self.0 {
str::from_utf8(&self.str[..len]).unwrap() BlankNodeContent::Named(id) => id,
BlankNodeContent::Anonymous { str, .. } => {
let len = str.iter().position(|x| x == &0).unwrap_or(32);
str::from_utf8(&str[..len]).unwrap()
}
}
} }
/// Returns the internal ID of this blank node /// Returns the underlying ID of this blank node
pub const fn id(&self) -> u128 { pub fn into_string(self) -> String {
self.id match self.0 {
BlankNodeContent::Named(id) => id,
BlankNodeContent::Anonymous { str, .. } => {
let len = str.iter().position(|x| x == &0).unwrap_or(32);
str::from_utf8(&str[..len]).unwrap().to_owned()
}
}
}
/// Returns the internal numerical ID of this blank node, if it exists
pub(crate) fn id(&self) -> Option<u128> {
match self.0 {
BlankNodeContent::Named(_) => None,
BlankNodeContent::Anonymous { id, .. } => Some(id),
}
} }
} }
@ -65,19 +117,114 @@ impl<'a> From<&'a BlankNode> for rio::BlankNode<'a> {
} }
} }
fn validate_blank_node_identifier(id: &str) -> Result<(), BlankNodeIdParseError> {
let mut chars = id.chars();
let front = chars.next().ok_or(BlankNodeIdParseError {})?;
match front {
'0'..='9'
| '_'
| ':'
| 'A'..='Z'
| 'a'..='z'
| '\u{00C0}'..='\u{00D6}'
| '\u{00D8}'..='\u{00F6}'
| '\u{00F8}'..='\u{02FF}'
| '\u{0370}'..='\u{037D}'
| '\u{037F}'..='\u{1FFF}'
| '\u{200C}'..='\u{200D}'
| '\u{2070}'..='\u{218F}'
| '\u{2C00}'..='\u{2FEF}'
| '\u{3001}'..='\u{D7FF}'
| '\u{F900}'..='\u{FDCF}'
| '\u{FDF0}'..='\u{FFFD}'
| '\u{10000}'..='\u{EFFFF}' => (),
_ => return Err(BlankNodeIdParseError {}),
}
for c in chars {
match c {
'.' // validated later
| '-'
| '0'..='9'
| '\u{00B7}'
| '\u{0300}'..='\u{036F}'
| '\u{203F}'..='\u{2040}'
| '_'
| ':'
| 'A'..='Z'
| 'a'..='z'
| '\u{00C0}'..='\u{00D6}'
| '\u{00D8}'..='\u{00F6}'
| '\u{00F8}'..='\u{02FF}'
| '\u{0370}'..='\u{037D}'
| '\u{037F}'..='\u{1FFF}'
| '\u{200C}'..='\u{200D}'
| '\u{2070}'..='\u{218F}'
| '\u{2C00}'..='\u{2FEF}'
| '\u{3001}'..='\u{D7FF}'
| '\u{F900}'..='\u{FDCF}'
| '\u{FDF0}'..='\u{FFFD}'
| '\u{10000}'..='\u{EFFFF}' => (),
_ => return Err(BlankNodeIdParseError {}),
}
}
// Could not end with a dot
if id.ends_with('.') {
Err(BlankNodeIdParseError {})
} else {
Ok(())
}
}
/// An error raised during `BlankNode` validation.
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct BlankNodeIdParseError {}
impl fmt::Display for BlankNodeIdParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "The blank node identifier is invalid")
}
}
impl Error for BlankNodeIdParseError {}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn as_str_partial() { fn as_str_partial() {
let b = BlankNode::new_from_unique_id(0x42); let b = BlankNode::new_from_unique_id(0x42_u128);
assert_eq!(b.as_str(), "42"); assert_eq!(b.as_str(), "42");
} }
#[test] #[test]
fn as_str_full() { fn as_str_full() {
let b = BlankNode::new_from_unique_id(0x7777_6666_5555_4444_3333_2222_1111_0000); let b = BlankNode::new_from_unique_id(0x7777_6666_5555_4444_3333_2222_1111_0000_u128);
assert_eq!(b.as_str(), "77776666555544443333222211110000"); assert_eq!(b.as_str(), "77776666555544443333222211110000");
} }
#[test]
fn new_validation() {
assert!(BlankNode::new("").is_err());
assert!(BlankNode::new("a").is_ok());
assert!(BlankNode::new("-").is_err());
assert!(BlankNode::new("a-").is_ok());
assert!(BlankNode::new(".").is_err());
assert!(BlankNode::new("a.").is_err());
assert!(BlankNode::new("a.a").is_ok());
}
#[test]
fn new_numerical() {
assert_eq!(
BlankNode::new("100a").unwrap(),
BlankNode::new_from_unique_id(0x100a_u128),
);
assert_ne!(
BlankNode::new("100A").unwrap(),
BlankNode::new_from_unique_id(0x100a_u128)
);
}
} }

@ -10,9 +10,12 @@ pub mod vocab;
pub(crate) mod xsd; pub(crate) mod xsd;
pub use crate::model::blank_node::BlankNode; pub use crate::model::blank_node::BlankNode;
pub use crate::model::blank_node::BlankNodeIdParseError;
pub use crate::model::literal::Literal; pub use crate::model::literal::Literal;
pub use crate::model::named_node::NamedNode; pub use crate::model::named_node::NamedNode;
pub use crate::model::triple::NamedOrBlankNode; pub use crate::model::triple::NamedOrBlankNode;
pub use crate::model::triple::Quad; pub use crate::model::triple::Quad;
pub use crate::model::triple::Term; pub use crate::model::triple::Term;
pub use crate::model::triple::Triple; pub use crate::model::triple::Triple;
pub use oxilangtag::LanguageTagParseError;
pub use oxiri::IriParseError;

@ -10,7 +10,7 @@ use std::fmt;
/// ///
/// assert_eq!( /// assert_eq!(
/// "<http://example.com/foo>", /// "<http://example.com/foo>",
/// NamedNode::parse("http://example.com/foo").unwrap().to_string() /// NamedNode::new("http://example.com/foo").unwrap().to_string()
/// ) /// )
/// ``` /// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
@ -20,10 +20,15 @@ pub struct NamedNode {
impl NamedNode { impl NamedNode {
/// Builds and validate an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) /// Builds and validate an RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri)
pub fn parse(iri: impl Into<String>) -> Result<Self, IriParseError> { pub fn new(iri: impl Into<String>) -> Result<Self, IriParseError> {
Ok(Self::new_from_iri(Iri::parse(iri.into())?)) Ok(Self::new_from_iri(Iri::parse(iri.into())?))
} }
#[deprecated(note = "Use the `new` method")]
pub fn parse(iri: impl Into<String>) -> Result<Self, IriParseError> {
Self::new(iri)
}
pub(crate) fn new_from_iri(iri: Iri<String>) -> Self { pub(crate) fn new_from_iri(iri: Iri<String>) -> Self {
Self::new_unchecked(iri.into_inner()) Self::new_unchecked(iri.into_inner())
} }

@ -19,13 +19,12 @@ use rio_api::model as rio;
use sha1::Sha1; use sha1::Sha1;
use sha2::{Sha256, Sha384, Sha512}; use sha2::{Sha256, Sha384, Sha512};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::hash::Hash; use std::hash::Hash;
use std::iter::Iterator; use std::iter::Iterator;
use std::iter::{empty, once}; use std::iter::{empty, once};
use std::str; use std::str;
use std::sync::Mutex;
const REGEX_SIZE_LIMIT: usize = 1_000_000; const REGEX_SIZE_LIMIT: usize = 1_000_000;
@ -34,7 +33,6 @@ type EncodedTuplesIterator<'a> = Box<dyn Iterator<Item = Result<EncodedTuple>> +
pub(crate) struct SimpleEvaluator<S: ReadableEncodedStore> { pub(crate) struct SimpleEvaluator<S: ReadableEncodedStore> {
dataset: DatasetView<S>, dataset: DatasetView<S>,
base_iri: Option<Iri<String>>, base_iri: Option<Iri<String>>,
bnodes_map: Mutex<BTreeMap<StrHash, u128>>,
now: DateTime, now: DateTime,
service_handler: Box<dyn ServiceHandler>, service_handler: Box<dyn ServiceHandler>,
} }
@ -47,7 +45,6 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
) -> Self { ) -> Self {
Self { Self {
dataset, dataset,
bnodes_map: Mutex::new(BTreeMap::default()),
base_iri, base_iri,
now: DateTime::now().unwrap(), now: DateTime::now().unwrap(),
service_handler, service_handler,
@ -946,23 +943,12 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
} }
} }
PlanExpression::BNode(id) => match id { PlanExpression::BNode(id) => match id {
Some(id) => { Some(id) => Some(
if let EncodedTerm::StringLiteral { value_id } = (&BlankNode::new(self.to_simple_string(self.eval_expression(id, tuple)?)?)
self.eval_expression(id, tuple)? .ok()?)
{ .into(),
Some(EncodedTerm::BlankNode { ),
id: *self None => Some(EncodedTerm::InlineBlankNode {
.bnodes_map
.lock()
.ok()?
.entry(value_id)
.or_insert_with(random::<u128>),
})
} else {
None
}
}
None => Some(EncodedTerm::BlankNode {
id: random::<u128>(), id: random::<u128>(),
}), }),
}, },
@ -1409,7 +1395,7 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
match term { match term {
EncodedTerm::DefaultGraph => None, EncodedTerm::DefaultGraph => None,
EncodedTerm::NamedNode { iri_id } => Some(iri_id), EncodedTerm::NamedNode { iri_id } => Some(iri_id),
EncodedTerm::BlankNode { .. } => None, EncodedTerm::InlineBlankNode { .. } | EncodedTerm::NamedBlankNode { .. } => None,
EncodedTerm::StringLiteral { value_id } EncodedTerm::StringLiteral { value_id }
| EncodedTerm::LangStringLiteral { value_id, .. } | EncodedTerm::LangStringLiteral { value_id, .. }
| EncodedTerm::TypedLiteral { value_id, .. } => Some(value_id), | EncodedTerm::TypedLiteral { value_id, .. } => Some(value_id),
@ -1617,7 +1603,8 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
match a { match a {
EncodedTerm::DefaultGraph EncodedTerm::DefaultGraph
| EncodedTerm::NamedNode { .. } | EncodedTerm::NamedNode { .. }
| EncodedTerm::BlankNode { .. } | EncodedTerm::InlineBlankNode { .. }
| EncodedTerm::NamedBlankNode { .. }
| EncodedTerm::LangStringLiteral { .. } => Some(a == b), | EncodedTerm::LangStringLiteral { .. } => Some(a == b),
EncodedTerm::StringLiteral { value_id: a } => match b { EncodedTerm::StringLiteral { value_id: a } => match b {
EncodedTerm::StringLiteral { value_id: b } => Some(a == b), EncodedTerm::StringLiteral { value_id: b } => Some(a == b),
@ -1664,7 +1651,8 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
EncodedTerm::TypedLiteral { .. } => match b { EncodedTerm::TypedLiteral { .. } => match b {
EncodedTerm::TypedLiteral { .. } if a == b => Some(true), EncodedTerm::TypedLiteral { .. } if a == b => Some(true),
EncodedTerm::NamedNode { .. } EncodedTerm::NamedNode { .. }
| EncodedTerm::BlankNode { .. } | EncodedTerm::InlineBlankNode { .. }
| EncodedTerm::NamedBlankNode { .. }
| EncodedTerm::LangStringLiteral { .. } => Some(false), | EncodedTerm::LangStringLiteral { .. } => Some(false),
_ => None, _ => None,
}, },
@ -1706,24 +1694,26 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
fn cmp_terms(&self, a: Option<EncodedTerm>, b: Option<EncodedTerm>) -> Ordering { fn cmp_terms(&self, a: Option<EncodedTerm>, b: Option<EncodedTerm>) -> Ordering {
match (a, b) { match (a, b) {
(Some(a), Some(b)) => match a { (Some(a), Some(b)) => match a {
EncodedTerm::BlankNode { id: a } => { EncodedTerm::InlineBlankNode { .. } | EncodedTerm::NamedBlankNode { .. } => {
if let EncodedTerm::BlankNode { id: b } = b { match b {
a.cmp(&b) EncodedTerm::InlineBlankNode { .. }
} else { | EncodedTerm::NamedBlankNode { .. } => Ordering::Equal,
Ordering::Less _ => Ordering::Less,
} }
} }
EncodedTerm::NamedNode { iri_id: a } => match b { EncodedTerm::NamedNode { iri_id: a } => match b {
EncodedTerm::NamedNode { iri_id: b } => { EncodedTerm::NamedNode { iri_id: b } => {
self.compare_str_ids(a, b).unwrap_or(Ordering::Equal) self.compare_str_ids(a, b).unwrap_or(Ordering::Equal)
} }
EncodedTerm::BlankNode { .. } => Ordering::Greater, EncodedTerm::InlineBlankNode { .. } | EncodedTerm::NamedBlankNode { .. } => {
Ordering::Greater
}
_ => Ordering::Less, _ => Ordering::Less,
}, },
a => match b { a => match b {
EncodedTerm::NamedNode { .. } | EncodedTerm::BlankNode { .. } => { EncodedTerm::NamedNode { .. }
Ordering::Greater | EncodedTerm::InlineBlankNode { .. }
} | EncodedTerm::NamedBlankNode { .. } => Ordering::Greater,
b => self.partial_cmp_literals(a, b).unwrap_or(Ordering::Equal), b => self.partial_cmp_literals(a, b).unwrap_or(Ordering::Equal),
}, },
}, },

@ -38,8 +38,8 @@ impl Query {
None None
}, },
namespaces: HashMap::default(), namespaces: HashMap::default(),
bnodes_map: HashMap::default(),
used_bnodes: HashSet::default(), used_bnodes: HashSet::default(),
currently_used_bnodes: HashSet::default(),
aggregations: Vec::default(), aggregations: Vec::default(),
}; };
@ -356,8 +356,8 @@ enum Either<L, R> {
pub struct ParserState { pub struct ParserState {
base_iri: Option<Iri<String>>, base_iri: Option<Iri<String>>,
namespaces: HashMap<String, String>, namespaces: HashMap<String, String>,
bnodes_map: HashMap<String, BlankNode>, used_bnodes: HashSet<BlankNode>,
used_bnodes: HashSet<String>, currently_used_bnodes: HashSet<BlankNode>,
aggregations: Vec<Vec<(Aggregation, Variable)>>, aggregations: Vec<Vec<(Aggregation, Variable)>>,
} }
@ -841,8 +841,8 @@ parser! {
} }
// We deal with blank nodes aliases rule (TODO: partial for now) // We deal with blank nodes aliases rule (TODO: partial for now)
state.used_bnodes.extend(state.bnodes_map.keys().cloned()); state.used_bnodes.extend(state.currently_used_bnodes.iter().cloned());
state.bnodes_map.clear(); state.currently_used_bnodes.clear();
if let Some(filter) = filter { if let Some(filter) = filter {
GraphPattern::Filter(filter, Box::new(g)) GraphPattern::Filter(filter, Box::new(g))
@ -1534,10 +1534,14 @@ parser! {
//[138] //[138]
rule BlankNode() -> BlankNode = rule BlankNode() -> BlankNode =
b:BLANK_NODE_LABEL() {? b:BLANK_NODE_LABEL() {?
if state.used_bnodes.contains(b) { match BlankNode::new(b) {
Ok(node) => if state.used_bnodes.contains(&node) {
Err("Already used blank node id") Err("Already used blank node id")
} else { } else {
Ok(*state.bnodes_map.entry(b.to_string()).or_insert_with(BlankNode::default)) state.currently_used_bnodes.insert(node.clone());
Ok(node)
},
Err(_) => Err("Invalid blank node identifier")
} }
} / } /
ANON() { BlankNode::default() } ANON() { BlankNode::default() }

@ -981,7 +981,7 @@ fn bnode_key(blank_nodes: &mut Vec<BlankNode>, blank_node: &BlankNode) -> usize
match slice_key(blank_nodes, blank_node) { match slice_key(blank_nodes, blank_node) {
Some(key) => key, Some(key) => key,
None => { None => {
blank_nodes.push(*blank_node); blank_nodes.push(blank_node.clone());
blank_nodes.len() - 1 blank_nodes.len() - 1
} }
} }

@ -177,7 +177,6 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
buffer: Vec::default(), buffer: Vec::default(),
namespace_buffer, namespace_buffer,
mapping, mapping,
bnodes_map: BTreeMap::default(),
}), }),
))); )));
} else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" { } else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" {
@ -250,7 +249,6 @@ struct ResultsIterator<R: BufRead> {
buffer: Vec<u8>, buffer: Vec<u8>,
namespace_buffer: Vec<u8>, namespace_buffer: Vec<u8>,
mapping: BTreeMap<Vec<u8>, usize>, mapping: BTreeMap<Vec<u8>, usize>,
bnodes_map: BTreeMap<Vec<u8>, BlankNode>,
} }
impl<R: BufRead> Iterator for ResultsIterator<R> { impl<R: BufRead> Iterator for ResultsIterator<R> {
@ -343,7 +341,7 @@ impl<R: BufRead> ResultsIterator<R> {
if attr.key == b"xml:lang" { if attr.key == b"xml:lang" {
lang = Some(attr.unescape_and_decode_value(&self.reader)?); lang = Some(attr.unescape_and_decode_value(&self.reader)?);
} else if attr.key == b"datatype" { } else if attr.key == b"datatype" {
datatype = Some(NamedNode::parse( datatype = Some(NamedNode::new(
attr.unescape_and_decode_value(&self.reader)?, attr.unescape_and_decode_value(&self.reader)?,
)?); )?);
} }
@ -363,16 +361,10 @@ impl<R: BufRead> ResultsIterator<R> {
let data = event.unescaped()?; let data = event.unescaped()?;
match state { match state {
State::Uri => { State::Uri => {
term = Some(NamedNode::parse(self.reader.decode(&data)?)?.into()) term = Some(NamedNode::new(self.reader.decode(&data)?)?.into())
} }
State::BNode => { State::BNode => {
term = Some( term = Some(BlankNode::new(self.reader.decode(&data)?)?.into())
self.bnodes_map
.entry(data.to_vec())
.or_insert_with(BlankNode::default)
.clone()
.into(),
)
} }
State::Literal => { State::Literal => {
term = Some( term = Some(

@ -27,7 +27,7 @@ use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// store.insert(quad.clone()); /// store.insert(quad.clone());
/// ///
@ -90,7 +90,7 @@ impl MemoryStore {
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
/// // insertions /// // insertions
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
/// ///
/// // SPARQL query /// // SPARQL query
@ -135,7 +135,7 @@ impl MemoryStore {
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// store.insert(quad.clone()); /// store.insert(quad.clone());
/// ///
@ -182,7 +182,7 @@ impl MemoryStore {
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// ///
/// // transaction /// // transaction
@ -223,7 +223,7 @@ impl MemoryStore {
/// ///
/// // quad filter /// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
@ -253,7 +253,7 @@ impl MemoryStore {
/// ///
/// // quad filter /// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
@ -857,7 +857,7 @@ impl<'a> MemoryTransaction<'a> {
/// ///
/// // quad filter /// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
@ -886,7 +886,7 @@ impl<'a> MemoryTransaction<'a> {
/// ///
/// // quad filter /// // quad filter
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect();
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results); /// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
@ -987,8 +987,8 @@ fn iso_canonicalize(g: &MemoryStore) -> Vec<Vec<u8>> {
fn distinguish( fn distinguish(
g: &MemoryStore, g: &MemoryStore,
hash: &TrivialHashMap<u128, u64>, hash: &TrivialHashMap<EncodedTerm, u64>,
partition: &[(u64, Vec<u128>)], partition: &[(u64, Vec<EncodedTerm>)],
) -> Vec<Vec<u8>> { ) -> Vec<Vec<u8>> {
let b_prime = partition let b_prime = partition
.iter() .iter()
@ -1021,19 +1021,21 @@ fn distinguish(
fn hash_bnodes( fn hash_bnodes(
g: &MemoryStore, g: &MemoryStore,
mut hashes: TrivialHashMap<u128, u64>, mut hashes: TrivialHashMap<EncodedTerm, u64>,
) -> (TrivialHashMap<u128, u64>, Vec<(u64, Vec<u128>)>) { ) -> (
TrivialHashMap<EncodedTerm, u64>,
Vec<(u64, Vec<EncodedTerm>)>,
) {
let mut to_hash = Vec::new(); let mut to_hash = Vec::new();
let mut partition: TrivialHashMap<u64, Vec<u128>> = let mut partition: TrivialHashMap<u64, Vec<EncodedTerm>> =
TrivialHashMap::with_hasher(BuildHasherDefault::<TrivialHasher>::default()); TrivialHashMap::with_hasher(BuildHasherDefault::<TrivialHasher>::default());
let mut partition_len = 0; let mut partition_len = 0;
loop { loop {
//TODO: improve termination //TODO: improve termination
let mut new_hashes = let mut new_hashes =
TrivialHashMap::with_hasher(BuildHasherDefault::<TrivialHasher>::default()); TrivialHashMap::with_hasher(BuildHasherDefault::<TrivialHasher>::default());
for (b, old_hash) in &hashes { for (bnode, old_hash) in &hashes {
let bnode = EncodedTerm::BlankNode { id: *b }; for q in g.encoded_quads_for_subject(*bnode) {
for q in g.encoded_quads_for_subject(bnode) {
to_hash.push(( to_hash.push((
hash_term(q.predicate, &hashes), hash_term(q.predicate, &hashes),
hash_term(q.object, &hashes), hash_term(q.object, &hashes),
@ -1041,7 +1043,7 @@ fn hash_bnodes(
0, 0,
)); ));
} }
for q in g.encoded_quads_for_object(bnode) { for q in g.encoded_quads_for_object(*bnode) {
to_hash.push(( to_hash.push((
hash_term(q.subject, &hashes), hash_term(q.subject, &hashes),
hash_term(q.predicate, &hashes), hash_term(q.predicate, &hashes),
@ -1049,7 +1051,7 @@ fn hash_bnodes(
1, 1,
)); ));
} }
for q in g.encoded_quads_for_graph(bnode) { for q in g.encoded_quads_for_graph(*bnode) {
to_hash.push(( to_hash.push((
hash_term(q.subject, &hashes), hash_term(q.subject, &hashes),
hash_term(q.predicate, &hashes), hash_term(q.predicate, &hashes),
@ -1060,8 +1062,8 @@ fn hash_bnodes(
to_hash.sort(); to_hash.sort();
let hash = hash_tuple((old_hash, &to_hash)); let hash = hash_tuple((old_hash, &to_hash));
to_hash.clear(); to_hash.clear();
new_hashes.insert(*b, hash); new_hashes.insert(*bnode, hash);
partition.entry(hash).or_default().push(*b); partition.entry(hash).or_default().push(*bnode);
} }
if partition.len() == partition_len { if partition.len() == partition_len {
let mut partition: Vec<_> = partition.into_iter().collect(); let mut partition: Vec<_> = partition.into_iter().collect();
@ -1074,23 +1076,23 @@ fn hash_bnodes(
} }
} }
fn bnodes(g: &MemoryStore) -> TrivialHashSet<u128> { fn bnodes(g: &MemoryStore) -> TrivialHashSet<EncodedTerm> {
let mut bnodes = TrivialHashSet::with_hasher(BuildHasherDefault::<TrivialHasher>::default()); let mut bnodes = TrivialHashSet::with_hasher(BuildHasherDefault::<TrivialHasher>::default());
for q in g.encoded_quads() { for q in g.encoded_quads() {
if let EncodedTerm::BlankNode { id } = q.subject { if q.subject.is_blank_node() {
bnodes.insert(id); bnodes.insert(q.subject);
} }
if let EncodedTerm::BlankNode { id } = q.object { if q.object.is_blank_node() {
bnodes.insert(id); bnodes.insert(q.object);
} }
if let EncodedTerm::BlankNode { id } = q.graph_name { if q.graph_name.is_blank_node() {
bnodes.insert(id); bnodes.insert(q.graph_name);
} }
} }
bnodes bnodes
} }
fn label(g: &MemoryStore, hashes: &TrivialHashMap<u128, u64>) -> Vec<Vec<u8>> { fn label(g: &MemoryStore, hashes: &TrivialHashMap<EncodedTerm, u64>) -> Vec<Vec<u8>> {
//TODO: better representation? //TODO: better representation?
let mut data: Vec<_> = g let mut data: Vec<_> = g
.encoded_quads() .encoded_quads()
@ -1113,19 +1115,19 @@ fn label(g: &MemoryStore, hashes: &TrivialHashMap<u128, u64>) -> Vec<Vec<u8>> {
data data
} }
fn map_term(term: EncodedTerm, bnodes_hash: &TrivialHashMap<u128, u64>) -> EncodedTerm { fn map_term(term: EncodedTerm, bnodes_hash: &TrivialHashMap<EncodedTerm, u64>) -> EncodedTerm {
if let EncodedTerm::BlankNode { id } = term { if term.is_blank_node() {
EncodedTerm::BlankNode { EncodedTerm::InlineBlankNode {
id: (*bnodes_hash.get(&id).unwrap()).into(), id: (*bnodes_hash.get(&term).unwrap()).into(),
} }
} else { } else {
term term
} }
} }
fn hash_term(term: EncodedTerm, bnodes_hash: &TrivialHashMap<u128, u64>) -> u64 { fn hash_term(term: EncodedTerm, bnodes_hash: &TrivialHashMap<EncodedTerm, u64>) -> u64 {
if let EncodedTerm::BlankNode { id } = term { if term.is_blank_node() {
*bnodes_hash.get(&id).unwrap() *bnodes_hash.get(&term).unwrap()
} else { } else {
hash_tuple(term) hash_tuple(term)
} }

@ -63,7 +63,8 @@ const XSD_DURATION_ID: StrHash = StrHash::constant(0x226af08ea5b7e6b08ceed6030c7
const TYPE_DEFAULT_GRAPH_ID: u8 = 0; const TYPE_DEFAULT_GRAPH_ID: u8 = 0;
const TYPE_NAMED_NODE_ID: u8 = 1; const TYPE_NAMED_NODE_ID: u8 = 1;
const TYPE_BLANK_NODE_ID: u8 = 2; const TYPE_INLINE_BLANK_NODE_ID: u8 = 2;
const TYPE_NAMED_BLANK_NODE_ID: u8 = 3;
const TYPE_LANG_STRING_LITERAL_ID: u8 = 4; const TYPE_LANG_STRING_LITERAL_ID: u8 = 4;
const TYPE_TYPED_LITERAL_ID: u8 = 5; const TYPE_TYPED_LITERAL_ID: u8 = 5;
const TYPE_STRING_LITERAL: u8 = 6; const TYPE_STRING_LITERAL: u8 = 6;
@ -122,9 +123,12 @@ pub enum EncodedTerm {
NamedNode { NamedNode {
iri_id: StrHash, iri_id: StrHash,
}, },
BlankNode { InlineBlankNode {
id: u128, id: u128,
}, },
NamedBlankNode {
id_id: StrHash,
},
StringLiteral { StringLiteral {
value_id: StrHash, value_id: StrHash,
}, },
@ -155,9 +159,14 @@ impl PartialEq for EncodedTerm {
EncodedTerm::NamedNode { iri_id: iri_id_a }, EncodedTerm::NamedNode { iri_id: iri_id_a },
EncodedTerm::NamedNode { iri_id: iri_id_b }, EncodedTerm::NamedNode { iri_id: iri_id_b },
) => iri_id_a == iri_id_b, ) => iri_id_a == iri_id_b,
(EncodedTerm::BlankNode { id: id_a }, EncodedTerm::BlankNode { id: id_b }) => { (
id_a == id_b EncodedTerm::InlineBlankNode { id: id_a },
} EncodedTerm::InlineBlankNode { id: id_b },
) => id_a == id_b,
(
EncodedTerm::NamedBlankNode { id_id: id_a },
EncodedTerm::NamedBlankNode { id_id: id_b },
) => id_a == id_b,
( (
EncodedTerm::StringLiteral { EncodedTerm::StringLiteral {
value_id: value_id_a, value_id: value_id_a,
@ -218,7 +227,8 @@ impl Hash for EncodedTerm {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
match self { match self {
EncodedTerm::NamedNode { iri_id } => iri_id.hash(state), EncodedTerm::NamedNode { iri_id } => iri_id.hash(state),
EncodedTerm::BlankNode { id } => id.hash(state), EncodedTerm::InlineBlankNode { id } => id.hash(state),
EncodedTerm::NamedBlankNode { id_id } => id_id.hash(state),
EncodedTerm::DefaultGraph => (), EncodedTerm::DefaultGraph => (),
EncodedTerm::StringLiteral { value_id } => value_id.hash(state), EncodedTerm::StringLiteral { value_id } => value_id.hash(state),
EncodedTerm::LangStringLiteral { EncodedTerm::LangStringLiteral {
@ -258,7 +268,7 @@ impl EncodedTerm {
pub fn is_blank_node(&self) -> bool { pub fn is_blank_node(&self) -> bool {
match self { match self {
EncodedTerm::BlankNode { .. } => true, EncodedTerm::InlineBlankNode { .. } | EncodedTerm::NamedBlankNode { .. } => true,
_ => false, _ => false,
} }
} }
@ -305,7 +315,8 @@ impl EncodedTerm {
match self { match self {
EncodedTerm::DefaultGraph { .. } => TYPE_DEFAULT_GRAPH_ID, EncodedTerm::DefaultGraph { .. } => TYPE_DEFAULT_GRAPH_ID,
EncodedTerm::NamedNode { .. } => TYPE_NAMED_NODE_ID, EncodedTerm::NamedNode { .. } => TYPE_NAMED_NODE_ID,
EncodedTerm::BlankNode { .. } => TYPE_BLANK_NODE_ID, EncodedTerm::InlineBlankNode { .. } => TYPE_INLINE_BLANK_NODE_ID,
EncodedTerm::NamedBlankNode { .. } => TYPE_NAMED_BLANK_NODE_ID,
EncodedTerm::StringLiteral { .. } => TYPE_STRING_LITERAL, EncodedTerm::StringLiteral { .. } => TYPE_STRING_LITERAL,
EncodedTerm::LangStringLiteral { .. } => TYPE_LANG_STRING_LITERAL_ID, EncodedTerm::LangStringLiteral { .. } => TYPE_LANG_STRING_LITERAL_ID,
EncodedTerm::TypedLiteral { .. } => TYPE_TYPED_LITERAL_ID, EncodedTerm::TypedLiteral { .. } => TYPE_TYPED_LITERAL_ID,
@ -410,7 +421,13 @@ impl<'a> From<rio::NamedNode<'a>> for EncodedTerm {
impl From<&BlankNode> for EncodedTerm { impl From<&BlankNode> for EncodedTerm {
fn from(node: &BlankNode) -> Self { fn from(node: &BlankNode) -> Self {
EncodedTerm::BlankNode { id: node.id() } if let Some(id) = node.id() {
EncodedTerm::InlineBlankNode { id }
} else {
EncodedTerm::NamedBlankNode {
id_id: StrHash::new(node.as_str()),
}
}
} }
} }
@ -560,13 +577,20 @@ impl<R: Read> TermReader for R {
iri_id: StrHash::from_be_bytes(buffer), iri_id: StrHash::from_be_bytes(buffer),
}) })
} }
TYPE_BLANK_NODE_ID => { TYPE_INLINE_BLANK_NODE_ID => {
let mut buffer = [0; 16]; let mut buffer = [0; 16];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::BlankNode { Ok(EncodedTerm::InlineBlankNode {
id: u128::from_be_bytes(buffer), id: u128::from_be_bytes(buffer),
}) })
} }
TYPE_NAMED_BLANK_NODE_ID => {
let mut buffer = [0; 16];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::NamedBlankNode {
id_id: StrHash::from_be_bytes(buffer),
})
}
TYPE_LANG_STRING_LITERAL_ID => { TYPE_LANG_STRING_LITERAL_ID => {
let mut language_buffer = [0; 16]; let mut language_buffer = [0; 16];
self.read_exact(&mut language_buffer)?; self.read_exact(&mut language_buffer)?;
@ -730,7 +754,8 @@ pub fn write_term(sink: &mut Vec<u8>, term: EncodedTerm) {
match term { match term {
EncodedTerm::DefaultGraph => {} EncodedTerm::DefaultGraph => {}
EncodedTerm::NamedNode { iri_id } => sink.extend_from_slice(&iri_id.to_be_bytes()), EncodedTerm::NamedNode { iri_id } => sink.extend_from_slice(&iri_id.to_be_bytes()),
EncodedTerm::BlankNode { id } => sink.extend_from_slice(&id.to_be_bytes()), EncodedTerm::InlineBlankNode { id } => sink.extend_from_slice(&id.to_be_bytes()),
EncodedTerm::NamedBlankNode { id_id } => sink.extend_from_slice(&id_id.to_be_bytes()),
EncodedTerm::StringLiteral { value_id } => sink.extend_from_slice(&value_id.to_be_bytes()), EncodedTerm::StringLiteral { value_id } => sink.extend_from_slice(&value_id.to_be_bytes()),
EncodedTerm::LangStringLiteral { EncodedTerm::LangStringLiteral {
value_id, value_id,
@ -1009,11 +1034,11 @@ impl<S: StrContainer> Encoder for S {
bnodes_map: &mut HashMap<String, u128>, bnodes_map: &mut HashMap<String, u128>,
) -> Result<EncodedTerm> { ) -> Result<EncodedTerm> {
Ok(if let Some(id) = bnodes_map.get(blank_node.id) { Ok(if let Some(id) = bnodes_map.get(blank_node.id) {
EncodedTerm::BlankNode { id: *id } EncodedTerm::InlineBlankNode { id: *id }
} else { } else {
let id = random::<u128>(); let id = random::<u128>();
bnodes_map.insert(blank_node.id.to_owned(), id); bnodes_map.insert(blank_node.id.to_owned(), id);
EncodedTerm::BlankNode { id } EncodedTerm::InlineBlankNode { id }
}) })
} }
@ -1185,7 +1210,10 @@ impl<S: StrLookup> Decoder for S {
EncodedTerm::NamedNode { iri_id } => { EncodedTerm::NamedNode { iri_id } => {
Ok(NamedNode::new_unchecked(get_required_str(self, iri_id)?).into()) Ok(NamedNode::new_unchecked(get_required_str(self, iri_id)?).into())
} }
EncodedTerm::BlankNode { id } => Ok(BlankNode::new_from_unique_id(id).into()), EncodedTerm::InlineBlankNode { id } => Ok(BlankNode::new_from_unique_id(id).into()),
EncodedTerm::NamedBlankNode { id_id } => {
Ok(BlankNode::new_unchecked(get_required_str(self, id_id)?).into())
}
EncodedTerm::StringLiteral { value_id } => { EncodedTerm::StringLiteral { value_id } => {
Ok(Literal::new_simple_literal(get_required_str(self, value_id)?).into()) Ok(Literal::new_simple_literal(get_required_str(self, value_id)?).into())
} }

@ -28,7 +28,7 @@ use std::sync::Arc;
/// let store = RocksDbStore::open("example.db")?; /// let store = RocksDbStore::open("example.db")?;
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// store.insert(&quad)?; /// store.insert(&quad)?;
/// ///
@ -757,7 +757,7 @@ fn store() -> Result<()> {
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
let main_s = NamedOrBlankNode::from(BlankNode::default()); let main_s = NamedOrBlankNode::from(BlankNode::default());
let main_p = NamedNode::parse("http://example.com")?; let main_p = NamedNode::new("http://example.com")?;
let main_o = Term::from(Literal::from(1)); let main_o = Term::from(Literal::from(1));
let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None); let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);

@ -28,7 +28,7 @@ use std::str;
/// let store = SledStore::open("example.db")?; /// let store = SledStore::open("example.db")?;
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::parse("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// store.insert(&quad)?; /// store.insert(&quad)?;
/// ///
@ -550,7 +550,7 @@ fn store() -> Result<()> {
use crate::*; use crate::*;
let main_s = NamedOrBlankNode::from(BlankNode::default()); let main_s = NamedOrBlankNode::from(BlankNode::default());
let main_p = NamedNode::parse("http://example.com")?; let main_p = NamedNode::new("http://example.com")?;
let main_o = Term::from(Literal::from(1)); let main_o = Term::from(Literal::from(1));
let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None); let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);

@ -53,8 +53,8 @@ fn two_service_test() {
named_node: &NamedNode, named_node: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
let service1 = NamedNode::parse("http://service1.org").unwrap(); let service1 = NamedNode::new("http://service1.org").unwrap();
let service2 = NamedNode::parse("http://service2.org").unwrap(); let service2 = NamedNode::new("http://service2.org").unwrap();
if named_node == &service1 { if named_node == &service1 {
let triples = br#" let triples = br#"
<http://example.com/bob> <http://xmlns.com/foaf/0.1/name> "Bob" . <http://example.com/bob> <http://xmlns.com/foaf/0.1/name> "Bob" .
@ -180,11 +180,11 @@ fn non_silent_service_test() {
} }
fn ex(id: &str) -> Term { fn ex(id: &str) -> Term {
Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", id)).unwrap()) Term::NamedNode(NamedNode::new(format!("http://example.com/{}", id)).unwrap())
} }
fn mailto(id: &str) -> Term { fn mailto(id: &str) -> Term {
Term::NamedNode(NamedNode::parse(format!("mailto:{}", id)).unwrap()) Term::NamedNode(NamedNode::new(format!("mailto:{}", id)).unwrap())
} }
fn literal(str: &str) -> Term { fn literal(str: &str) -> Term {

@ -11,7 +11,7 @@ mod test {
let store = MemoryStore::new(); let store = MemoryStore::new();
// insertion // insertion
let ex = NamedNode::parse("http://example.com").unwrap(); let ex = NamedNode::new("http://example.com").unwrap();
let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
store.insert(quad.clone()); store.insert(quad.clone());
// quad filter // quad filter

@ -176,8 +176,7 @@ impl Iterator for TestManifest {
None => { None => {
match self.manifests_to_do.pop() { match self.manifests_to_do.pop() {
Some(url) => { Some(url) => {
let manifest = let manifest = NamedOrBlankNode::from(NamedNode::new(url.clone()).unwrap());
NamedOrBlankNode::from(NamedNode::parse(url.clone()).unwrap());
if let Err(error) = load_to_store(&url, &self.graph, None) { if let Err(error) = load_to_store(&url, &self.graph, None) {
return Some(Err(error)); return Some(Err(error));
} }
@ -186,11 +185,12 @@ impl Iterator for TestManifest {
match object_for_subject_predicate(&self.graph, &manifest, &*mf::INCLUDE) { match object_for_subject_predicate(&self.graph, &manifest, &*mf::INCLUDE) {
Some(Term::BlankNode(list)) => { Some(Term::BlankNode(list)) => {
self.manifests_to_do.extend( self.manifests_to_do.extend(
RdfListIterator::iter(&self.graph, list.clone().into()) RdfListIterator::iter(&self.graph, list.into()).filter_map(
.filter_map(|m| match m { |m| match m {
Term::NamedNode(nm) => Some(nm.into_string()), Term::NamedNode(nm) => Some(nm.into_string()),
_ => None, _ => None,
}), },
),
); );
} }
Some(_) => return Some(Err(Error::msg("invalid tests list"))), Some(_) => return Some(Err(Error::msg("invalid tests list"))),
@ -200,10 +200,8 @@ impl Iterator for TestManifest {
// New tests // New tests
match object_for_subject_predicate(&self.graph, &manifest, &*mf::ENTRIES) { match object_for_subject_predicate(&self.graph, &manifest, &*mf::ENTRIES) {
Some(Term::BlankNode(list)) => { Some(Term::BlankNode(list)) => {
self.tests_to_do.extend(RdfListIterator::iter( self.tests_to_do
&self.graph, .extend(RdfListIterator::iter(&self.graph, list.into()));
list.clone().into(),
));
} }
Some(term) => { Some(term) => {
return Some(Err(Error::msg(format!( return Some(Err(Error::msg(format!(

@ -79,7 +79,7 @@ fn evaluate_sparql_test(test: &Test) -> Result<()> {
load_to_store( load_to_store(
&graph_data, &graph_data,
&store, &store,
Some(&NamedNode::parse(graph_data)?.into()), Some(&NamedNode::new(graph_data)?.into()),
)?; )?;
} }
let query_file = test let query_file = test
@ -163,7 +163,7 @@ impl StaticServiceHandler {
services services
.iter() .iter()
.map(|(name, data)| { .map(|(name, data)| {
let name = NamedNode::parse(name)?; let name = NamedNode::new(name)?;
let store = MemoryStore::new(); let store = MemoryStore::new();
load_to_store(&data, &store, None)?; load_to_store(&data, &store, None)?;
Ok((name, store)) Ok((name, store))
@ -210,7 +210,7 @@ fn to_dataset(result: QueryResult<'_>, with_order: bool) -> Result<MemoryStore>
let store = MemoryStore::new(); let store = MemoryStore::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( store.insert(Quad::new(
result_set, result_set.clone(),
rdf::TYPE.clone(), rdf::TYPE.clone(),
rs::RESULT_SET.clone(), rs::RESULT_SET.clone(),
None, None,
@ -227,14 +227,14 @@ fn to_dataset(result: QueryResult<'_>, with_order: bool) -> Result<MemoryStore>
let store = MemoryStore::new(); let store = MemoryStore::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( store.insert(Quad::new(
result_set, result_set.clone(),
rdf::TYPE.clone(), rdf::TYPE.clone(),
rs::RESULT_SET.clone(), rs::RESULT_SET.clone(),
None, None,
)); ));
for variable in solutions.variables() { for variable in solutions.variables() {
store.insert(Quad::new( store.insert(Quad::new(
result_set, result_set.clone(),
rs::RESULT_VARIABLE.clone(), rs::RESULT_VARIABLE.clone(),
Literal::new_simple_literal(variable.as_str()), Literal::new_simple_literal(variable.as_str()),
None, None,
@ -244,15 +244,25 @@ fn to_dataset(result: QueryResult<'_>, with_order: bool) -> Result<MemoryStore>
let solution = solution?; let solution = solution?;
let solution_id = BlankNode::default(); let solution_id = BlankNode::default();
store.insert(Quad::new( store.insert(Quad::new(
result_set, result_set.clone(),
rs::SOLUTION.clone(), rs::SOLUTION.clone(),
solution_id, solution_id.clone(),
None, None,
)); ));
for (variable, value) in solution.iter() { for (variable, value) in solution.iter() {
let binding = BlankNode::default(); let binding = BlankNode::default();
store.insert(Quad::new(solution_id, rs::BINDING.clone(), binding, None)); store.insert(Quad::new(
store.insert(Quad::new(binding, rs::VALUE.clone(), value.clone(), None)); solution_id.clone(),
rs::BINDING.clone(),
binding.clone(),
None,
));
store.insert(Quad::new(
binding.clone(),
rs::VALUE.clone(),
value.clone(),
None,
));
store.insert(Quad::new( store.insert(Quad::new(
binding, binding,
rs::VARIABLE.clone(), rs::VARIABLE.clone(),

@ -4,29 +4,26 @@ pub mod rs {
lazy_static! { lazy_static! {
pub static ref RESULT_SET: NamedNode = pub static ref RESULT_SET: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#ResultSet") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#ResultSet")
.unwrap(); .unwrap();
pub static ref RESULT_VARIABLE: NamedNode = NamedNode::parse( pub static ref RESULT_VARIABLE: NamedNode =
"http://www.w3.org/2001/sw/DataAccess/tests/result-set#resultVariable" NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#resultVariable")
)
.unwrap(); .unwrap();
pub static ref SOLUTION: NamedNode = pub static ref SOLUTION: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#solution") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#solution")
.unwrap(); .unwrap();
pub static ref BINDING: NamedNode = pub static ref BINDING: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#binding") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#binding")
.unwrap(); .unwrap();
pub static ref VALUE: NamedNode = pub static ref VALUE: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#value") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#value").unwrap();
.unwrap();
pub static ref VARIABLE: NamedNode = pub static ref VARIABLE: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#variable") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#variable")
.unwrap(); .unwrap();
pub static ref INDEX: NamedNode = pub static ref INDEX: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#index") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#index").unwrap();
.unwrap();
pub static ref BOOLEAN: NamedNode = pub static ref BOOLEAN: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/result-set#boolean") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/result-set#boolean")
.unwrap(); .unwrap();
} }
} }
@ -37,19 +34,19 @@ pub mod mf {
lazy_static! { lazy_static! {
pub static ref INCLUDE: NamedNode = pub static ref INCLUDE: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#include") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#include")
.unwrap(); .unwrap();
pub static ref ENTRIES: NamedNode = pub static ref ENTRIES: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#entries") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#entries")
.unwrap(); .unwrap();
pub static ref NAME: NamedNode = pub static ref NAME: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#name") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#name")
.unwrap(); .unwrap();
pub static ref ACTION: NamedNode = pub static ref ACTION: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#action")
.unwrap(); .unwrap();
pub static ref RESULT: NamedNode = pub static ref RESULT: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#result") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#result")
.unwrap(); .unwrap();
} }
} }
@ -60,18 +57,17 @@ pub mod qt {
lazy_static! { lazy_static! {
pub static ref QUERY: NamedNode = pub static ref QUERY: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#query") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-query#query").unwrap();
.unwrap();
pub static ref DATA: NamedNode = pub static ref DATA: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#data").unwrap(); NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-query#data").unwrap();
pub static ref GRAPH_DATA: NamedNode = pub static ref GRAPH_DATA: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#graphData") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-query#graphData")
.unwrap(); .unwrap();
pub static ref SERVICE_DATA: NamedNode = pub static ref SERVICE_DATA: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#serviceData") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-query#serviceData")
.unwrap(); .unwrap();
pub static ref ENDPOINT: NamedNode = pub static ref ENDPOINT: NamedNode =
NamedNode::parse("http://www.w3.org/2001/sw/DataAccess/tests/test-query#endpoint") NamedNode::new("http://www.w3.org/2001/sw/DataAccess/tests/test-query#endpoint")
.unwrap(); .unwrap();
} }
} }

@ -259,7 +259,7 @@ impl WikibaseLoader {
} }
fn load_entity_data(&self, uri: &str, data: impl Read) -> Result<()> { fn load_entity_data(&self, uri: &str, data: impl Read) -> Result<()> {
let graph_name = NamedNode::parse(uri)?.into(); let graph_name = NamedNode::new(uri)?.into();
self.store.transaction(|transaction| { self.store.transaction(|transaction| {
let to_remove = self let to_remove = self
.store .store
@ -272,7 +272,7 @@ impl WikibaseLoader {
transaction.load_graph( transaction.load_graph(
BufReader::new(data), BufReader::new(data),
GraphSyntax::NTriples, GraphSyntax::NTriples,
Some(&NamedNode::parse(uri)?.into()), Some(&NamedNode::new(uri)?.into()),
None, None,
) )
})?; })?;

Loading…
Cancel
Save