Enable new database strict mode configuration

This commit is contained in:
Tobie Morgan Hitchcock 2022-07-27 14:05:28 +01:00
parent f633769b57
commit d619633340
15 changed files with 279 additions and 82 deletions

View file

@ -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);

View file

@ -50,21 +50,31 @@ impl<'a> Document<'a> {
opt: &Options,
txn: &Transaction,
) -> Result<DefineTableStatement, Error> {
// 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

View file

@ -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<Error> for String {
#[cfg(feature = "kv-echodb")]
impl From<echodb::err::Error> 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<indxdb::err::Error> 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<tikv::Error> 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()),
}
}
}

View file

@ -159,6 +159,7 @@ impl Datastore {
txt: &str,
sess: &Session,
vars: Variables,
strict: bool,
) -> Result<Vec<Response>, 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<Vec<Response>, 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<Value, Error> {
// 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

View file

@ -714,40 +714,77 @@ 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<DefineNamespaceStatement, Error> {
/// 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<DefineNamespaceStatement, Error> {
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(),
};
let _ = self.put(key, &val).await;
self.put(key, &val).await?;
Ok(val)
}
/// Add a database with a default configuration.
pub async fn add_db(&mut self, ns: &str, db: &str) -> Result<DefineDatabaseStatement, Error> {
true => Err(Error::NsNotFound),
},
Err(e) => Err(e),
Ok(v) => Ok(v),
}
}
/// 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<DefineDatabaseStatement, Error> {
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(),
};
let _ = self.put(key, &val).await;
self.put(key, &val).await?;
Ok(val)
}
/// Add a table with a default configuration.
true => Err(Error::DbNotFound),
},
Err(e) => Err(e),
Ok(v) => Ok(v),
}
}
/// 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<DefineTableStatement, Error> {
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()
};
let _ = self.put(key, &val).await;
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<Vec<u8>>) -> Result<(), Error> {
// Output OPTIONS

View file

@ -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);

View file

@ -5,6 +5,7 @@ pub static CF: OnceCell<Config> = 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,

View file

@ -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')

View file

@ -5,9 +5,16 @@ use surrealdb::Datastore;
pub static DB: OnceCell<Datastore> = 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

View file

@ -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<String, Error> {
pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<String, Error> {
// 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<Stri
// Setup the query session
let sess = Session::for_db(&ns, &db);
// Compute the value with the params
match kvs.compute(val, &sess, vars).await {
match kvs.compute(val, &sess, vars, opt.strict).await {
// The signin value succeeded
Ok(val) => match val.rid() {
// There is a record returned

View file

@ -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<String, Error> {
pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<String, Error> {
// 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<Stri
// Setup the query session
let sess = Session::for_db(&ns, &db);
// Compute the value with the params
match kvs.compute(val, &sess, vars).await {
match kvs.compute(val, &sess, vars, opt.strict).await {
// The signin value succeeded
Ok(val) => match val.rid() {
// There is a record returned

View file

@ -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)),

View file

@ -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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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<impl warp::Reply, warp::Rejection> {
// 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)),

View file

@ -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<Value, Error> {
// 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<Value, Error> {
// 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<Value, Error> {
// 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<Value, Error> {
// 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::<Vec<Value>>().into();
// Return the result to the client
@ -321,10 +330,12 @@ impl Rpc {
async fn query_with(&self, sql: Strand, mut vars: Object) -> Result<Value, Error> {
// 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::<Vec<Value>>().into();
// Return the result to the client
@ -338,6 +349,8 @@ impl Rpc {
async fn select(&self, what: Value) -> Result<Value, Error> {
// 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<Option<Value>>) -> Result<Value, Error> {
// 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<Option<Value>>) -> Result<Value, Error> {
// 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<Option<Value>>) -> Result<Value, Error> {
// 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<Option<Value>>) -> Result<Value, Error> {
// 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<Value, Error> {
// 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

View file

@ -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<impl warp::Reply, warp::Rejection> {
// 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