From 832431c70f6ab660ca751a161b994bf01acc1311 Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Thu, 28 Apr 2022 22:42:27 +0800 Subject: [PATCH] start doing mutation --- src/db.rs | 1 + src/db/eval.rs | 2 +- src/db/mutation.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++ src/grammar.pest | 12 ++-- 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 src/db/mutation.rs diff --git a/src/db.rs b/src/db.rs index 4d974985..2f770263 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,3 +1,4 @@ pub mod engine; pub mod eval; pub mod plan; +pub mod mutation; diff --git a/src/db/eval.rs b/src/db/eval.rs index 2e13374c..2ac32254 100644 --- a/src/db/eval.rs +++ b/src/db/eval.rs @@ -196,7 +196,7 @@ pub trait Environment<'t, T: AsRef<[u8]>> where Self: Sized { let in_root = match pair.as_rule() { Rule::global_def => true, Rule::local_def => false, - _ => unreachable!() + r => panic!("Encountered definition with rule {:?}", r) }; let (need_id, (name, mut tuple)) = self.parse_definition( diff --git a/src/db/mutation.rs b/src/db/mutation.rs new file mode 100644 index 00000000..450a5aa9 --- /dev/null +++ b/src/db/mutation.rs @@ -0,0 +1,148 @@ +use std::borrow::Cow; +use std::collections::{BTreeMap, BTreeSet}; +use pest::iterators::Pair; +use crate::db::engine::Session; +use crate::db::eval::Environment; +use crate::error::CozoError::LogicError; +use crate::error::Result; +use crate::parser::Rule; +use crate::parser::text_identifier::build_name_in_def; +use crate::relation::tuple::OwnTuple; +use crate::relation::value::Value; + +impl<'a, 't> Session<'a, 't> { + pub fn run_mutation(&mut self, pair: Pair) -> Result<()> { + let mut pairs = pair.into_inner(); + let kind = pairs.next().unwrap(); + let (evaluated, expr) = self.partial_eval(Value::from_pair(pairs.next().unwrap())?)?; + if !evaluated { + return Err(LogicError("Mutation encountered unevaluated expression".to_string())); + } + let expr = match expr { + Value::List(v) => v, + _ => return Err(LogicError("Mutation requires iterator of values".to_string())) + }; + let mut default_kind = None; + let mut filters: Option<()> = None; + for p in pairs { + match p.as_rule() { + Rule::name_in_def => default_kind = Some(build_name_in_def(p, true)?), + Rule::mutation_filter => filters = Some(()), // TODO + _ => unreachable!() + } + } + println!("{:?}", kind); + println!("{:?}", expr); + println!("{:?}", default_kind); + println!("{:?}", filters); + + let mut coercion_manager = TableCoercionManager::new(self, default_kind); + // Coercion + + for item in expr { + let val_map = match item { + Value::Dict(d) => d, + _ => return Err(LogicError("Must be structs".to_string())) + }; + coercion_manager.add(val_map)?; + // let explicit_tbl = val_map.get("_type"); + // let tbl = match explicit_tbl { + // None => if let Some(v) = &default_kind { + // v as &str + // } else { + // return Err(LogicError("Cannot determine table kind".to_string())); + // } + // Some(v) => { + // match v { + // Value::Text(t) => t as &str, + // _ => return Err(LogicError("Cannot determine table kind".to_string())) + // } + // } + // }; + // coercion_manager.coerce(tbl, val_map); + } + + Ok(()) + } +} + +struct TableCoercionManager<'a, 'b, 't> { + sess: &'a Session<'b, 't>, + cache: BTreeMap, + categorized: BTreeMap>, + default_tbl: Option, +} + +impl<'a, 'b, 't> TableCoercionManager<'a, 'b, 't> { + fn new(sess: &'a Session<'b, 't>, default_tbl: Option) -> Self { + Self { sess, cache: BTreeMap::new(), categorized: BTreeMap::new(), default_tbl } + } + fn add(&mut self, val_map: BTreeMap, Value>) -> Result<()> { + let tbl_name = match val_map.get("_type") { + Some(Value::Text(t)) => t as &str, + Some(_) => return Err(LogicError("Table kind must be text".to_string())), + None => match &self.default_tbl { + Some(v) => v as &str, + None => return Err(LogicError("Cannot determine table kind".to_string())) + } + }; + match self.cache.get(tbl_name) { + None => { + self.cache.insert(tbl_name.to_string(), ()); + } + Some(t) => {}, + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::fs; + use pest::Parser as PestParser; + use crate::db::engine::Engine; + use crate::db::eval::Environment; + use crate::parser::{Parser, Rule}; + + #[test] + fn test_mutation() { + let db_path = "_test_db_mutation"; + let engine = Engine::new(db_path.to_string(), true).unwrap(); + + { + let mut sess = engine.session().unwrap(); + let s = r#" + create node "Person" { + *id: Int, + name: Text, + email: ?Text, + habits: ?[?Text] + } + + create edge (Person)-[Friend]->(Person) { + relation: ?Text + } + "#; + for p in Parser::parse(Rule::file, s).unwrap() { + if p.as_rule() == Rule::EOI { + break; + } + sess.run_definition(p).unwrap(); + } + sess.commit().unwrap(); + } + + { + let mut sess = engine.session().unwrap(); + println!("{:#?}", sess.resolve("Person")); + let s = r#" + insert [{id: 1, name: "Jack"}, {id: 2, name: "Joe", habits: ["Balls"]}] as Person; + "#; + let p = Parser::parse(Rule::file, s).unwrap().next().unwrap(); + sess.run_mutation(p); + } + + drop(engine); + let _ = fs::remove_dir_all(db_path); + } +} \ No newline at end of file diff --git a/src/grammar.pest b/src/grammar.pest index 42455d7a..0477de28 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -1,4 +1,4 @@ -file = _{SOI ~ statement* ~ EOI} +file = _{SOI ~ (statement | mutation)* ~ EOI} // whitespace @@ -147,13 +147,13 @@ cols_def = { "{" ~ col_entry ~ ("," ~ col_entry)* ~ ","? ~ "}" } col_list = {"(" ~ name_in_def ~ ("," ~ name_in_def)* ~ ","? ~ ")"} -node_def = { "node" ~ name_in_def ~ cols_def } -associate_def = { "assoc" ~ name_in_def ~ ":" ~ name_in_def ~ cols_def } +node_def = { "node" ~ name_in_def ~ cols_def ~ ";"?} +associate_def = { "assoc" ~ name_in_def ~ ":" ~ name_in_def ~ cols_def ~ ";"? } edge_def = { "edge" ~ "(" ~ name_in_def ~ ")" ~ "-" ~ "[" ~ name_in_def ~ "]" ~ "->" ~ "(" ~ name_in_def ~ ")" - ~ cols_def? } -index_def = { "index" ~ (name_in_def ~ ":")? ~ name_in_def ~ col_list } -type_def = { "type" ~ name_in_def ~ "=" ~ typing } + ~ cols_def? ~ ";"? } +index_def = { "index" ~ (name_in_def ~ ":")? ~ name_in_def ~ col_list ~ ";"? } +type_def = { "type" ~ name_in_def ~ "=" ~ typing ~ ";"? } definition = _{ node_def | associate_def | edge_def | type_def | index_def } global_def = { "create" ~ definition }