Skip to main content

drizzle_mysql/traits/
value.rs

1//! [`DrizzleMySQLColumn`], for custom column types, and the JSON projection
2//! metadata of the built-in SQL type markers.
3
4use crate::values::{MySQLValue, OwnedMySQLValue};
5use drizzle_core::error::DrizzleError;
6
7/// A Rust type that can be stored in a `MySQL` column.
8///
9/// Implement it to use your own type as a field of a `#[MySQLTable]`.
10/// [`SQLType`](Self::SQLType) sets which expressions the column can be used
11/// in, [`SQL_TYPE`](Self::SQL_TYPE) is written into the DDL, and
12/// [`decode`](Self::decode) / [`encode`](Self::encode) convert values.
13/// Implementing it also gives `From<T> for MySQLValue`, which goes through
14/// [`encode_owned`](Self::encode_owned).
15///
16/// # Examples
17///
18/// A `u32` stored as four big-endian bytes:
19///
20/// ```
21/// use drizzle_core::error::DrizzleError;
22/// use drizzle_mysql::traits::DrizzleMySQLColumn;
23/// use drizzle_mysql::types::Binary;
24/// use drizzle_mysql::values::MySQLValue;
25/// use std::borrow::Cow;
26///
27/// struct U32Be(u32);
28///
29/// impl DrizzleMySQLColumn for U32Be {
30///     type SQLType = Binary;
31///     const SQL_TYPE: &'static str = "BINARY(4)";
32///
33///     fn decode(value: MySQLValue<'_>) -> Result<Self, DrizzleError> {
34///         let MySQLValue::Bytes(bytes) = value else {
35///             return Err(DrizzleError::ConversionError("expected bytes".into()));
36///         };
37///         let bytes: [u8; 4] = bytes
38///             .as_ref()
39///             .try_into()
40///             .map_err(|_| DrizzleError::ConversionError("expected 4 bytes".into()))?;
41///         Ok(Self(u32::from_be_bytes(bytes)))
42///     }
43///
44///     fn encode(&self) -> MySQLValue<'_> {
45///         MySQLValue::Bytes(Cow::Owned(self.0.to_be_bytes().to_vec()))
46///     }
47/// }
48///
49/// let value = MySQLValue::from(U32Be(7));
50/// assert_eq!(value, MySQLValue::Bytes(Cow::Borrowed(&[0, 0, 0, 7][..])));
51/// assert_eq!(U32Be::decode(value).unwrap().0, 7);
52/// assert!(U32Be::decode(MySQLValue::Int(7)).is_err());
53/// ```
54#[diagnostic::on_unimplemented(
55    message = "`{Self}` cannot be used as a MySQL column type",
56    note = "add #[derive(MySQLEnum)] for enum types, or implement DrizzleMySQLColumn"
57)]
58pub trait DrizzleMySQLColumn: Sized {
59    /// The SQL type marker of the column, one of the markers in
60    /// [`crate::types`].
61    type SQLType: MySQLColumnType;
62
63    /// The column type written into the DDL, such as `BINARY(4)`, `TEXT` or
64    /// `BIGINT UNSIGNED`.
65    const SQL_TYPE: &'static str;
66
67    /// Decodes a value read from the database.
68    ///
69    /// # Errors
70    ///
71    /// Returns [`DrizzleError::ConversionError`] when `value` does not match
72    /// this column's storage representation.
73    fn decode(value: MySQLValue<'_>) -> Result<Self, DrizzleError>;
74
75    /// Encodes the value as a bind parameter, borrowing from `self` where
76    /// possible.
77    fn encode(&self) -> MySQLValue<'_>;
78
79    /// Encodes the value as an owned bind parameter.
80    ///
81    /// The default calls [`encode`](Self::encode) and copies the result.
82    /// Override it when the type can move its buffer into the value instead.
83    fn encode_owned(self) -> OwnedMySQLValue {
84        self.encode().into_owned()
85    }
86
87    /// Decodes a cell from the JSON produced by the relational query API
88    /// (`query` feature).
89    ///
90    /// The default converts the JSON to the value the driver would return
91    /// for [`SQLType`](Self::SQLType) and calls [`decode`](Self::decode).
92    /// Override it only when the JSON form differs from that.
93    ///
94    /// # Errors
95    ///
96    /// Returns [`DrizzleError::ConversionError`] if the JSON does not match
97    /// the column's type or [`decode`](Self::decode) rejects it.
98    #[cfg(feature = "query")]
99    fn decode_json(value: &serde_json::Value) -> Result<Self, DrizzleError>
100    where
101        Self::SQLType: MySQLColumnType,
102    {
103        let value = crate::driver::projected_value(
104            value,
105            <Self::SQLType as MySQLColumnType>::JSON_STORAGE,
106        )?;
107        Self::decode(value.into())
108    }
109}
110
111/// MySQL JSON representation used for a typed relational projection.
112#[cfg(feature = "query")]
113#[doc(hidden)]
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum MySQLJsonStorage {
116    Signed,
117    Unsigned,
118    Double,
119    Boolean,
120    Text,
121    SignedText,
122    UnsignedText,
123    FloatText,
124    Binary,
125    Json,
126    Date,
127    Time,
128    DateTime,
129}
130
131/// How the relational query API projects a built-in `MySQL` SQL type marker
132/// to JSON. Sealed.
133#[doc(hidden)]
134pub trait MySQLColumnType: drizzle_core::types::DataType + private::Sealed {
135    #[cfg(feature = "query")]
136    const JSON_PROJECTION: drizzle_core::query::JsonProjectionKind;
137    #[cfg(feature = "query")]
138    const JSON_STORAGE: MySQLJsonStorage;
139}
140
141mod private {
142    pub trait Sealed {}
143}
144
145macro_rules! mysql_column_types {
146    ($($type:ty => ($projection:ident, $storage:ident)),+ $(,)?) => {
147        $(
148            impl private::Sealed for $type {}
149            impl MySQLColumnType for $type {
150                #[cfg(feature = "query")]
151                const JSON_PROJECTION: drizzle_core::query::JsonProjectionKind =
152                    drizzle_core::query::JsonProjectionKind::$projection;
153                #[cfg(feature = "query")]
154                const JSON_STORAGE: MySQLJsonStorage = MySQLJsonStorage::$storage;
155            }
156        )+
157    };
158}
159
160mysql_column_types! {
161    crate::types::TinyInt => (Native, Signed),
162    crate::types::SmallInt => (Native, Signed),
163    crate::types::MediumInt => (Native, Signed),
164    crate::types::Int => (Native, Signed),
165    crate::types::TinyIntUnsigned => (Native, Unsigned),
166    crate::types::SmallIntUnsigned => (Native, Unsigned),
167    crate::types::MediumIntUnsigned => (Native, Unsigned),
168    crate::types::IntUnsigned => (Native, Unsigned),
169    crate::types::BigInt => (Text, SignedText),
170    crate::types::BigIntUnsigned => (Text, UnsignedText),
171    crate::types::Float => (Text, FloatText),
172    crate::types::Double => (Native, Double),
173    crate::types::Decimal => (Text, Text),
174    crate::types::Boolean => (Native, Boolean),
175    crate::types::Char => (Native, Text),
176    crate::types::Varchar => (Native, Text),
177    crate::types::TinyText => (Native, Text),
178    crate::types::Text => (Native, Text),
179    crate::types::MediumText => (Native, Text),
180    crate::types::LongText => (Native, Text),
181    crate::types::Binary => (TaggedHex, Binary),
182    crate::types::Varbinary => (TaggedHex, Binary),
183    crate::types::TinyBlob => (TaggedHex, Binary),
184    crate::types::Blob => (TaggedHex, Binary),
185    crate::types::MediumBlob => (TaggedHex, Binary),
186    crate::types::LongBlob => (TaggedHex, Binary),
187    crate::types::Json => (Native, Json),
188    crate::types::Date => (Text, Date),
189    crate::types::Time => (Text, Time),
190    crate::types::DateTime => (Text, DateTime),
191    crate::types::Timestamp => (Text, DateTime),
192    crate::types::Year => (Native, Unsigned),
193    crate::types::Enum => (Native, Text),
194    crate::types::Set => (Native, Text),
195    crate::types::Bit => (Unsigned, Unsigned),
196    crate::types::Any => (Native, Json),
197}