use crate::dao::eo::{ForeignKey, UniqueKey};
use crate::dao::{calc_key_of_foreign_key, get_from_foreign_keys, get_from_unique_keys};
use anyhow::anyhow;
use idworker::IdWorkerError;
use regex::{Captures, Regex};
use robotech_macros::log_call;
use sea_orm::DbErr;
use std::sync::LazyLock;
use std::time::SystemTimeError;
static REGEX_DUPLICATE_KEY_POSTGRES: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"duplicate key value violates unique constraint \\"(?P<ak_name>[^']*)\\"", detail: Some\("Key \(([^)]+)\)=\((?P<value>[^)]*)\) already exists\."#)
.expect("正则表达式错误")
});
static REGEX_DUPLICATE_KEY_MYSQL: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"Duplicate entry '(?P<value>[^']+)' for key '(?P<ak_name>[^']*)'"#)
.expect("正则表达式错误")
});
static REGEX_INSERT_VIOLATE_FK_POSTGRES: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"insert or update on table \\"(?P<fk_table>[A-Za-z_0-9]+)\\" violates foreign key constraint \\"fk_(?P<fk_column>[A-Za-z_0-9]+)__from__(?P<pk_table>[A-Za-z_0-9]+)"#).expect("正则表达式错误")
});
static REGEX_INSERT_VIOLATE_FK_MYSQL: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"Cannot add or update a child row: a foreign key constraint fails \(`[A-Za-z_0-9]+`\.`(?P<fk_table>[A-Za-z_0-9]+)`, CONSTRAINT `[A-Za-z_0-9]+` FOREIGN KEY \(`(?P<fk_column>[A-Za-z_0-9]+)`\) REFERENCES `(?P<pk_table>[A-Za-z_0-9]+)`"#).expect("正则表达式错误")
});
static REGEX_DELETE_VIOLATE_FK_POSTGRES: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"update or delete on table \\"(?P<pk_table>[A-Za-z_0-9]+)\\" violates foreign key constraint \\"fk_(?P<fk_column>[A-Za-z_0-9]+)__from__[A-Za-z_0-9]+\\" on table \\"(?P<fk_table>[A-Za-z_0-9]+)\\""#).expect("正则表达式错误")
});
static REGEX_DELETE_VIOLATE_FK_MYSQL: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"Cannot delete or update a parent row: a foreign key constraint fails \(`[A-Za-z_0-9]+`\.`(?P<fk_table>[A-Za-z_0-9]+)`, CONSTRAINT `[A-Za-z_0-9]+` FOREIGN KEY \(`(?P<fk_column>[A-Za-z_0-9]+)`\) REFERENCES `(?P<pk_table>[A-Za-z_0-9]+)`"#).expect("正则表达式错误")
});
#[derive(Debug, thiserror::Error)]
pub enum DaoError {
#[error("运行时错误: {0}")]
Runtime(#[from] anyhow::Error),
#[error("获取DB_CONN错误")]
GetDbConn(),
#[error("系统时钟错误: {0}")]
SystemTime(#[from] SystemTimeError),
#[error("ID工作者错误: {0}")]
IdWorker(#[from] IdWorkerError),
#[error("重复键错误: {0} -> {1}")]
DuplicateKey(UniqueKey, String),
#[error("插入(或更新)操作违反了数据库外键约束条件: {0}")]
InsertViolateFk(ForeignKey),
#[error("删除(或更新)操作违反了数据库外键约束条件: {0}")]
DeleteViolateFk(ForeignKey),
#[error("数据库错误: {0}")]
Db(#[from] DbErr),
#[error("未初始化错误: {0}")]
NotInitialized(String),
#[error("已经初始化错误: {0}")]
AlreadyInitialized(String),
}
impl DaoError {
#[log_call(level = warn, mode = enter)]
pub fn parse_db_err(db_err: DbErr) -> DaoError {
let db_err_string = format!("{:?}", db_err);
if let Some(caps) = REGEX_DUPLICATE_KEY_POSTGRES.captures(&db_err_string) {
return Self::parse_duplicate_key(caps);
} else if let Some(caps) = REGEX_DUPLICATE_KEY_MYSQL.captures(&db_err_string) {
return Self::parse_duplicate_key(caps);
} else if let Some(caps) = REGEX_INSERT_VIOLATE_FK_POSTGRES.captures(&db_err_string) {
return Self::parse_insert_violate_fk(caps);
} else if let Some(caps) = REGEX_INSERT_VIOLATE_FK_MYSQL.captures(&db_err_string) {
return Self::parse_insert_violate_fk(caps);
} else if let Some(caps) = REGEX_DELETE_VIOLATE_FK_POSTGRES.captures(&db_err_string) {
return Self::parse_delete_violate_fk(caps);
} else if let Some(caps) = REGEX_DELETE_VIOLATE_FK_MYSQL.captures(&db_err_string) {
return Self::parse_delete_violate_fk(caps);
}
DaoError::from(db_err)
}
fn parse_duplicate_key(caps: Captures) -> DaoError {
let ak_name = caps["ak_name"].to_lowercase().to_string();
let value = caps["value"].to_string();
let unique_filed = match get_from_unique_keys(&ak_name) {
Ok(Some(unique_filed)) => unique_filed,
Ok(None) => {
return DaoError::from(anyhow!(format!("获取unique字段列表错误: {ak_name}不存在")));
}
Err(e) => {
return DaoError::from(anyhow!(format!("获取unique字段列表错误: {e}")));
}
};
DaoError::DuplicateKey(unique_filed.clone(), value)
}
fn parse_violate_fk(caps: Captures) -> Result<ForeignKey, DaoError> {
let fk_table = caps["fk_table"].to_string();
let fk_column = caps["fk_column"].to_string();
let pk_table = caps["pk_table"].to_string();
let key = calc_key_of_foreign_key(&fk_table, &fk_column, &pk_table);
let foreign_key = match get_from_foreign_keys(&key) {
Ok(Some(foreign_key)) => foreign_key,
Ok(None) => {
return Err(DaoError::from(anyhow!(format!(
"获取foreign key列表错误: {key}不存在"
))))?;
}
Err(e) => {
return Err(DaoError::from(anyhow!(format!(
"获取foreign key列表错误: {e}"
))))?;
}
};
Ok(foreign_key.clone())
}
fn parse_insert_violate_fk(caps: Captures) -> DaoError {
match Self::parse_violate_fk(caps) {
Ok(foreign_key) => DaoError::InsertViolateFk(foreign_key),
Err(e) => e,
}
}
fn parse_delete_violate_fk(caps: Captures) -> DaoError {
match Self::parse_violate_fk(caps) {
Ok(foreign_key) => DaoError::DeleteViolateFk(foreign_key),
Err(e) => e,
}
}
}