use nedb_engine::db::Db;
use nedb_engine::pgwire::execute_sql;
use serde_json::json;
use std::sync::Arc;
fn fixture() -> (Arc<Db>, tempfile::TempDir) {
let dir = tempfile::tempdir().unwrap();
let db = Arc::new(Db::open(dir.path(), None).unwrap());
for (id, who, total, region) in [
("1", "acme", 100, "us"),
("2", "globex", 250, "us"),
("3", "initech", 300, "eu"),
("4", "umbrella", 50, "eu"),
] {
db.put(
"orders",
id,
json!({ "who": who, "total": total, "region": region }),
vec![],
None,
None,
)
.unwrap();
}
(db, dir)
}
fn whos(db: &Arc<Db>, sql: &str) -> Vec<String> {
let mut v: Vec<String> = execute_sql(db, sql, true)
.expect(sql)
.rows
.iter()
.filter_map(|r| r.get("who").and_then(|w| w.as_str()).map(String::from))
.collect();
v.sort();
v
}
#[test]
fn update_can_select_rows_with_a_subquery() {
let (db, _d) = fixture();
let done = execute_sql(
&db,
"UPDATE orders SET status = 'flagged' \
WHERE _id IN (SELECT _id FROM orders WHERE total > 200)",
false,
)
.expect("a subquery must be usable to select rows for an UPDATE");
assert_eq!(done.tag, "UPDATE 2", "only the two orders over 200");
assert_eq!(
whos(&db, "SELECT who FROM orders WHERE status = 'flagged'"),
vec!["globex", "initech"],
"and it must be the RIGHT two -- a count alone would pass while \
flagging the wrong rows"
);
}
#[test]
fn delete_can_select_rows_with_a_subquery_and_still_return_them() {
let (db, _d) = fixture();
let done = execute_sql(
&db,
"DELETE FROM orders WHERE _id IN (SELECT _id FROM orders WHERE region = 'eu') \
RETURNING who",
false,
)
.expect("a subquery must be usable to select rows for a DELETE");
assert_eq!(done.tag, "DELETE 2");
let mut returned: Vec<String> = done
.rows
.iter()
.filter_map(|r| r.get("who").and_then(|w| w.as_str()).map(String::from))
.collect();
returned.sort();
assert_eq!(
returned,
vec!["initech", "umbrella"],
"RETURNING must carry the rows as they were, read before the delete"
);
assert_eq!(whos(&db, "SELECT who FROM orders"), vec!["acme", "globex"]);
}
#[test]
fn a_write_against_an_absent_collection_fails_rather_than_reporting_zero() {
let (db, _d) = fixture();
for sql in [
"UPDATE nowhere SET x = 1 WHERE y = 2",
"DELETE FROM nowhere WHERE y = 2",
] {
let e = match execute_sql(&db, sql, false) {
Err(e) => e,
Ok(done) => panic!(
"{:?} must be refused, but it succeeded with {:?}. An absent \
collection is a legitimately empty scan for the evaluator, so \
without the guard a write reports success having written nothing.",
sql, done.tag
),
};
assert!(
e.contains("nowhere") && e.contains("does not exist"),
"the refusal must name the relation: {} -> {}",
sql,
e
);
}
}
#[test]
fn an_update_matches_exactly_what_a_select_matches() {
for pred in [
"total > 200",
"total BETWEEN 1 AND 120",
"region = 'eu' AND total < 100",
"who LIKE 'a%'",
"who IN ('acme', 'globex')",
"total > 200 OR region = 'us'",
"_id IN (SELECT _id FROM orders WHERE total > 90)",
"total > 100000",
] {
let (db, _d) = fixture();
let selected = execute_sql(
&db,
&format!("SELECT _id FROM orders WHERE {}", pred),
true,
)
.unwrap_or_else(|e| panic!("SELECT with {:?} failed: {}", pred, e))
.rows
.len();
let tag = execute_sql(
&db,
&format!("UPDATE orders SET touched = 1 WHERE {}", pred),
false,
)
.unwrap_or_else(|e| panic!("UPDATE with {:?} failed: {}", pred, e))
.tag;
assert_eq!(
tag,
format!("UPDATE {}", selected),
"SELECT and UPDATE disagree about which rows {:?} matches -- that \
is two predicate implementations, which is what this change removed",
pred
);
}
}