sqlx_mysql/
value.rs

1use std::borrow::Cow;
2use std::str::from_utf8;
3
4use bytes::Bytes;
5pub(crate) use sqlx_core::value::*;
6
7use crate::error::{BoxDynError, UnexpectedNullError};
8use crate::protocol::text::ColumnType;
9use crate::{MySql, MySqlTypeInfo};
10
11#[derive(Debug, Clone, Copy)]
12#[repr(u8)]
13pub enum MySqlValueFormat {
14    Text,
15    Binary,
16}
17
18/// Implementation of [`Value`] for MySQL.
19#[derive(Clone)]
20pub struct MySqlValue {
21    value: Option<Bytes>,
22    type_info: MySqlTypeInfo,
23    format: MySqlValueFormat,
24}
25
26/// Implementation of [`ValueRef`] for MySQL.
27#[derive(Clone)]
28pub struct MySqlValueRef<'r> {
29    pub(crate) value: Option<&'r [u8]>,
30    pub(crate) row: Option<&'r Bytes>,
31    pub(crate) type_info: MySqlTypeInfo,
32    pub(crate) format: MySqlValueFormat,
33}
34
35impl<'r> MySqlValueRef<'r> {
36    pub(crate) fn format(&self) -> MySqlValueFormat {
37        self.format
38    }
39
40    pub(crate) fn as_bytes(&self) -> Result<&'r [u8], BoxDynError> {
41        match &self.value {
42            Some(v) => Ok(v),
43            None => Err(UnexpectedNullError.into()),
44        }
45    }
46
47    pub(crate) fn as_str(&self) -> Result<&'r str, BoxDynError> {
48        Ok(from_utf8(self.as_bytes()?)?)
49    }
50}
51
52impl Value for MySqlValue {
53    type Database = MySql;
54
55    fn as_ref(&self) -> MySqlValueRef<'_> {
56        MySqlValueRef {
57            value: self.value.as_deref(),
58            row: None,
59            type_info: self.type_info.clone(),
60            format: self.format,
61        }
62    }
63
64    fn type_info(&self) -> Cow<'_, MySqlTypeInfo> {
65        Cow::Borrowed(&self.type_info)
66    }
67
68    fn is_null(&self) -> bool {
69        is_null(self.value.as_deref(), &self.type_info)
70    }
71}
72
73impl<'r> ValueRef<'r> for MySqlValueRef<'r> {
74    type Database = MySql;
75
76    fn to_owned(&self) -> MySqlValue {
77        let value = match (self.row, self.value) {
78            (Some(row), Some(value)) => Some(row.slice_ref(value)),
79
80            (None, Some(value)) => Some(Bytes::copy_from_slice(value)),
81
82            _ => None,
83        };
84
85        MySqlValue {
86            value,
87            format: self.format,
88            type_info: self.type_info.clone(),
89        }
90    }
91
92    fn type_info(&self) -> Cow<'_, MySqlTypeInfo> {
93        Cow::Borrowed(&self.type_info)
94    }
95
96    #[inline]
97    fn is_null(&self) -> bool {
98        is_null(self.value, &self.type_info)
99    }
100}
101
102fn is_null(value: Option<&[u8]>, ty: &MySqlTypeInfo) -> bool {
103    if let Some(value) = value {
104        // zero dates and date times should be treated the same as NULL
105        if matches!(
106            ty.r#type,
107            ColumnType::Date | ColumnType::Timestamp | ColumnType::Datetime
108        ) && value.starts_with(b"\0")
109        {
110            return true;
111        }
112    }
113
114    value.is_none()
115}