Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d949eff
style(cognitarium): use explicit function to verify owner
ccamel Aug 1, 2023
784b352
chore(cognitarium): add function to get variables from TriplePattern
ccamel Aug 3, 2023
6356800
refactor(cognitarium): move storer to its own module
ccamel Aug 3, 2023
b33bd6a
chore(cognitarium): extend storer to support triples deletion
ccamel Aug 3, 2023
6722a52
chore(cognitarium): add a bunch of type converters
ccamel Aug 3, 2023
758c172
feat(cognitarium): implement DeleteData message
ccamel Aug 3, 2023
2118d5b
style(cognitarium): adopt more idiomatic rust style
ccamel Aug 3, 2023
9579b9d
style(cognitarium): provide more specific error message for conversions
ccamel Aug 3, 2023
dc0ed63
refactor(cognitarium): move converters into own module
ccamel Aug 3, 2023
76abfcd
fix(cognitarium): fix incorrect variable resolution on delete
ccamel Aug 7, 2023
91d77a8
test(cognitarium): add proper delete test cases
ccamel Aug 7, 2023
760c068
test(cognitarium): put variables() under unit tests
ccamel Aug 7, 2023
5638c5c
test(cognitarium): turn proper_display test to data driven test
ccamel Aug 9, 2023
1996d9b
test(cognitarium): add triple_pattern_resolve test cases
ccamel Aug 10, 2023
0637a67
style(cognitarium): polish the code
ccamel Aug 10, 2023
cf6df89
test(cognitarium): add invalid_delete test cases
ccamel Aug 10, 2023
47ae927
style(cognitarium): implement Default trait for InstantiateMsg
ccamel Aug 10, 2023
5ce531b
refactor(cognitarium)!: make delete triples optional and enforce wher…
ccamel Aug 10, 2023
3f2f9a0
feat(cognitarium): support 'delete where {}' form
ccamel Aug 10, 2023
65c5096
test(cognitarium): add test case for 'delete where {}' form
ccamel Aug 10, 2023
ea023d8
refactor(cognitarium): commonalize prefix management
ccamel Aug 10, 2023
a785d72
test(cognitarium): improve test after refactoring on prefixes
ccamel Aug 10, 2023
e292234
style(cognitarium): polish the code (a bit)
ccamel Aug 11, 2023
4d5ee31
docs(cognitarium): re-generate docs after contract change
ccamel Aug 10, 2023
74c9461
fix(cognitarium): decrease the store byte size upon deletion
ccamel Sep 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
444 changes: 390 additions & 54 deletions contracts/okp4-cognitarium/src/contract.rs

Large diffs are not rendered by default.

113 changes: 107 additions & 6 deletions contracts/okp4-cognitarium/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::collections::BTreeMap;

