use crate::error::EFResult;
use async_trait::async_trait;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum DbValue {
Null,
Bool(bool),
I16(i16),
I32(i32),
I64(i64),
F32(f32),
F64(f64),
String(String),
Bytes(Vec<u8>),
#[cfg(feature = "chrono")]
DateTime(chrono::DateTime<chrono::Utc>),
#[cfg(feature = "chrono")]
NaiveDateTime(chrono::NaiveDateTime),
#[cfg(feature = "chrono")]
NaiveDate(chrono::NaiveDate),
#[cfg(feature = "uuid")]
Uuid(uuid::Uuid),
#[cfg(feature = "decimal")]
Decimal(rust_decimal::Decimal),
}
impl fmt::Display for DbValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DbValue::Null => write!(f, "NULL"),
DbValue::Bool(v) => write!(f, "{}", if *v { "TRUE" } else { "FALSE" }),
DbValue::I16(v) => write!(f, "{}", v),
DbValue::I32(v) => write!(f, "{}", v),
DbValue::I64(v) => write!(f, "{}", v),
DbValue::F32(v) => write!(f, "{}", v),
DbValue::F64(v) => write!(f, "{}", v),
DbValue::String(v) => write!(f, "'{}'", v.replace('\'', "''")),
DbValue::Bytes(v) => write!(f, "{}", hex::encode(v)),
#[cfg(feature = "chrono")]
DbValue::DateTime(v) => write!(f, "'{}'", v.to_rfc3339()),
#[cfg(feature = "chrono")]
DbValue::NaiveDateTime(v) => write!(f, "'{}'", v),
#[cfg(feature = "chrono")]
DbValue::NaiveDate(v) => write!(f, "'{}'", v),
#[cfg(feature = "uuid")]
DbValue::Uuid(v) => write!(f, "'{}'", v),
#[cfg(feature = "decimal")]
DbValue::Decimal(v) => write!(f, "'{}'", v),
}
}
}
mod hex {
pub fn encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
}
impl From<i32> for DbValue {
fn from(v: i32) -> Self {
DbValue::I32(v)
}
}
impl From<&i32> for DbValue {
fn from(v: &i32) -> Self {
DbValue::I32(*v)
}
}
impl From<i64> for DbValue {
fn from(v: i64) -> Self {
DbValue::I64(v)
}
}
impl From<&i64> for DbValue {
fn from(v: &i64) -> Self {
DbValue::I64(*v)
}
}
impl From<String> for DbValue {
fn from(v: String) -> Self {
DbValue::String(v)
}
}
impl From<&str> for DbValue {
fn from(v: &str) -> Self {
DbValue::String(v.to_string())
}
}
impl From<bool> for DbValue {
fn from(v: bool) -> Self {
DbValue::Bool(v)
}
}
impl From<f64> for DbValue {
fn from(v: f64) -> Self {
DbValue::F64(v)
}
}
impl From<f32> for DbValue {
fn from(v: f32) -> Self {
DbValue::F32(v)
}
}
impl From<i16> for DbValue {
fn from(v: i16) -> Self {
DbValue::I16(v)
}
}
impl From<Vec<u8>> for DbValue {
fn from(v: Vec<u8>) -> Self {
DbValue::Bytes(v)
}
}
#[cfg(feature = "chrono")]
impl From<chrono::DateTime<chrono::Utc>> for DbValue {
fn from(dt: chrono::DateTime<chrono::Utc>) -> Self {
DbValue::DateTime(dt)
}
}
#[cfg(feature = "chrono")]
impl From<chrono::NaiveDateTime> for DbValue {
fn from(ndt: chrono::NaiveDateTime) -> Self {
DbValue::NaiveDateTime(ndt)
}
}
#[cfg(feature = "chrono")]
impl From<chrono::NaiveDate> for DbValue {
fn from(nd: chrono::NaiveDate) -> Self {
DbValue::NaiveDate(nd)
}
}
#[cfg(feature = "uuid")]
impl From<uuid::Uuid> for DbValue {
fn from(u: uuid::Uuid) -> Self {
DbValue::Uuid(u)
}
}
#[cfg(feature = "decimal")]
impl From<rust_decimal::Decimal> for DbValue {
fn from(d: rust_decimal::Decimal) -> Self {
DbValue::Decimal(d)
}
}
impl<T> From<Option<T>> for DbValue
where
T: Into<DbValue>,
{
fn from(v: Option<T>) -> Self {
match v {
Some(val) => val.into(),
None => DbValue::Null,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DbValueConvertError {
pub source: DbValue,
pub target_type: &'static str,
}
impl fmt::Display for DbValueConvertError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"cannot convert {:?} to {}",
self.source, self.target_type
)
}
}
impl std::error::Error for DbValueConvertError {}
impl From<DbValueConvertError> for crate::error::EFError {
fn from(e: DbValueConvertError) -> Self {
crate::error::EFError::type_conversion(e.to_string())
}
}
impl TryFrom<DbValue> for i32 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I32(n) => Ok(n),
DbValue::I16(n) => Ok(n as i32),
DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I64(n),
target_type: "i32",
}),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "i32",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "i32",
}),
}
}
}
impl TryFrom<DbValue> for i64 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I64(n) => Ok(n),
DbValue::I32(n) => Ok(n as i64),
DbValue::I16(n) => Ok(n as i64),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "i64",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "i64",
}),
}
}
}
impl TryFrom<DbValue> for f64 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::F64(x) => Ok(x),
DbValue::F32(x) => Ok(x as f64),
DbValue::I32(n) => Ok(n as f64),
DbValue::I64(n) => Ok(n as f64),
DbValue::I16(n) => Ok(n as f64),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "f64",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "f64",
}),
}
}
}
impl TryFrom<DbValue> for f32 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::F32(x) => Ok(x),
DbValue::F64(x) => Ok(x as f32),
DbValue::I32(n) => Ok(n as f32),
DbValue::I64(n) => Ok(n as f32),
DbValue::I16(n) => Ok(n as f32),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "f32",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "f32",
}),
}
}
}
impl TryFrom<DbValue> for String {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::String(s) => Ok(s),
DbValue::Bool(b) => Ok(b.to_string()),
DbValue::I16(n) => Ok(n.to_string()),
DbValue::I32(n) => Ok(n.to_string()),
DbValue::I64(n) => Ok(n.to_string()),
DbValue::F32(x) => Ok(x.to_string()),
DbValue::F64(x) => Ok(x.to_string()),
#[cfg(feature = "chrono")]
DbValue::DateTime(dt) => Ok(dt.to_rfc3339()),
#[cfg(feature = "chrono")]
DbValue::NaiveDateTime(ndt) => Ok(ndt.to_string()),
#[cfg(feature = "chrono")]
DbValue::NaiveDate(nd) => Ok(nd.to_string()),
#[cfg(feature = "uuid")]
DbValue::Uuid(u) => Ok(u.to_string()),
#[cfg(feature = "decimal")]
DbValue::Decimal(d) => Ok(d.to_string()),
other => Err(DbValueConvertError {
source: other,
target_type: "String",
}),
}
}
}
impl TryFrom<DbValue> for bool {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::Bool(b) => Ok(b),
DbValue::I64(n) => Ok(n != 0),
DbValue::I32(n) => Ok(n != 0),
DbValue::I16(n) => Ok(n != 0),
DbValue::String(s) => {
let lower = s.to_ascii_lowercase();
match lower.as_str() {
"true" | "t" | "1" => Ok(true),
"false" | "f" | "0" => Ok(false),
_ => Err(DbValueConvertError {
source: DbValue::String(s),
target_type: "bool",
}),
}
}
other => Err(DbValueConvertError {
source: other,
target_type: "bool",
}),
}
}
}
impl TryFrom<DbValue> for Vec<u8> {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::Bytes(b) => Ok(b),
other => Err(DbValueConvertError {
source: other,
target_type: "Vec<u8>",
}),
}
}
}
impl TryFrom<DbValue> for i16 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I16(n) => Ok(n),
DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I32(n),
target_type: "i16",
}),
DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I64(n),
target_type: "i16",
}),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "i16",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "i16",
}),
}
}
}
impl TryFrom<DbValue> for i8 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I16(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I16(n),
target_type: "i8",
}),
DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I32(n),
target_type: "i8",
}),
DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I64(n),
target_type: "i8",
}),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "i8",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "i8",
}),
}
}
}
impl TryFrom<DbValue> for u32 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I16(n) => (n as i32).try_into().map_err(|_| DbValueConvertError {
source: DbValue::I16(n),
target_type: "u32",
}),
DbValue::I32(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I32(n),
target_type: "u32",
}),
DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I64(n),
target_type: "u32",
}),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "u32",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "u32",
}),
}
}
}
impl TryFrom<DbValue> for u64 {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::I16(n) => (n as i64).try_into().map_err(|_| DbValueConvertError {
source: DbValue::I16(n),
target_type: "u64",
}),
DbValue::I32(n) => (n as i64).try_into().map_err(|_| DbValueConvertError {
source: DbValue::I32(n),
target_type: "u64",
}),
DbValue::I64(n) => n.try_into().map_err(|_| DbValueConvertError {
source: DbValue::I64(n),
target_type: "u64",
}),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "u64",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "u64",
}),
}
}
}
#[cfg(feature = "chrono")]
impl TryFrom<DbValue> for chrono::DateTime<chrono::Utc> {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::DateTime(dt) => Ok(dt),
DbValue::String(s) => chrono::DateTime::parse_from_rfc3339(&s)
.map(|dt| dt.with_timezone(&chrono::Utc))
.map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "DateTime<Utc>",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "DateTime<Utc>",
}),
}
}
}
#[cfg(feature = "chrono")]
impl TryFrom<DbValue> for chrono::NaiveDateTime {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::NaiveDateTime(ndt) => Ok(ndt),
DbValue::String(s) => {
chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%d %H:%M:%S")
.or_else(|_| {
chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S")
})
.map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "NaiveDateTime",
})
}
other => Err(DbValueConvertError {
source: other,
target_type: "NaiveDateTime",
}),
}
}
}
#[cfg(feature = "chrono")]
impl TryFrom<DbValue> for chrono::NaiveDate {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::NaiveDate(nd) => Ok(nd),
DbValue::String(s) => {
chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "NaiveDate",
})
}
other => Err(DbValueConvertError {
source: other,
target_type: "NaiveDate",
}),
}
}
}
#[cfg(feature = "uuid")]
impl TryFrom<DbValue> for uuid::Uuid {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::Uuid(u) => Ok(u),
DbValue::String(s) => uuid::Uuid::parse_str(&s).map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "Uuid",
}),
other => Err(DbValueConvertError {
source: other,
target_type: "Uuid",
}),
}
}
}
#[cfg(feature = "decimal")]
impl TryFrom<DbValue> for rust_decimal::Decimal {
type Error = DbValueConvertError;
fn try_from(v: DbValue) -> Result<Self, Self::Error> {
match v {
DbValue::Decimal(d) => Ok(d),
DbValue::String(s) => s.parse().map_err(|_| DbValueConvertError {
source: DbValue::String(s),
target_type: "Decimal",
}),
DbValue::I32(n) => Ok(rust_decimal::Decimal::from(n)),
DbValue::I64(n) => Ok(rust_decimal::Decimal::from(n)),
other => Err(DbValueConvertError {
source: other,
target_type: "Decimal",
}),
}
}
}
pub trait ISqlGenerator: Send + Sync {
fn select(&self, table: &str, columns: &[&str]) -> String;
fn insert(&self, table: &str, columns: &[&str], returning: bool) -> String;
fn insert_batch(&self, table: &str, columns: &[&str], row_count: usize) -> String {
let _ = (table, columns, row_count);
String::new()
}
fn update(&self, table: &str, set_columns: &[&str], where_clause: &str) -> String;
fn update_batch(
&self,
table: &str,
set_columns: &[&str],
pk_col: &str,
row_count: usize,
where_clause: &str,
) -> String {
let mut idx = 1usize;
let sets: Vec<String> = set_columns
.iter()
.map(|col| {
let whens: Vec<String> = (0..row_count)
.map(|_| {
let pk_ph = self.parameter_placeholder(idx);
idx += 1;
let val_ph = self.parameter_placeholder(idx);
idx += 1;
format!("WHEN {} THEN {}", pk_ph, val_ph)
})
.collect();
format!(
"{} = CASE {} {} END",
self.quote_identifier(col),
self.quote_identifier(pk_col),
whens.join(" ")
)
})
.collect();
format!(
"UPDATE {} SET {} WHERE {}",
self.quote_identifier(table),
sets.join(", "),
where_clause
)
}
fn delete(&self, table: &str, where_clause: &str) -> String;
fn create_table(&self, table: &str, columns: &[(String, String)]) -> String;
fn drop_table(&self, table: &str) -> String;
fn pagination(&self, skip: Option<usize>, take: Option<usize>) -> String;
fn parameter_placeholder(&self, index: usize) -> String;
fn quote_identifier(&self, identifier: &str) -> String;
fn auto_increment_syntax(&self) -> &'static str;
fn supports_returning(&self) -> bool {
false
}
fn last_insert_id_sql(&self) -> Option<&'static str> {
None
}
fn last_insert_id_returns_first(&self) -> bool {
true
}
fn upsert_batch(
&self,
table: &str,
columns: &[&str],
conflict_cols: &[&str],
row_count: usize,
) -> String {
let _ = (table, columns, conflict_cols, row_count);
String::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
#[async_trait]
pub trait IAsyncConnection: Send + Sync {
async fn execute(&mut self, sql: &str, params: &[DbValue]) -> EFResult<u64>;
async fn query(&mut self, sql: &str, params: &[DbValue]) -> EFResult<Vec<Vec<DbValue>>>;
async fn begin_transaction(&mut self) -> EFResult<()>;
async fn commit_transaction(&mut self) -> EFResult<()>;
async fn rollback_transaction(&mut self) -> EFResult<()>;
async fn create_savepoint(&mut self, name: &str) -> EFResult<()>;
async fn release_savepoint(&mut self, name: &str) -> EFResult<()>;
async fn rollback_to_savepoint(&mut self, name: &str) -> EFResult<()>;
async fn set_transaction_isolation(&mut self, level: IsolationLevel) -> EFResult<()>;
#[cfg(feature = "tracing")]
fn set_slow_query_threshold(&mut self, _threshold: std::time::Duration) {}
}
#[async_trait]
pub trait IDatabaseProvider: Send + Sync {
fn sql_generator(&self) -> &'static dyn ISqlGenerator;
async fn get_connection(&self) -> EFResult<Box<dyn IAsyncConnection>>;
async fn execute_migration_command(&self, sql: &str) -> EFResult<()>;
fn name(&self) -> &str;
fn migration_dialect(&self) -> crate::migration::MigrationDialect;
#[cfg(feature = "tracing")]
fn set_slow_query_threshold(&self, _threshold: std::time::Duration) {}
}