use crate::type_::{BaseType, Type};
use alloc::sync::Arc;
use qusql_parse::SQLDialect;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Coercion<'a> {
Exact(Type<'a>),
Implicit(Type<'a>),
Incompatible,
}
impl<'a> Coercion<'a> {
pub fn ok(self) -> Option<Type<'a>> {
match self {
Coercion::Exact(t) | Coercion::Implicit(t) => Some(t),
Coercion::Incompatible => None,
}
}
pub fn is_compatible(&self) -> bool {
!matches!(self, Coercion::Incompatible)
}
}
pub(crate) fn implicit_coerce<'a>(
dialect: SQLDialect,
strict: bool,
from: &Type<'a>,
to: &Type<'a>,
) -> Coercion<'a> {
let from_base = from.base();
let to_base = to.base();
if from_base == BaseType::Any {
return Coercion::Exact(to.clone());
}
if to_base == BaseType::Any {
return Coercion::Exact(from.clone());
}
if from_base == to_base {
return Coercion::Exact(to.clone());
}
if strict {
return Coercion::Incompatible;
}
match dialect {
SQLDialect::MariaDB => mysql_coerce(from_base, to_base, to),
SQLDialect::PostgreSQL => postgres_coerce(from_base, to_base, to),
SQLDialect::Sqlite => Coercion::Implicit(to.clone()),
}
}
pub(crate) fn binary_coerce<'a>(
dialect: SQLDialect,
strict: bool,
lhs: &Type<'a>,
rhs: &Type<'a>,
) -> Coercion<'a> {
match implicit_coerce(dialect.clone(), strict, lhs, rhs) {
Coercion::Incompatible => implicit_coerce(dialect, strict, rhs, lhs),
ok => ok,
}
}
pub(crate) fn resolve_now_return(context: BaseType) -> BaseType {
match context {
BaseType::TimeStamp => BaseType::TimeStamp,
_ => BaseType::DateTime,
}
}
fn mysql_coerce<'a>(from: BaseType, to: BaseType, to_type: &Type<'a>) -> Coercion<'a> {
use BaseType::*;
match (from, to) {
(String, Integer | Float | Decimal) | (Integer | Float | Decimal, String) => {
Coercion::Implicit(to_type.clone())
}
(Integer, Float | Decimal)
| (Float | Decimal, Integer)
| (Float, Decimal)
| (Decimal, Float) => Coercion::Implicit(to_type.clone()),
(Integer | Float | Decimal | String, Bool) | (Bool, Integer | Float | Decimal | String) => {
Coercion::Implicit(to_type.clone())
}
(String, Date | DateTime | Time | TimeStamp)
| (Date | DateTime | Time | TimeStamp, String) => Coercion::Implicit(to_type.clone()),
(Integer, Date | DateTime | TimeStamp) | (Date | DateTime | TimeStamp, Integer) => {
Coercion::Implicit(to_type.clone())
}
_ => Coercion::Incompatible,
}
}
fn postgres_coerce<'a>(from: BaseType, to: BaseType, to_type: &Type<'a>) -> Coercion<'a> {
use BaseType::*;
match (from, to) {
(Integer, Integer) => Coercion::Exact(to_type.clone()),
(Float, Float) | (Integer, Float) => Coercion::Implicit(to_type.clone()),
(String, Date | DateTime | Time | TimeStamp | Uuid | Network) => {
Coercion::Implicit(to_type.clone())
}
_ => Coercion::Incompatible,
}
}
pub(crate) fn arc_type<'a>(t: Type<'a>) -> Arc<Type<'a>> {
Arc::new(t)
}