/// Instantiate message
#[cw_serde]
#[derive(Default)]
pub struct InstantiateMsg {
/// Limitations regarding store usage.
#[serde(default)]
Expand Down Expand Up @@ -66,11 +67,12 @@ pub enum ExecuteMsg {
DeleteData {
/// The prefixes used in the operation.
prefixes: Vec<Prefix>,
/// The items to delete.
/// Specifies the specific triple patterns to delete.
/// If nothing is provided, the patterns from the `where` clause are used for deletion.
delete: Vec<TriplePattern>,
/// The WHERE clause to apply.
/// If not provided, all the RDF triples are considered.
r#where: Option<WhereClause>,
/// Defines the patterns that data (RDF triples) should match in order for it to be
/// considered for deletion.
r#where: WhereClause,
},
}

Expand Down Expand Up @@ -430,7 +432,7 @@ pub struct ConstructQuery {
}

/// # Prefix
/// Represents a prefix in a [SelectQuery]. A prefix is a shortcut for a namespace used in the query.
/// Represents a prefix, i.e. a shortcut for a namespace used in a query.
#[cw_serde]
pub struct Prefix {
/// The prefix.
Expand Down Expand Up @@ -483,6 +485,27 @@ pub struct TriplePattern {
pub object: VarOrNodeOrLiteral,
}

impl TriplePattern {
/// Returns the variables used in the triple pattern.
pub fn variables(&self) -> Vec<String> {
let mut variables: Vec<String> = vec![];

if let VarOrNode::Variable(var) = &self.subject {
variables.push(var.clone());
}

if let VarOrNode::Variable(var) = &self.predicate {
variables.push(var.clone());
}

if let VarOrNodeOrLiteral::Variable(var) = &self.object {
variables.push(var.clone());
}

variables
}
}

/// # VarOrNode
/// Represents either a variable or a node.
#[cw_serde]
Expand Down Expand Up @@ -562,7 +585,12 @@ pub enum Node {

#[cfg(test)]
mod tests {
use crate::msg::{InstantiateMsg, StoreLimitsInput};
use crate::msg::Literal::Simple;
use crate::msg::Node::{BlankNode, NamedNode};
use crate::msg::IRI::{Full, Prefixed};
use crate::msg::{
InstantiateMsg, StoreLimitsInput, TriplePattern, VarOrNode, VarOrNodeOrLiteral,
};
use cosmwasm_std::Uint128;
use schemars::_serde_json;

Expand Down Expand Up @@ -597,4 +625,77 @@ mod tests {
assert_eq!(msg.limits.max_insert_data_byte_size, Uint128::MAX);
assert_eq!(msg.limits.max_insert_data_triple_count, Uint128::MAX);
}

#[test]
fn variables_from_triple_pattern() {
let (s, p, o) = ("s".to_string(), "p".to_string(), "o".to_string());
let (node, prefixed, literal) = (
"node".to_string(),
"a:node".to_string(),
"literal".to_string(),
);

let cases = vec![
(
TriplePattern {
subject: VarOrNode::Variable(s.clone()),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![s.clone(), p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(NamedNode(Full(node.clone()))),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(NamedNode(Prefixed(prefixed.clone()))),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(BlankNode(node.clone())),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Variable(s.clone()),
predicate: VarOrNode::Node(NamedNode(Full(node.clone()))),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![s.clone(), o],
),
(
TriplePattern {
subject: VarOrNode::Variable(s.clone()),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Literal(Simple(literal.clone())),
},
vec![s, p],
),
(
TriplePattern {
subject: VarOrNode::Node(BlankNode(node)),
predicate: VarOrNode::Node(NamedNode(Prefixed(prefixed))),
object: VarOrNodeOrLiteral::Literal(Simple(literal)),
},
vec![],
),
];

for (triple_pattern, expected) in cases {
assert_eq!(triple_pattern.variables(), expected);
}
}
}
8 changes: 4 additions & 4 deletions contracts/okp4-cognitarium/src/querier/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<'a> QueryEngine<'a> {
SelectItem::Variable(v) => v,
})
.map(|name| -> StdResult<(String, usize)> {
match plan.get_var_index(name.as_str()) {
match plan.get_var_index(name) {
Some(index) => Ok((name.clone(), index)),
None => Err(StdError::generic_err(
"Selected variable not found in query",
Expand Down Expand Up @@ -443,7 +443,7 @@ mod test {
use crate::rdf::TripleReader;
use crate::state;
use crate::state::{Literal, Store, StoreStat, NAMESPACE_KEY_INCREMENT, STORE};
use crate::storer::TripleStorer;
use crate::storer::StoreEngine;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::{Addr, Uint128};
use std::env;
Expand All @@ -455,7 +455,7 @@ mod test {
let mut bytes: Vec<u8> = Vec::new();

File::open(
Path::new(env::var("CARGO_MANIFEST_DIR").unwrap().as_str())
Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
.join("testdata")
.join(file),
)
Expand All @@ -481,7 +481,7 @@ mod test {
let data = read_test_data("sample.rdf.xml");
let buf = BufReader::new(data.as_slice());
let mut reader = TripleReader::new(&DataFormat::RDFXml, buf);
let mut storer = TripleStorer::new(storage).unwrap();
let mut storer = StoreEngine::new(storage).unwrap();
let count = storer.store_all(&mut reader).unwrap();

assert_eq!(count, Uint128::new(40u128));
Expand Down
2 changes: 1 addition & 1 deletion contracts/okp4-cognitarium/src/querier/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct QueryPlan {
impl QueryPlan {
pub fn get_var_index(&self, var_name: &str) -> Option<usize> {
self.variables.iter().enumerate().find_map(|(index, it)| {
if it.as_str() == var_name {
if it == var_name {
return Some(index);
}
None
Expand Down
97 changes: 35 additions & 62 deletions contracts/okp4-cognitarium/src/querier/plan_builder.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
use crate::msg::{
Literal, Node, Prefix, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral,
WhereClause, WhereCondition, IRI,
Literal, Node, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral, WhereClause,
WhereCondition, IRI,
};
use crate::querier::plan::{PatternValue, QueryNode, QueryPlan};
use crate::rdf::expand_uri;
use crate::state::{namespaces, Object, Predicate, Subject};
use crate::{rdf, state};
use cosmwasm_std::{StdError, StdResult, Storage};
use std::collections::HashMap;

pub struct PlanBuilder<'a> {
storage: &'a dyn Storage,
prefixes: HashMap<String, String>,
prefixes: &'a HashMap<String, String>,
variables: Vec<String>,
limit: Option<usize>,
skip: Option<usize>,
}

impl<'a> PlanBuilder<'a> {
pub fn new(storage: &'a dyn Storage, prefixes: &[Prefix]) -> Self {
pub fn new(storage: &'a dyn Storage, prefixes: &'a HashMap<String, String>) -> Self {
Self {
storage,
prefixes: Self::make_prefixes(prefixes),
prefixes,
variables: Vec::new(),
skip: None,
limit: None,
Expand Down Expand Up @@ -156,34 +157,10 @@ impl<'a> PlanBuilder<'a> {

fn build_named_node(&mut self, value: IRI) -> StdResult<state::Node> {
match value {
IRI::Prefixed(prefixed) => prefixed
.rfind(':')
.map_or_else(
|| {
Err(StdError::generic_err(
"Malformed prefixed IRI: no prefix delimiter found",
))
},
Ok,
)
.and_then(|index| {
self.prefixes
.get(&prefixed.as_str()[..index])
.map(|resolved_prefix| {
[resolved_prefix, &prefixed.as_str()[index + 1..]].join("")
})
.map_or_else(
|| {
Err(StdError::generic_err(
"Malformed prefixed IRI: prefix not found",
))
},
Ok,
)
}),
IRI::Prefixed(prefixed) => expand_uri(&prefixed, self.prefixes),
IRI::Full(full) => Ok(full),
}
.and_then(|iri| rdf::explode_iri(iri.as_str()))
.and_then(|iri| rdf::explode_iri(&iri))
.and_then(|(ns_key, v)| {
namespaces()
.load(self.storage, ns_key)
Expand All @@ -202,18 +179,13 @@ impl<'a> PlanBuilder<'a> {
self.variables.push(v);
self.variables.len() - 1
}

fn make_prefixes(as_list: &[Prefix]) -> HashMap<String, String> {
as_list.iter().fold(HashMap::new(), |mut map, prefix| {
map.insert(prefix.prefix.clone(), prefix.namespace.clone());
map
})
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::msg::Prefix;
use crate::rdf::PrefixMap;
use crate::state::Namespace;
use cosmwasm_std::testing::mock_dependencies;

Expand Down Expand Up @@ -273,14 +245,16 @@ mod test {
let deps = mock_dependencies();

for case in cases {
let builder = PlanBuilder::new(&deps.storage, &case.0);
let prefixes = &PrefixMap::from(case.0).into_inner();
let builder = PlanBuilder::new(&deps.storage, prefixes);
assert_eq!(builder.skip, None);
assert_eq!(builder.limit, None);
assert_eq!(builder.variables, Vec::<String>::new());
assert_eq!(builder.prefixes, case.1);
assert_eq!(builder.prefixes, &case.1);
}

let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);
builder = builder.with_skip(20usize).with_limit(50usize);
assert_eq!(builder.skip, Some(20usize));
assert_eq!(builder.limit, Some(50usize));
Expand Down Expand Up @@ -315,15 +289,11 @@ mod test {
),
(
IRI::Prefixed("resource".to_string()),
Err(StdError::generic_err(
"Malformed prefixed IRI: no prefix delimiter found",
)),
Err(StdError::generic_err("Malformed CURIE: resource")),
),
(
IRI::Prefixed("okp5:resource".to_string()),
Err(StdError::generic_err(
"Malformed prefixed IRI: prefix not found",
)),
Err(StdError::generic_err("Prefix not found: okp5")),
),
];

Expand Down Expand Up @@ -351,19 +321,19 @@ mod test {
)
.unwrap();

let mut builder = PlanBuilder::new(
&deps.storage,
&[
Prefix {
prefix: "rdf".to_string(),
namespace: "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
},
Prefix {
prefix: "okp4".to_string(),
namespace: "http://okp4.space/".to_string(),
},
],
);
let prefixes = &<PrefixMap>::from(vec![
Prefix {
prefix: "rdf".to_string(),
namespace: "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
},
Prefix {
prefix: "okp4".to_string(),
namespace: "http://okp4.space/".to_string(),
},
])
.into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);

for case in cases {
assert_eq!(builder.build_named_node(case.0), case.1);
}
Expand Down Expand Up @@ -526,7 +496,9 @@ mod test {
},
)
.unwrap();
let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);

for case in cases {
assert_eq!(builder.build_triple_pattern(&case.0), case.1);
}
Expand Down Expand Up @@ -729,7 +701,8 @@ mod test {
.unwrap();

for case in cases {
let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);
if let Some(skip) = case.0 {
builder = builder.with_skip(skip);
}
Expand Down
Loading