Return error when selecting from non-existent table in strict mode

Closes #13
This commit is contained in:
Tobie Morgan Hitchcock 2022-08-08 22:41:33 +01:00
parent bac8aa31a3
commit af45b33fa0
4 changed files with 63 additions and 5 deletions

View file

@ -30,6 +30,8 @@ impl Iterable {
chn.send((None, val)).await?; chn.send((None, val)).await?;
} }
Iterable::Thing(v) => { Iterable::Thing(v) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -42,6 +44,8 @@ impl Iterable {
chn.send((Some(v), val)).await?; chn.send((Some(v), val)).await?;
} }
Iterable::Mergeable(v, o) => { Iterable::Mergeable(v, o) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -56,6 +60,8 @@ impl Iterable {
chn.send((Some(v), val)).await?; chn.send((Some(v), val)).await?;
} }
Iterable::Relatable(f, v, w) => { Iterable::Relatable(f, v, w) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -70,6 +76,8 @@ impl Iterable {
chn.send((Some(v), val)).await?; chn.send((Some(v), val)).await?;
} }
Iterable::Table(v) => { Iterable::Table(v) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v, opt.strict).await?;
// Prepare the start and end keys // Prepare the start and end keys
let beg = thing::prefix(opt.ns(), opt.db(), &v); let beg = thing::prefix(opt.ns(), opt.db(), &v);
let end = thing::suffix(opt.ns(), opt.db(), &v); let end = thing::suffix(opt.ns(), opt.db(), &v);

View file

