1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2018 TiKV Project Authors. Licensed under Apache-2.0.

use std::fmt;

/// Function implementations' parameter data types.
///
/// It is similar to the `EvalType` in TiDB, but doesn't provide type `Timestamp`, which is
/// handled by the same type as `DateTime` here, instead of a new type. Also, `String` is
/// called `Bytes` here to be less confusing.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum EvalType {
    Int,
    Real,
    Decimal,
    Bytes,
    DateTime,
    Duration,
    Json,
    Enum,
    Set,
}

impl EvalType {
    /// Converts `EvalType` into one of the compatible `FieldTypeTp`s.
    ///
    /// This function should be only useful in test scenarios that only cares about `EvalType` but
    /// accepts a `FieldTypeTp`.
    pub fn into_certain_field_type_tp_for_test(self) -> crate::FieldTypeTp {
        match self {
            EvalType::Int => crate::FieldTypeTp::LongLong,
            EvalType::Real => crate::FieldTypeTp::Double,
            EvalType::Decimal => crate::FieldTypeTp::NewDecimal,
            EvalType::Bytes => crate::FieldTypeTp::String,
            EvalType::DateTime => crate::FieldTypeTp::DateTime,
            EvalType::Duration => crate::FieldTypeTp::Duration,
            EvalType::Json => crate::FieldTypeTp::JSON,
            EvalType::Enum => crate::FieldTypeTp::Enum,
            EvalType::Set => crate::FieldTypeTp::Set,
        }
    }
}

impl fmt::Display for EvalType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

impl std::convert::TryFrom<crate::FieldTypeTp> for EvalType {
    type Error = crate::DataTypeError;

    // Succeeds for all field types supported as eval types, fails for unsupported types.
    fn try_from(tp: crate::FieldTypeTp) -> Result<Self, crate::DataTypeError> {
        let eval_type = match tp {
            crate::FieldTypeTp::Tiny
            | crate::FieldTypeTp::Short
            | crate::FieldTypeTp::Int24
            | crate::FieldTypeTp::Long
            | crate::FieldTypeTp::LongLong
            | crate::FieldTypeTp::Year => EvalType::Int,
            crate::FieldTypeTp::Float | crate::FieldTypeTp::Double => EvalType::Real,
            crate::FieldTypeTp::NewDecimal => EvalType::Decimal,
            crate::FieldTypeTp::Timestamp
            | crate::FieldTypeTp::Date
            | crate::FieldTypeTp::DateTime => EvalType::DateTime,
            crate::FieldTypeTp::Duration => EvalType::Duration,
            crate::FieldTypeTp::JSON => EvalType::Json,
            crate::FieldTypeTp::VarChar
            | crate::FieldTypeTp::TinyBlob
            | crate::FieldTypeTp::MediumBlob
            | crate::FieldTypeTp::LongBlob
            | crate::FieldTypeTp::Blob
            | crate::FieldTypeTp::VarString
            | crate::FieldTypeTp::String
            | crate::FieldTypeTp::Null => EvalType::Bytes,
            crate::FieldTypeTp::Enum => EvalType::Enum,
            _ => {
                // In TiDB, Bit's eval type is Int, but it is not yet supported in TiKV.
                // TODO: we need to handle FieldTypeTp::{Enum, Set} after we implement encode and decode.
                return Err(crate::DataTypeError::UnsupportedType {
                    name: tp.to_string(),
                });
            }
        };
        Ok(eval_type)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::FieldTypeAccessor;
    use crate::FieldTypeTp::*;
    use std::convert::TryFrom;

    #[test]
    fn test_fieldtype_to_evaltype() {
        let cases = vec![
            (Unspecified, None),
            (Tiny, Some(EvalType::Int)),
            (Short, Some(EvalType::Int)),
            (Long, Some(EvalType::Int)),
            (Float, Some(EvalType::Real)),
            (Double, Some(EvalType::Real)),
            (Null, Some(EvalType::Bytes)),
            (Timestamp, Some(EvalType::DateTime)),
            (LongLong, Some(EvalType::Int)),
            (Int24, Some(EvalType::Int)),
            (Date, Some(EvalType::DateTime)),
            (Duration, Some(EvalType::Duration)),
            (DateTime, Some(EvalType::DateTime)),
            (Year, Some(EvalType::Int)),
            (NewDate, None),
            (VarChar, Some(EvalType::Bytes)),
            (Bit, None),
            (JSON, Some(EvalType::Json)),
            (NewDecimal, Some(EvalType::Decimal)),
            (Enum, Some(EvalType::Enum)),
            (Set, None),
            (TinyBlob, Some(EvalType::Bytes)),
            (MediumBlob, Some(EvalType::Bytes)),
            (LongBlob, Some(EvalType::Bytes)),
            (Blob, Some(EvalType::Bytes)),
            (VarString, Some(EvalType::Bytes)),
            (String, Some(EvalType::Bytes)),
            (Geometry, None),
        ];

        for (tp, etype) in cases {
            let mut ft = tipb::FieldType::default();
            ft.set_tp(tp as i32);

            let ftt = EvalType::try_from(ft.as_accessor().tp());

            if let Some(etype) = etype {
                assert_eq!(ftt.unwrap(), etype);
            } else {
                assert!(ftt.is_err());
            }
        }
    }
}