mssql_types/
value.rs

1//! SQL value representation.
2
3use bytes::Bytes;
4
5use crate::tvp::TvpData;
6
7/// A SQL value that can represent any SQL Server data type.
8///
9/// This enum provides a type-safe way to handle SQL values that may be
10/// of various types, including NULL.
11#[derive(Debug, Clone, PartialEq)]
12#[non_exhaustive]
13pub enum SqlValue {
14    /// NULL value.
15    Null,
16    /// Boolean value (BIT).
17    Bool(bool),
18    /// 8-bit unsigned integer (TINYINT).
19    TinyInt(u8),
20    /// 16-bit signed integer (SMALLINT).
21    SmallInt(i16),
22    /// 32-bit signed integer (INT).
23    Int(i32),
24    /// 64-bit signed integer (BIGINT).
25    BigInt(i64),
26    /// 32-bit floating point (REAL).
27    Float(f32),
28    /// 64-bit floating point (FLOAT).
29    Double(f64),
30    /// String value (CHAR, VARCHAR, NCHAR, NVARCHAR, TEXT, NTEXT).
31    String(String),
32    /// Binary value (BINARY, VARBINARY, IMAGE).
33    Binary(Bytes),
34    /// Decimal value (DECIMAL, NUMERIC, MONEY, SMALLMONEY).
35    #[cfg(feature = "decimal")]
36    Decimal(rust_decimal::Decimal),
37    /// UUID value (UNIQUEIDENTIFIER).
38    #[cfg(feature = "uuid")]
39    Uuid(uuid::Uuid),
40    /// Date value (DATE).
41    #[cfg(feature = "chrono")]
42    Date(chrono::NaiveDate),
43    /// Time value (TIME).
44    #[cfg(feature = "chrono")]
45    Time(chrono::NaiveTime),
46    /// DateTime value (DATETIME, DATETIME2, SMALLDATETIME).
47    #[cfg(feature = "chrono")]
48    DateTime(chrono::NaiveDateTime),
49    /// DateTimeOffset value (DATETIMEOFFSET).
50    #[cfg(feature = "chrono")]
51    DateTimeOffset(chrono::DateTime<chrono::FixedOffset>),
52    /// JSON value (JSON type in SQL Server 2016+).
53    #[cfg(feature = "json")]
54    Json(serde_json::Value),
55    /// XML value (XML type).
56    Xml(String),
57    /// Table-Valued Parameter (TVP).
58    ///
59    /// TVPs allow passing collections of structured data to SQL Server stored
60    /// procedures. Boxed due to large size.
61    Tvp(Box<TvpData>),
62}
63
64impl SqlValue {
65    /// Check if the value is NULL.
66    #[must_use]
67    pub fn is_null(&self) -> bool {
68        matches!(self, Self::Null)
69    }
70
71    /// Get the value as a bool, if it is one.
72    #[must_use]
73    pub fn as_bool(&self) -> Option<bool> {
74        match self {
75            Self::Bool(v) => Some(*v),
76            _ => None,
77        }
78    }
79
80    /// Get the value as an i32, if it is one.
81    #[must_use]
82    pub fn as_i32(&self) -> Option<i32> {
83        match self {
84            Self::Int(v) => Some(*v),
85            Self::SmallInt(v) => Some(*v as i32),
86            Self::TinyInt(v) => Some(*v as i32),
87            _ => None,
88        }
89    }
90
91    /// Get the value as an i64, if it is one.
92    #[must_use]
93    pub fn as_i64(&self) -> Option<i64> {
94        match self {
95            Self::BigInt(v) => Some(*v),
96            Self::Int(v) => Some(*v as i64),
97            Self::SmallInt(v) => Some(*v as i64),
98            Self::TinyInt(v) => Some(*v as i64),
99            _ => None,
100        }
101    }
102
103    /// Get the value as an f64, if it is one.
104    #[must_use]
105    pub fn as_f64(&self) -> Option<f64> {
106        match self {
107            Self::Double(v) => Some(*v),
108            Self::Float(v) => Some(*v as f64),
109            _ => None,
110        }
111    }
112
113    /// Get the value as a string slice, if it is one.
114    #[must_use]
115    pub fn as_str(&self) -> Option<&str> {
116        match self {
117            Self::String(v) => Some(v),
118            Self::Xml(v) => Some(v),
119            _ => None,
120        }
121    }
122
123    /// Get the value as bytes, if it is binary.
124    #[must_use]
125    pub fn as_bytes(&self) -> Option<&[u8]> {
126        match self {
127            Self::Binary(v) => Some(v),
128            _ => None,
129        }
130    }
131
132    /// Get the type name as a string.
133    #[must_use]
134    pub fn type_name(&self) -> &'static str {
135        match self {
136            Self::Null => "NULL",
137            Self::Bool(_) => "BIT",
138            Self::TinyInt(_) => "TINYINT",
139            Self::SmallInt(_) => "SMALLINT",
140            Self::Int(_) => "INT",
141            Self::BigInt(_) => "BIGINT",
142            Self::Float(_) => "REAL",
143            Self::Double(_) => "FLOAT",
144            Self::String(_) => "NVARCHAR",
145            Self::Binary(_) => "VARBINARY",
146            #[cfg(feature = "decimal")]
147            Self::Decimal(_) => "DECIMAL",
148            #[cfg(feature = "uuid")]
149            Self::Uuid(_) => "UNIQUEIDENTIFIER",
150            #[cfg(feature = "chrono")]
151            Self::Date(_) => "DATE",
152            #[cfg(feature = "chrono")]
153            Self::Time(_) => "TIME",
154            #[cfg(feature = "chrono")]
155            Self::DateTime(_) => "DATETIME2",
156            #[cfg(feature = "chrono")]
157            Self::DateTimeOffset(_) => "DATETIMEOFFSET",
158            #[cfg(feature = "json")]
159            Self::Json(_) => "JSON",
160            Self::Xml(_) => "XML",
161            Self::Tvp(_) => "TVP",
162        }
163    }
164
165    /// Get the value as a TVP, if it is one.
166    #[must_use]
167    pub fn as_tvp(&self) -> Option<&TvpData> {
168        match self {
169            Self::Tvp(v) => Some(v),
170            _ => None,
171        }
172    }
173}
174
175impl Default for SqlValue {
176    fn default() -> Self {
177        Self::Null
178    }
179}
180
181impl From<bool> for SqlValue {
182    fn from(v: bool) -> Self {
183        Self::Bool(v)
184    }
185}
186
187impl From<i32> for SqlValue {
188    fn from(v: i32) -> Self {
189        Self::Int(v)
190    }
191}
192
193impl From<i64> for SqlValue {
194    fn from(v: i64) -> Self {
195        Self::BigInt(v)
196    }
197}
198
199impl From<f32> for SqlValue {
200    fn from(v: f32) -> Self {
201        Self::Float(v)
202    }
203}
204
205impl From<f64> for SqlValue {
206    fn from(v: f64) -> Self {
207        Self::Double(v)
208    }
209}
210
211impl From<String> for SqlValue {
212    fn from(v: String) -> Self {
213        Self::String(v)
214    }
215}
216
217impl From<&str> for SqlValue {
218    fn from(v: &str) -> Self {
219        Self::String(v.to_owned())
220    }
221}
222
223impl<T> From<Option<T>> for SqlValue
224where
225    T: Into<SqlValue>,
226{
227    fn from(v: Option<T>) -> Self {
228        match v {
229            Some(v) => v.into(),
230            None => Self::Null,
231        }
232    }
233}
234
235#[cfg(feature = "uuid")]
236impl From<uuid::Uuid> for SqlValue {
237    fn from(v: uuid::Uuid) -> Self {
238        Self::Uuid(v)
239    }
240}
241
242#[cfg(feature = "decimal")]
243impl From<rust_decimal::Decimal> for SqlValue {
244    fn from(v: rust_decimal::Decimal) -> Self {
245        Self::Decimal(v)
246    }
247}
248
249#[cfg(feature = "chrono")]
250impl From<chrono::NaiveDate> for SqlValue {
251    fn from(v: chrono::NaiveDate) -> Self {
252        Self::Date(v)
253    }
254}
255
256#[cfg(feature = "chrono")]
257impl From<chrono::NaiveDateTime> for SqlValue {
258    fn from(v: chrono::NaiveDateTime) -> Self {
259        Self::DateTime(v)
260    }
261}
262
263#[cfg(feature = "json")]
264impl From<serde_json::Value> for SqlValue {
265    fn from(v: serde_json::Value) -> Self {
266        Self::Json(v)
267    }
268}
269
270impl From<TvpData> for SqlValue {
271    fn from(v: TvpData) -> Self {
272        Self::Tvp(Box::new(v))
273    }
274}