@ -30,6 +30,8 @@ impl Iterable {
ite.process(ctx, opt, txn, stm, None, val).await; ite.process(ctx, opt, txn, stm, None, val).await;
} }
Iterable::Thing(v) => { Iterable::Thing(v) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -42,6 +44,8 @@ impl Iterable {
ite.process(ctx, opt, txn, stm, Some(v), val).await; ite.process(ctx, opt, txn, stm, Some(v), val).await;
} }
Iterable::Mergeable(v, o) => { Iterable::Mergeable(v, o) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -56,6 +60,8 @@ impl Iterable {
ite.process(ctx, opt, txn, stm, Some(v), val).await; ite.process(ctx, opt, txn, stm, Some(v), val).await;
} }
Iterable::Relatable(f, v, w) => { Iterable::Relatable(f, v, w) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?;
// Fetch the data from the store // Fetch the data from the store
let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id); let key = thing::new(opt.ns(), opt.db(), &v.tb, &v.id);
let val = txn.clone().lock().await.get(key).await?; let val = txn.clone().lock().await.get(key).await?;
@ -70,6 +76,8 @@ impl Iterable {
ite.process(ctx, opt, txn, stm, Some(v), val).await; ite.process(ctx, opt, txn, stm, Some(v), val).await;
} }
Iterable::Table(v) => { Iterable::Table(v) => {
// Check that the table exists
txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v, opt.strict).await?;
// Prepare the start and end keys // Prepare the start and end keys
let beg = thing::prefix(opt.ns(), opt.db(), &v); let beg = thing::prefix(opt.ns(), opt.db(), &v);
let end = thing::suffix(opt.ns(), opt.db(), &v); let end = thing::suffix(opt.ns(), opt.db(), &v);

View file

@ -1134,6 +1134,26 @@ impl Transaction {
Ok(v) => Ok(v), Ok(v) => Ok(v),
} }
} }
/// Retrieve and cache a specific table definition.
pub async fn check_ns_db_tb(
&mut self,
ns: &str,
db: &str,
tb: &str,
strict: bool,
) -> Result<(), Error> {
match strict {
// Strict mode is disabled
false => Ok(()),
// Strict mode is enabled
true => {
self.get_and_cache_ns(ns).await?;
self.get_and_cache_db(ns, db).await?;
self.get_and_cache_tb(ns, db, tb).await?;
Ok(())
}
}
}
/// Writes the full database contents as binary SQL. /// Writes the full database contents as binary SQL.
pub async fn export(&mut self, ns: &str, db: &str, chn: Sender<Vec<u8>>) -> Result<(), Error> { pub async fn export(&mut self, ns: &str, db: &str, chn: Sender<Vec<u8>>) -> Result<(), Error> {
// Output OPTIONS // Output OPTIONS

View file

@ -13,11 +13,15 @@ async fn strict_mode_no_namespace() -> Result<(), Error> {
DEFINE TABLE test; DEFINE TABLE test;
DEFINE FIELD extra ON test VALUE true; DEFINE FIELD extra ON test VALUE true;
CREATE test:tester; CREATE test:tester;
SELECT * FROM test;
"; ";
let dbs = Datastore::new("memory").await?; let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test"); let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, true).await?; let res = &mut dbs.execute(&sql, &ses, None, true).await?;
assert_eq!(res.len(), 4); assert_eq!(res.len(), 5);
//
let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
// //
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::NsNotFound))); assert!(matches!(tmp.err(), Some(Error::NsNotFound)));
@ -42,11 +46,12 @@ async fn strict_mode_no_database() -> Result<(), Error> {
DEFINE TABLE test; DEFINE TABLE test;
DEFINE FIELD extra ON test VALUE true; DEFINE FIELD extra ON test VALUE true;
CREATE test:tester; CREATE test:tester;
SELECT * FROM test;
"; ";
let dbs = Datastore::new("memory").await?; let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test"); let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, true).await?; let res = &mut dbs.execute(&sql, &ses, None, true).await?;
assert_eq!(res.len(), 4); assert_eq!(res.len(), 5);
// //
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(tmp.is_ok()); assert!(tmp.is_ok());
@ -60,6 +65,9 @@ async fn strict_mode_no_database() -> Result<(), Error> {
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::DbNotFound))); assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
// //
let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::DbNotFound)));
//
Ok(()) Ok(())
} }
@ -71,11 +79,12 @@ async fn strict_mode_no_table() -> Result<(), Error> {
-- DEFINE TABLE test; -- DEFINE TABLE test;
DEFINE FIELD extra ON test VALUE true; DEFINE FIELD extra ON test VALUE true;
CREATE test:tester; CREATE test:tester;
SELECT * FROM test;
"; ";
let dbs = Datastore::new("memory").await?; let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test"); let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, true).await?; let res = &mut dbs.execute(&sql, &ses, None, true).await?;
assert_eq!(res.len(), 4); assert_eq!(res.len(), 5);
// //
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(tmp.is_ok()); assert!(tmp.is_ok());
@ -89,6 +98,9 @@ async fn strict_mode_no_table() -> Result<(), Error> {
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::TbNotFound))); assert!(matches!(tmp.err(), Some(Error::TbNotFound)));
// //
let tmp = res.remove(0).result;
assert!(matches!(tmp.err(), Some(Error::TbNotFound)));
//
Ok(()) Ok(())
} }
@ -100,11 +112,12 @@ async fn strict_mode_all_ok() -> Result<(), Error> {
DEFINE TABLE test; DEFINE TABLE test;
DEFINE FIELD extra ON test VALUE true; DEFINE FIELD extra ON test VALUE true;
CREATE test:tester; CREATE test:tester;
SELECT * FROM test;
"; ";
let dbs = Datastore::new("memory").await?; let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test"); let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, true).await?; let res = &mut dbs.execute(&sql, &ses, None, true).await?;
assert_eq!(res.len(), 5); assert_eq!(res.len(), 6);
// //
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(tmp.is_ok()); assert!(tmp.is_ok());
@ -122,6 +135,10 @@ async fn strict_mode_all_ok() -> Result<(), Error> {
let val = Value::parse("[{ id: test:tester, extra: true }]"); let val = Value::parse("[{ id: test:tester, extra: true }]");
assert_eq!(tmp, val); assert_eq!(tmp, val);
// //
let tmp = res.remove(0).result?;
let val = Value::parse("[{ id: test:tester, extra: true }]");
assert_eq!(tmp, val);
//
Ok(()) Ok(())
} }
@ -130,6 +147,7 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
let sql = " let sql = "
DEFINE FIELD extra ON test VALUE true; DEFINE FIELD extra ON test VALUE true;
CREATE test:tester; CREATE test:tester;
SELECT * FROM test;
INFO FOR KV; INFO FOR KV;
INFO FOR NS; INFO FOR NS;
INFO FOR DB; INFO FOR DB;
@ -138,7 +156,7 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
let dbs = Datastore::new("memory").await?; let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test"); let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?; let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 6); assert_eq!(res.len(), 7);
// //
let tmp = res.remove(0).result; let tmp = res.remove(0).result;
assert!(tmp.is_ok()); assert!(tmp.is_ok());
@ -148,6 +166,10 @@ async fn loose_mode_all_ok() -> Result<(), Error> {
assert_eq!(tmp, val); assert_eq!(tmp, val);
// //
let tmp = res.remove(0).result?; let tmp = res.remove(0).result?;
let val = Value::parse("[{ id: test:tester, extra: true }]");
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse( let val = Value::parse(
"{ "{
ns: { test: 'DEFINE NAMESPACE test' }, ns: { test: 'DEFINE NAMESPACE test' },