use std::future::Future;
use std::pin::Pin;
use pgwire::api::results::{Response, Tag};
use pgwire::error::{ErrorInfo, PgWireError, PgWireResult};
use crate::bridge::envelope::{ErrorCode, Response as DpResponse};
use crate::control::security::identity::AuthenticatedIdentity;
use crate::control::server::pgwire::types::error_to_sqlstate;
use crate::control::server::shared::ddl::sqlstate::error_code_to_sqlstate;
use crate::control::server::shared::session::{AbortReason, CommitOutcome, TxnDataPlane, commit};
use nodedb_physical::physical_task::PhysicalTask;
use super::super::core::NodeDbPgHandler;
use super::errors::calvin_cancelled_error;
pub(in crate::control::server::pgwire::handler) struct PgwireTxnDp<'a> {
pub(in crate::control::server::pgwire::handler) handler: &'a NodeDbPgHandler,
}
impl TxnDataPlane for PgwireTxnDp<'_> {
fn dispatch_no_wal<'a>(
&'a self,
task: PhysicalTask,
wal_lsn: Option<crate::types::Lsn>,
) -> Pin<Box<dyn Future<Output = crate::Result<DpResponse>> + Send + 'a>> {
Box::pin(self.handler.dispatch_task_no_wal(task, None, wal_lsn))
}
}
impl NodeDbPgHandler {
pub(in crate::control::server::pgwire::handler) async fn handle_commit(
&self,
identity: &AuthenticatedIdentity,
addr: &std::net::SocketAddr,
) -> PgWireResult<Vec<Response>> {
let dp = PgwireTxnDp { handler: self };
match commit::run_commit(&self.sessions, addr, identity, &self.state, &dp).await {
CommitOutcome::Committed => Ok(vec![Response::Execution(Tag::new("COMMIT"))]),
CommitOutcome::Aborted { reason } => Err(commit_abort_to_pgerror(&reason)),
}
}
}
fn commit_abort_to_pgerror(reason: &AbortReason) -> PgWireError {
let (severity, code, message): (&'static str, &'static str, String) = match reason {
AbortReason::Serialization => (
"ERROR",
"40001",
"could not serialize access due to concurrent update".to_owned(),
),
AbortReason::NoTransaction => (
"ERROR",
"25000",
"current transaction is aborted, commands ignored until end of transaction block"
.to_owned(),
),
AbortReason::BatchRejected { code } => {
let code = code.clone().unwrap_or(ErrorCode::RejectedPrevalidation {
reason: "transaction commit failed".to_owned(),
});
error_code_to_sqlstate(&code)
}
AbortReason::CalvinCancelled => return calvin_cancelled_error(),
AbortReason::CalvinTimeout => (
"ERROR",
"57014",
"timed out waiting for Calvin sequencer".to_owned(),
),
AbortReason::Dispatch(e) | AbortReason::DdlPropose(e) => error_to_sqlstate(e),
};
PgWireError::UserError(Box::new(ErrorInfo::new(
severity.to_owned(),
code.to_owned(),
message,
)))
}