//! Container for all SQL value types.

use std::borrow::Cow;

use crate::{ColumnType, CommonSqlQueryBuilder, QueryBuilder, RcOrArc, StringLen};

#[cfg(test)]
mod tests;

pub mod prelude;
#[allow(unused_imports)]
use prelude::*;

#[cfg(feature = "hashable-value")]
mod hashable_value;

mod value_class;
pub use value_class::*;

mod value_tuple;
pub use value_tuple::*;

#[cfg(feature = "with-json")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
mod with_json;

#[cfg(feature = "with-json")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
pub use with_json::sea_value_to_json_value;

#[cfg(feature = "with-chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
mod with_chrono;

#[cfg(feature = "with-time")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
pub mod time_format;

#[cfg(feature = "with-time")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
mod with_time;

#[cfg(feature = "with-jiff")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
pub mod with_jiff;

#[cfg(feature = "with-rust_decimal")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
mod with_rust_decimal;

#[cfg(feature = "with-bigdecimal")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
mod with_bigdecimal;

#[cfg(feature = "with-uuid")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
mod with_uuid;

#[cfg(feature = "with-ipnetwork")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
mod with_ipnetwork;

#[cfg(feature = "with-mac_address")]
#[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
mod with_mac_address;

#[cfg(feature = "postgres-array")]
#[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
pub mod postgres_array;

#[cfg(feature = "postgres-vector")]
#[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
mod postgres_vector;

#[cfg(feature = "postgres-range")]
#[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
mod postgres_range;

#[cfg(all(test, feature = "serde", feature = "with-json"))]
mod serde_tests;

/// [`Value`] types variant for Postgres array
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ArrayType {
    Bool,
    TinyInt,
    SmallInt,
    Int,
    BigInt,
    TinyUnsigned,
    SmallUnsigned,
    Unsigned,
    BigUnsigned,
    Float,
    Double,
    String,
    // box it because value size limit
    #[cfg(feature = "backend-postgres")]
    #[cfg_attr(docsrs, doc(cfg(feature = "backend-postgres")))]
    Enum(Box<EnumTypeName>),
    Char,
    Bytes,

    #[cfg(feature = "with-json")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
    Json,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDate,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoTime,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTime,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeUtc,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeLocal,

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeWithTimeZone,

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDate,

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeTime,

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDateTime,

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDateTimeWithTimeZone,

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffDate,

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffTime,

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffDateTime,

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffTimestamp,

    #[cfg(feature = "with-uuid")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
    Uuid,

    #[cfg(feature = "with-rust_decimal")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
    Decimal,

    #[cfg(feature = "with-bigdecimal")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
    BigDecimal,

    #[cfg(feature = "with-ipnetwork")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
    IpNetwork,

    #[cfg(feature = "with-mac_address")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
    MacAddress,

    #[cfg(feature = "postgres-range")]
    #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
    Range,
}

// TODO: Arc<str> or Arc<String>?
pub type EnumTypeName = RcOrArc<str>;

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Enum {
    pub type_name: EnumTypeName,
    pub value: Cow<'static, str>,
}

// I’m not sure we really need this abstraction, but array_type method requires the enum name so I added this type to avoid runtime panics.
// Once array_type no longer needs it, we can remove it.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OptionEnum {
    Some(Box<Enum>),
    None(EnumTypeName),
}

impl OptionEnum {
    /// Check if an enum value is set
    ///
    /// ```
    /// use sea_query::{Enum, EnumTypeName, OptionEnum};
    ///
    /// let type_name: EnumTypeName = "Test".into();
    ///
    /// assert_eq!(false, OptionEnum::None(type_name.clone()).is_some());
    /// assert_eq!(
    ///     true,
    ///     OptionEnum::Some(Box::new(Enum {
    ///         type_name,
    ///         value: "Foo".into()
    ///     }))
    ///     .is_some()
    /// );
    /// ```
    pub fn is_some(&self) -> bool {
        matches!(*self, OptionEnum::Some(_))
    }
}

