Skip to main content

kimberlite_query/
value.rs

1//! Typed SQL values.
2
3#![allow(clippy::match_same_arms)]
4
5use std::cmp::Ordering;
6use std::fmt::{self, Display};
7
8use bytes::Bytes;
9use kimberlite_types::Timestamp;
10use serde::{Deserialize, Serialize};
11
12use crate::error::{QueryError, Result};
13use crate::schema::DataType;
14
15/// A typed SQL value.
16///
17/// Represents values that can appear in query parameters, row data,
18/// and comparison predicates.
19///
20/// Note: Real and Decimal types use total ordering (NaN < -Inf < values < Inf)
21/// for comparisons to enable use in B+tree indexes.
22#[derive(Debug, Clone, Default, Serialize, Deserialize)]
23#[serde(untagged)]
24pub enum Value {
25    /// SQL NULL.
26    #[default]
27    Null,
28
29    // ===== Integer Types =====
30    /// 8-bit signed integer (-128 to 127).
31    TinyInt(i8),
32    /// 16-bit signed integer (-32,768 to 32,767).
33    SmallInt(i16),
34    /// 32-bit signed integer (-2^31 to 2^31-1).
35    Integer(i32),
36    /// 64-bit signed integer (-2^63 to 2^63-1).
37    BigInt(i64),
38
39    // ===== Numeric Types =====
40    /// 64-bit floating point (IEEE 754 double precision).
41    Real(f64),
42    /// Fixed-precision decimal (value in smallest units, scale).
43    ///
44    /// Stored as (i128, u8) where the second field is the scale.
45    /// Example: Decimal(12345, 2) represents 123.45
46    #[serde(skip)] // Complex serialization, handled separately
47    Decimal(i128, u8),
48
49    // ===== String Types =====
50    /// UTF-8 text string.
51    Text(String),
52
53    // ===== Binary Types =====
54    /// Raw bytes (base64 encoded in JSON).
55    #[serde(with = "bytes_base64")]
56    Bytes(Bytes),
57
58    // ===== Boolean Type =====
59    /// Boolean value.
60    Boolean(bool),
61
62    // ===== Date/Time Types =====
63    /// Date (days since Unix epoch).
64    Date(i32),
65    /// Time of day (nanoseconds within day).
66    Time(i64),
67    /// Timestamp (nanoseconds since Unix epoch).
68    Timestamp(Timestamp),
69
70    // ===== Structured Types =====
71    /// UUID (RFC 4122, 128-bit).
72    Uuid([u8; 16]),
73    /// JSON document (validated).
74    Json(serde_json::Value),
75
76    /// Parameter placeholder ($1, $2, etc.) - 1-indexed.
77    /// This is an intermediate representation used during parsing,
78    /// and should be bound to actual values before execution.
79    #[serde(skip)]
80    Placeholder(usize),
81}
82
83impl PartialEq for Value {
84    fn eq(&self, other: &Self) -> bool {
85        match (self, other) {
86            (Value::Null, Value::Null) => true,
87            (Value::TinyInt(a), Value::TinyInt(b)) => a == b,
88            (Value::SmallInt(a), Value::SmallInt(b)) => a == b,
89            (Value::Integer(a), Value::Integer(b)) => a == b,
90            (Value::BigInt(a), Value::BigInt(b)) => a == b,
91            (Value::Real(a), Value::Real(b)) => {
92                // Use total ordering for floats: NaN == NaN
93                a.to_bits() == b.to_bits()
94            }
95            (Value::Decimal(a_val, a_scale), Value::Decimal(b_val, b_scale)) => {
96                a_val == b_val && a_scale == b_scale
97            }
98            (Value::Text(a), Value::Text(b)) => a == b,
99            (Value::Bytes(a), Value::Bytes(b)) => a == b,
100            (Value::Boolean(a), Value::Boolean(b)) => a == b,
101            (Value::Date(a), Value::Date(b)) => a == b,
102            (Value::Time(a), Value::Time(b)) => a == b,
103            (Value::Timestamp(a), Value::Timestamp(b)) => a == b,
104            (Value::Uuid(a), Value::Uuid(b)) => a == b,
105            (Value::Json(a), Value::Json(b)) => a == b,
106            (Value::Placeholder(a), Value::Placeholder(b)) => a == b,
107            _ => false, // Different types are not equal
108        }
109    }
110}
111
112impl Eq for Value {}
113
114impl std::hash::Hash for Value {
115    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
116        // Hash the discriminant first
117        std::mem::discriminant(self).hash(state);
118
119        // Hash the value based on type
120        match self {
121            Value::Null => {}
122            Value::TinyInt(v) => v.hash(state),
123            Value::SmallInt(v) => v.hash(state),
124            Value::Integer(v) => v.hash(state),
125            Value::BigInt(v) => v.hash(state),
126            Value::Real(v) => v.to_bits().hash(state), // Use total ordering
127            Value::Decimal(val, scale) => {
128                val.hash(state);
129                scale.hash(state);
130            }
131            Value::Text(v) => v.hash(state),
132            Value::Bytes(v) => v.hash(state),
133            Value::Boolean(v) => v.hash(state),
134            Value::Date(v) => v.hash(state),
135            Value::Time(v) => v.hash(state),
136            Value::Timestamp(v) => v.hash(state),
137            Value::Uuid(v) => v.hash(state),
138            Value::Json(v) => v.to_string().hash(state), // Hash JSON string representation
139            Value::Placeholder(v) => v.hash(state),
140        }
141    }
142}
143
144/// Parses a decimal string like "123.45" with a given scale.
145fn parse_decimal_string(s: &str, scale: u8) -> Result<i128> {
146    let parts: Vec<&str> = s.split('.').collect();
147    match parts.as_slice() {
148        [int_part] => {
149            // No decimal point: "123" -> 12300 (with scale=2)
150            let int_val: i128 = int_part.parse().map_err(|_| QueryError::TypeMismatch {
151                expected: format!("decimal with scale {scale}"),
152                actual: s.to_string(),
153            })?;
154            Ok(int_val * 10_i128.pow(u32::from(scale)))
155        }
156        [int_part, frac_part] => {
157            // With decimal point: "123.45" -> 12345 (with scale=2)
158            let int_val: i128 = int_part.parse().map_err(|_| QueryError::TypeMismatch {
159                expected: format!("decimal with scale {scale}"),
160                actual: s.to_string(),
161            })?;
162
163            // Pad or truncate fractional part to match scale
164            let mut frac_str = (*frac_part).to_string();
165            if frac_str.len() > scale as usize {
166                frac_str.truncate(scale as usize);
167            } else {
168                frac_str.push_str(&"0".repeat(scale as usize - frac_str.len()));
169            }
170
171            let frac_val: i128 = frac_str.parse().map_err(|_| QueryError::TypeMismatch {
172                expected: format!("decimal with scale {scale}"),
173                actual: s.to_string(),
174            })?;
175
176            let multiplier = 10_i128.pow(u32::from(scale));
177            // For negative decimals like "-1234.56", the fractional part must be
178            // subtracted (not added) to extend the magnitude away from zero.
179            // Use the original string's sign to handle "-0.xx" correctly.
180            let is_negative = s.starts_with('-');
181            if is_negative {
182                Ok(int_val * multiplier - frac_val)
183            } else {
184                Ok(int_val * multiplier + frac_val)
185            }
186        }
187        _ => Err(QueryError::TypeMismatch {
188            expected: format!("decimal with scale {scale}"),
189            actual: s.to_string(),
190        }),
191    }
192}
193
194/// Parses a UUID string (hyphenated or raw hex).
195fn parse_uuid_string(s: &str) -> Result<[u8; 16]> {
196    // Remove hyphens if present
197    let hex_str = s.replace('-', "");
198
199    if hex_str.len() != 32 {
200        return Err(QueryError::TypeMismatch {
201            expected: "UUID (32 hex digits)".to_string(),
202            actual: s.to_string(),
203        });
204    }
205
206    let mut bytes = [0u8; 16];
207    for (i, chunk) in hex_str.as_bytes().chunks(2).enumerate() {
208        let hex_byte = std::str::from_utf8(chunk).map_err(|_| QueryError::TypeMismatch {
209            expected: "UUID (valid hex)".to_string(),
210            actual: s.to_string(),
211        })?;
212
213        bytes[i] = u8::from_str_radix(hex_byte, 16).map_err(|_| QueryError::TypeMismatch {
214            expected: "UUID (valid hex)".to_string(),
215            actual: s.to_string(),
216        })?;
217    }
218
219    Ok(bytes)
220}
221
222/// Total ordering for f64 values.
223///
224/// NaN < -Inf < negative values < -0.0 < +0.0 < positive values < +Inf
225///
226/// This enables f64 values to be used as B+tree keys.
227fn total_cmp_f64(a: f64, b: f64) -> Ordering {
228    // Use bit representation for total ordering
229    let a_bits = a.to_bits();
230    let b_bits = b.to_bits();
231
232    // Flip sign bit for negatives to get correct ordering
233    let a_key = if a.is_sign_negative() {
234        !a_bits
235    } else {
236        a_bits ^ (1u64 << 63)
237    };
238
239    let b_key = if b.is_sign_negative() {
240        !b_bits
241    } else {
242        b_bits ^ (1u64 << 63)
243    };
244
245    a_key.cmp(&b_key)
246}
247
248impl Value {
249    /// Returns the data type of this value.
250    ///
251    /// Returns `None` for `Null` and `Placeholder` since they have no concrete type.
252    pub fn data_type(&self) -> Option<DataType> {
253        match self {
254            Value::Null | Value::Placeholder(_) => None,
255            Value::TinyInt(_) => Some(DataType::TinyInt),
256            Value::SmallInt(_) => Some(DataType::SmallInt),
257            Value::Integer(_) => Some(DataType::Integer),
258            Value::BigInt(_) => Some(DataType::BigInt),
259            Value::Real(_) => Some(DataType::Real),
260            Value::Decimal(_, scale) => Some(DataType::Decimal {
261                precision: 38, // Max precision for i128
262                scale: *scale,
263            }),
264            Value::Text(_) => Some(DataType::Text),
265            Value::Bytes(_) => Some(DataType::Bytes),
266            Value::Boolean(_) => Some(DataType::Boolean),
267            Value::Date(_) => Some(DataType::Date),
268            Value::Time(_) => Some(DataType::Time),
269            Value::Timestamp(_) => Some(DataType::Timestamp),
270            Value::Uuid(_) => Some(DataType::Uuid),
271            Value::Json(_) => Some(DataType::Json),
272        }
273    }
274
275    /// Returns true if this value is NULL.
276    pub fn is_null(&self) -> bool {
277        matches!(self, Value::Null)
278    }
279
280    /// Returns the value as an i64, if it is a `BigInt`.
281    pub fn as_bigint(&self) -> Option<i64> {
282        match self {
283            Value::BigInt(v) => Some(*v),
284            _ => None,
285        }
286    }
287
288    /// Returns the value as a string slice, if it is Text.
289    pub fn as_text(&self) -> Option<&str> {
290        match self {
291            Value::Text(s) => Some(s),
292            _ => None,
293        }
294    }
295
296    /// Returns the value as a bool, if it is Boolean.
297    pub fn as_boolean(&self) -> Option<bool> {
298        match self {
299            Value::Boolean(b) => Some(*b),
300            _ => None,
301        }
302    }
303
304    /// Returns the value as a Timestamp, if it is Timestamp.
305    pub fn as_timestamp(&self) -> Option<Timestamp> {
306        match self {
307            Value::Timestamp(ts) => Some(*ts),
308            _ => None,
309        }
310    }
311
312    /// Returns the value as bytes, if it is Bytes.
313    pub fn as_bytes(&self) -> Option<&Bytes> {
314        match self {
315            Value::Bytes(b) => Some(b),
316            _ => None,
317        }
318    }
319
320    /// Returns the value as an i8, if it is a `TinyInt`.
321    pub fn as_tinyint(&self) -> Option<i8> {
322        match self {
323            Value::TinyInt(v) => Some(*v),
324            _ => None,
325        }
326    }
327
328    /// Returns the value as an i16, if it is a `SmallInt`.
329    pub fn as_smallint(&self) -> Option<i16> {
330        match self {
331            Value::SmallInt(v) => Some(*v),
332            _ => None,
333        }
334    }
335
336    /// Returns the value as an i32, if it is an `Integer`.
337    pub fn as_integer(&self) -> Option<i32> {
338        match self {
339            Value::Integer(v) => Some(*v),
340            _ => None,
341        }
342    }
343
344    /// Returns the value as an f64, if it is a `Real`.
345    pub fn as_real(&self) -> Option<f64> {
346        match self {
347            Value::Real(v) => Some(*v),
348            _ => None,
349        }
350    }
351
352    /// Returns the value as a `Decimal` (value, scale), if it is a `Decimal`.
353    pub fn as_decimal(&self) -> Option<(i128, u8)> {
354        match self {
355            Value::Decimal(v, s) => Some((*v, *s)),
356            _ => None,
357        }
358    }
359
360    /// Returns the value as a `Uuid`, if it is a `Uuid`.
361    pub fn as_uuid(&self) -> Option<&[u8; 16]> {
362        match self {
363            Value::Uuid(u) => Some(u),
364            _ => None,
365        }
366    }
367
368    /// Returns the value as a `Json`, if it is a `Json`.
369    pub fn as_json(&self) -> Option<&serde_json::Value> {
370        match self {
371            Value::Json(j) => Some(j),
372            _ => None,
373        }
374    }
375
376    /// Returns the value as a `Date`, if it is a `Date`.
377    pub fn as_date(&self) -> Option<i32> {
378        match self {
379            Value::Date(d) => Some(*d),
380            _ => None,
381        }
382    }
383
384    /// Returns the value as a `Time`, if it is a `Time`.
385    pub fn as_time(&self) -> Option<i64> {
386        match self {
387            Value::Time(t) => Some(*t),
388            _ => None,
389        }
390    }
391
392    /// Compares two values for ordering.
393    ///
394    /// NULL values are considered less than all non-NULL values.
395    /// Values of different types return None (incomparable).
396    ///
397    /// For `Real` values, uses total ordering: NaN < -Inf < values < Inf.
398    pub fn compare(&self, other: &Value) -> Option<Ordering> {
399        match (self, other) {
400            (Value::Null, Value::Null) => Some(Ordering::Equal),
401            (Value::Null, _) => Some(Ordering::Less),
402            (_, Value::Null) => Some(Ordering::Greater),
403            (Value::TinyInt(a), Value::TinyInt(b)) => Some(a.cmp(b)),
404            (Value::SmallInt(a), Value::SmallInt(b)) => Some(a.cmp(b)),
405            (Value::Integer(a), Value::Integer(b)) => Some(a.cmp(b)),
406            (Value::BigInt(a), Value::BigInt(b)) => Some(a.cmp(b)),
407            (Value::Real(a), Value::Real(b)) => Some(total_cmp_f64(*a, *b)),
408            (Value::Decimal(a_val, a_scale), Value::Decimal(b_val, b_scale)) => {
409                // Only compare if same scale
410                if a_scale == b_scale {
411                    Some(a_val.cmp(b_val))
412                } else {
413                    None
414                }
415            }
416            (Value::Text(a), Value::Text(b)) => Some(a.cmp(b)),
417            (Value::Bytes(a), Value::Bytes(b)) => Some(a.as_ref().cmp(b.as_ref())),
418            (Value::Boolean(a), Value::Boolean(b)) => Some(a.cmp(b)),
419            (Value::Date(a), Value::Date(b)) => Some(a.cmp(b)),
420            (Value::Time(a), Value::Time(b)) => Some(a.cmp(b)),
421            (Value::Timestamp(a), Value::Timestamp(b)) => Some(a.cmp(b)),
422            (Value::Uuid(a), Value::Uuid(b)) => Some(a.cmp(b)),
423            (Value::Json(a), Value::Json(b)) => {
424                // JSON comparison is complex, use string representation
425                Some(a.to_string().cmp(&b.to_string()))
426            }
427            _ => None, // Different types are incomparable
428        }
429    }
430
431    /// Checks if this value can be assigned to a column of the given type.
432    pub fn is_compatible_with(&self, data_type: DataType) -> bool {
433        match self {
434            Value::Null => true,           // NULL is compatible with any type
435            Value::Placeholder(_) => true, // Placeholder will be bound to actual value
436            Value::TinyInt(_) => data_type == DataType::TinyInt,
437            Value::SmallInt(_) => data_type == DataType::SmallInt,
438            Value::Integer(_) => data_type == DataType::Integer,
439            Value::BigInt(_) => data_type == DataType::BigInt,
440            Value::Real(_) => data_type == DataType::Real,
441            Value::Decimal(_, scale) => {
442                matches!(data_type, DataType::Decimal { scale: s, .. } if s == *scale)
443            }
444            Value::Text(_) => data_type == DataType::Text,
445            Value::Bytes(_) => data_type == DataType::Bytes,
446            Value::Boolean(_) => data_type == DataType::Boolean,
447            Value::Date(_) => data_type == DataType::Date,
448            Value::Time(_) => data_type == DataType::Time,
449            Value::Timestamp(_) => data_type == DataType::Timestamp,
450            Value::Uuid(_) => data_type == DataType::Uuid,
451            Value::Json(_) => data_type == DataType::Json,
452        }
453    }
454
455    /// Converts this value to JSON.
456    ///
457    /// # Panics
458    ///
459    /// Panics if the value is a `Placeholder` (should be bound before conversion).
460    pub fn to_json(&self) -> serde_json::Value {
461        match self {
462            Value::Null => serde_json::Value::Null,
463            Value::TinyInt(v) => serde_json::Value::Number((*v).into()),
464            Value::SmallInt(v) => serde_json::Value::Number((*v).into()),
465            Value::Integer(v) => serde_json::Value::Number((*v).into()),
466            Value::BigInt(v) => serde_json::Value::Number((*v).into()),
467            Value::Real(v) => {
468                serde_json::Number::from_f64(*v)
469                    .map_or(serde_json::Value::Null, serde_json::Value::Number) // NaN/Inf become null
470            }
471            Value::Decimal(val, scale) => {
472                // Convert to string representation
473                let divisor = 10_i128.pow(u32::from(*scale));
474                let int_part = val / divisor;
475                let frac_part = (val % divisor).abs();
476                let s = format!("{int_part}.{frac_part:0width$}", width = *scale as usize);
477                serde_json::Value::String(s)
478            }
479            Value::Text(s) => serde_json::Value::String(s.clone()),
480            Value::Bytes(b) => {
481                use base64::Engine;
482                let encoded = base64::engine::general_purpose::STANDARD.encode(b);
483                serde_json::Value::String(encoded)
484            }
485            Value::Boolean(b) => serde_json::Value::Bool(*b),
486            Value::Date(d) => serde_json::Value::Number((*d).into()),
487            Value::Time(t) => serde_json::Value::Number((*t).into()),
488            Value::Timestamp(ts) => serde_json::Value::Number(ts.as_nanos().into()),
489            Value::Uuid(u) => {
490                // Format as RFC 4122 hyphenated string
491                let s = format!(
492                    "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
493                    u[0],
494                    u[1],
495                    u[2],
496                    u[3],
497                    u[4],
498                    u[5],
499                    u[6],
500                    u[7],
501                    u[8],
502                    u[9],
503                    u[10],
504                    u[11],
505                    u[12],
506                    u[13],
507                    u[14],
508                    u[15]
509                );
510                serde_json::Value::String(s)
511            }
512            Value::Json(j) => j.clone(),
513            Value::Placeholder(idx) => {
514                panic!("Cannot convert unbound placeholder ${idx} to JSON - bind parameters first")
515            }
516        }
517    }
518
519    /// Parses a value from JSON with an expected data type.
520    pub fn from_json(json: &serde_json::Value, data_type: DataType) -> Result<Self> {
521        match (json, data_type) {
522            (serde_json::Value::Null, _) => Ok(Value::Null),
523            (serde_json::Value::Number(n), DataType::TinyInt) => n
524                .as_i64()
525                .and_then(|v| i8::try_from(v).ok())
526                .map(Value::TinyInt)
527                .ok_or_else(|| QueryError::TypeMismatch {
528                    expected: "tinyint (-128 to 127)".to_string(),
529                    actual: format!("number {n}"),
530                }),
531            (serde_json::Value::Number(n), DataType::SmallInt) => n
532                .as_i64()
533                .and_then(|v| i16::try_from(v).ok())
534                .map(Value::SmallInt)
535                .ok_or_else(|| QueryError::TypeMismatch {
536                    expected: "smallint (-32768 to 32767)".to_string(),
537                    actual: format!("number {n}"),
538                }),
539            (serde_json::Value::Number(n), DataType::Integer) => n
540                .as_i64()
541                .and_then(|v| i32::try_from(v).ok())
542                .map(Value::Integer)
543                .ok_or_else(|| QueryError::TypeMismatch {
544                    expected: "integer (-2^31 to 2^31-1)".to_string(),
545                    actual: format!("number {n}"),
546                }),
547            (serde_json::Value::Number(n), DataType::BigInt) => n
548                .as_i64()
549                .map(Value::BigInt)
550                .ok_or_else(|| QueryError::TypeMismatch {
551                    expected: "bigint".to_string(),
552                    actual: format!("number {n}"),
553                }),
554            (serde_json::Value::Number(n), DataType::Real) => n
555                .as_f64()
556                .map(Value::Real)
557                .ok_or_else(|| QueryError::TypeMismatch {
558                    expected: "real (f64)".to_string(),
559                    actual: format!("number {n}"),
560                }),
561            (
562                serde_json::Value::String(s),
563                DataType::Decimal {
564                    precision: _,
565                    scale,
566                },
567            ) => {
568                // Parse decimal string like "123.45"
569                parse_decimal_string(s, scale).map(|val| Value::Decimal(val, scale))
570            }
571            (serde_json::Value::String(s), DataType::Text) => Ok(Value::Text(s.clone())),
572            (serde_json::Value::String(s), DataType::Bytes) => {
573                use base64::Engine;
574                let decoded = base64::engine::general_purpose::STANDARD
575                    .decode(s)
576                    .map_err(|e| QueryError::TypeMismatch {
577                        expected: "base64 bytes".to_string(),
578                        actual: e.to_string(),
579                    })?;
580                Ok(Value::Bytes(Bytes::from(decoded)))
581            }
582            (serde_json::Value::Bool(b), DataType::Boolean) => Ok(Value::Boolean(*b)),
583            (serde_json::Value::Number(n), DataType::Date) => n
584                .as_i64()
585                .and_then(|v| i32::try_from(v).ok())
586                .map(Value::Date)
587                .ok_or_else(|| QueryError::TypeMismatch {
588                    expected: "date (i32 days)".to_string(),
589                    actual: format!("number {n}"),
590                }),
591            (serde_json::Value::Number(n), DataType::Time) => n
592                .as_i64()
593                .map(Value::Time)
594                .ok_or_else(|| QueryError::TypeMismatch {
595                    expected: "time (i64 nanos)".to_string(),
596                    actual: format!("number {n}"),
597                }),
598            (serde_json::Value::Number(n), DataType::Timestamp) => n
599                .as_u64()
600                .map(|nanos| Value::Timestamp(Timestamp::from_nanos(nanos)))
601                .ok_or_else(|| QueryError::TypeMismatch {
602                    expected: "timestamp".to_string(),
603                    actual: format!("number {n}"),
604                }),
605            (serde_json::Value::String(s), DataType::Uuid) => parse_uuid_string(s).map(Value::Uuid),
606            (
607                json @ (serde_json::Value::Object(_)
608                | serde_json::Value::Array(_)
609                | serde_json::Value::String(_)
610                | serde_json::Value::Number(_)
611                | serde_json::Value::Bool(_)),
612                DataType::Json,
613            ) => Ok(Value::Json(json.clone())),
614            (json, dt) => Err(QueryError::TypeMismatch {
615                expected: format!("{dt:?}"),
616                actual: format!("{json:?}"),
617            }),
618        }
619    }
620}
621
622impl Display for Value {
623    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624        match self {
625            Value::Null => write!(f, "NULL"),
626            Value::TinyInt(v) => write!(f, "{v}"),
627            Value::SmallInt(v) => write!(f, "{v}"),
628            Value::Integer(v) => write!(f, "{v}"),
629            Value::BigInt(v) => write!(f, "{v}"),
630            Value::Real(v) => write!(f, "{v}"),
631            Value::Decimal(val, scale) => {
632                let divisor = 10_i128.pow(u32::from(*scale));
633                let int_part = val / divisor;
634                let frac_part = (val % divisor).abs();
635                write!(f, "{int_part}.{frac_part:0width$}", width = *scale as usize)
636            }
637            Value::Text(s) => write!(f, "'{s}'"),
638            Value::Bytes(b) => write!(f, "<{} bytes>", b.len()),
639            Value::Boolean(b) => write!(f, "{b}"),
640            Value::Date(d) => write!(f, "DATE({d})"),
641            Value::Time(t) => write!(f, "TIME({t})"),
642            Value::Timestamp(ts) => write!(f, "{ts}"),
643            Value::Uuid(u) => {
644                write!(
645                    f,
646                    "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
647                    u[0],
648                    u[1],
649                    u[2],
650                    u[3],
651                    u[4],
652                    u[5],
653                    u[6],
654                    u[7],
655                    u[8],
656                    u[9],
657                    u[10],
658                    u[11],
659                    u[12],
660                    u[13],
661                    u[14],
662                    u[15]
663                )
664            }
665            Value::Json(j) => write!(f, "{j}"),
666            Value::Placeholder(idx) => write!(f, "${idx}"),
667        }
668    }
669}
670
671impl From<i8> for Value {
672    fn from(v: i8) -> Self {
673        Value::TinyInt(v)
674    }
675}
676
677impl From<i16> for Value {
678    fn from(v: i16) -> Self {
679        Value::SmallInt(v)
680    }
681}
682
683impl From<i32> for Value {
684    fn from(v: i32) -> Self {
685        Value::Integer(v)
686    }
687}
688
689impl From<i64> for Value {
690    fn from(v: i64) -> Self {
691        Value::BigInt(v)
692    }
693}
694
695impl From<f64> for Value {
696    fn from(v: f64) -> Self {
697        Value::Real(v)
698    }
699}
700
701impl From<String> for Value {
702    fn from(s: String) -> Self {
703        Value::Text(s)
704    }
705}
706
707impl From<&str> for Value {
708    fn from(s: &str) -> Self {
709        Value::Text(s.to_string())
710    }
711}
712
713impl From<bool> for Value {
714    fn from(b: bool) -> Self {
715        Value::Boolean(b)
716    }
717}
718
719impl From<Timestamp> for Value {
720    fn from(ts: Timestamp) -> Self {
721        Value::Timestamp(ts)
722    }
723}
724
725impl From<Bytes> for Value {
726    fn from(b: Bytes) -> Self {
727        Value::Bytes(b)
728    }
729}
730
731impl From<[u8; 16]> for Value {
732    fn from(u: [u8; 16]) -> Self {
733        Value::Uuid(u)
734    }
735}
736
737impl From<serde_json::Value> for Value {
738    fn from(j: serde_json::Value) -> Self {
739        Value::Json(j)
740    }
741}
742
743/// Serde module for base64 encoding of bytes.
744mod bytes_base64 {
745    use base64::Engine;
746    use bytes::Bytes;
747    use serde::{Deserialize, Deserializer, Serializer};
748
749    pub fn serialize<S>(bytes: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
750    where
751        S: Serializer,
752    {
753        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
754        serializer.serialize_str(&encoded)
755    }
756
757    pub fn deserialize<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
758    where
759        D: Deserializer<'de>,
760    {
761        let s = String::deserialize(deserializer)?;
762        let decoded = base64::engine::general_purpose::STANDARD
763            .decode(&s)
764            .map_err(serde::de::Error::custom)?;
765        Ok(Bytes::from(decoded))
766    }
767}