use super::*;
fn scope_graph() -> DirGraph {
let mut graph = DirGraph::new();
run_write(
&mut graph,
"CREATE (x:N {id:'x'}) CREATE (y:N {id:'y'}) CREATE (z:N {id:'z'}) \
CREATE (x)-[:R {w:1}]->(y) CREATE (x)-[:R {w:2}]->(z)",
);
graph
}
fn run_write(graph: &mut DirGraph, source: &str) -> CypherResult {
let query = parser::parse_cypher(source)
.unwrap_or_else(|e| panic!("failed to parse: {source}\n error: {e}"));
execute_mutable(
graph,
&query,
HashMap::new(),
crate::graph::algorithms::Interrupt::default(),
)
.unwrap_or_else(|e| panic!("failed to execute: {source}\n error: {e}"))
}
fn read_with(graph: &DirGraph, source: &str, optimize: bool) -> CypherResult {
let params = HashMap::new();
let mut query = parser::parse_cypher(source)
.unwrap_or_else(|e| panic!("failed to parse: {source}\n error: {e}"));
if optimize {
crate::graph::languages::cypher::planner::optimize(&mut query, graph, ¶ms);
}
CypherExecutor::with_params(graph, ¶ms, None)
.execute(&query)
.unwrap_or_else(|e| panic!("failed to execute: {source}\n error: {e}"))
}
fn assert_rows(graph: &DirGraph, source: &str, expected: Vec<Vec<Value>>) {
for optimize in [false, true] {
let result = read_with(graph, source, optimize);
assert_eq!(
result.rows, expected,
"rows for `{source}` (optimize={optimize})"
);
}
}
fn assert_columns(graph: &DirGraph, source: &str, expected: &[&str]) {
for optimize in [false, true] {
let result = read_with(graph, source, optimize);
assert_eq!(
result.columns, expected,
"columns for `{source}` (optimize={optimize})"
);
}
}
fn s(text: &str) -> Value {
Value::String(text.to_string())
}
#[test]
fn a_name_the_with_drops_binds_afresh_in_a_later_match() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (a:N {id:'x'}), (b:N {id:'y'}) WITH 1 AS u \
MATCH (a:N {id:'x'}), (b:N {id:'z'}) RETURN a.id, b.id",
vec![vec![s("x"), s("z")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u MATCH (a:N {id:'y'}) RETURN a.id",
vec![vec![s("y")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}), (b:N {id:'y'}) WITH 1 AS u \
MATCH (c:N {id:'x'}), (d:N {id:'z'}) RETURN c.id, d.id",
vec![vec![s("x"), s("z")]],
);
}
#[test]
fn every_non_aggregating_with_spelling_drops_the_binding() {
let graph = scope_graph();
for source in [
"MATCH (a:N {id:'x'}) WITH 1 AS u MATCH (a:N {id:'y'}) RETURN a.id",
"MATCH (a:N {id:'x'}) WITH DISTINCT 1 AS u MATCH (a:N {id:'y'}) RETURN a.id",
"MATCH (a:N {id:'x'}) WITH 1 AS u WHERE u = 1 MATCH (a:N {id:'y'}) RETURN a.id",
"MATCH (a:N {id:'x'}) WITH 1 AS u ORDER BY u LIMIT 1 MATCH (a:N {id:'y'}) RETURN a.id",
"MATCH (a:N {id:'x'}) WITH 1 AS u WITH u AS v MATCH (a:N {id:'y'}) RETURN a.id",
"MATCH (a:N {id:'x'}) WITH a, count(*) AS c WITH 1 AS u \
MATCH (a:N {id:'y'}) RETURN a.id",
] {
assert_rows(&graph, source, vec![vec![s("y")]]);
}
assert_rows(
&graph,
"MATCH (a:N {id:'x'}), (b:N {id:'y'}) WITH count(*) AS n \
MATCH (a:N {id:'x'}), (b:N {id:'z'}) RETURN n, a.id, b.id",
vec![vec![Value::Int64(1), s("x"), s("z")]],
);
}
#[test]
fn an_alias_moves_the_binding_and_frees_the_old_name() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a AS k MATCH (a:N {id:'y'}) RETURN a.id, k.id",
vec![vec![s("y"), s("x")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a AS k MATCH (k)-[:R]->(m) RETURN k.id, m.id ORDER BY m.id",
vec![vec![s("x"), s("y")], vec![s("x"), s("z")]],
);
}
#[test]
fn optional_match_after_a_with_rebinds_instead_of_reading_a_stale_node() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u OPTIONAL MATCH (a:N {id:'y'}) RETURN a.id",
vec![vec![s("y")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u OPTIONAL MATCH (a:N {id:'nope'}) RETURN a.id",
vec![vec![Value::Null]],
);
}
#[test]
fn an_edge_variable_the_with_drops_binds_afresh() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (:N {id:'x'})-[r:R]->(:N {id:'y'}) WITH 1 AS u \
MATCH (:N {id:'x'})-[r:R]->(q:N {id:'z'}) RETURN q.id, r.w",
vec![vec![s("z"), Value::Int64(2)]],
);
}
#[test]
fn a_dropped_name_does_not_constrain_a_later_scan() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u MATCH (a:N) RETURN count(*) AS c",
vec![vec![Value::Int64(3)]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'})-[:R]->(b) WITH a MATCH (b:N {id:'z'}) RETURN a.id, b.id",
vec![vec![s("x"), s("z")], vec![s("x"), s("z")]],
);
}
#[test]
fn return_star_after_a_with_lists_only_the_projected_names() {
let graph = scope_graph();
assert_columns(&graph, "MATCH (a:N {id:'x'}) WITH 1 AS u RETURN *", &["u"]);
assert_columns(
&graph,
"MATCH (:N {id:'x'})-[r:R]->() WITH 1 AS u RETURN *",
&["u"],
);
assert_columns(
&graph,
"MATCH p = (:N {id:'x'})-[:R]->() WITH 1 AS u RETURN *",
&["u"],
);
}
#[test]
fn a_with_keeps_the_names_it_projects() {
let graph = scope_graph();
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a MATCH (a:N) RETURN a.id",
vec![vec![s("x")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a MATCH (a)-[:R]->(m) RETURN m.id ORDER BY m.id",
vec![vec![s("y")], vec![s("z")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH * MATCH (a)-[:R]->(m) RETURN m.id ORDER BY m.id",
vec![vec![s("y")], vec![s("z")]],
);
assert_rows(
&graph,
"MATCH p = (:N {id:'x'})-[:R]->(b) WITH p, b RETURN length(p) AS l, b.id ORDER BY b.id",
vec![vec![Value::Int64(1), s("y")], vec![Value::Int64(1), s("z")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a, count(*) AS c MATCH (a)-[:R]->(m) RETURN m.id ORDER BY m.id",
vec![vec![s("y")], vec![s("z")]],
);
assert_rows(
&graph,
"MATCH (a:N {id:'x'}) WITH a \
CALL { WITH a MATCH (a)-[:R]->(m) RETURN m.id AS mid } RETURN mid ORDER BY mid",
vec![vec![s("y")], vec![s("z")]],
);
}
#[test]
fn create_after_a_with_rebinds_both_endpoints() {
let mut graph = scope_graph();
let result = run_write(
&mut graph,
"MATCH (a:N {id:'x'}), (b:N {id:'y'}) CREATE (a)-[:K]->(b) WITH 1 AS u \
MATCH (a:N {id:'x'}), (b:N {id:'z'}) CREATE (a)-[:K]->(b)",
);
assert_eq!(result.stats.unwrap().relationships_created, 2);
assert_rows(
&graph,
"MATCH (:N {id:'x'})-[:K]->(t) RETURN t.id ORDER BY t.id",
vec![vec![s("y")], vec![s("z")]],
);
}
#[test]
fn set_and_merge_after_a_with_act_on_the_rebound_node() {
let mut graph = scope_graph();
let result = run_write(
&mut graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u MATCH (a:N {id:'y'}) SET a.tag = 'hit'",
);
assert_eq!(result.stats.unwrap().properties_set, 1);
assert_rows(
&graph,
"MATCH (n:N) WHERE n.tag = 'hit' RETURN n.id",
vec![vec![s("y")]],
);
let mut graph = scope_graph();
let result = run_write(
&mut graph,
"MATCH (a:N {id:'x'}) WITH 1 AS u MERGE (a:N {id:'w'}) RETURN a.id",
);
assert_eq!(result.rows, vec![vec![s("w")]]);
assert_rows(
&graph,
"MATCH (n:N) RETURN count(*) AS c",
vec![vec![Value::Int64(4)]],
);
}