/// Value variants
///
/// We want the inner Value to be exactly 1 pointer sized, so anything larger should be boxed.
///
/// If the `hashable-value` feature is enabled, NaN == NaN, which contradicts Rust's built-in
/// implementation of NaN != NaN.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(not(feature = "hashable-value"), derive(PartialEq))]
pub enum Value {
    Bool(Option<bool>),
    TinyInt(Option<i8>),
    SmallInt(Option<i16>),
    Int(Option<i32>),
    BigInt(Option<i64>),
    TinyUnsigned(Option<u8>),
    SmallUnsigned(Option<u16>),
    Unsigned(Option<u32>),
    BigUnsigned(Option<u64>),
    Float(Option<f32>),
    Double(Option<f64>),
    String(Option<String>),
    Enum(OptionEnum),
    Char(Option<char>),

    #[allow(clippy::box_collection)]
    Bytes(Option<Vec<u8>>),

    #[cfg(feature = "with-json")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
    Json(Option<Box<Json>>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDate(Option<NaiveDate>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoTime(Option<NaiveTime>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTime(Option<NaiveDateTime>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeUtc(Option<DateTime<Utc>>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeLocal(Option<DateTime<Local>>),

    #[cfg(feature = "with-chrono")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
    ChronoDateTimeWithTimeZone(Option<DateTime<FixedOffset>>),

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDate(Option<time::Date>),

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeTime(Option<time::Time>),

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDateTime(Option<PrimitiveDateTime>),

    #[cfg(feature = "with-time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
    TimeDateTimeWithTimeZone(Option<OffsetDateTime>),

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffDate(Option<jiff::civil::Date>),

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffTime(Option<jiff::civil::Time>),

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffDateTime(Option<Box<jiff::civil::DateTime>>),

    #[cfg(feature = "with-jiff")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
    JiffTimestamp(Option<Box<Timestamp>>),

    #[cfg(feature = "with-uuid")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
    Uuid(Option<Uuid>),

    #[cfg(feature = "with-rust_decimal")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
    Decimal(Option<Decimal>),

    #[cfg(feature = "with-bigdecimal")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
    BigDecimal(Option<Box<BigDecimal>>),

    #[cfg(feature = "postgres-array")]
    #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
    Array(ArrayType, Option<Box<Vec<Value>>>),

    #[cfg(feature = "postgres-vector")]
    #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
    Vector(Option<pgvector::Vector>),

    #[cfg(feature = "with-ipnetwork")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
    IpNetwork(Option<IpNetwork>),

    #[cfg(feature = "with-mac_address")]
    #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
    MacAddress(Option<MacAddress>),

    #[cfg(feature = "postgres-range")]
    #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
    Range(Option<Box<RangeType>>),
}

/// This test is to check if the size of [`Value`] exceeds the limit.
///
/// If the size exceeds the limit, you should box the variant.
/// Previously, the size was 24. We bumped it to 32 such that `String`
/// can be unboxed.
///
/// This value should *never* be raised as this is critical to stability
/// in production, which can cause OOM or stack overflow for certain
/// high throughput workloads.
pub const VALUE_SIZE: usize = check_value_size();
const MAX_VALUE_SIZE: usize = 32;

const fn check_value_size() -> usize {
    if std::mem::size_of::<Value>() > MAX_VALUE_SIZE {
        panic!(
            "the size of Value shouldn't be greater than the expected MAX_VALUE_SIZE (32 bytes by default)"
        )
    }
    std::mem::size_of::<Value>()
}

impl Value {
    pub fn unwrap<T>(self) -> T
    where
        T: ValueType,
    {
        T::unwrap(self)
    }

    pub fn expect<T>(self, msg: &str) -> T
    where
        T: ValueType,
    {
        T::expect(self, msg)
    }

    /// Get the null variant of self
    ///
    /// ```
    /// use sea_query::Value;
    ///
    /// let v = Value::Int(Some(2));
    /// let n = v.as_null();
    ///
    /// assert_eq!(n, Value::Int(None));
    ///
    /// // one liner:
    /// assert_eq!(Into::<Value>::into(2.2).as_null(), Value::Double(None));
    /// ```
    pub fn as_null(&self) -> Self {
        match self {
            Self::Bool(_) => Self::Bool(None),
            Self::TinyInt(_) => Self::TinyInt(None),
            Self::SmallInt(_) => Self::SmallInt(None),
            Self::Int(_) => Self::Int(None),
            Self::BigInt(_) => Self::BigInt(None),
            Self::TinyUnsigned(_) => Self::TinyUnsigned(None),
            Self::SmallUnsigned(_) => Self::SmallUnsigned(None),
            Self::Unsigned(_) => Self::Unsigned(None),
            Self::BigUnsigned(_) => Self::BigUnsigned(None),
            Self::Float(_) => Self::Float(None),
            Self::Double(_) => Self::Double(None),
            Self::String(_) => Self::String(None),
            Self::Enum(OptionEnum::Some(v)) => Self::Enum(OptionEnum::None(v.type_name.clone())),
            Self::Enum(OptionEnum::None(type_name)) => {
                Self::Enum(OptionEnum::None(type_name.clone()))
            }
            Self::Char(_) => Self::Char(None),
            Self::Bytes(_) => Self::Bytes(None),

            #[cfg(feature = "with-json")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
            Self::Json(_) => Self::Json(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDate(_) => Self::ChronoDate(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoTime(_) => Self::ChronoTime(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTime(_) => Self::ChronoDateTime(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeUtc(_) => Self::ChronoDateTimeUtc(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeLocal(_) => Self::ChronoDateTimeLocal(None),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeWithTimeZone(_) => Self::ChronoDateTimeWithTimeZone(None),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDate(_) => Self::TimeDate(None),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeTime(_) => Self::TimeTime(None),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTime(_) => Self::TimeDateTime(None),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTimeWithTimeZone(_) => Self::TimeDateTimeWithTimeZone(None),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDate(_) => Self::JiffDate(None),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTime(_) => Self::JiffTime(None),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDateTime(_) => Self::JiffDateTime(None),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTimestamp(_) => Self::JiffTimestamp(None),

            #[cfg(feature = "with-uuid")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
            Self::Uuid(_) => Self::Uuid(None),

            #[cfg(feature = "with-rust_decimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
            Self::Decimal(_) => Self::Decimal(None),

            #[cfg(feature = "with-bigdecimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
            Self::BigDecimal(_) => Self::BigDecimal(None),

            #[cfg(feature = "postgres-array")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
            Self::Array(ty, _) => Self::Array(ty.clone(), None),

            #[cfg(feature = "postgres-vector")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
            Self::Vector(_) => Self::Vector(None),

            #[cfg(feature = "with-ipnetwork")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
            Self::IpNetwork(_) => Self::IpNetwork(None),

            #[cfg(feature = "with-mac_address")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
            Self::MacAddress(_) => Self::MacAddress(None),

            #[cfg(feature = "postgres-range")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
            Self::Range(_) => Self::Range(None),
        }
    }

    /// Check if a value is set
    ///
    /// ```
    /// use sea_query::Value;
    ///
    /// assert_eq!(true, Value::Int(Some(2)).is_some());
    /// assert_eq!(false, Value::Int(None).is_some());
    /// ```
    pub fn is_some(&self) -> bool {
        match self {
            Self::Bool(v) => v.is_some(),
            Self::TinyInt(v) => v.is_some(),
            Self::SmallInt(v) => v.is_some(),
            Self::Int(v) => v.is_some(),
            Self::BigInt(v) => v.is_some(),
            Self::TinyUnsigned(v) => v.is_some(),
            Self::SmallUnsigned(v) => v.is_some(),
            Self::Unsigned(v) => v.is_some(),
            Self::BigUnsigned(v) => v.is_some(),
            Self::Float(v) => v.is_some(),
            Self::Double(v) => v.is_some(),
            Self::String(v) => v.is_some(),
            Self::Enum(v) => v.is_some(),
            Self::Char(v) => v.is_some(),
            Self::Bytes(v) => v.is_some(),

            #[cfg(feature = "with-json")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
            Self::Json(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDate(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoTime(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTime(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeUtc(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeLocal(v) => v.is_some(),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeWithTimeZone(v) => v.is_some(),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDate(v) => v.is_some(),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeTime(v) => v.is_some(),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTime(v) => v.is_some(),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTimeWithTimeZone(v) => v.is_some(),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDate(v) => v.is_some(),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTime(v) => v.is_some(),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDateTime(v) => v.is_some(),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTimestamp(v) => v.is_some(),

            #[cfg(feature = "with-uuid")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
            Self::Uuid(v) => v.is_some(),

            #[cfg(feature = "with-rust_decimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
            Self::Decimal(v) => v.is_some(),

            #[cfg(feature = "with-bigdecimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
            Self::BigDecimal(v) => v.is_some(),

            #[cfg(feature = "postgres-array")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
            Self::Array(_, v) => v.is_some(),

            #[cfg(feature = "postgres-vector")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
            Self::Vector(v) => v.is_some(),

            #[cfg(feature = "with-ipnetwork")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
            Self::IpNetwork(v) => v.is_some(),

            #[cfg(feature = "with-mac_address")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
            Self::MacAddress(v) => v.is_some(),

            #[cfg(feature = "postgres-range")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
            Self::Range(v) => v.is_some(),
        }
    }

    /// Get a default value of self's type
    ///
    /// ```
    /// use sea_query::Value;
    ///
    /// let v = Value::Int(None);
    /// let n = v.dummy_value();
    /// assert_eq!(n, Value::Int(Some(0)));
    /// ```
    pub fn dummy_value(&self) -> Self {
        match self {
            Self::Bool(_) => Self::Bool(Some(Default::default())),
            Self::TinyInt(_) => Self::TinyInt(Some(Default::default())),
            Self::SmallInt(_) => Self::SmallInt(Some(Default::default())),
            Self::Int(_) => Self::Int(Some(Default::default())),
            Self::BigInt(_) => Self::BigInt(Some(Default::default())),
            Self::TinyUnsigned(_) => Self::TinyUnsigned(Some(Default::default())),
            Self::SmallUnsigned(_) => Self::SmallUnsigned(Some(Default::default())),
            Self::Unsigned(_) => Self::Unsigned(Some(Default::default())),
            Self::BigUnsigned(_) => Self::BigUnsigned(Some(Default::default())),
            Self::Float(_) => Self::Float(Some(Default::default())),
            Self::Double(_) => Self::Double(Some(Default::default())),
            Self::String(_) => Self::String(Some(Default::default())),
            Self::Enum(v) => {
                let type_name = match v {
                    OptionEnum::Some(v) => v.type_name.clone(),
                    OptionEnum::None(type_name) => type_name.clone(),
                };
                Self::Enum(OptionEnum::Some(Box::new(Enum {
                    type_name,
                    value: Cow::Borrowed(""),
                })))
            }
            Self::Char(_) => Self::Char(Some(Default::default())),
            Self::Bytes(_) => Self::Bytes(Some(Default::default())),

            #[cfg(feature = "with-json")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
            Self::Json(_) => Self::Json(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDate(_) => Self::ChronoDate(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoTime(_) => Self::ChronoTime(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTime(_) => Self::ChronoDateTime(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeUtc(_) => Self::ChronoDateTimeUtc(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeLocal(_) => Self::ChronoDateTimeLocal(Some(Default::default())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeWithTimeZone(_) => {
                Self::ChronoDateTimeWithTimeZone(Some(Default::default()))
            }

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDate(_) => Self::TimeDate(Some(time::Date::MIN)),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeTime(_) => Self::TimeTime(Some(time::Time::MIDNIGHT)),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTime(_) => Self::TimeDateTime(Some(PrimitiveDateTime::MIN)),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTimeWithTimeZone(_) => {
                Self::TimeDateTimeWithTimeZone(Some(OffsetDateTime::UNIX_EPOCH))
            }

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDate(_) => Self::JiffDate(Some(jiff::civil::date(1970, 1, 1))),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTime(_) => Self::JiffTime(Some(jiff::civil::time(0, 0, 0, 0))),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDateTime(_) => {
                Self::JiffDateTime(Some(jiff::civil::date(1970, 1, 1).at(0, 0, 0, 0).into()))
            }

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTimestamp(_) => Self::JiffTimestamp(Some(Timestamp::UNIX_EPOCH.into())),

            #[cfg(feature = "with-uuid")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
            Self::Uuid(_) => Self::Uuid(Some(Default::default())),

            #[cfg(feature = "with-rust_decimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
            Self::Decimal(_) => Self::Decimal(Some(Default::default())),

            #[cfg(feature = "with-bigdecimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
            Self::BigDecimal(_) => Self::BigDecimal(Some(Default::default())),

            #[cfg(feature = "postgres-array")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
            Self::Array(ty, _) => Self::Array(ty.clone(), Some(Default::default())),

            #[cfg(feature = "postgres-vector")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
            Self::Vector(_) => Self::Vector(Some(vec![].into())),

            #[cfg(feature = "with-ipnetwork")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
            Self::IpNetwork(_) => Self::IpNetwork(Some("0.0.0.0".parse().unwrap())),

            #[cfg(feature = "with-mac_address")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
            Self::MacAddress(_) => Self::MacAddress(Some(Default::default())),

            #[cfg(feature = "postgres-range")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
            Self::Range(_) => Self::Range(Some(Default::default())),
        }
    }

    pub fn array_type(&self) -> ArrayType {
        #[allow(unused_imports)]
        use std::ops::Deref;

        fn array_type_of<V: ValueType>(_: &Option<V>) -> ArrayType {
            V::array_type()
        }

        #[allow(dead_code)]
        fn array_type_of_ref<V: ValueType>(_: Option<&V>) -> ArrayType {
            V::array_type()
        }

        match self {
            Self::Bool(v) => array_type_of(v),
            Self::TinyInt(v) => array_type_of(v),
            Self::SmallInt(v) => array_type_of(v),
            Self::Int(v) => array_type_of(v),
            Self::BigInt(v) => array_type_of(v),
            Self::TinyUnsigned(v) => array_type_of(v),
            Self::SmallUnsigned(v) => array_type_of(v),
            Self::Unsigned(v) => array_type_of(v),
            Self::BigUnsigned(v) => array_type_of(v),
            Self::Float(v) => array_type_of(v),
            Self::Double(v) => array_type_of(v),
            Self::String(v) => array_type_of(v),

            #[cfg(feature = "backend-postgres")]
            Self::Enum(v) => ArrayType::Enum(Box::new(match v {
                OptionEnum::Some(v) => v.type_name.clone(),
                OptionEnum::None(type_name) => type_name.clone(),
            })),
            #[cfg(not(feature = "backend-postgres"))]
            Self::Enum(_) => ArrayType::String,
            Self::Char(v) => array_type_of(v),
            Self::Bytes(v) => array_type_of(v),

            #[cfg(feature = "with-json")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
            Self::Json(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDate(v) => array_type_of(v),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoTime(v) => array_type_of(v),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTime(v) => array_type_of(v),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeUtc(v) => array_type_of(v),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeLocal(v) => array_type_of(v),

            #[cfg(feature = "with-chrono")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
            Self::ChronoDateTimeWithTimeZone(v) => array_type_of(v),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDate(v) => array_type_of(v),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeTime(v) => array_type_of(v),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTime(v) => array_type_of(v),

            #[cfg(feature = "with-time")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
            Self::TimeDateTimeWithTimeZone(v) => array_type_of(v),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDate(v) => array_type_of(v),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTime(v) => array_type_of(v),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffDateTime(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())),

            #[cfg(feature = "with-jiff")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))]
            Self::JiffTimestamp(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())),

            #[cfg(feature = "with-uuid")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
            Self::Uuid(v) => array_type_of(v),

            #[cfg(feature = "with-rust_decimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
            Self::Decimal(v) => array_type_of(v),

            #[cfg(feature = "with-bigdecimal")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
            Self::BigDecimal(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())),

            #[cfg(feature = "postgres-array")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))]
            Self::Array(v, _) => v.clone(),

            #[cfg(feature = "postgres-vector")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))]
            Self::Vector(v) => array_type_of(v),

            #[cfg(feature = "with-ipnetwork")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
            Self::IpNetwork(v) => array_type_of(v),

            #[cfg(feature = "with-mac_address")]
            #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))]
            Self::MacAddress(v) => array_type_of(v),

            #[cfg(feature = "postgres-range")]
            #[cfg_attr(docsrs, doc(cfg(feature = "postgres-range")))]
            Self::Range(v) => array_type_of_ref(v.as_ref().map(|v| v.deref())),
        }
    }
}

impl From<&[u8]> for Value {
    fn from(x: &[u8]) -> Value {
        Value::Bytes(Some(x.into()))
    }
}

impl From<&str> for Value {
    fn from(x: &str) -> Value {
        Value::String(Some(x.to_owned()))
    }
}

impl From<&String> for Value {
    fn from(x: &String) -> Value {
        Value::String(Some(x.clone()))
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value> + Nullable,
{
    fn from(x: Option<T>) -> Value {
        match x {
            Some(v) => v.into(),
            None => T::null(),
        }
    }
}

impl From<Cow<'_, str>> for Value {
    fn from(x: Cow<'_, str>) -> Value {
        x.into_owned().into()
    }
}

impl From<Enum> for Value {
    fn from(value: Enum) -> Value {
        Value::Enum(OptionEnum::Some(Box::new(value)))
    }
}

impl IntoIterator for Values {
    type Item = Value;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl std::fmt::Display for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        CommonSqlQueryBuilder.write_value(f, self)
    }
}

pub trait ValueType: Sized {
    fn try_from(v: Value) -> Result<Self, ValueTypeErr>;

    fn unwrap(v: Value) -> Self {
        Self::try_from(v).unwrap()
    }

    fn expect(v: Value, msg: &str) -> Self {
        Self::try_from(v).expect(msg)
    }

    fn is_option() -> bool {
        false
    }

    fn type_name() -> String;

    fn array_type() -> ArrayType;

    fn column_type() -> ColumnType;

    fn enum_type_name() -> Option<&'static str> {
        None
    }
}

impl<T> ValueType for Option<T>
where
    T: ValueType + Nullable,
{
    fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
        if v == T::null() {
            Ok(None)
        } else {
            Ok(Some(T::try_from(v)?))
        }
    }

    fn is_option() -> bool {
        true
    }

    fn type_name() -> String {
        format!("Option<{}>", T::type_name())
    }

    fn array_type() -> ArrayType {
        T::array_type()
    }

    fn column_type() -> ColumnType {
        T::column_type()
    }
}

impl ValueType for Cow<'_, str> {
    fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
        match v {
            Value::String(Some(x)) => Ok((x).into()),
            _ => Err(ValueTypeErr),
        }
    }

    fn type_name() -> String {
        "Cow<str>".into()
    }

    fn array_type() -> ArrayType {
        ArrayType::String
    }

    fn column_type() -> ColumnType {
        ColumnType::String(StringLen::None)
    }
}

#[derive(Debug)]
pub struct ValueTypeErr;

impl std::error::Error for ValueTypeErr {}

impl std::fmt::Display for ValueTypeErr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("Value type mismatch")
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Values(pub Vec<Value>);

impl Values {
    pub fn iter(&self) -> impl Iterator<Item = &Value> {
        self.0.iter()
    }
}

pub trait Nullable {
    fn null() -> Value;
}

impl Nullable for &str {
    fn null() -> Value {
        Value::String(None)
    }
}

impl Nullable for Enum {
    fn null() -> Value {
        Value::Enum(OptionEnum::None("".into()))
    }
}

impl ValueType for Enum {
    fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
        match v {
            Value::Enum(OptionEnum::Some(v)) => Ok(*v),
            _ => Err(ValueTypeErr),
        }
    }

    fn type_name() -> String {
        "Enum".into()
    }

    // These implementations probably won’t actually be used, so there
    // shouldn’t cause any runtime issues.
    fn array_type() -> ArrayType {
        ArrayType::String
    }

    fn column_type() -> ColumnType {
        ColumnType::String(StringLen::None)
    }
}

macro_rules! type_to_value {
    ( $type: ty, $name: ident, $col_type: expr ) => {
        impl From<$type> for Value {
            fn from(x: $type) -> Value {
                Value::$name(Some(x))
            }
        }

        impl Nullable for $type {
            fn null() -> Value {
                Value::$name(None)
            }
        }

        impl ValueType for $type {
            fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
                match v {
                    Value::$name(Some(x)) => Ok(x),
                    _ => Err(ValueTypeErr),
                }
            }

            fn type_name() -> String {
                stringify!($type).to_owned()
            }

            fn array_type() -> ArrayType {
                ArrayType::$name
            }

            fn column_type() -> ColumnType {
                use ColumnType::*;
                $col_type
            }
        }
    };
}

#[allow(unused_imports)]
use type_to_value;

type_to_value!(bool, Bool, Boolean);
type_to_value!(i8, TinyInt, TinyInteger);
type_to_value!(i16, SmallInt, SmallInteger);
type_to_value!(i32, Int, Integer);
type_to_value!(i64, BigInt, BigInteger);
type_to_value!(u8, TinyUnsigned, TinyUnsigned);
type_to_value!(u16, SmallUnsigned, SmallUnsigned);
type_to_value!(u32, Unsigned, Unsigned);
type_to_value!(u64, BigUnsigned, BigUnsigned);
type_to_value!(f32, Float, Float);
type_to_value!(f64, Double, Double);
type_to_value!(char, Char, Char(None));
type_to_value!(Vec<u8>, Bytes, VarBinary(StringLen::None));
type_to_value!(String, String, String(StringLen::None));

macro_rules! type_ref_to_value {
    ( $type: ty, $name: ident ) => {
        impl From<&$type> for Value {
            fn from(x: &$type) -> Value {
                Value::$name(Some(*x))
            }
        }
    };
}

type_ref_to_value!(bool, Bool);
type_ref_to_value!(i8, TinyInt);
type_ref_to_value!(i16, SmallInt);
type_ref_to_value!(i32, Int);
type_ref_to_value!(i64, BigInt);
type_ref_to_value!(u8, TinyUnsigned);
type_ref_to_value!(u16, SmallUnsigned);
type_ref_to_value!(u32, Unsigned);
type_ref_to_value!(u64, BigUnsigned);
type_ref_to_value!(f32, Float);
type_ref_to_value!(f64, Double);
type_ref_to_value!(char, Char);

#[allow(unused_macros)]
macro_rules! type_to_box_value {
    ( $type: ty, $name: ident, $col_type: expr ) => {
        impl From<$type> for Value {
            fn from(x: $type) -> Value {
                Value::$name(Some(Box::new(x)))
            }
        }

        impl Nullable for $type {
            fn null() -> Value {
                Value::$name(None)
            }
        }

        impl ValueType for $type {
            fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
                match v {
                    Value::$name(Some(x)) => Ok(*x),
                    _ => Err(ValueTypeErr),
                }
            }

            fn type_name() -> String {
                stringify!($type).to_owned()
            }

            fn array_type() -> ArrayType {
                ArrayType::$name
            }

            fn column_type() -> ColumnType {
                use ColumnType::*;
                $col_type
            }
        }
    };
}

#[allow(unused_imports)]
use type_to_box_value;
