From d6196333400befc7047cc1bfbd8ed812991f2cec Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Wed, 27 Jul 2022 14:05:28 +0100 Subject: [PATCH] Enable new database strict mode configuration --- lib/src/dbs/options.rs | 17 ++++++- lib/src/doc/document.rs | 28 ++++++---- lib/src/err/mod.rs | 19 +++++-- lib/src/kvs/ds.rs | 9 ++++ lib/src/kvs/tx.rs | 87 +++++++++++++++++++++++--------- lib/src/sql/statements/define.rs | 40 +++++++-------- src/cli/config.rs | 4 ++ src/cli/mod.rs | 8 +++ src/dbs/mod.rs | 7 +++ src/iam/signin.rs | 5 +- src/iam/signup.rs | 5 +- src/net/import.rs | 5 +- src/net/key.rs | 73 ++++++++++++++++++++++++--- src/net/rpc.rs | 45 +++++++++++++---- src/net/sql.rs | 9 +++- 15 files changed, 279 insertions(+), 82 deletions(-) diff --git a/lib/src/dbs/options.rs b/lib/src/dbs/options.rs index af5d2baf..121b78c9 100644 --- a/lib/src/dbs/options.rs +++ b/lib/src/dbs/options.rs @@ -35,6 +35,8 @@ pub struct Options { pub events: bool, // Should we process table queries? pub tables: bool, + // Should we error if tables don't exist? + pub strict: bool, // Should we process function futures? pub futures: bool, } @@ -59,6 +61,7 @@ impl Options { fields: true, events: true, tables: true, + strict: false, futures: false, auth: Arc::new(auth), } @@ -168,6 +171,17 @@ impl Options { } } + // Create a new Options object for a subquery + pub fn strict(&self, v: bool) -> Options { + Options { + auth: self.auth.clone(), + ns: self.ns.clone(), + db: self.db.clone(), + strict: v, + ..*self + } + } + // Create a new Options object for a subquery pub fn futures(&self, v: bool) -> Options { Options { @@ -179,6 +193,7 @@ impl Options { } } + // Check whether realtime queries are supported pub fn realtime(&self) -> Result<(), Error> { if !self.live { return Err(Error::RealtimeDisabled); @@ -194,7 +209,7 @@ impl Options { Ok(()) } - // Check whether the authentication permissions are ok + // Check whether the necessary NS / DB options have been set pub fn needs(&self, level: Level) -> Result<(), Error> { if self.ns.is_none() && matches!(level, Level::Ns | Level::Db) { return Err(Error::NsEmpty); diff --git a/lib/src/doc/document.rs b/lib/src/doc/document.rs index 43efadd5..28fc7f25 100644 --- a/lib/src/doc/document.rs +++ b/lib/src/doc/document.rs @@ -50,21 +50,31 @@ impl<'a> Document<'a> { opt: &Options, txn: &Transaction, ) -> Result { + // Clone transaction + let run = txn.clone(); + // Claim transaction + let mut run = run.lock().await; // Get the record id - let id = self.id.as_ref().unwrap(); + let rid = self.id.as_ref().unwrap(); // Get the table definition - let tb = txn.clone().lock().await.get_tb(opt.ns(), opt.db(), &id.tb).await; + let tb = run.get_tb(opt.ns(), opt.db(), &rid.tb).await; // Return the table or attempt to define it match tb { + // The table doesn't exist + Err(Error::TbNotFound) => match opt.auth.check(Level::Db) { + // We can create the table automatically + true => { + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &rid.tb, opt.strict).await + } + // We can't create the table so error + false => Err(Error::TbNotFound), + }, + // There was an error + Err(err) => Err(err), // The table exists Ok(tb) => Ok(tb), - // The table doesn't exist - Err(e) => match opt.auth.check(Level::Db) { - // We can create the table automatically - true => txn.clone().lock().await.add_tb(opt.ns(), opt.db(), &id.tb).await, - // We can't create the table so error - false => Err(e), - }, } } // Get the foreign tables for this document diff --git a/lib/src/err/mod.rs b/lib/src/err/mod.rs index 79cc9c81..5915e7c6 100644 --- a/lib/src/err/mod.rs +++ b/lib/src/err/mod.rs @@ -33,6 +33,10 @@ pub enum Error { #[error("Value being checked was not correct")] TxConditionNotMet, + /// The key being inserted in the transaction already exists + #[error("The key being inserted already exists")] + TxKeyAlreadyExists, + /// No namespace has been selected #[error("Specify a namespace to use")] NsEmpty, @@ -259,21 +263,30 @@ impl From for String { #[cfg(feature = "kv-echodb")] impl From for Error { fn from(e: echodb::err::Error) -> Error { - Error::Tx(e.to_string()) + match e { + echodb::err::Error::KeyAlreadyExists => Error::TxKeyAlreadyExists, + _ => Error::Tx(e.to_string()), + } } } #[cfg(feature = "kv-indxdb")] impl From for Error { fn from(e: indxdb::err::Error) -> Error { - Error::Tx(e.to_string()) + match e { + indxdb::err::Error::KeyAlreadyExists => Error::TxKeyAlreadyExists, + _ => Error::Tx(e.to_string()), + } } } #[cfg(feature = "kv-tikv")] impl From for Error { fn from(e: tikv::Error) -> Error { - Error::Tx(e.to_string()) + match e { + tikv::Error::DuplicateKeyInsertion => Error::TxKeyAlreadyExists, + _ => Error::Tx(e.to_string()), + } } } diff --git a/lib/src/kvs/ds.rs b/lib/src/kvs/ds.rs index 9cea3d62..e39db89c 100644 --- a/lib/src/kvs/ds.rs +++ b/lib/src/kvs/ds.rs @@ -159,6 +159,7 @@ impl Datastore { txt: &str, sess: &Session, vars: Variables, + strict: bool, ) -> Result, Error> { // Create a new query options let mut opt = Options::default(); @@ -179,6 +180,8 @@ impl Datastore { // Set current NS and DB opt.ns = sess.ns(); opt.db = sess.db(); + // Set strict config + opt.strict = strict; // Process all statements exe.execute(ctx, opt, ast).await } @@ -189,6 +192,7 @@ impl Datastore { ast: Query, sess: &Session, vars: Variables, + strict: bool, ) -> Result, Error> { // Create a new query options let mut opt = Options::default(); @@ -207,6 +211,8 @@ impl Datastore { // Set current NS and DB opt.ns = sess.ns(); opt.db = sess.db(); + // Set strict config + opt.strict = strict; // Process all statements exe.execute(ctx, opt, ast).await } @@ -217,6 +223,7 @@ impl Datastore { val: Value, sess: &Session, vars: Variables, + strict: bool, ) -> Result { // Start a new transaction let txn = self.transaction(val.writeable(), false).await?; @@ -235,6 +242,8 @@ impl Datastore { // Set current NS and DB opt.ns = sess.ns(); opt.db = sess.db(); + // Set strict config + opt.strict = strict; // Compute the value let res = val.compute(&ctx, &opt, &txn, None).await?; // Store any data diff --git a/lib/src/kvs/tx.rs b/lib/src/kvs/tx.rs index 6c89cddc..590513ae 100644 --- a/lib/src/kvs/tx.rs +++ b/lib/src/kvs/tx.rs @@ -714,39 +714,76 @@ impl Transaction { let val = self.get(key).await?.ok_or(Error::TbNotFound)?; Ok(val.into()) } - /// Add a namespace with a default configuration. - pub async fn add_ns(&mut self, ns: &str) -> Result { - let key = crate::key::ns::new(ns); - let val = DefineNamespaceStatement { - name: ns.to_owned().into(), - }; - let _ = self.put(key, &val).await; - Ok(val) + /// Add a namespace with a default configuration, only if we are in dynamic mode. + pub async fn add_ns( + &mut self, + ns: &str, + strict: bool, + ) -> Result { + match self.get_ns(ns).await { + Err(Error::NsNotFound) => match strict { + false => { + let key = crate::key::ns::new(ns); + let val = DefineNamespaceStatement { + name: ns.to_owned().into(), + }; + self.put(key, &val).await?; + Ok(val) + } + true => Err(Error::NsNotFound), + }, + Err(e) => Err(e), + Ok(v) => Ok(v), + } } - /// Add a database with a default configuration. - pub async fn add_db(&mut self, ns: &str, db: &str) -> Result { - let key = crate::key::db::new(ns, db); - let val = DefineDatabaseStatement { - name: db.to_owned().into(), - }; - let _ = self.put(key, &val).await; - Ok(val) + /// Add a database with a default configuration, only if we are in dynamic mode. + pub async fn add_db( + &mut self, + ns: &str, + db: &str, + strict: bool, + ) -> Result { + match self.get_db(ns, db).await { + Err(Error::DbNotFound) => match strict { + false => { + let key = crate::key::db::new(ns, db); + let val = DefineDatabaseStatement { + name: db.to_owned().into(), + }; + self.put(key, &val).await?; + Ok(val) + } + true => Err(Error::DbNotFound), + }, + Err(e) => Err(e), + Ok(v) => Ok(v), + } } - /// Add a table with a default configuration. + /// Add a table with a default configuration, only if we are in dynamic mode. pub async fn add_tb( &mut self, ns: &str, db: &str, tb: &str, + strict: bool, ) -> Result { - let key = crate::key::tb::new(ns, db, tb); - let val = DefineTableStatement { - name: tb.to_owned().into(), - permissions: Permissions::none(), - ..DefineTableStatement::default() - }; - let _ = self.put(key, &val).await; - Ok(val) + match self.get_tb(ns, db, tb).await { + Err(Error::TbNotFound) => match strict { + false => { + let key = crate::key::tb::new(ns, db, tb); + let val = DefineTableStatement { + name: tb.to_owned().into(), + permissions: Permissions::none(), + ..DefineTableStatement::default() + }; + self.put(key, &val).await?; + Ok(val) + } + true => Err(Error::TbNotFound), + }, + Err(e) => Err(e), + Ok(v) => Ok(v), + } } /// Writes the full database contents as binary SQL. pub async fn export(&mut self, ns: &str, db: &str, chn: Sender>) -> Result<(), Error> { diff --git a/lib/src/sql/statements/define.rs b/lib/src/sql/statements/define.rs index bf4a55a6..5ce27096 100644 --- a/lib/src/sql/statements/define.rs +++ b/lib/src/sql/statements/define.rs @@ -173,7 +173,7 @@ impl DefineDatabaseStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::db::new(opt.ns(), &self.name); - run.add_ns(opt.ns()).await?; + run.add_ns(opt.ns(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -232,7 +232,7 @@ impl DefineLoginStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::nl::new(opt.ns(), &self.name); - run.add_ns(opt.ns()).await?; + run.add_ns(opt.ns(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -248,8 +248,8 @@ impl DefineLoginStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::dl::new(opt.ns(), opt.db(), &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -361,7 +361,7 @@ impl DefineTokenStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::nt::new(opt.ns(), &self.name); - run.add_ns(opt.ns()).await?; + run.add_ns(opt.ns(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -377,8 +377,8 @@ impl DefineTokenStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::dt::new(opt.ns(), opt.db(), &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -461,8 +461,8 @@ impl DefineScopeStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::sc::new(opt.ns(), opt.db(), &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -583,8 +583,8 @@ impl DefineTableStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::tb::new(opt.ns(), opt.db(), &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.set(key, self).await?; // Check if table is a view if let Some(view) = &self.view { @@ -752,9 +752,9 @@ impl DefineEventStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::ev::new(opt.ns(), opt.db(), &self.what, &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; - run.add_tb(opt.ns(), opt.db(), &self.what).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -833,9 +833,9 @@ impl DefineFieldStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::fd::new(opt.ns(), opt.db(), &self.what, &self.name.to_string()); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; - run.add_tb(opt.ns(), opt.db(), &self.what).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; // Ok all good Ok(Value::None) @@ -973,9 +973,9 @@ impl DefineIndexStatement { let mut run = run.lock().await; // Process the statement let key = crate::key::ix::new(opt.ns(), opt.db(), &self.what, &self.name); - run.add_ns(opt.ns()).await?; - run.add_db(opt.ns(), opt.db()).await?; - run.add_tb(opt.ns(), opt.db(), &self.what).await?; + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; // Remove the index data let beg = crate::key::index::prefix(opt.ns(), opt.db(), &self.what, &self.name); diff --git a/src/cli/config.rs b/src/cli/config.rs index c25c8dab..dfb76cad 100644 --- a/src/cli/config.rs +++ b/src/cli/config.rs @@ -5,6 +5,7 @@ pub static CF: OnceCell = OnceCell::new(); #[derive(Clone, Debug)] pub struct Config { + pub strict: bool, pub bind: SocketAddr, pub path: String, pub user: String, @@ -29,8 +30,11 @@ pub fn init(matches: &clap::ArgMatches) { // Parse any TLS server security options let crt = matches.value_of("web-crt").map(|v| v.to_owned()); let key = matches.value_of("web-key").map(|v| v.to_owned()); + // Check if database strict mode is enabled + let strict = matches.is_present("strict"); // Store the new config object let _ = CF.set(Config { + strict, bind, path, user, diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 102b8265..8ce88b8a 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -219,6 +219,14 @@ pub fn init() { .forbid_empty_values(true) .help("Path to the private key file for encrypted client connections"), ) + .arg( + Arg::new("strict") + .short('s') + .long("strict") + .required(false) + .takes_value(false) + .help("Whether strict mode is enabled on this database instance"), + ) .arg( Arg::new("log") .short('l') diff --git a/src/dbs/mod.rs b/src/dbs/mod.rs index 68bee962..365a89dd 100644 --- a/src/dbs/mod.rs +++ b/src/dbs/mod.rs @@ -5,9 +5,16 @@ use surrealdb::Datastore; pub static DB: OnceCell = OnceCell::new(); +const LOG: &str = "surrealdb::dbs"; + pub async fn init() -> Result<(), Error> { // Get local copy of options let opt = CF.get().unwrap(); + // Log authentication options + match opt.strict { + true => info!(target: LOG, "Database strict mode is enabled"), + false => info!(target: LOG, "Database strict mode is disabled"), + }; // Parse and setup the desired kv datastore let dbs = Datastore::new(&opt.path).await?; // Store database instance diff --git a/src/iam/signin.rs b/src/iam/signin.rs index b161b662..745f7b4c 100644 --- a/src/iam/signin.rs +++ b/src/iam/signin.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::cnf::SERVER_NAME; use crate::dbs::DB; use crate::err::Error; @@ -76,6 +77,8 @@ pub async fn signin(vars: Object) -> Result { pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Create a new readonly transaction let mut tx = kvs.transaction(false, false).await?; // Check if the supplied NS Login exists @@ -89,7 +92,7 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result match val.rid() { // There is a record returned diff --git a/src/iam/signup.rs b/src/iam/signup.rs index 7f19d2e7..87a0bf66 100644 --- a/src/iam/signup.rs +++ b/src/iam/signup.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::cnf::SERVER_NAME; use crate::dbs::DB; use crate::err::Error; @@ -31,6 +32,8 @@ pub async fn signup(vars: Object) -> Result { pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Create a new readonly transaction let mut tx = kvs.transaction(false, false).await?; // Check if the supplied NS Login exists @@ -44,7 +47,7 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result match val.rid() { // There is a record returned diff --git a/src/net/import.rs b/src/net/import.rs index 6d5602de..40221323 100644 --- a/src/net/import.rs +++ b/src/net/import.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::dbs::DB; use crate::err::Error; use crate::net::output; @@ -30,10 +31,12 @@ async fn handler( true => { // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Convert the body to a byte slice let sql = std::str::from_utf8(&sql).unwrap(); // Execute the sql query in the database - match db.execute(sql, &session, None).await { + match db.execute(sql, &session, None, opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), diff --git a/src/net/key.rs b/src/net/key.rs index 207ab5fe..ce644336 100644 --- a/src/net/key.rs +++ b/src/net/key.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::dbs::DB; use crate::err::Error; use crate::net::output; @@ -124,16 +125,22 @@ async fn select_all( table: String, query: Query, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Specify the request statement let sql = format!( "SELECT * FROM type::table($table) LIMIT {l} START {s}", l = query.limit.unwrap_or_else(|| String::from("100")), s = query.start.unwrap_or_else(|| String::from("0")), ); + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), }; - match db.execute(sql.as_str(), &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql.as_str(), &session, Some(vars), opt.strict).await { Ok(ref res) => match output.as_ref() { "application/json" => Ok(output::json(res)), "application/cbor" => Ok(output::cbor(res)), @@ -150,16 +157,24 @@ async fn create_all( table: String, body: Bytes, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Convert the HTTP request body let data = str::from_utf8(&body).unwrap(); + // Parse the request body as JSON match surrealdb::sql::json(data) { Ok(data) => { + // Specify the request statement let sql = "CREATE type::table($table) CONTENT $data"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("data") => data, }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -178,12 +193,18 @@ async fn delete_all( output: String, table: String, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Specify the request statement let sql = "DELETE type::table($table)"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -204,13 +225,19 @@ async fn select_one( table: String, id: String, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Specify the request statement let sql = "SELECT * FROM type::thing($table, $id)"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("id") => Value::from(id), }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -228,17 +255,25 @@ async fn create_one( id: String, body: Bytes, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Convert the HTTP request body let data = str::from_utf8(&body).unwrap(); + // Parse the request body as JSON match surrealdb::sql::json(data) { Ok(data) => { + // Specify the request statement let sql = "CREATE type::thing($table, $id) CONTENT $data"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("id") => Value::from(id), String::from("data") => data, }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -259,17 +294,25 @@ async fn update_one( id: String, body: Bytes, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Convert the HTTP request body let data = str::from_utf8(&body).unwrap(); + // Parse the request body as JSON match surrealdb::sql::json(data) { Ok(data) => { + // Specify the request statement let sql = "UPDATE type::thing($table, $id) CONTENT $data"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("id") => Value::from(id), String::from("data") => data, }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -290,17 +333,25 @@ async fn modify_one( id: String, body: Bytes, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Convert the HTTP request body let data = str::from_utf8(&body).unwrap(); + // Parse the request body as JSON match surrealdb::sql::json(data) { Ok(data) => { + // Specify the request statement let sql = "UPDATE type::thing($table, $id) MERGE $data"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("id") => Value::from(id), String::from("data") => data, }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), @@ -320,13 +371,19 @@ async fn delete_one( table: String, id: String, ) -> Result { + // Get the datastore reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); + // Specify the request statement let sql = "DELETE type::thing($table, $id)"; + // Specify the request variables let vars = map! { String::from("table") => Value::from(table), String::from("id") => Value::from(id), }; - match db.execute(sql, &session, Some(vars)).await { + // Execute the query and return the result + match db.execute(sql, &session, Some(vars), opt.strict).await { Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), "application/cbor" => Ok(output::cbor(&res)), diff --git a/src/net/rpc.rs b/src/net/rpc.rs index 9d0ea263..d1c8e6ad 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::cnf::MAX_CONCURRENT_CALLS; use crate::dbs::DB; use crate::err::Error; @@ -232,10 +233,12 @@ impl Rpc { async fn info(&self) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "SELECT * FROM $auth"; // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, None).await?; + let mut res = kvs.execute(sql, &self.session, None, opt.strict).await?; // Extract the first value from the result let res = res.remove(0).result?.first(); // Return the result to the client @@ -268,6 +271,8 @@ impl Rpc { async fn kill(&self, id: Value) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "KILL $id"; // Specify the query paramaters @@ -276,7 +281,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -286,6 +291,8 @@ impl Rpc { async fn live(&self, tb: Value) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "LIVE SELECT * FROM $tb"; // Specify the query paramaters @@ -294,7 +301,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -308,10 +315,12 @@ impl Rpc { async fn query(&self, sql: Strand) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the query paramaters let var = Some(self.vars.clone()); // Execute the query on the database - let res = kvs.execute(&sql, &self.session, var).await?; + let res = kvs.execute(&sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.into_iter().collect::>().into(); // Return the result to the client @@ -321,10 +330,12 @@ impl Rpc { async fn query_with(&self, sql: Strand, mut vars: Object) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the query paramaters let var = Some(mrg! { vars.0, &self.vars }); // Execute the query on the database - let res = kvs.execute(&sql, &self.session, var).await?; + let res = kvs.execute(&sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.into_iter().collect::>().into(); // Return the result to the client @@ -338,6 +349,8 @@ impl Rpc { async fn select(&self, what: Value) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "SELECT * FROM $what"; // Specify the query paramaters @@ -346,7 +359,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -360,6 +373,8 @@ impl Rpc { async fn create(&self, what: Value, data: impl Into>) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "CREATE $what CONTENT $data RETURN AFTER"; // Specify the query paramaters @@ -369,7 +384,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -383,6 +398,8 @@ impl Rpc { async fn update(&self, what: Value, data: impl Into>) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "UPDATE $what CONTENT $data RETURN AFTER"; // Specify the query paramaters @@ -392,7 +409,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -406,6 +423,8 @@ impl Rpc { async fn change(&self, what: Value, data: impl Into>) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "UPDATE $what MERGE $data RETURN AFTER"; // Specify the query paramaters @@ -415,7 +434,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -429,6 +448,8 @@ impl Rpc { async fn modify(&self, what: Value, data: impl Into>) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "UPDATE $what PATCH $data RETURN DIFF"; // Specify the query paramaters @@ -438,7 +459,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client @@ -452,6 +473,8 @@ impl Rpc { async fn delete(&self, what: Value) -> Result { // Get a database reference let kvs = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Specify the SQL query string let sql = "DELETE $what"; // Specify the query paramaters @@ -460,7 +483,7 @@ impl Rpc { => &self.vars }); // Execute the query on the database - let mut res = kvs.execute(sql, &self.session, var).await?; + let mut res = kvs.execute(sql, &self.session, var, opt.strict).await?; // Extract the first query result let res = res.remove(0).result?; // Return the result to the client diff --git a/src/net/sql.rs b/src/net/sql.rs index 986f9999..c52737ca 100644 --- a/src/net/sql.rs +++ b/src/net/sql.rs @@ -1,3 +1,4 @@ +use crate::cli::CF; use crate::dbs::DB; use crate::err::Error; use crate::net::output; @@ -39,10 +40,12 @@ async fn handler( ) -> Result { // Get a database reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Convert the received sql query let sql = std::str::from_utf8(&sql).unwrap(); // Execute the received sql query - match db.execute(sql, &session, None).await { + match db.execute(sql, &session, None, opt.strict).await { // Convert the response to JSON Ok(res) => match output.as_ref() { "application/json" => Ok(output::json(&res)), @@ -65,8 +68,10 @@ async fn socket(ws: WebSocket, session: Session) { if let Ok(sql) = msg.to_str() { // Get a database reference let db = DB.get().unwrap(); + // Get local copy of options + let opt = CF.get().unwrap(); // Execute the received sql query - let _ = match db.execute(sql, &session, None).await { + let _ = match db.execute(sql, &session, None, opt.strict).await { // Convert the response to JSON Ok(v) => match serde_json::to_string(&v) { // Send the JSON response to the client