use std::fmt;
use tipb::ColumnInfo;
use tipb::FieldType;
use crate::error::DataTypeError;
#[derive(PartialEq, Debug, Clone, Copy)]
#[repr(i32)]
pub enum FieldTypeTp {
Unspecified = 0,
Tiny = 1,
Short = 2,
Long = 3,
Float = 4,
Double = 5,
Null = 6,
Timestamp = 7,
LongLong = 8,
Int24 = 9,
Date = 10,
Duration = 11,
DateTime = 12,
Year = 13,
NewDate = 14,
VarChar = 15,
Bit = 16,
JSON = 0xf5,
NewDecimal = 0xf6,
Enum = 0xf7,
Set = 0xf8,
TinyBlob = 0xf9,
MediumBlob = 0xfa,
LongBlob = 0xfb,
Blob = 0xfc,
VarString = 0xfd,
String = 0xfe,
Geometry = 0xff,
}
impl FieldTypeTp {
fn from_i32(i: i32) -> Option<FieldTypeTp> {
if (i >= FieldTypeTp::Unspecified as i32 && i <= FieldTypeTp::Bit as i32)
|| (i >= FieldTypeTp::JSON as i32 && i <= FieldTypeTp::Geometry as i32)
{
Some(unsafe { ::std::mem::transmute::<i32, FieldTypeTp>(i) })
} else {
None
}
}
pub fn from_u8(i: u8) -> Option<FieldTypeTp> {
if i <= FieldTypeTp::Bit as u8 || i >= FieldTypeTp::JSON as u8 {
Some(unsafe { ::std::mem::transmute::<i32, FieldTypeTp>(i32::from(i)) })
} else {
None
}
}
pub fn to_u8(self) -> Option<u8> {
Some(self as i32 as u8)
}
}
impl fmt::Display for FieldTypeTp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl From<FieldTypeTp> for FieldType {
fn from(fp: FieldTypeTp) -> FieldType {
let mut ft = FieldType::default();
ft.as_mut_accessor().set_tp(fp);
ft
}
}
impl From<FieldTypeTp> for ColumnInfo {
fn from(fp: FieldTypeTp) -> ColumnInfo {
let mut ft = ColumnInfo::default();
ft.as_mut_accessor().set_tp(fp);
ft
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
#[repr(i32)]
pub enum Collation {
Binary = 63,
Utf8Mb4Bin = -46,
Utf8Mb4BinNoPadding = 46,
Utf8Mb4GeneralCi = -45,
Utf8Mb4UnicodeCi = -224,
Latin1Bin = -47,
}
impl Collation {
pub fn from_i32(n: i32) -> Result<Self, DataTypeError> {
match n {
-33 | -45 => Ok(Collation::Utf8Mb4GeneralCi),
-46 | -83 | -65 => Ok(Collation::Utf8Mb4Bin),
-47 => Ok(Collation::Latin1Bin),
-63 | 63 | 47 => Ok(Collation::Binary),
-224 | -192 => Ok(Collation::Utf8Mb4UnicodeCi),
n if n >= 0 => Ok(Collation::Utf8Mb4BinNoPadding),
n => Err(DataTypeError::UnsupportedCollation { code: n }),
}
}
pub fn is_bin_collation(&self) -> bool {
matches!(self, Collation::Utf8Mb4Bin | Collation::Latin1Bin)
}
}
impl fmt::Display for Collation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
bitflags! {
pub struct FieldTypeFlag: u32 {
const NOT_NULL = 1;
const UNSIGNED = 1 << 5;
const BINARY = 1 << 7;
const PARSE_TO_JSON = 1 << 18;
const IS_BOOLEAN = 1 << 19;
const ENUM_SET_AS_INT = 1 << 21;
}
}
pub trait FieldTypeAccessor {
fn tp(&self) -> FieldTypeTp;
fn set_tp(&mut self, tp: FieldTypeTp) -> &mut dyn FieldTypeAccessor;
fn flag(&self) -> FieldTypeFlag;
fn set_flag(&mut self, flag: FieldTypeFlag) -> &mut dyn FieldTypeAccessor;
fn flen(&self) -> isize;
fn set_flen(&mut self, flen: isize) -> &mut dyn FieldTypeAccessor;
fn decimal(&self) -> isize;
fn set_decimal(&mut self, decimal: isize) -> &mut dyn FieldTypeAccessor;
fn collation(&self) -> Result<Collation, DataTypeError>;
fn set_collation(&mut self, collation: Collation) -> &mut dyn FieldTypeAccessor;
fn as_accessor(&self) -> &dyn FieldTypeAccessor
where
Self: Sized,
{
self as &dyn FieldTypeAccessor
}
fn as_mut_accessor(&mut self) -> &mut dyn FieldTypeAccessor
where
Self: Sized,
{
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn is_hybrid(&self) -> bool {
let tp = self.tp();
tp == FieldTypeTp::Enum || tp == FieldTypeTp::Bit || tp == FieldTypeTp::Set
}
#[inline]
fn is_blob_like(&self) -> bool {
let tp = self.tp();
tp == FieldTypeTp::TinyBlob
|| tp == FieldTypeTp::MediumBlob
|| tp == FieldTypeTp::Blob
|| tp == FieldTypeTp::LongBlob
}
#[inline]
fn is_char_like(&self) -> bool {
let tp = self.tp();
tp == FieldTypeTp::String || tp == FieldTypeTp::VarChar
}
#[inline]
fn is_varchar_like(&self) -> bool {
let tp = self.tp();
tp == FieldTypeTp::VarString || tp == FieldTypeTp::VarChar
}
#[inline]
fn is_string_like(&self) -> bool {
self.is_blob_like()
|| self.is_char_like()
|| self.is_varchar_like()
|| self.tp() == FieldTypeTp::Unspecified
}
#[inline]
fn is_binary_string_like(&self) -> bool {
self.collation()
.map(|col| col == Collation::Binary)
.unwrap_or(false)
&& self.is_string_like()
}
#[inline]
fn is_non_binary_string_like(&self) -> bool {
self.collation()
.map(|col| col != Collation::Binary)
.unwrap_or(true)
&& self.is_string_like()
}
#[inline]
fn is_unsigned(&self) -> bool {
self.flag().contains(FieldTypeFlag::UNSIGNED)
}
#[inline]
fn is_bool(&self) -> bool {
self.flag().contains(FieldTypeFlag::IS_BOOLEAN)
}
#[inline]
fn need_restored_data(&self) -> bool {
self.is_non_binary_string_like()
&& (!self
.collation()
.map(|col| col.is_bin_collation())
.unwrap_or(false)
|| self.is_varchar_like())
}
}
impl FieldTypeAccessor for FieldType {
#[inline]
fn tp(&self) -> FieldTypeTp {
FieldTypeTp::from_i32(self.get_tp()).unwrap_or(FieldTypeTp::Unspecified)
}
#[inline]
fn set_tp(&mut self, tp: FieldTypeTp) -> &mut dyn FieldTypeAccessor {
FieldType::set_tp(self, tp as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn flag(&self) -> FieldTypeFlag {
FieldTypeFlag::from_bits_truncate(self.get_flag())
}
#[inline]
fn set_flag(&mut self, flag: FieldTypeFlag) -> &mut dyn FieldTypeAccessor {
FieldType::set_flag(self, flag.bits());
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn flen(&self) -> isize {
self.get_flen() as isize
}
#[inline]
fn set_flen(&mut self, flen: isize) -> &mut dyn FieldTypeAccessor {
FieldType::set_flen(self, flen as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn decimal(&self) -> isize {
self.get_decimal() as isize
}
#[inline]
fn set_decimal(&mut self, decimal: isize) -> &mut dyn FieldTypeAccessor {
FieldType::set_decimal(self, decimal as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn collation(&self) -> Result<Collation, DataTypeError> {
Collation::from_i32(self.get_collate())
}
#[inline]
fn set_collation(&mut self, collation: Collation) -> &mut dyn FieldTypeAccessor {
FieldType::set_collate(self, collation as i32);
self as &mut dyn FieldTypeAccessor
}
}
impl FieldTypeAccessor for ColumnInfo {
#[inline]
fn tp(&self) -> FieldTypeTp {
FieldTypeTp::from_i32(self.get_tp()).unwrap_or(FieldTypeTp::Unspecified)
}
#[inline]
fn set_tp(&mut self, tp: FieldTypeTp) -> &mut dyn FieldTypeAccessor {
ColumnInfo::set_tp(self, tp as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn flag(&self) -> FieldTypeFlag {
FieldTypeFlag::from_bits_truncate(self.get_flag() as u32)
}
#[inline]
fn set_flag(&mut self, flag: FieldTypeFlag) -> &mut dyn FieldTypeAccessor {
ColumnInfo::set_flag(self, flag.bits() as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn flen(&self) -> isize {
self.get_column_len() as isize
}
#[inline]
fn set_flen(&mut self, flen: isize) -> &mut dyn FieldTypeAccessor {
ColumnInfo::set_column_len(self, flen as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn decimal(&self) -> isize {
self.get_decimal() as isize
}
#[inline]
fn set_decimal(&mut self, decimal: isize) -> &mut dyn FieldTypeAccessor {
ColumnInfo::set_decimal(self, decimal as i32);
self as &mut dyn FieldTypeAccessor
}
#[inline]
fn collation(&self) -> Result<Collation, DataTypeError> {
Collation::from_i32(self.get_collation())
}
#[inline]
fn set_collation(&mut self, collation: Collation) -> &mut dyn FieldTypeAccessor {
ColumnInfo::set_collation(self, collation as i32);
self as &mut dyn FieldTypeAccessor
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::i32;
fn field_types() -> Vec<FieldTypeTp> {
vec![
FieldTypeTp::Unspecified,
FieldTypeTp::Tiny,
FieldTypeTp::Short,
FieldTypeTp::Long,
FieldTypeTp::Float,
FieldTypeTp::Double,
FieldTypeTp::Null,
FieldTypeTp::Timestamp,
FieldTypeTp::LongLong,
FieldTypeTp::Int24,
FieldTypeTp::Date,
FieldTypeTp::Duration,
FieldTypeTp::DateTime,
FieldTypeTp::Year,
FieldTypeTp::NewDate,
FieldTypeTp::VarChar,
FieldTypeTp::Bit,
FieldTypeTp::JSON,
FieldTypeTp::NewDecimal,
FieldTypeTp::Enum,
FieldTypeTp::Set,
FieldTypeTp::TinyBlob,
FieldTypeTp::MediumBlob,
FieldTypeTp::LongBlob,
FieldTypeTp::Blob,
FieldTypeTp::VarString,
FieldTypeTp::String,
FieldTypeTp::Geometry,
]
}
#[test]
fn test_field_type_from_i32() {
for ft in field_types() {
assert_eq!(FieldTypeTp::from_i32(ft as i32), Some(ft));
}
let fail_cases = vec![-1, -42, i32::MIN, i32::MAX, 17, 42, 0xf4, 0x100];
for fail_case in fail_cases {
assert_eq!(FieldTypeTp::from_i32(fail_case), None);
}
}
#[test]
fn test_field_type_from_u8() {
for ft in field_types() {
assert_eq!(FieldTypeTp::from_u8(ft as i32 as u8), Some(ft));
}
for fail_case in 17u8..=0xf4u8 {
assert_eq!(FieldTypeTp::from_u8(fail_case), None);
}
}
#[test]
fn test_field_type_to_u8() {
for ft in field_types() {
assert_eq!(FieldTypeTp::from_u8(ft.to_u8().unwrap()), Some(ft));
}
}
#[test]
fn test_collate_from_i32() {
let cases = vec![
(33, Some(Collation::Utf8Mb4BinNoPadding)),
(-33, Some(Collation::Utf8Mb4GeneralCi)),
(45, Some(Collation::Utf8Mb4BinNoPadding)),
(-45, Some(Collation::Utf8Mb4GeneralCi)),
(46, Some(Collation::Utf8Mb4BinNoPadding)),
(-46, Some(Collation::Utf8Mb4Bin)),
(63, Some(Collation::Binary)),
(-63, Some(Collation::Binary)),
(83, Some(Collation::Utf8Mb4BinNoPadding)),
(-83, Some(Collation::Utf8Mb4Bin)),
(255, Some(Collation::Utf8Mb4BinNoPadding)),
(-255, None),
(i32::MAX, Some(Collation::Utf8Mb4BinNoPadding)),
(i32::MIN, None),
(-192, Some(Collation::Utf8Mb4UnicodeCi)),
(-224, Some(Collation::Utf8Mb4UnicodeCi)),
];
for (collate, expected) in cases {
let mut ft = tipb::FieldType::default();
ft.set_collate(collate);
let coll = ft.as_accessor().collation();
if let Some(c) = expected {
assert_eq!(coll.unwrap(), c);
} else {
assert!(coll.is_err());
}
}
}
}