use marsdb::Database;
fn seeded(indexed: bool) -> Database {
let db = Database::in_memory().unwrap();
if indexed {
db.execute("CREATE INDEX ON :M(year)").unwrap();
}
db.execute_batch(
"CREATE (:M {t: 'a', year: 1990}); \
CREATE (:M {t: 'b', year: 2000}); \
CREATE (:M {t: 'c', year: 2005}); \
CREATE (:M {t: 'd', year: 2005.5}); \
CREATE (:M {t: 'e', year: 2010}); \
CREATE (:M {t: 'f', year: 'not a year'}); \
CREATE (:M {t: 'g'})",
)
.unwrap();
db
}
fn titles(db: &Database, cypher: &str) -> Vec<String> {
let result = db.execute(cypher).unwrap();
let mut out: Vec<String> = result
.rows
.iter()
.map(|row| format!("{:?}", row[0]))
.collect();
out.sort();
out
}
#[test]
fn indexed_and_unindexed_results_agree() {
let with = seeded(true);
let without = seeded(false);
for cypher in [
"MATCH (m:M) WHERE m.year > 2000 RETURN m.t",
"MATCH (m:M) WHERE m.year >= 2000 RETURN m.t",
"MATCH (m:M) WHERE m.year < 2005 RETURN m.t",
"MATCH (m:M) WHERE m.year <= 2005 RETURN m.t",
"MATCH (m:M) WHERE m.year > 2000 AND m.year < 2010 RETURN m.t",
"MATCH (m:M) WHERE m.year > 2004.9 RETURN m.t",
"MATCH (m:M) WHERE m.year > 2005.5 RETURN m.t",
"MATCH (m:M) WHERE m.t > 'b' RETURN m.t",
"MATCH (m:M) WHERE m.year > 99999999999999999 RETURN m.t",
] {
assert_eq!(titles(&with, cypher), titles(&without, cypher), "{cypher}");
}
}
#[test]
fn cross_type_numerics_match_cypher_semantics() {
let db = seeded(true);
let got = titles(&db, "MATCH (m:M) WHERE m.year > 2000 RETURN m.t");
assert_eq!(got.len(), 3, "{got:?}"); let got = titles(&db, "MATCH (m:M) WHERE m.year > 2005.4 RETURN m.t");
assert_eq!(got.len(), 2, "{got:?}"); let got = titles(&db, "MATCH (m:M) WHERE m.year >= 1990 RETURN m.t");
assert_eq!(got.len(), 5, "{got:?}");
}
#[test]
fn explain_shows_the_range_seek() {
let db = seeded(true);
let plan = db
.execute("EXPLAIN MATCH (m:M) WHERE m.year > 2000 AND m.year <= 2010 RETURN m.t")
.unwrap();
let text = format!("{:?}", plan.rows);
assert!(text.contains("IndexRangeSeek"), "{text}");
assert!(!text.contains("NodeByLabelScan"), "{text}");
let db = seeded(false);
let plan = db
.execute("EXPLAIN MATCH (m:M) WHERE m.year > 2000 RETURN m.t")
.unwrap();
let text = format!("{:?}", plan.rows);
assert!(text.contains("NodeByLabelScan"), "{text}");
assert!(!text.contains("IndexRangeSeek"), "{text}");
}
#[test]
fn keyset_pagination_pages_through_everything() {
let db = Database::in_memory().unwrap();
db.execute("CREATE INDEX ON :P(seq)").unwrap();
for i in 0..25 {
db.execute_with_params(
"CREATE (:P {seq: $i})",
&std::collections::HashMap::from([("i".to_string(), marsdb::PropertyValue::Int(i))]),
)
.unwrap();
}
let mut seen = Vec::new();
let mut last: i64 = -1;
loop {
let page = db
.execute_with_params(
"MATCH (p:P) WHERE p.seq > $last RETURN p.seq AS seq LIMIT 10",
&std::collections::HashMap::from([(
"last".to_string(),
marsdb::PropertyValue::Int(last),
)]),
)
.unwrap();
if page.rows.is_empty() {
break;
}
for row in &page.rows {
let v = match &row[0] {
marsdb::Value::Property(marsdb::PropertyValue::Int(i)) => *i,
other => panic!("{other:?}"),
};
seen.push(v);
last = last.max(v);
}
}
seen.sort_unstable();
assert_eq!(seen, (0..25).collect::<Vec<i64>>());
}