use crate::comm::{ExecuteContext, ExecuteResult};
use crate::errors::{AkitaError, Result};
use crate::interceptor::non_blocking::AsyncAkitaInterceptor;
use crate::interceptor::*;
use async_trait::async_trait;
#[async_trait]
impl AsyncAkitaInterceptor for FieldFillInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
if !self.enabled {
return Ok(());
}
let operation = ctx.operation_type();
let fill_values = self.get_fill_values(&operation);
if fill_values.is_empty() {
return Ok(());
}
for (field, value) in fill_values {
ctx.set_metadata(format!("fill_{}", field), value.to_string());
}
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for SoftDeleteInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let table = ctx.table_info().name.clone();
if self.should_ignore_table(&table) {
return Ok(());
}
match ctx.operation_type() {
OperationType::Delete => {
ctx.set_metadata("soft_delete_rewrite".to_string(), "true".to_string());
ctx.set_metadata(
"soft_delete_column".to_string(),
self.config().column.clone(),
);
}
OperationType::Select => {
ctx.set_metadata("soft_delete_filter".to_string(), "true".to_string());
ctx.set_metadata(
"soft_delete_column".to_string(),
self.config().column.clone(),
);
}
_ => {}
}
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for PaginationInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let pagination_json = ctx.get_metadata("pagination");
if let Some(_pagination) = pagination_json {
ctx.set_metadata("pagination_active".to_string(), "true".to_string());
}
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for CursorPaginationInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let cursor_pagination_json = ctx.get_metadata("cursor_pagination");
if let Some(_cursor_pagination) = cursor_pagination_json {
ctx.set_metadata("cursor_pagination_active".to_string(), "true".to_string());
}
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for OptimisticLockerInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let table = ctx.table_info().name.clone();
if self.should_ignore_table(&table) {
return Ok(());
}
if !matches!(ctx.operation_type(), OperationType::Update) {
return Ok(());
}
ctx.set_metadata(
"optimistic_lock_column".to_string(),
self.config().column.clone(),
);
ctx.set_metadata("optimistic_lock_active".to_string(), "true".to_string());
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for TenantLineInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
let table = ctx.table_info().name.clone();
if self.should_ignore_table(&table) {
return Ok(());
}
ctx.set_metadata("tenant_column".to_string(), self.config().column.clone());
ctx.set_metadata("tenant_id".to_string(), self.config().tenant_id.to_string());
ctx.set_metadata("tenant_active".to_string(), "true".to_string());
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for CacheInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
if !self.enabled {
return Ok(());
}
let sql = ctx.final_sql();
let key = self.generate_cache_key(sql);
if matches!(ctx.operation_type(), OperationType::Select) {
if let Some(cached) = self.get_from_cache(&key) {
ctx.set_metadata("cache_hit".to_string(), "true".to_string());
ctx.set_metadata("cache_key".to_string(), key);
ctx.set_metadata(
"cached_data".to_string(),
String::from_utf8_lossy(&cached).to_string(),
);
} else {
ctx.set_metadata("cache_hit".to_string(), "false".to_string());
ctx.set_metadata("cache_key".to_string(), key);
}
} else {
ctx.set_metadata("cache_invalidate".to_string(), "true".to_string());
}
Ok(())
}
async fn after_execute(
&self,
ctx: &mut ExecuteContext,
_result: &mut std::result::Result<ExecuteResult, AkitaError>,
) -> Result<()> {
if !self.enabled {
return Ok(());
}
if matches!(ctx.operation_type(), OperationType::Select) {
let cache_hit = ctx
.get_metadata("cache_hit")
.map(|v| v.to_string())
.unwrap_or_default();
if cache_hit == "false" {
let key = ctx
.get_metadata("cache_key")
.map(|v| v.to_string())
.unwrap_or_default();
if !key.is_empty() {
self.set_in_cache(&key, b"cached".to_vec());
}
}
}
Ok(())
}
}
#[async_trait]
impl AsyncAkitaInterceptor for PerformanceInterceptor {
async fn before_execute(&self, ctx: &mut ExecuteContext) -> Result<()> {
ctx.set_metadata(
"performance_start_time".to_string(),
chrono::Utc::now().timestamp_millis().to_string(),
);
Ok(())
}
async fn after_execute(
&self,
ctx: &mut ExecuteContext,
_result: &mut std::result::Result<ExecuteResult, AkitaError>,
) -> Result<()> {
let start_time_str = ctx
.get_metadata("performance_start_time")
.map(|v| v.to_string())
.unwrap_or_default();
if let Ok(start_time) = start_time_str.parse::<i64>() {
let now = chrono::Utc::now().timestamp_millis();
let execution_time_ms = (now - start_time).max(0) as u64;
let is_slow = execution_time_ms > self.config().slow_query_threshold_ms;
if self.config().enable_metrics {
self.metrics_collector()
.record_query(execution_time_ms, is_slow);
}
ctx.set_metadata(
"execution_time_ms".to_string(),
execution_time_ms.to_string(),
);
ctx.set_metadata("is_slow_query".to_string(), is_slow.to_string());
if is_slow {
tracing::warn!(
"Slow query detected: {}ms (threshold: {}ms)\nSQL: {}",
execution_time_ms,
self.config().slow_query_threshold_ms,
ctx.final_sql()
);
}
}
Ok(())
}
}