Skip to main content

drizzle_sqlite/values/
mod.rs

1//! `SQLite` value types and conversions
2//!
3//! This module contains the core `SQLiteValue` type and all its conversions.
4
5mod conversions;
6mod drivers;
7mod insert;
8#[cfg(feature = "serde")]
9mod json;
10pub mod owned;
11mod update;
12
13pub use insert::*;
14pub use owned::*;
15pub use update::*;
16
17use crate::prelude::*;
18use crate::traits::FromSQLiteValue;
19use drizzle_core::{dialect::Dialect, error::DrizzleError, sql::SQL, traits::SQLParam};
20
21//------------------------------------------------------------------------------
22// SQLiteValue Definition
23//------------------------------------------------------------------------------
24
25/// Represents a `SQLite` value
26#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
27pub enum SQLiteValue<'a> {
28    /// Integer value (i64)
29    Integer(i64),
30    /// Real value (f64)
31    Real(f64),
32    /// Text value (borrowed or owned string)
33    Text(Cow<'a, str>),
34    /// Blob value (borrowed or owned binary data)
35    Blob(Cow<'a, [u8]>),
36    /// NULL value
37    #[default]
38    Null,
39}
40
41/// Borrowed view of a `SQLite` value.
42///
43/// This is the zero-copy read-side representation used by custom column
44/// decoders. Text and blob payloads borrow directly from the driver row or
45/// from an existing [`SQLiteValue`].
46#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
47pub enum SQLiteValueRef<'a> {
48    /// Integer value (i64)
49    Integer(i64),
50    /// Real value (f64)
51    Real(f64),
52    /// Text value
53    Text(&'a str),
54    /// Blob value
55    Blob(&'a [u8]),
56    /// NULL value
57    #[default]
58    Null,
59}
60
61impl<'a> SQLiteValueRef<'a> {
62    /// Converts this borrowed value into a `SQLiteValue`.
63    #[inline]
64    #[must_use]
65    pub const fn into_value(self) -> SQLiteValue<'a> {
66        match self {
67            Self::Integer(value) => SQLiteValue::Integer(value),
68            Self::Real(value) => SQLiteValue::Real(value),
69            Self::Text(value) => SQLiteValue::Text(Cow::Borrowed(value)),
70            Self::Blob(value) => SQLiteValue::Blob(Cow::Borrowed(value)),
71            Self::Null => SQLiteValue::Null,
72        }
73    }
74
75    /// Converts a rusqlite borrowed value into a dialect-neutral borrowed
76    /// value.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`DrizzleError::ConversionError`] if a `TEXT` value is not valid
81    /// UTF-8.
82    #[cfg(feature = "rusqlite")]
83    #[inline]
84    pub fn try_from_rusqlite_value_ref(
85        value: ::rusqlite::types::ValueRef<'a>,
86    ) -> Result<Self, DrizzleError> {
87        match value {
88            ::rusqlite::types::ValueRef::Null => Ok(Self::Null),
89            ::rusqlite::types::ValueRef::Integer(value) => Ok(Self::Integer(value)),
90            ::rusqlite::types::ValueRef::Real(value) => Ok(Self::Real(value)),
91            ::rusqlite::types::ValueRef::Text(value) => {
92                let value = core::str::from_utf8(value).map_err(|e| {
93                    DrizzleError::ConversionError(format!("invalid UTF-8: {e}").into())
94                })?;
95                Ok(Self::Text(value))
96            }
97            ::rusqlite::types::ValueRef::Blob(value) => Ok(Self::Blob(value)),
98        }
99    }
100}
101
102impl<'a> From<SQLiteValueRef<'a>> for SQLiteValue<'a> {
103    #[inline]
104    fn from(value: SQLiteValueRef<'a>) -> Self {
105        value.into_value()
106    }
107}
108
109impl<'a> From<&'a SQLiteValue<'_>> for SQLiteValueRef<'a> {
110    #[inline]
111    fn from(value: &'a SQLiteValue<'_>) -> Self {
112        value.as_ref()
113    }
114}
115
116impl SQLiteValue<'_> {
117    /// Returns true if this value is NULL.
118    #[inline]
119    #[must_use]
120    pub const fn is_null(&self) -> bool {
121        matches!(self, SQLiteValue::Null)
122    }
123
124    /// Returns the integer value if this is an INTEGER.
125    #[inline]
126    #[must_use]
127    pub const fn as_i64(&self) -> Option<i64> {
128        match self {
129            SQLiteValue::Integer(value) => Some(*value),
130            _ => None,
131        }
132    }
133
134    /// Returns the real value if this is a REAL.
135    #[inline]
136    #[must_use]
137    pub const fn as_f64(&self) -> Option<f64> {
138        match self {
139            SQLiteValue::Real(value) => Some(*value),
140            _ => None,
141        }
142    }
143
144    /// Returns the text value if this is TEXT.
145    #[inline]
146    #[must_use]
147    pub fn as_str(&self) -> Option<&str> {
148        match self {
149            SQLiteValue::Text(value) => Some(value.as_ref()),
150            _ => None,
151        }
152    }
153
154    /// Returns the blob value if this is BLOB.
155    #[inline]
156    #[must_use]
157    pub fn as_bytes(&self) -> Option<&[u8]> {
158        match self {
159            SQLiteValue::Blob(value) => Some(value.as_ref()),
160            _ => None,
161        }
162    }
163
164    /// Returns a borrowed view of this value.
165    #[inline]
166    #[must_use]
167    pub fn as_ref(&self) -> SQLiteValueRef<'_> {
168        match self {
169            SQLiteValue::Integer(value) => SQLiteValueRef::Integer(*value),
170            SQLiteValue::Real(value) => SQLiteValueRef::Real(*value),
171            SQLiteValue::Text(value) => SQLiteValueRef::Text(value.as_ref()),
172            SQLiteValue::Blob(value) => SQLiteValueRef::Blob(value.as_ref()),
173            SQLiteValue::Null => SQLiteValueRef::Null,
174        }
175    }
176
177    /// Converts this value into an owned representation.
178    #[inline]
179    #[must_use]
180    pub fn into_owned(self) -> OwnedSQLiteValue {
181        self.into()
182    }
183
184    /// Convert this `SQLite` value to a Rust type using the `FromSQLiteValue` trait.
185    ///
186    /// This provides a unified conversion interface for all types that implement
187    /// `FromSQLiteValue`, including primitives and enum types.
188    ///
189    /// # Errors
190    ///
191    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
192    /// be decoded into `T`.
193    ///
194    /// # Example
195    /// ```rust
196    /// # let _ = r####"
197    /// let value = SQLiteValue::Integer(42);
198    /// let num: i64 = value.convert()?;
199    /// # "####;
200    /// ```
201    pub fn convert<T: FromSQLiteValue>(self) -> Result<T, DrizzleError> {
202        T::from_sqlite_ref(self.as_ref())
203    }
204
205    /// Convert a reference to this `SQLite` value to a Rust type.
206    ///
207    /// # Errors
208    ///
209    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
210    /// be decoded into `T`.
211    pub fn convert_ref<T: FromSQLiteValue>(&self) -> Result<T, DrizzleError> {
212        T::from_sqlite_ref(self.as_ref())
213    }
214}
215
216impl core::fmt::Display for SQLiteValue<'_> {
217    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218        let value = match self {
219            SQLiteValue::Integer(i) => i.to_string(),
220            SQLiteValue::Real(r) => r.to_string(),
221            SQLiteValue::Text(cow) => cow.to_string(),
222            SQLiteValue::Blob(cow) => String::from_utf8_lossy(cow).to_string(),
223            SQLiteValue::Null => String::new(),
224        };
225        write!(f, "{value}")
226    }
227}
228
229// Implement core traits required by Drizzle
230impl SQLParam for SQLiteValue<'_> {
231    const DIALECT: Dialect = Dialect::SQLite;
232    type DialectMarker = drizzle_core::dialect::SQLiteDialect;
233
234    fn write_literal(&self, buf: &mut String) -> bool {
235        use core::fmt::Write;
236        match self {
237            SQLiteValue::Null => buf.push_str("NULL"),
238            SQLiteValue::Integer(value) => {
239                let _ = write!(buf, "{value}");
240            }
241            // `{:?}` keeps a decimal point or exponent, so SQLite reads a
242            // REAL rather than an INTEGER. SQLite has no NaN literal, and
243            // stores NaN as NULL anyway.
244            SQLiteValue::Real(value) if value.is_nan() => return false,
245            SQLiteValue::Real(value) if value.is_infinite() => {
246                buf.push_str(if value.is_sign_positive() {
247                    "9e999"
248                } else {
249                    "-9e999"
250                });
251            }
252            SQLiteValue::Real(value) => {
253                let _ = write!(buf, "{value:?}");
254            }
255            SQLiteValue::Text(text) if text.contains('\0') => return false,
256            SQLiteValue::Text(text) => {
257                buf.push('\'');
258                buf.push_str(&text.replace('\'', "''"));
259                buf.push('\'');
260            }
261            SQLiteValue::Blob(bytes) => {
262                buf.push_str("X'");
263                for byte in bytes.iter() {
264                    let _ = write!(buf, "{byte:02X}");
265                }
266                buf.push('\'');
267            }
268        }
269        true
270    }
271}
272
273impl<'a> From<SQLiteValue<'a>> for SQL<'a, SQLiteValue<'a>> {
274    fn from(value: SQLiteValue<'a>) -> Self {
275        SQL::param(value)
276    }
277}
278
279impl FromIterator<OwnedSQLiteValue> for Vec<SQLiteValue<'_>> {
280    fn from_iter<T: IntoIterator<Item = OwnedSQLiteValue>>(iter: T) -> Self {
281        iter.into_iter().map(SQLiteValue::from).collect()
282    }
283}
284
285impl<'a> FromIterator<&'a OwnedSQLiteValue> for Vec<SQLiteValue<'a>> {
286    fn from_iter<T: IntoIterator<Item = &'a OwnedSQLiteValue>>(iter: T) -> Self {
287        iter.into_iter().map(SQLiteValue::from).collect()
288    }
289}