From 35c250d410fdfff46e4f1103190938e3a0d16a84 Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Mon, 2 May 2022 19:21:38 +0800 Subject: [PATCH] add field and idx accessors --- src/db/eval.rs | 62 ++++++++++++++++++++++++++++++++++--------- src/db/plan.rs | 14 +++++++--- src/relation/tuple.rs | 60 ++++++++++++++++++++++++++++++++++++++--- src/relation/value.rs | 37 ++++++++++++++++++++++++-- 4 files changed, 152 insertions(+), 21 deletions(-) diff --git a/src/db/eval.rs b/src/db/eval.rs index 34ec94f2..bf1933ce 100644 --- a/src/db/eval.rs +++ b/src/db/eval.rs @@ -28,17 +28,17 @@ impl<'s> Session<'s> { self.define_data(name, data, in_root) } fn resolve_value(&self, name: &str) -> Result> { - match self.resolve(name)? { - None => Ok(None), - Some(t) => { - match t.data_kind()? { - DataKind::Value => Ok(Some(t.get(0) - .ok_or_else(|| CozoError::LogicError("Corrupt".to_string()))? - .to_static())), - k => Err(CozoError::UnexpectedDataKind(k)) - } + match self.resolve(name)? { + None => Ok(None), + Some(t) => { + match t.data_kind()? { + DataKind::Value => Ok(Some(t.get(0) + .ok_or_else(|| CozoError::LogicError("Corrupt".to_string()))? + .to_static())), + k => Err(CozoError::UnexpectedDataKind(k)) } } + } } fn encode_definable_key(&self, name: &str, in_root: bool) -> OwnTuple { let depth_code = if in_root { 0 } else { self.get_stack_depth() as i64 }; @@ -289,8 +289,12 @@ impl<'s> Session<'s> { Ok((is_ev, v.into())) } Value::Variable(v) => { - if let Some(d) = params.get(v.as_ref()) { - Ok((true, d.clone())) + if v.starts_with('$') { + Ok(if let Some(d) = params.get(v.as_ref()) { + (true, d.clone()) + } else { + (false, Value::Variable(v)) + }) } else { Ok(match self.resolve_value(&v)? { None => (false, Value::Variable(v)), @@ -300,7 +304,37 @@ impl<'s> Session<'s> { }) } } - + Value::FieldAccess(field, arg) => { + match *arg { + v @ (Value::Variable(_) | + Value::IdxAccess(_, _) | + Value::FieldAccess(_, _) | + Value::Apply(_, _)) => Ok((false, Value::FieldAccess(field, v.into()))), + Value::Dict(mut d) => { + Ok(d.remove(field.as_ref()) + .map(|v| (v.is_evaluated(), v)) + .unwrap_or((true, Value::Null))) + } + _ => Err(LogicError("Field access failed".to_string())) + } + } + Value::IdxAccess(idx, arg) => { + match *arg { + v @ (Value::Variable(_) | + Value::IdxAccess(_, _) | + Value::FieldAccess(_, _) | + Value::Apply(_, _)) => Ok((false, Value::IdxAccess(idx, v.into()))), + Value::List(mut l) => { + if idx >= l.len() { + Ok((true, Value::Null)) + } else { + let v = l.swap_remove(idx); + Ok((v.is_evaluated(), v)) + } + } + _ => Err(LogicError("Idx access failed".to_string())) + } + } Value::Apply(op, args) => { Ok(match op.as_ref() { value::OP_ADD => self.add_values(args)?, @@ -619,6 +653,8 @@ impl<'s> Session<'s> { Value::List(_) | Value::Dict(_) => Err(Err(CozoError::InvalidArgument)), cur_val @ (Value::Variable(_) | + Value::IdxAccess(_, _) | + Value::FieldAccess(_, _) | Value::Apply(_, _)) => { collected.push(cur_val); Ok((false, has_null, collected)) @@ -679,6 +715,8 @@ impl<'s> Session<'s> { Value::List(_) | Value::Dict(_) => Err(Err(CozoError::InvalidArgument)), cur_val @ (Value::Variable(_) | + Value::IdxAccess(_, _) | + Value::FieldAccess(_, _) | Value::Apply(_, _)) => { collected.push(cur_val); Ok((false, has_null, collected)) diff --git a/src/db/plan.rs b/src/db/plan.rs index 818b2a6e..b82425ad 100644 --- a/src/db/plan.rs +++ b/src/db/plan.rs @@ -184,6 +184,7 @@ mod tests { use crate::parser::{Parser, Rule}; use pest::Parser as PestParser; use crate::db::engine::Engine; + use crate::relation::value::Value; #[test] fn parse_patterns() { @@ -223,16 +224,21 @@ mod tests { } sess.commit().unwrap(); + let s = "from a:Friend, (b:Person)-[:Friend]->(c:Z), x:Person"; + let parsed = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); + assert_eq!(parsed.as_rule(), Rule::from_pattern); + assert!(sess.parse_from_pattern(parsed).is_err()); let s = "from a:Friend, (b:Person)-[:Friend]->(c:Person), x:Person"; let parsed = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); assert_eq!(parsed.as_rule(), Rule::from_pattern); sess.parse_from_pattern(parsed).unwrap(); - let s = "from a:Friend, (b:Person)-[:Friend]->(c:Z), x:Person"; - let parsed = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); - assert_eq!(parsed.as_rule(), Rule::from_pattern); - assert!(sess.parse_from_pattern(parsed).is_err()); + let s = "where b.id > c.id, a.id == 5, x.name == 'Joe', x.name.len() == 3"; + let parsed = Parser::parse(Rule::where_pattern, s).unwrap().next().unwrap(); + let first = parsed.into_inner().next().unwrap(); + println!("{:#?}", first); + println!("{:#?}", Value::from_pair(first)); } drop(engine); let _ = fs::remove_dir_all(db_path); diff --git a/src/relation/tuple.rs b/src/relation/tuple.rs index 5be6e529..d09aea26 100644 --- a/src/relation/tuple.rs +++ b/src/relation/tuple.rs @@ -85,9 +85,11 @@ impl> Tuple { let slen = slen as usize; start + slen + offset } - Tag::List => start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize, - Tag::Apply => start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize, - Tag::Dict => start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize, + Tag::List | + Tag::Apply | + Tag::Dict | + Tag::IdxAccess | + Tag::FieldAccess => start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize, Tag::MaxTag => panic!(), }; self.idx_cache.borrow_mut().push(nxt); @@ -296,6 +298,29 @@ impl> Tuple { (end_pos, collected.into()) } Tag::MaxTag => (start, Value::EndSentinel), + Tag::IdxAccess => { + let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; + let mut start_pos = start + 4; + let (idx, offset) = self.parse_varint(start_pos); + start_pos += offset; + let (val, _) = self.parse_value_at(start_pos); + (end_pos, Value::IdxAccess(idx as usize, val.into())) + } + Tag::FieldAccess => { + let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; + let mut start_pos = start + 4; + + let (slen, offset) = self.parse_varint(start); + let slen = slen as usize; + let field = unsafe { + std::str::from_utf8_unchecked(&data[start + offset..start + offset + slen]) + }; + + start_pos += slen + offset; + + let (val, _) = self.parse_value_at(start_pos); + (end_pos, Value::FieldAccess(field.into(), val.into())) + } }; (val, nxt) } @@ -465,6 +490,35 @@ impl OwnTuple { cache.truncate(start_len); cache.push(self.data.len()); } + Value::FieldAccess(field, arg) => { + self.push_tag(Tag::IdxAccess); + let start_pos = self.data.len(); + let start_len = self.idx_cache.borrow().len(); + self.data.extend(0u32.to_be_bytes()); + self.push_varint(field.len() as u64); + self.data.extend_from_slice(field.as_bytes()); + self.push_value(arg); + let length = (self.data.len() - start_pos) as u32; + let length_bytes = length.to_be_bytes(); + self.data[start_pos..(4 + start_pos)].clone_from_slice(&length_bytes[..4]); + let mut cache = self.idx_cache.borrow_mut(); + cache.truncate(start_len); + cache.push(self.data.len()); + } + Value::IdxAccess(idx, arg) => { + self.push_tag(Tag::IdxAccess); + let start_pos = self.data.len(); + let start_len = self.idx_cache.borrow().len(); + self.data.extend(0u32.to_be_bytes()); + self.push_varint(*idx as u64); + self.push_value(arg); + let length = (self.data.len() - start_pos) as u32; + let length_bytes = length.to_be_bytes(); + self.data[start_pos..(4 + start_pos)].clone_from_slice(&length_bytes[..4]); + let mut cache = self.idx_cache.borrow_mut(); + cache.truncate(start_len); + cache.push(self.data.len()); + } Value::Dict(d) => { self.push_tag(Tag::Dict); let start_pos = self.data.len(); diff --git a/src/relation/value.rs b/src/relation/value.rs index 3031e44b..a8862f42 100644 --- a/src/relation/value.rs +++ b/src/relation/value.rs @@ -27,6 +27,8 @@ pub enum Tag { List = 128, Dict = 129, + IdxAccess = 251, + FieldAccess = 252, Variable = 253, Apply = 254, MaxTag = 255, @@ -47,6 +49,8 @@ impl TryFrom for Tag { 7 => Uuid, 128 => List, 129 => Dict, + 251 => IdxAccess, + 252 => FieldAccess, 253 => Variable, 254 => Apply, 255 => MaxTag, @@ -94,6 +98,8 @@ pub enum Value<'a> { Dict(BTreeMap, Value<'a>>), Variable(Cow<'a, str>), Apply(Cow<'a, str>, Vec>), + FieldAccess(Cow<'a, str>, Box>), + IdxAccess(usize, Box>), EndSentinel, } @@ -119,6 +125,12 @@ impl<'a> Value<'a> { .map(|(k, v)| (Cow::Owned(k.into_owned()), v.to_static())) .collect::, StaticValue>>().into(), Value::EndSentinel => panic!("Cannot process sentinel value"), + Value::FieldAccess(field, value) => { + Value::FieldAccess(Cow::from(field.into_owned()),value.to_static().into()) + } + Value::IdxAccess(idx, value) => { + Value::IdxAccess(idx,value.to_static().into()) + } } } #[inline] @@ -134,7 +146,9 @@ impl<'a> Value<'a> { Value::List(l) => l.iter().all(|v| v.is_evaluated()), Value::Dict(d) => d.values().all(|v| v.is_evaluated()), Value::Variable(_) => false, - Value::Apply(_, _) => false + Value::Apply(_, _) => false, + Value::FieldAccess(_, _) => false, + Value::IdxAccess(_, _) => false } } #[inline] @@ -290,6 +304,12 @@ impl<'a> Display for Value<'a> { write!(f, "({} {})", op, args.iter().map(|v| v.to_string()).collect::>().join(" "))?; } + Value::FieldAccess(field, value) => { + write!(f, "(.{} {})", field, value)?; + } + Value::IdxAccess(idx, value) => { + write!(f, "(.{} {})", idx, value)?; + } } Ok(()) } @@ -360,7 +380,20 @@ fn build_expr_infix<'a>(lhs: Result>, op: Pair, rhs: Result) -> Result { match pair.as_rule() { Rule::expr => build_expr_primary(pair.into_inner().next().unwrap()), - Rule::term => build_expr_primary(pair.into_inner().next().unwrap()), + Rule::term => { + let mut pairs = pair.into_inner(); + let mut head = build_expr_primary(pairs.next().unwrap())?; + for p in pairs { + match p.as_rule() { + Rule::accessor => { + let accessor_key = p.into_inner().next().unwrap().as_str(); + head = Value::Apply(".".into(), vec![accessor_key.into()]) + } + _ => todo!() + } + } + Ok(head) + } Rule::grouping => Value::from_pair(pair.into_inner().next().unwrap()), Rule::unary => {