sqlx_mysql/types/
uint.rs

1use crate::collation::Collation;
2use crate::decode::Decode;
3use crate::encode::{Encode, IsNull};
4use crate::error::BoxDynError;
5use crate::protocol::text::{ColumnFlags, ColumnType};
6use crate::types::Type;
7use crate::{MySql, MySqlTypeInfo, MySqlValueFormat, MySqlValueRef};
8use byteorder::{ByteOrder, LittleEndian};
9
10fn uint_type_info(ty: ColumnType) -> MySqlTypeInfo {
11    MySqlTypeInfo {
12        r#type: ty,
13        flags: ColumnFlags::BINARY | ColumnFlags::UNSIGNED,
14        collation: Collation::BINARY,
15        max_size: None,
16    }
17}
18
19fn uint_compatible(ty: &MySqlTypeInfo) -> bool {
20    matches!(
21        ty.r#type,
22        ColumnType::Tiny
23            | ColumnType::Short
24            | ColumnType::Long
25            | ColumnType::Int24
26            | ColumnType::LongLong
27            | ColumnType::Year
28            | ColumnType::Bit
29    ) && ty.flags.contains(ColumnFlags::UNSIGNED)
30}
31
32impl Type<MySql> for u8 {
33    fn type_info() -> MySqlTypeInfo {
34        uint_type_info(ColumnType::Tiny)
35    }
36
37    fn compatible(ty: &MySqlTypeInfo) -> bool {
38        uint_compatible(ty)
39    }
40}
41
42impl Type<MySql> for u16 {
43    fn type_info() -> MySqlTypeInfo {
44        uint_type_info(ColumnType::Short)
45    }
46
47    fn compatible(ty: &MySqlTypeInfo) -> bool {
48        uint_compatible(ty)
49    }
50}
51
52impl Type<MySql> for u32 {
53    fn type_info() -> MySqlTypeInfo {
54        uint_type_info(ColumnType::Long)
55    }
56
57    fn compatible(ty: &MySqlTypeInfo) -> bool {
58        uint_compatible(ty)
59    }
60}
61
62impl Type<MySql> for u64 {
63    fn type_info() -> MySqlTypeInfo {
64        uint_type_info(ColumnType::LongLong)
65    }
66
67    fn compatible(ty: &MySqlTypeInfo) -> bool {
68        uint_compatible(ty)
69    }
70}
71
72impl Encode<'_, MySql> for u8 {
73    fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
74        buf.extend(&self.to_le_bytes());
75
76        Ok(IsNull::No)
77    }
78}
79
80impl Encode<'_, MySql> for u16 {
81    fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
82        buf.extend(&self.to_le_bytes());
83
84        Ok(IsNull::No)
85    }
86}
87
88impl Encode<'_, MySql> for u32 {
89    fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
90        buf.extend(&self.to_le_bytes());
91
92        Ok(IsNull::No)
93    }
94}
95
96impl Encode<'_, MySql> for u64 {
97    fn encode_by_ref(&self, buf: &mut Vec<u8>) -> Result<IsNull, BoxDynError> {
98        buf.extend(&self.to_le_bytes());
99
100        Ok(IsNull::No)
101    }
102}
103
104fn uint_decode(value: MySqlValueRef<'_>) -> Result<u64, BoxDynError> {
105    if value.type_info.r#type == ColumnType::Bit {
106        // NOTE: Regardless of the value format, there is raw binary data here
107
108        let buf = value.as_bytes()?;
109        let mut value: u64 = 0;
110
111        for b in buf {
112            value = (*b as u64) | (value << 8);
113        }
114
115        return Ok(value);
116    }
117
118    Ok(match value.format() {
119        MySqlValueFormat::Text => value.as_str()?.parse()?,
120
121        MySqlValueFormat::Binary => {
122            let buf = value.as_bytes()?;
123
124            // Check conditions that could cause `read_uint()` to panic.
125            if buf.is_empty() {
126                return Err("empty buffer".into());
127            }
128
129            if buf.len() > 8 {
130                return Err(format!(
131                    "expected no more than 8 bytes for unsigned integer value, got {}",
132                    buf.len()
133                )
134                .into());
135            }
136
137            LittleEndian::read_uint(buf, buf.len())
138        }
139    })
140}
141
142impl Decode<'_, MySql> for u8 {
143    fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
144        uint_decode(value)?.try_into().map_err(Into::into)
145    }
146}
147
148impl Decode<'_, MySql> for u16 {
149    fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
150        uint_decode(value)?.try_into().map_err(Into::into)
151    }
152}
153
154impl Decode<'_, MySql> for u32 {
155    fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
156        uint_decode(value)?.try_into().map_err(Into::into)
157    }
158}
159
160impl Decode<'_, MySql> for u64 {
161    fn decode(value: MySqlValueRef<'_>) -> Result<Self, BoxDynError> {
162        uint_decode(value)
163    }
164}