Skip to main content

duckdb/types/
value_ref.rs

1use super::{Type, Value};
2use crate::types::{Decimal, FromSqlError, FromSqlResult, OrderedMap};
3
4use crate::{Error, Result, Row};
5use arrow::{
6    array::{
7        Array, ArrayRef, DictionaryArray, FixedSizeListArray, LargeListArray, ListArray, MapArray, StringArray,
8        StructArray, UnionArray,
9    },
10    datatypes::{UInt8Type, UInt16Type, UInt32Type},
11};
12
13/// An absolute length of time in seconds, milliseconds, microseconds or nanoseconds.
14/// Copy from arrow::datatypes::TimeUnit
15#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub enum TimeUnit {
17    /// Time in seconds.
18    Second,
19    /// Time in milliseconds.
20    Millisecond,
21    /// Time in microseconds.
22    Microsecond,
23    /// Time in nanoseconds.
24    Nanosecond,
25}
26
27impl TimeUnit {
28    /// Convert a number of `TimeUnit` to microseconds.
29    pub fn to_micros(&self, value: i64) -> i64 {
30        match self {
31            Self::Second => value * 1_000_000,
32            Self::Millisecond => value * 1000,
33            Self::Microsecond => value,
34            Self::Nanosecond => value / 1000,
35        }
36    }
37}
38
39/// A non-owning [static type value](https://duckdb.org/docs/sql/data_types/overview). Typically the
40/// memory backing this value is owned by DuckDB.
41///
42/// Container variants decode child values without DuckDB child logical type
43/// metadata. See the [`crate::types`] compatibility notes for `UHUGEINT`
44/// behavior inside containers.
45///
46/// See [`Value`](Value) for an owning dynamic type value.
47#[derive(Copy, Clone, Debug, PartialEq)]
48#[non_exhaustive]
49pub enum ValueRef<'a> {
50    /// The value is a `NULL` value.
51    Null,
52    /// The value is a boolean.
53    Boolean(bool),
54    /// The value is a signed tiny integer.
55    TinyInt(i8),
56    /// The value is a signed small integer.
57    SmallInt(i16),
58    /// The value is a signed integer.
59    Int(i32),
60    /// The value is a signed big integer.
61    BigInt(i64),
62    /// The value is a signed huge integer.
63    HugeInt(i128),
64    /// The value is an unsigned huge integer.
65    ///
66    /// Top-level `UHUGEINT` result values decode to this variant because row
67    /// decoding uses DuckDB logical type metadata. Borrowed container values do
68    /// not carry that metadata and cannot recover nested `UHUGEINT` values
69    /// above `i128::MAX`.
70    UHugeInt(u128),
71    /// The value is a unsigned tiny integer.
72    UTinyInt(u8),
73    /// The value is a unsigned small integer.
74    USmallInt(u16),
75    /// The value is a unsigned integer.
76    UInt(u32),
77    /// The value is a unsigned big integer.
78    UBigInt(u64),
79    /// The value is a f32.
80    Float(f32),
81    /// The value is a f64.
82    Double(f64),
83    /// The value is a DuckDB decimal.
84    ///
85    /// [`Decimal`] stores DuckDB's decimal width, scale, and scaled integer
86    /// payload.
87    Decimal(Decimal),
88    /// The value is a timestamp.
89    Timestamp(TimeUnit, i64),
90    /// The value is a text string.
91    Text(&'a [u8]),
92    /// The value is a blob of data
93    Blob(&'a [u8]),
94    /// The value is a top-level `GEOMETRY`, represented as WKB bytes.
95    ///
96    /// Nested `GEOMETRY` values currently materialize through their Arrow
97    /// binary carrier as `Blob` values. See [`Value::Geometry`] for binding
98    /// and appender guidance.
99    Geometry(&'a [u8]),
100    /// The value is a date32
101    Date32(i32),
102    /// The value is a time64
103    Time64(TimeUnit, i64),
104    /// The value is an interval (month, day, nano)
105    Interval {
106        /// months
107        months: i32,
108        /// days
109        days: i32,
110        /// nanos
111        nanos: i64,
112    },
113    /// The value is a list.
114    List(ListType<'a>, usize),
115    /// The value is an enum
116    Enum(EnumType<'a>, usize),
117    /// The value is a struct.
118    Struct(&'a StructArray, usize),
119    /// The value is an array.
120    Array(&'a FixedSizeListArray, usize),
121    /// The value is a map.
122    Map(&'a MapArray, usize),
123    /// The value is a union.
124    Union(&'a ArrayRef, usize),
125}
126
127/// Wrapper type for different list sizes
128#[derive(Debug, Copy, Clone, PartialEq)]
129pub enum ListType<'a> {
130    /// The underlying list is a `ListArray`
131    Regular(&'a ListArray),
132    /// The underlying list is a `LargeListArray`
133    Large(&'a LargeListArray),
134}
135
136/// Wrapper type for different enum sizes
137#[derive(Debug, Copy, Clone, PartialEq)]
138pub enum EnumType<'a> {
139    /// The underlying enum type is u8
140    UInt8(&'a DictionaryArray<UInt8Type>),
141    /// The underlying enum type is u16
142    UInt16(&'a DictionaryArray<UInt16Type>),
143    /// The underlying enum type is u32
144    UInt32(&'a DictionaryArray<UInt32Type>),
145}
146
147impl ValueRef<'_> {
148    /// Returns DuckDB fundamental datatype.
149    #[inline]
150    pub fn data_type(&self) -> Type {
151        match *self {
152            ValueRef::Null => Type::Null,
153            ValueRef::Boolean(_) => Type::Boolean,
154            ValueRef::TinyInt(_) => Type::TinyInt,
155            ValueRef::SmallInt(_) => Type::SmallInt,
156            ValueRef::Int(_) => Type::Int,
157            ValueRef::BigInt(_) => Type::BigInt,
158            ValueRef::HugeInt(_) => Type::HugeInt,
159            ValueRef::UHugeInt(_) => Type::UHugeInt,
160            ValueRef::UTinyInt(_) => Type::UTinyInt,
161            ValueRef::USmallInt(_) => Type::USmallInt,
162            ValueRef::UInt(_) => Type::UInt,
163            ValueRef::UBigInt(_) => Type::UBigInt,
164            ValueRef::Float(_) => Type::Float,
165            ValueRef::Double(_) => Type::Double,
166            ValueRef::Decimal(_) => Type::Decimal,
167            ValueRef::Timestamp(..) => Type::Timestamp,
168            ValueRef::Text(_) => Type::Text,
169            ValueRef::Blob(_) => Type::Blob,
170            ValueRef::Geometry(_) => Type::Geometry,
171            ValueRef::Date32(_) => Type::Date32,
172            ValueRef::Time64(..) => Type::Time64,
173            ValueRef::Interval { .. } => Type::Interval,
174            ValueRef::Struct(arr, _) => arr.data_type().into(),
175            ValueRef::Map(arr, _) => arr.data_type().into(),
176            ValueRef::Array(arr, _) => arr.data_type().into(),
177            ValueRef::List(arr, _) => match arr {
178                ListType::Large(arr) => arr.data_type().into(),
179                ListType::Regular(arr) => arr.data_type().into(),
180            },
181            ValueRef::Enum(..) => Type::Enum,
182            ValueRef::Union(arr, _) => arr.data_type().into(),
183        }
184    }
185
186    /// Returns an owned version of this ValueRef
187    pub fn to_owned(&self) -> Value {
188        (*self).into()
189    }
190}
191
192impl<'a> ValueRef<'a> {
193    /// If `self` is `Text` or `Enum`, returns the string value. Otherwise,
194    /// returns `Err(FromSqlError::InvalidType)`.
195    #[inline]
196    pub fn as_str(&self) -> FromSqlResult<&'a str> {
197        match *self {
198            ValueRef::Text(t) => std::str::from_utf8(t).map_err(|e| FromSqlError::Other(Box::new(e))),
199            ValueRef::Enum(ref enum_type, idx) => {
200                let (values, key) = match enum_type {
201                    EnumType::UInt8(arr) => (arr.values(), arr.key(idx)),
202                    EnumType::UInt16(arr) => (arr.values(), arr.key(idx)),
203                    EnumType::UInt32(arr) => (arr.values(), arr.key(idx)),
204                };
205                let dict_key = key.ok_or(FromSqlError::InvalidType)?;
206                let string_array = values
207                    .as_any()
208                    .downcast_ref::<StringArray>()
209                    .ok_or_else(|| FromSqlError::Other("enum dictionary values are not strings".into()))?;
210                if dict_key >= string_array.len() {
211                    return Err(FromSqlError::Other(
212                        format!("enum key {} out of bounds (len {})", dict_key, string_array.len()).into(),
213                    ));
214                }
215                Ok(string_array.value(dict_key))
216            }
217            _ => Err(FromSqlError::InvalidType),
218        }
219    }
220
221    /// If `self` is `Blob`, `Text`, or `Geometry`, returns the byte slice.
222    /// Otherwise, returns `Err(FromSqlError::InvalidType)`.
223    #[inline]
224    pub fn as_blob(&self) -> FromSqlResult<&'a [u8]> {
225        match *self {
226            ValueRef::Blob(b) | ValueRef::Text(b) | ValueRef::Geometry(b) => Ok(b),
227            _ => Err(FromSqlError::InvalidType),
228        }
229    }
230
231    /// If `self` is `Geometry`, returns the WKB byte slice. Otherwise, returns
232    /// `Err(FromSqlError::InvalidType)`.
233    #[inline]
234    pub fn as_geometry_wkb(&self) -> FromSqlResult<&'a [u8]> {
235        match *self {
236            ValueRef::Geometry(wkb) => Ok(wkb),
237            _ => Err(FromSqlError::InvalidType),
238        }
239    }
240}
241
242impl From<ValueRef<'_>> for Value {
243    #[inline]
244    fn from(borrowed: ValueRef<'_>) -> Self {
245        match borrowed {
246            ValueRef::Null => Self::Null,
247            ValueRef::Boolean(i) => Self::Boolean(i),
248            ValueRef::TinyInt(i) => Self::TinyInt(i),
249            ValueRef::SmallInt(i) => Self::SmallInt(i),
250            ValueRef::Int(i) => Self::Int(i),
251            ValueRef::BigInt(i) => Self::BigInt(i),
252            ValueRef::HugeInt(i) => Self::HugeInt(i),
253            ValueRef::UHugeInt(i) => Self::UHugeInt(i),
254            ValueRef::UTinyInt(i) => Self::UTinyInt(i),
255            ValueRef::USmallInt(i) => Self::USmallInt(i),
256            ValueRef::UInt(i) => Self::UInt(i),
257            ValueRef::UBigInt(i) => Self::UBigInt(i),
258            ValueRef::Float(i) => Self::Float(i),
259            ValueRef::Double(i) => Self::Double(i),
260            ValueRef::Decimal(i) => Self::Decimal(i),
261            ValueRef::Timestamp(tu, t) => Self::Timestamp(tu, t),
262            ValueRef::Text(s) => {
263                let s = std::str::from_utf8(s).expect("invalid UTF-8");
264                Self::Text(s.to_string())
265            }
266            ValueRef::Blob(b) => Self::Blob(b.to_vec()),
267            ValueRef::Geometry(wkb) => Self::Geometry(wkb.to_vec()),
268            ValueRef::Date32(d) => Self::Date32(d),
269            ValueRef::Time64(t, d) => Self::Time64(t, d),
270            ValueRef::Interval { months, days, nanos } => Self::Interval { months, days, nanos },
271            ValueRef::List(items, idx) => from_list_value(items, idx),
272            ValueRef::Enum(items, idx) => {
273                let dict_values = match items {
274                    EnumType::UInt8(res) => res.values(),
275                    EnumType::UInt16(res) => res.values(),
276                    EnumType::UInt32(res) => res.values(),
277                }
278                .as_any()
279                .downcast_ref::<StringArray>()
280                .expect("Enum value is not a string");
281                let dict_key = match items {
282                    EnumType::UInt8(res) => res.key(idx),
283                    EnumType::UInt16(res) => res.key(idx),
284                    EnumType::UInt32(res) => res.key(idx),
285                }
286                .unwrap();
287                Self::Enum(dict_values.value(dict_key).to_string())
288            }
289            ValueRef::Struct(items, idx) => from_struct(items, idx),
290            ValueRef::Map(arr, idx) => from_map(arr, idx),
291            ValueRef::Array(items, idx) => from_array(items, idx),
292            ValueRef::Union(column, idx) => from_union(column, idx),
293        }
294    }
295}
296
297fn from_list_value(items: ListType<'_>, idx: usize) -> Value {
298    match items {
299        ListType::Regular(items) => {
300            let offsets = items.offsets();
301            from_list(
302                offsets[idx].try_into().unwrap(),
303                offsets[idx + 1].try_into().unwrap(),
304                items.values(),
305            )
306        }
307        ListType::Large(items) => {
308            let offsets = items.offsets();
309            from_list(
310                offsets[idx].try_into().unwrap(),
311                offsets[idx + 1].try_into().unwrap(),
312                items.values(),
313            )
314        }
315    }
316}
317
318fn from_list(start: usize, end: usize, values: &ArrayRef) -> Value {
319    let capacity = end - start;
320    let mut list_vec = Vec::with_capacity(capacity);
321    list_vec.extend((start..end).map(|row| Row::value_ref_internal(row, values).to_owned()));
322    Value::List(list_vec)
323}
324
325fn from_struct(items: &StructArray, idx: usize) -> Value {
326    let capacity = items.columns().len();
327    let mut value = Vec::with_capacity(capacity);
328    value.extend(
329        items
330            .columns()
331            .iter()
332            .zip(items.fields().iter().map(|f| f.name().to_owned()))
333            .map(|(column, name)| -> (String, Value) { (name, Row::value_ref_internal(idx, column).to_owned()) }),
334    );
335    Value::Struct(OrderedMap::from(value))
336}
337
338fn from_map(arr: &MapArray, idx: usize) -> Value {
339    let keys = arr.keys();
340    let values = arr.values();
341    let offsets = arr.offsets();
342    let range = offsets[idx]..offsets[idx + 1];
343    let capacity = range.len();
344    let mut map_vec = Vec::with_capacity(capacity);
345    map_vec.extend(range.map(|row| {
346        let row = row.try_into().unwrap();
347        let key = Row::value_ref_internal(row, keys).to_owned();
348        let value = Row::value_ref_internal(row, values).to_owned();
349        (key, value)
350    }));
351    Value::Map(OrderedMap::from(map_vec))
352}
353
354fn from_array(items: &FixedSizeListArray, idx: usize) -> Value {
355    let value_length = usize::try_from(items.value_length()).unwrap();
356    let range = (idx * value_length)..((idx + 1) * value_length);
357    let mut array_vec = Vec::with_capacity(value_length);
358    array_vec.extend(range.map(|row| Row::value_ref_internal(row, items.values()).to_owned()));
359    Value::Array(array_vec)
360}
361
362fn from_union(column: &ArrayRef, idx: usize) -> Value {
363    let column = column.as_any().downcast_ref::<UnionArray>().unwrap();
364    let type_id = column.type_id(idx);
365    // Arrow's value offset is the row in the active child array. DuckDB
366    // currently emits sparse unions where this equals `idx`, but using the
367    // offset keeps the decoder correct for dense unions too.
368    let value_offset = column.value_offset(idx);
369    let tag = Row::value_ref_internal(value_offset, column.child(type_id));
370    Value::Union(Box::new(tag.to_owned()))
371}
372
373impl<'a> From<&'a str> for ValueRef<'a> {
374    #[inline]
375    fn from(s: &str) -> ValueRef<'_> {
376        ValueRef::Text(s.as_bytes())
377    }
378}
379
380impl<'a> From<&'a [u8]> for ValueRef<'a> {
381    #[inline]
382    fn from(s: &[u8]) -> ValueRef<'_> {
383        ValueRef::Blob(s)
384    }
385}
386
387impl From<Decimal> for ValueRef<'_> {
388    #[inline]
389    fn from(decimal: Decimal) -> Self {
390        Self::Decimal(decimal)
391    }
392}
393
394pub(crate) fn value_ref_from_value<'a>(
395    value: &'a Value,
396    unsupported_message: impl FnOnce(&'static str) -> String,
397) -> Result<ValueRef<'a>> {
398    if let Some(variant) = unsupported_value_variant(value) {
399        return Err(Error::ToSqlConversionFailure(unsupported_message(variant).into()));
400    }
401
402    Ok(ValueRef::from(value))
403}
404
405pub(crate) fn binding_unsupported_value(value_type: impl std::fmt::Display) -> String {
406    format!("binding {value_type} parameters is not yet supported")
407}
408
409fn unsupported_value_variant(value: &Value) -> Option<&'static str> {
410    match value {
411        Value::Null
412        | Value::Boolean(_)
413        | Value::TinyInt(_)
414        | Value::SmallInt(_)
415        | Value::Int(_)
416        | Value::BigInt(_)
417        | Value::HugeInt(_)
418        | Value::UHugeInt(_)
419        | Value::UTinyInt(_)
420        | Value::USmallInt(_)
421        | Value::UInt(_)
422        | Value::UBigInt(_)
423        | Value::Float(_)
424        | Value::Double(_)
425        | Value::Decimal(_)
426        | Value::Timestamp(_, _)
427        | Value::Text(_)
428        | Value::Blob(_)
429        | Value::Geometry(_)
430        | Value::Date32(_)
431        | Value::Time64(_, _)
432        | Value::Interval { .. } => None,
433        Value::List(_) => Some("List"),
434        Value::Array(_) => Some("Array"),
435        Value::Struct(_) => Some("Struct"),
436        Value::Map(_) => Some("Map"),
437        Value::Union(_) => Some("Union"),
438        Value::Enum(_) => Some("Enum"),
439    }
440}
441
442impl<'a> From<&'a Value> for ValueRef<'a> {
443    /// Converts non-container `Value` variants into a borrowed `ValueRef`.
444    ///
445    /// # Panics
446    ///
447    /// Panics for `Value::Enum`, `Value::List`, `Value::Struct`, `Value::Map`,
448    /// `Value::Array`, and `Value::Union`. Internal bind and append paths use
449    /// `value_ref_from_value` for fallible conversion.
450    #[inline]
451    fn from(value: &'a Value) -> Self {
452        match *value {
453            Value::Null => ValueRef::Null,
454            Value::Boolean(i) => ValueRef::Boolean(i),
455            Value::TinyInt(i) => ValueRef::TinyInt(i),
456            Value::SmallInt(i) => ValueRef::SmallInt(i),
457            Value::Int(i) => ValueRef::Int(i),
458            Value::BigInt(i) => ValueRef::BigInt(i),
459            Value::HugeInt(i) => ValueRef::HugeInt(i),
460            Value::UHugeInt(i) => ValueRef::UHugeInt(i),
461            Value::UTinyInt(i) => ValueRef::UTinyInt(i),
462            Value::USmallInt(i) => ValueRef::USmallInt(i),
463            Value::UInt(i) => ValueRef::UInt(i),
464            Value::UBigInt(i) => ValueRef::UBigInt(i),
465            Value::Float(i) => ValueRef::Float(i),
466            Value::Double(i) => ValueRef::Double(i),
467            Value::Decimal(i) => ValueRef::Decimal(i),
468            Value::Timestamp(tu, t) => ValueRef::Timestamp(tu, t),
469            Value::Text(ref s) => ValueRef::Text(s.as_bytes()),
470            Value::Blob(ref b) => ValueRef::Blob(b),
471            Value::Geometry(ref wkb) => ValueRef::Geometry(wkb),
472            Value::Date32(d) => ValueRef::Date32(d),
473            Value::Time64(t, d) => ValueRef::Time64(t, d),
474            Value::Interval { months, days, nanos } => ValueRef::Interval { months, days, nanos },
475            Value::Enum(..)
476            | Value::List(..)
477            | Value::Struct(..)
478            | Value::Map(..)
479            | Value::Array(..)
480            | Value::Union(..) => {
481                let variant = unsupported_value_variant(value).expect("unsupported Value variant");
482                panic!("ValueRef::from(&Value::{variant}) is not supported; use value_ref_from_value")
483            }
484        }
485    }
486}
487
488impl<'a, T> From<Option<T>> for ValueRef<'a>
489where
490    T: Into<Self>,
491{
492    #[inline]
493    fn from(s: Option<T>) -> Self {
494        match s {
495            Some(x) => x.into(),
496            None => ValueRef::Null,
497        }
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use crate::types::{Type, Value, ValueRef};
504    use crate::{Connection, Result};
505
506    #[test]
507    #[should_panic(expected = "ValueRef::from(&Value::List)")]
508    fn value_ref_from_list_value_panics() {
509        let value = Value::List(vec![Value::Int(1)]);
510        let _ = ValueRef::from(&value);
511    }
512
513    #[test]
514    fn test_geometry_ref_is_wkb_bytes() {
515        use crate::types::FromSql;
516
517        let wkb = &[1_u8, 1, 0, 0, 0];
518        let geometry = ValueRef::Geometry(wkb);
519
520        assert_eq!(geometry.data_type(), Type::Geometry);
521        assert_eq!(geometry.as_geometry_wkb().unwrap(), wkb);
522        assert_eq!(geometry.as_blob().unwrap(), wkb);
523        assert!(geometry.as_str().is_err());
524        assert!(ValueRef::Blob(wkb).as_geometry_wkb().is_err());
525        assert_eq!(ValueRef::Blob(wkb).as_blob().unwrap(), wkb);
526        assert_eq!(Vec::<u8>::column_result(geometry).unwrap(), wkb.to_vec());
527        assert_eq!(geometry.to_owned(), Value::Geometry(wkb.to_vec()));
528    }
529
530    #[test]
531    fn test_text_ref_is_convertible_to_bytes() {
532        use crate::types::FromSql;
533
534        let text = ValueRef::Text(b"goose");
535
536        assert_eq!(text.as_blob().unwrap(), b"goose");
537        assert_eq!(Vec::<u8>::column_result(text).unwrap(), b"goose".to_vec());
538    }
539
540    #[test]
541    fn test_list_types() -> Result<()> {
542        let conn = Connection::open_in_memory()?;
543        conn.execute(
544            "CREATE TABLE test_table (float_list FLOAT[], double_list DOUBLE[], int_list INT[])",
545            [],
546        )?;
547        conn.execute("INSERT INTO test_table VALUES ([1.5, 2.5], [3.5, 4.5], [1, 2])", [])?;
548
549        let mut stmt = conn.prepare("SELECT float_list, double_list, int_list FROM test_table")?;
550        let mut rows = stmt.query([])?;
551        let row = rows.next()?.unwrap();
552
553        let float_list = row.get_ref_unwrap(0);
554        assert!(
555            matches!(float_list.data_type(), Type::List(ref inner_type) if **inner_type == Type::Float),
556            "Expected Type::List(Type::Float), got {:?}",
557            float_list.data_type()
558        );
559
560        let double_list = row.get_ref_unwrap(1);
561        assert!(
562            matches!(double_list.data_type(), Type::List(ref inner_type) if **inner_type == Type::Double),
563            "Expected Type::List(Type::Double), got {:?}",
564            double_list.data_type()
565        );
566
567        let int_list = row.get_ref_unwrap(2);
568        assert!(
569            matches!(int_list.data_type(), Type::List(ref inner_type) if **inner_type == Type::Int),
570            "Expected Type::List(Type::Int), got {:?}",
571            int_list.data_type()
572        );
573
574        Ok(())
575    }
576}