Finish implementation of executor transaction logic

This commit is contained in:
Tobie Morgan Hitchcock 2022-01-26 13:57:17 +00:00
parent ca57df132e
commit 835018d5f4
4 changed files with 125 additions and 53 deletions

View file

@ -5,6 +5,7 @@ use crate::dbs::Level;
use crate::dbs::Options; use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::err::Error; use crate::err::Error;
use crate::kvs::transaction;
use crate::kvs::Transaction; use crate::kvs::Transaction;
use crate::sql::query::Query; use crate::sql::query::Query;
use crate::sql::statement::Statement; use crate::sql::statement::Statement;
@ -51,6 +52,61 @@ impl<'a> Executor<'a> {
todo!() todo!()
} }
async fn begin(&mut self) -> bool {
match self.txn {
Some(_) => false,
None => match transaction(true, false).await {
Ok(v) => {
self.txn = Some(Arc::new(Mutex::new(v)));
true
}
Err(e) => {
self.err = Some(e);
false
}
},
}
}
async fn commit(&mut self, local: bool) {
if local {
match &self.txn {
Some(txn) => {
let txn = txn.clone();
let mut txn = txn.lock().await;
if let Err(e) = txn.commit().await {
self.err = Some(e);
}
self.txn = None;
}
None => unreachable!(),
}
}
}
async fn cancel(&mut self, local: bool) {
if local {
match &self.txn {
Some(txn) => {
let txn = txn.clone();
let mut txn = txn.lock().await;
if let Err(e) = txn.cancel().await {
self.err = Some(e);
}
self.txn = None;
}
None => unreachable!(),
}
}
}
async fn finish(&mut self, res: &Result<Value, Error>, local: bool) {
match res {
Ok(_) => self.commit(local).await,
Err(_) => self.cancel(local).await,
}
}
pub async fn execute(&mut self, mut ctx: Runtime, qry: Query) -> Result<Responses, Error> { pub async fn execute(&mut self, mut ctx: Runtime, qry: Query) -> Result<Responses, Error> {
// Initialise array of responses // Initialise array of responses
let mut out: Vec<Response> = vec![]; let mut out: Vec<Response> = vec![];
@ -59,7 +115,7 @@ impl<'a> Executor<'a> {
// Process all statements in query // Process all statements in query
for stm in qry.statements().iter() { for stm in qry.statements().iter() {
// Log the statement // Log the statement
debug!(target: NAME, "{}", stm); debug!("{}", stm);
// Reset errors // Reset errors
if self.txn.is_none() { if self.txn.is_none() {
self.err = None; self.err = None;
@ -102,8 +158,7 @@ impl<'a> Executor<'a> {
// Commit a running transaction // Commit a running transaction
Statement::Use(stm) => { Statement::Use(stm) => {
let res = stm.compute(&ctx, &opt, self, None).await; let res = stm.compute(&ctx, &opt, self, None).await;
self.err = res.err(); res
continue;
} }
// Process param definition statements // Process param definition statements
Statement::Set(stm) => { Statement::Set(stm) => {
@ -119,7 +174,13 @@ impl<'a> Executor<'a> {
Ok(Value::None) Ok(Value::None)
} }
// Process all other normal statements // Process all other normal statements
_ => { _ => match self.err {
// This transaction has failed
Some(_) => Err(Error::QueryExecutionError),
// Compute the statement normally
None => {
// Create a transaction
let loc = self.begin().await;
// Enable context override // Enable context override
let mut ctx = Context::new(&ctx).freeze(); let mut ctx = Context::new(&ctx).freeze();
// Specify statement timeout // Specify statement timeout
@ -128,32 +189,28 @@ impl<'a> Executor<'a> {
new.add_timeout(timeout); new.add_timeout(timeout);
ctx = new.freeze(); ctx = new.freeze();
} }
// Process statement // Process the statement
let res = stm.compute(&ctx, &opt, self, None).await; let res = stm.compute(&ctx, &opt, self, None).await;
// Catch statement timeout // Catch statement timeout
if let Some(timeout) = stm.timeout() { let res = match stm.timeout() {
if ctx.is_timedout() { Some(timeout) => match ctx.is_timedout() {
self.err = Some(Error::QueryTimeoutError { true => Err(Error::QueryTimeoutError {
timer: timeout, timer: timeout,
}); }),
} false => res,
} },
// Continue with result None => res,
};
// Finalise transaction
self.finish(&res, loc).await;
// Return the result
res res
} }
},
}; };
// Get the statement end time // Get the statement end time
let dur = now.elapsed(); let dur = now.elapsed();
// Check transaction errors // Buffer the returned result
match &self.err {
Some(e) => out.push(Response {
time: format!("{:?}", dur),
status: String::from("ERR"),
detail: Some(format!("{}", e)),
result: None,
}),
None => {
// Format responses
match res { match res {
Ok(v) => out.push(Response { Ok(v) => out.push(Response {
time: format!("{:?}", dur), time: format!("{:?}", dur),
@ -161,13 +218,16 @@ impl<'a> Executor<'a> {
detail: None, detail: None,
result: v.output(), result: v.output(),
}), }),
Err(e) => out.push(Response { Err(e) => {
// Output the error
out.push(Response {
time: format!("{:?}", dur), time: format!("{:?}", dur),
status: String::from("ERR"), status: String::from("ERR"),
detail: Some(format!("{}", e)), detail: Some(format!("{}", e)),
result: None, result: None,
}), });
} // Keep the error
self.err = Some(e);
} }
} }
} }

View file

@ -70,6 +70,9 @@ pub enum Error {
timer: Duration, timer: Duration,
}, },
#[error("Query not executed due to failed transaction")]
QueryExecutionError,
#[error("You don't have permission to perform this query type")] #[error("You don't have permission to perform this query type")]
QueryPermissionsError, QueryPermissionsError,

View file

@ -24,7 +24,16 @@ impl CommitStatement {
_doc: Option<&Value>, _doc: Option<&Value>,
) -> Result<Value, Error> { ) -> Result<Value, Error> {
match &exe.txn { match &exe.txn {
Some(txn) => { Some(txn) => match &exe.err {
Some(_) => {
let txn = txn.clone();
let mut txn = txn.lock().await;
match txn.cancel().await {
Ok(_) => Ok(Value::None),
Err(e) => Err(e),
}
}
None => {
let txn = txn.clone(); let txn = txn.clone();
let mut txn = txn.lock().await; let mut txn = txn.lock().await;
match txn.commit().await { match txn.commit().await {
@ -32,6 +41,7 @@ impl CommitStatement {
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
},
None => Ok(Value::None), None => Ok(Value::None),
} }
} }

View file

@ -399,7 +399,6 @@ impl Value {
match self { match self {
Value::None => None, Value::None => None,
Value::Void => None, Value::Void => None,
Value::Null => None,
_ => Some(self), _ => Some(self),
} }
} }