main
Ziyang Hu 2 years ago
parent aeb4ae1522
commit d969e97e70

@ -1,445 +0,0 @@
use crate::db::engine::Session;
use crate::db::table::TableInfo;
use crate::error::CozoError::LogicError;
use crate::error::{CozoError, Result};
use crate::parser::text_identifier::{build_name_in_def, parse_string};
use crate::parser::Rule;
use crate::relation::data::DataKind;
use crate::relation::value;
use crate::relation::value::{StaticValue, Value};
use pest::iterators::Pair;
use std::collections::BTreeMap;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum FromEl {
Simple(Box<SimpleFromEl>),
Chain(Vec<EdgeOrNodeEl>),
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct SimpleFromEl {
pub table: String,
pub associates: Vec<(String, TableInfo)>,
pub binding: String,
pub info: TableInfo,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum EdgeOrNodeKind {
FwdEdge,
BwdEdge,
Node,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct EdgeOrNodeEl {
pub table: String,
pub binding: Option<String>,
pub associates: Vec<(String, TableInfo)>,
pub info: TableInfo,
pub kind: EdgeOrNodeKind,
pub left_outer_marker: bool,
pub right_outer_marker: bool,
}
impl<'a> Session<'a> {
pub fn parse_from_pattern(&self, pair: Pair) -> Result<Vec<FromEl>> {
let res: Result<Vec<_>> = pair
.into_inner()
.map(|p| match p.as_rule() {
Rule::simple_from_pattern => self.parse_simple_from_pattern(p),
Rule::node_edge_pattern => self.parse_node_edge_pattern(p),
_ => unreachable!(),
})
.collect();
res
}
fn parse_simple_from_pattern(&self, pair: Pair) -> Result<FromEl> {
let mut pairs = pair.into_inner();
let name = pairs.next().unwrap().as_str();
if name.starts_with('_') {
return Err(CozoError::LogicError(
"Pattern binding cannot start with underscore".to_string(),
));
}
let table_name = build_name_in_def(pairs.next().unwrap(), true)?;
let table_info = self.get_table_info(&table_name)?;
let associates = pairs
.map(|p| -> Result<(String, TableInfo)> {
let a_name = build_name_in_def(p, true)?;
let a_info = self.get_table_info(&a_name)?;
Ok((a_name, a_info))
})
.collect::<Result<Vec<_>>>()?;
let ret = FromEl::Simple(Box::new(SimpleFromEl {
binding: name.to_string(),
table: table_name,
info: table_info,
associates,
}));
Ok(ret)
}
fn parse_node_edge_pattern(&self, pair: Pair) -> Result<FromEl> {
let res: Result<Vec<_>> = pair
.into_inner()
.map(|p| match p.as_rule() {
Rule::node_pattern => self.parse_node_pattern(p),
Rule::edge_pattern => {
// let right_join;
let mut pairs = p.into_inner();
let nxt = pairs.next().unwrap();
if nxt.as_rule() == Rule::outer_join_marker {
// right_join = true;
// nxt = pairs.next().unwrap();
return Err(LogicError(
"Right outer join not supported here".to_string(),
));
} else {
// right_join = false;
}
let mut edge = match nxt.as_rule() {
Rule::fwd_edge_pattern => self.parse_edge_pattern(nxt, true)?,
Rule::bwd_edge_pattern => self.parse_edge_pattern(nxt, false)?,
_ => unreachable!(),
};
edge.left_outer_marker = pairs.next().is_some();
edge.right_outer_marker = false; // right_join;
Ok(edge)
}
_ => unreachable!(),
})
.collect();
let res = res?;
let connects = res.windows(2).all(|v| {
let left = &v[0];
let right = &v[1];
match (&left.kind, &right.kind) {
(EdgeOrNodeKind::FwdEdge, EdgeOrNodeKind::Node) => {
left.info.dst_table_id == right.info.table_id
}
(EdgeOrNodeKind::BwdEdge, EdgeOrNodeKind::Node) => {
left.info.src_table_id == right.info.table_id
}
(EdgeOrNodeKind::Node, EdgeOrNodeKind::FwdEdge) => {
left.info.table_id == right.info.src_table_id
}
(EdgeOrNodeKind::Node, EdgeOrNodeKind::BwdEdge) => {
left.info.table_id == right.info.dst_table_id
}
_ => unreachable!(),
}
});
if !connects {
return Err(CozoError::LogicError("Chain does not connect".to_string()));
}
if res.is_empty() {
return Err(CozoError::LogicError("Empty chain not allowed".to_string()));
}
Ok(FromEl::Chain(res))
}
fn parse_node_pattern(&self, pair: Pair) -> Result<EdgeOrNodeEl> {
let (table, binding, info, associates) = self.parse_node_or_edge(pair)?;
if info.kind != DataKind::Node {
return Err(CozoError::LogicError(format!("{} is not a node", table)));
}
Ok(EdgeOrNodeEl {
table,
binding,
info,
kind: EdgeOrNodeKind::Node,
left_outer_marker: false,
right_outer_marker: false,
associates,
})
}
fn parse_edge_pattern(&self, pair: Pair, is_fwd: bool) -> Result<EdgeOrNodeEl> {
let (table, binding, info, associates) = self.parse_node_or_edge(pair)?;
if info.kind != DataKind::Edge {
return Err(CozoError::LogicError(format!("{} is not an edge", table)));
}
Ok(EdgeOrNodeEl {
table,
binding,
info,
kind: if is_fwd {
EdgeOrNodeKind::FwdEdge
} else {
EdgeOrNodeKind::BwdEdge
},
left_outer_marker: false,
right_outer_marker: false,
associates,
})
}
fn parse_node_or_edge(
&self,
pair: Pair,
) -> Result<(String, Option<String>, TableInfo, Vec<(String, TableInfo)>)> {
let name;
let mut pairs = pair.into_inner();
let mut cur_pair = pairs.next().unwrap();
if cur_pair.as_rule() == Rule::ident {
name = Some(cur_pair.as_str());
cur_pair = pairs.next().unwrap();
} else {
name = None;
}
let table_name = build_name_in_def(cur_pair, true)?;
let table_info = self.get_table_info(&table_name)?;
// println!("{:?}, {}, {:?}", name, table_name, table_info);
let associates = pairs
.map(|p| -> Result<(String, TableInfo)> {
let a_name = build_name_in_def(p, true)?;
let a_info = self.get_table_info(&a_name)?;
Ok((a_name, a_info))
})
.collect::<Result<Vec<_>>>()?;
Ok((
table_name,
name.map(|v| v.to_string()),
table_info,
associates,
))
}
pub fn parse_where_pattern(&self, pair: Pair) -> Result<Value> {
let conditions = pair
.into_inner()
.map(Value::from_pair)
.collect::<Result<Vec<_>>>()?;
Ok(Value::Apply(value::OP_AND.into(), conditions).to_static())
}
pub fn parse_select_pattern(&self, pair: Pair) -> Result<Selection> {
let mut pairs = pair.into_inner();
let mut nxt = pairs.next().unwrap();
let scoped = match nxt.as_rule() {
Rule::scoped_dict => {
let mut pp = nxt.into_inner();
let name = pp.next().unwrap().as_str();
nxt = pp.next().unwrap();
Some(name.to_string())
}
_ => None,
};
let mut keys = vec![];
let mut merged = vec![];
let mut collected_vals = BTreeMap::new();
for p in nxt.into_inner() {
match p.as_rule() {
Rule::grouped_pair => {
let mut pp = p.into_inner();
let id = parse_string(pp.next().unwrap())?;
let val = Value::from_pair(pp.next().unwrap())?;
keys.push((id, val.to_static()));
}
Rule::dict_pair => {
let mut inner = p.into_inner();
let name = parse_string(inner.next().unwrap())?;
let val_pair = inner.next().unwrap();
let val = Value::from_pair(val_pair)?;
collected_vals.insert(name.into(), val);
}
Rule::spreading => {
let el = p.into_inner().next().unwrap();
let to_concat = Value::from_pair(el)?;
if !matches!(
to_concat,
Value::Dict(_)
| Value::Variable(_)
| Value::IdxAccess(_, _)
| Value::FieldAccess(_, _)
| Value::Apply(_, _)
) {
return Err(CozoError::LogicError("Cannot spread".to_string()));
}
if !collected_vals.is_empty() {
merged.push(Value::Dict(collected_vals));
collected_vals = BTreeMap::new();
}
merged.push(to_concat);
}
Rule::scoped_accessor => {
let name = parse_string(p.into_inner().next().unwrap())?;
let val =
Value::FieldAccess(name.clone().into(), Value::Variable("_".into()).into());
collected_vals.insert(name.into(), val);
}
_ => unreachable!(),
}
}
let vals = if merged.is_empty() {
collected_vals
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_static()))
.collect::<Vec<_>>()
} else {
// construct it with help of partial eval
todo!()
// if !collected_vals.is_empty() {
// merged.push(Value::Dict(collected_vals));
// }
// Value::Apply(value::METHOD_MERGE.into(), merged).to_static()
};
let mut ordering = vec![];
let mut limit = None;
let mut offset = None;
for p in pairs {
match p.as_rule() {
Rule::order_pattern => {
for p in p.into_inner() {
ordering.push((
p.as_rule() == Rule::order_asc,
Value::from_pair(p.into_inner().next().unwrap())?.to_static(),
))
}
}
Rule::offset_pattern => {
for p in p.into_inner() {
match p.as_rule() {
Rule::limit_clause => {
limit = Some(
p.into_inner()
.next()
.unwrap()
.as_str()
.replace('_', "")
.parse::<i64>()?,
);
}
Rule::offset_clause => {
offset = Some(
p.into_inner()
.next()
.unwrap()
.as_str()
.replace('_', "")
.parse::<i64>()?,
);
}
_ => unreachable!(),
}
}
}
_ => unreachable!(),
}
}
println!("ordering {:?}", ordering);
Ok(Selection {
scoped,
keys,
vals,
ordering,
limit,
offset,
})
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Selection {
pub scoped: Option<String>,
pub keys: Vec<(String, StaticValue)>,
pub vals: Vec<(String, StaticValue)>,
pub ordering: Vec<(bool, StaticValue)>,
pub limit: Option<i64>,
pub offset: Option<i64>,
}
#[cfg(test)]
mod tests {
use std::fs;
// use super::*;
use crate::db::engine::Engine;
use crate::parser::{Parser, Rule};
use pest::Parser as PestParser;
#[test]
fn parse_patterns() {
let db_path = "_test_db_query";
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
}
create node Z {
*id: Text
}
create assoc WorkInfo : Person {
work_id: Int
}
create assoc RelationshipData: Person {
status: 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 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);
let from_pattern = sess.parse_from_pattern(parsed).unwrap();
println!("{:#?}", from_pattern);
let s = "where b.id > c.id || x.name.is_null(), a.id == 5, x.name == 'Joe', x.name.len() == 3";
let parsed = Parser::parse(Rule::where_pattern, s)
.unwrap()
.next()
.unwrap();
let where_result = sess.parse_where_pattern(parsed).unwrap();
println!("{:#?}", where_result);
let s = "select {*id: a.id, b: a.b, c: a.c} ordered [e, +c, -b] limit 1 offset 2";
let parsed = Parser::parse(Rule::select_pattern, s)
.unwrap()
.next()
.unwrap();
let select_result = sess.parse_select_pattern(parsed).unwrap();
println!("{:#?}", select_result);
}
drop(engine);
let _ = fs::remove_dir_all(db_path);
}
}
Loading…
Cancel
Save