Skip to main content

duckdb/
row.rs

1use std::{convert, sync::Arc};
2
3use super::{Error, Result, Statement};
4use crate::core::LogicalTypeId;
5use crate::types::{self, Decimal, EnumType, FromSql, FromSqlError, ListType, ValueRef};
6
7use arrow::{
8    array::{
9        self, Array, ArrayRef, DictionaryArray, FixedSizeBinaryArray, FixedSizeListArray, ListArray, MapArray,
10        StructArray,
11    },
12    datatypes::*,
13};
14use fallible_iterator::FallibleIterator;
15use fallible_streaming_iterator::FallibleStreamingIterator;
16
17/// An handle for the resulting rows of a query.
18#[must_use = "Rows is lazy and will do nothing unless consumed"]
19pub struct Rows<'stmt> {
20    pub(crate) stmt: Option<&'stmt Statement<'stmt>>,
21    arr: Arc<Option<StructArray>>,
22    row: Option<Row<'stmt>>,
23    current_batch_row: usize,
24}
25
26impl<'stmt> Rows<'stmt> {
27    #[inline]
28    fn reset(&mut self) {
29        self.current_batch_row = 0;
30        self.arr = Arc::new(None);
31    }
32
33    /// Attempt to get the next row from the query. Returns `Ok(Some(Row))` if
34    /// there is another row, `Err(...)` if there was an error
35    /// getting the next row, and `Ok(None)` if all rows have been retrieved.
36    ///
37    /// ## Note
38    ///
39    /// This interface is not compatible with Rust's `Iterator` trait, because
40    /// the lifetime of the returned row is tied to the lifetime of `self`.
41    /// This is a fallible "streaming iterator". For a more natural interface,
42    /// consider using [`query_map`](crate::Statement::query_map) or
43    /// [`query_and_then`](crate::Statement::query_and_then) instead, which
44    /// return types that implement `Iterator`.
45    #[allow(clippy::should_implement_trait)] // cannot implement Iterator
46    #[inline]
47    pub fn next(&mut self) -> Result<Option<&Row<'stmt>>> {
48        self.advance()?;
49        Ok((*self).get())
50    }
51
52    #[inline]
53    fn batch_row_count(&self) -> usize {
54        if self.arr.is_none() {
55            return 0;
56        }
57        self.arr.as_ref().as_ref().unwrap().len()
58    }
59
60    /// Map over this `Rows`, converting it to a [`Map`], which
61    /// implements `FallibleIterator`.
62    ///
63    /// **Note:** This method requires the closure to return `duckdb::Result<B>`.
64    /// If you need to use custom error types, consider using [`and_then`](Self::and_then)
65    /// instead, which allows any error type that implements `From<duckdb::Error>`.
66    ///
67    /// ```rust,no_run
68    /// use fallible_iterator::FallibleIterator;
69    /// # use duckdb::{Result, Statement};
70    /// fn query(stmt: &mut Statement) -> Result<Vec<i64>> {
71    ///     let rows = stmt.query([])?;
72    ///     rows.map(|r| r.get(0)).collect()
73    /// }
74    /// ```
75    // FIXME Hide FallibleStreamingIterator::map
76    #[inline]
77    pub fn map<F, B>(self, f: F) -> Map<'stmt, F>
78    where
79        F: FnMut(&Row<'_>) -> Result<B>,
80    {
81        Map { rows: self, f }
82    }
83
84    /// Map over this `Rows`, converting it to a [`MappedRows`], which
85    /// implements `Iterator`.
86    #[inline]
87    pub fn mapped<F, B>(self, f: F) -> MappedRows<'stmt, F>
88    where
89        F: FnMut(&Row<'_>) -> Result<B>,
90    {
91        MappedRows { rows: self, map: f }
92    }
93
94    /// Map over this `Rows` with a fallible function, converting it to a
95    /// [`AndThenRows`], which implements `Iterator` (instead of
96    /// `FallibleStreamingIterator`).
97    #[inline]
98    pub fn and_then<F, T, E>(self, f: F) -> AndThenRows<'stmt, F>
99    where
100        F: FnMut(&Row<'_>) -> Result<T, E>,
101    {
102        AndThenRows { rows: self, map: f }
103    }
104
105    /// Access the underlying statement
106    ///
107    /// This method provides a way to access the `Statement` that created these `Rows`
108    /// without additional borrowing conflicts. This is particularly useful when you need
109    /// to access statement metadata (like column count or names) while iterating over results.
110    ///
111    /// # Example
112    ///
113    /// ```rust,no_run
114    /// # use duckdb::{Connection, Result};
115    /// fn process_results(conn: &Connection) -> Result<()> {
116    ///     let mut stmt = conn.prepare("SELECT id, name FROM people")?;
117    ///     let mut rows = stmt.query([])?;
118    ///
119    ///     let column_count = rows.as_ref().unwrap().column_count();
120    ///     println!("Processing {} columns", column_count);
121    ///
122    ///     while let Some(row) = rows.next()? {
123    ///         // Process row...
124    ///     }
125    ///     Ok(())
126    /// }
127    /// ```
128    pub fn as_ref(&self) -> Option<&Statement<'stmt>> {
129        self.stmt
130    }
131}
132
133impl<'stmt> Rows<'stmt> {
134    #[inline]
135    pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Self {
136        Rows {
137            stmt: Some(stmt),
138            arr: Arc::new(None),
139            row: None,
140            current_batch_row: 0,
141        }
142    }
143
144    #[inline]
145    pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> {
146        match self.next()? {
147            Some(row) => Ok(row),
148            None => Err(Error::QueryReturnedNoRows),
149        }
150    }
151}
152
153/// `F` is used to transform the _streaming_ iterator into a _fallible_
154/// iterator.
155#[must_use = "iterators are lazy and do nothing unless consumed"]
156pub struct Map<'stmt, F> {
157    rows: Rows<'stmt>,
158    f: F,
159}
160
161impl<F, B> FallibleIterator for Map<'_, F>
162where
163    F: FnMut(&Row<'_>) -> Result<B>,
164{
165    type Error = Error;
166    type Item = B;
167
168    #[inline]
169    fn next(&mut self) -> Result<Option<B>> {
170        match self.rows.next()? {
171            Some(v) => Ok(Some((self.f)(v)?)),
172            None => Ok(None),
173        }
174    }
175}
176
177/// An iterator over the mapped resulting rows of a query.
178///
179/// `F` is used to transform the _streaming_ iterator into a _standard_
180/// iterator.
181#[must_use = "iterators are lazy and do nothing unless consumed"]
182pub struct MappedRows<'stmt, F> {
183    rows: Rows<'stmt>,
184    map: F,
185}
186
187impl<T, F> Iterator for MappedRows<'_, F>
188where
189    F: FnMut(&Row<'_>) -> Result<T>,
190{
191    type Item = Result<T>;
192
193    #[inline]
194    fn next(&mut self) -> Option<Result<T>> {
195        let map = &mut self.map;
196        self.rows.next().transpose().map(|row_result| row_result.and_then(map))
197    }
198}
199
200/// An iterator over the mapped resulting rows of a query, with an Error type
201/// unifying with Error.
202#[must_use = "iterators are lazy and do nothing unless consumed"]
203pub struct AndThenRows<'stmt, F> {
204    rows: Rows<'stmt>,
205    map: F,
206}
207
208impl<T, E, F> Iterator for AndThenRows<'_, F>
209where
210    E: convert::From<Error>,
211    F: FnMut(&Row<'_>) -> Result<T, E>,
212{
213    type Item = Result<T, E>;
214
215    #[inline]
216    fn next(&mut self) -> Option<Self::Item> {
217        let map = &mut self.map;
218        self.rows
219            .next()
220            .transpose()
221            .map(|row_result| row_result.map_err(E::from).and_then(map))
222    }
223}
224
225/// `FallibleStreamingIterator` differs from the standard library's `Iterator`
226/// in two ways:
227/// * each call to `next` (sqlite3_step) can fail.
228/// * returned `Row` is valid until `next` is called again or `Statement` is
229///   reset or finalized.
230///
231/// While these iterators cannot be used with Rust `for` loops, `while let`
232/// loops offer a similar level of ergonomics:
233/// ```rust,no_run
234/// # use duckdb::{Result, Statement};
235/// fn query(stmt: &mut Statement) -> Result<()> {
236///     let mut rows = stmt.query([])?;
237///     while let Some(row) = rows.next()? {
238///         // scan columns value
239///     }
240///     Ok(())
241/// }
242/// ```
243impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
244    type Error = Error;
245    type Item = Row<'stmt>;
246
247    #[inline]
248    fn advance(&mut self) -> Result<()> {
249        match self.stmt {
250            Some(stmt) => {
251                while self.current_batch_row >= self.batch_row_count() {
252                    self.arr = Arc::new(stmt.step()?);
253                    if self.arr.is_none() {
254                        self.reset();
255                        self.row = None;
256                        return Ok(());
257                    }
258                    self.current_batch_row = 0;
259                }
260
261                self.row = Some(Row {
262                    stmt,
263                    arr: self.arr.clone(),
264                    current_row: self.current_batch_row,
265                });
266                self.current_batch_row += 1;
267                Ok(())
268            }
269            None => {
270                self.row = None;
271                Ok(())
272            }
273        }
274    }
275
276    #[inline]
277    fn get(&self) -> Option<&Row<'stmt>> {
278        self.row.as_ref()
279    }
280}
281
282/// A single result row of a query.
283pub struct Row<'stmt> {
284    pub(crate) stmt: &'stmt Statement<'stmt>,
285    arr: Arc<Option<StructArray>>,
286    current_row: usize,
287}
288
289fn decimal_value_ref(width: u8, scale: i8, value: i128) -> ValueRef<'static> {
290    let scale = Decimal::validate_signed_scale(width, scale)
291        .unwrap_or_else(|err| panic!("Invalid Arrow decimal metadata for width {width} scale {scale}: {err}"));
292    ValueRef::Decimal(Decimal::from_chunk(width, scale, value))
293}
294
295#[allow(clippy::needless_lifetimes)]
296impl<'stmt> Row<'stmt> {
297    /// Get the value of a particular column of the result row.
298    ///
299    /// ## Failure
300    ///
301    /// Panics if calling [`row.get(idx)`](Row::get) would return an error,
302    /// including:
303    ///
304    /// * If the underlying DuckDB column type is not a valid type as a source
305    ///   for `T`
306    /// * If the underlying DuckDB integral value is outside the range
307    ///   representable by `T`
308    /// * If `idx` is outside the range of columns in the returned query
309    pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
310        self.get(idx).unwrap()
311    }
312
313    /// Get the value of a particular column of the result row.
314    ///
315    /// ## Failure
316    ///
317    /// Returns an `Error::InvalidColumnType` if the underlying DuckDB column
318    /// type is not a valid type as a source for `T`.
319    ///
320    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
321    /// column range for this row.
322    ///
323    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
324    /// name for this row.
325    ///
326    /// If the result type is i128 (which requires the `i128_blob` feature to be
327    /// enabled), and the underlying DuckDB column is a blob whose size is not
328    /// 16 bytes, `Error::InvalidColumnType` will also be returned.
329    pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
330        let idx = idx.idx(self.stmt)?;
331        let value = self.value_ref(self.current_row, idx);
332        FromSql::column_result(value).map_err(|err| match err {
333            FromSqlError::InvalidType => {
334                Error::InvalidColumnType(idx, self.stmt.column_name_unwrap(idx).into(), value.data_type())
335            }
336            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
337            FromSqlError::OutOfRangeUnsigned(i) => Error::UnsignedIntegralValueOutOfRange(idx, i),
338            FromSqlError::Other(err) => Error::FromSqlConversionFailure(idx, value.data_type(), err),
339            #[cfg(feature = "uuid")]
340            FromSqlError::InvalidUuidSize(_) => {
341                Error::InvalidColumnType(idx, self.stmt.column_name_unwrap(idx).into(), value.data_type())
342            }
343        })
344    }
345
346    /// Get the value of a particular column of the result row as a `ValueRef`,
347    /// allowing data to be read out of a row without copying.
348    ///
349    /// This `ValueRef` is valid only as long as this Row, which is enforced by
350    /// it's lifetime. This means that while this method is completely safe,
351    /// it can be somewhat difficult to use, and most callers will be better
352    /// served by [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap).
353    ///
354    /// ## Failure
355    ///
356    /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
357    /// column range for this row.
358    ///
359    /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column
360    /// name for this row.
361    pub fn get_ref<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> {
362        let idx = idx.idx(self.stmt)?;
363        // Narrowing from `ValueRef<'stmt>` (which `self.value_ref(row, idx)`
364        // returns) to `ValueRef<'a>` is needed because it is only valid until
365        // the next row fetch.
366        let val_ref = self.value_ref(self.current_row, idx);
367        Ok(val_ref)
368    }
369
370    fn value_ref(&self, row: usize, col: usize) -> ValueRef<'_> {
371        let column = self.arr.as_ref().as_ref().unwrap().column(col);
372        let value = Self::value_ref_internal(row, column);
373
374        // Some DuckDB logical types share Arrow physical carriers. Executed
375        // result metadata recovers those DuckDB type tags.
376        match value {
377            ValueRef::Blob(bytes) => match self.stmt.result_column_logical_id(col) {
378                Some(LogicalTypeId::Geometry) => ValueRef::Geometry(bytes),
379                _ => ValueRef::Blob(bytes),
380            },
381            // Arrow gives HUGEINT, UHUGEINT, and DECIMAL(38,0) the same
382            // Decimal128(38,0) physical shape. value_ref_internal keeps that
383            // ambiguous shape in the signed HugeInt carrier. Top-level result
384            // metadata recovers UHUGEINT and DECIMAL; otherwise preserve the
385            // existing HugeInt fallback for parameter-derived or metadata-less
386            // results.
387            ValueRef::HugeInt(value) => match self.stmt.result_column_logical_id(col) {
388                Some(LogicalTypeId::UHugeint) => ValueRef::UHugeInt(value as u128),
389                Some(LogicalTypeId::Decimal) => {
390                    if let DataType::Decimal128(width, 0) = column.data_type() {
391                        decimal_value_ref(*width, 0, value)
392                    } else {
393                        ValueRef::HugeInt(value)
394                    }
395                }
396                _ => ValueRef::HugeInt(value),
397            },
398            value => value,
399        }
400    }
401
402    pub(crate) fn value_ref_internal<'a>(row: usize, column: &'a ArrayRef) -> ValueRef<'a> {
403        if column.is_null(row) {
404            return ValueRef::Null;
405        }
406        // duckdb.cpp SetArrowFormat
407        // https://github.com/duckdb/duckdb/blob/71f1c7a7e4b8737cff5e78d1f090c54f5e78e17b/src/main/query_result.cpp#L148
408        match column.data_type() {
409            DataType::Utf8 => {
410                let array = column.as_any().downcast_ref::<array::StringArray>().unwrap();
411                ValueRef::from(array.value(row))
412            }
413            DataType::LargeUtf8 => {
414                let array = column.as_any().downcast_ref::<array::LargeStringArray>().unwrap();
415                ValueRef::from(array.value(row))
416            }
417            DataType::Binary => {
418                let array = column.as_any().downcast_ref::<array::BinaryArray>().unwrap();
419                ValueRef::Blob(array.value(row))
420            }
421            DataType::LargeBinary => {
422                let array = column.as_any().downcast_ref::<array::LargeBinaryArray>().unwrap();
423                ValueRef::Blob(array.value(row))
424            }
425            DataType::FixedSizeBinary(_) => {
426                let array = column.as_any().downcast_ref::<FixedSizeBinaryArray>().unwrap();
427                ValueRef::Blob(array.value(row))
428            }
429            DataType::Boolean => {
430                let array = column.as_any().downcast_ref::<array::BooleanArray>().unwrap();
431                ValueRef::Boolean(array.value(row))
432            }
433            DataType::Int8 => {
434                let array = column.as_any().downcast_ref::<array::Int8Array>().unwrap();
435                ValueRef::TinyInt(array.value(row))
436            }
437            DataType::Int16 => {
438                let array = column.as_any().downcast_ref::<array::Int16Array>().unwrap();
439                ValueRef::SmallInt(array.value(row))
440            }
441            DataType::Int32 => {
442                let array = column.as_any().downcast_ref::<array::Int32Array>().unwrap();
443                ValueRef::Int(array.value(row))
444            }
445            DataType::Int64 => {
446                let array = column.as_any().downcast_ref::<array::Int64Array>().unwrap();
447                ValueRef::BigInt(array.value(row))
448            }
449            DataType::UInt8 => {
450                let array = column.as_any().downcast_ref::<array::UInt8Array>().unwrap();
451                ValueRef::UTinyInt(array.value(row))
452            }
453            DataType::UInt16 => {
454                let array = column.as_any().downcast_ref::<array::UInt16Array>().unwrap();
455                ValueRef::USmallInt(array.value(row))
456            }
457            DataType::UInt32 => {
458                let array = column.as_any().downcast_ref::<array::UInt32Array>().unwrap();
459                ValueRef::UInt(array.value(row))
460            }
461            DataType::UInt64 => {
462                let array = column.as_any().downcast_ref::<array::UInt64Array>().unwrap();
463                ValueRef::UBigInt(array.value(row))
464            }
465            DataType::Float16 => {
466                let array = column.as_any().downcast_ref::<array::Float32Array>().unwrap();
467                ValueRef::Float(array.value(row))
468            }
469            DataType::Float32 => {
470                let array = column.as_any().downcast_ref::<array::Float32Array>().unwrap();
471                ValueRef::Float(array.value(row))
472            }
473            DataType::Float64 => {
474                let array = column.as_any().downcast_ref::<array::Float64Array>().unwrap();
475                ValueRef::Double(array.value(row))
476            }
477            DataType::Decimal32(width, scale) => {
478                let array = column.as_any().downcast_ref::<array::Decimal32Array>().unwrap();
479                let value = i128::from(array.value(row));
480                decimal_value_ref(*width, *scale, value)
481            }
482            DataType::Decimal64(width, scale) => {
483                let array = column.as_any().downcast_ref::<array::Decimal64Array>().unwrap();
484                let value = i128::from(array.value(row));
485                decimal_value_ref(*width, *scale, value)
486            }
487            DataType::Decimal128(width, scale) => {
488                let array = column.as_any().downcast_ref::<array::Decimal128Array>().unwrap();
489                let value = array.value(row);
490                if *scale == 0 && *width == Decimal::MAX_WIDTH {
491                    ValueRef::HugeInt(value)
492                } else {
493                    decimal_value_ref(*width, *scale, value)
494                }
495            }
496            DataType::Timestamp(unit, _) if *unit == TimeUnit::Second => {
497                let array = column.as_any().downcast_ref::<array::TimestampSecondArray>().unwrap();
498                ValueRef::Timestamp(types::TimeUnit::Second, array.value(row))
499            }
500            DataType::Timestamp(unit, _) if *unit == TimeUnit::Millisecond => {
501                let array = column
502                    .as_any()
503                    .downcast_ref::<array::TimestampMillisecondArray>()
504                    .unwrap();
505                ValueRef::Timestamp(types::TimeUnit::Millisecond, array.value(row))
506            }
507            DataType::Timestamp(unit, _) if *unit == TimeUnit::Microsecond => {
508                let array = column
509                    .as_any()
510                    .downcast_ref::<array::TimestampMicrosecondArray>()
511                    .unwrap();
512                ValueRef::Timestamp(types::TimeUnit::Microsecond, array.value(row))
513            }
514            DataType::Timestamp(unit, _) if *unit == TimeUnit::Nanosecond => {
515                let array = column
516                    .as_any()
517                    .downcast_ref::<array::TimestampNanosecondArray>()
518                    .unwrap();
519                ValueRef::Timestamp(types::TimeUnit::Nanosecond, array.value(row))
520            }
521            DataType::Date32 => {
522                let array = column.as_any().downcast_ref::<array::Date32Array>().unwrap();
523                ValueRef::Date32(array.value(row))
524            }
525            DataType::Time64(TimeUnit::Microsecond) => {
526                let array = column.as_any().downcast_ref::<array::Time64MicrosecondArray>().unwrap();
527                ValueRef::Time64(types::TimeUnit::Microsecond, array.value(row))
528            }
529            DataType::Interval(unit) => match unit {
530                IntervalUnit::MonthDayNano => {
531                    let array = column
532                        .as_any()
533                        .downcast_ref::<array::IntervalMonthDayNanoArray>()
534                        .unwrap();
535                    let value = array.value(row);
536
537                    ValueRef::Interval {
538                        months: value.months,
539                        days: value.days,
540                        nanos: value.nanoseconds,
541                    }
542                }
543                _ => unimplemented!("{:?}", unit),
544            },
545            // TODO: support more data types
546            // NOTE: DataTypes not supported by duckdb
547            // DataType::Date64 => make_string_date!(array::Date64Array, column, row),
548            // DataType::Time32(unit) if *unit == TimeUnit::Second => {
549            //     make_string_time!(array::Time32SecondArray, column, row)
550            // }
551            // DataType::Time32(unit) if *unit == TimeUnit::Millisecond => {
552            //     make_string_time!(array::Time32MillisecondArray, column, row)
553            // }
554            // DataType::Time64(unit) if *unit == TimeUnit::Nanosecond => {
555            //     make_string_time!(array::Time64NanosecondArray, column, row)
556            // }
557            DataType::LargeList(..) => {
558                let arr = column.as_any().downcast_ref::<array::LargeListArray>().unwrap();
559
560                ValueRef::List(ListType::Large(arr), row)
561            }
562            DataType::List(..) => {
563                let arr = column.as_any().downcast_ref::<ListArray>().unwrap();
564
565                ValueRef::List(ListType::Regular(arr), row)
566            }
567            DataType::Dictionary(key_type, ..) => {
568                let column = column.as_any();
569                ValueRef::Enum(
570                    match key_type.as_ref() {
571                        DataType::UInt8 => {
572                            EnumType::UInt8(column.downcast_ref::<DictionaryArray<UInt8Type>>().unwrap())
573                        }
574                        DataType::UInt16 => {
575                            EnumType::UInt16(column.downcast_ref::<DictionaryArray<UInt16Type>>().unwrap())
576                        }
577                        DataType::UInt32 => {
578                            EnumType::UInt32(column.downcast_ref::<DictionaryArray<UInt32Type>>().unwrap())
579                        }
580                        typ => panic!("Unsupported key type: {typ:?}"),
581                    },
582                    row,
583                )
584            }
585            DataType::Struct(_) => {
586                let res = column.as_any().downcast_ref::<StructArray>().unwrap();
587                ValueRef::Struct(res, row)
588            }
589            DataType::Map(..) => {
590                let arr = column.as_any().downcast_ref::<MapArray>().unwrap();
591                ValueRef::Map(arr, row)
592            }
593            DataType::FixedSizeList(..) => {
594                let arr = column.as_any().downcast_ref::<FixedSizeListArray>().unwrap();
595                ValueRef::Array(arr, row)
596            }
597            DataType::Union(..) => ValueRef::Union(column, row),
598            _ => unreachable!("invalid value: {}", column.data_type()),
599        }
600    }
601
602    /// Get the value of a particular column of the result row as a `ValueRef`,
603    /// allowing data to be read out of a row without copying.
604    ///
605    /// This `ValueRef` is valid only as long as this Row, which is enforced by
606    /// it's lifetime. This means that while this method is completely safe,
607    /// it can be difficult to use, and most callers will be better served by
608    /// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap).
609    ///
610    /// ## Failure
611    ///
612    /// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an
613    /// error, including:
614    ///
615    /// * If `idx` is outside the range of columns in the returned query.
616    /// * If `idx` is not a valid column name for this row.
617    pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
618        self.get_ref(idx).unwrap()
619    }
620}
621
622impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
623    fn as_ref(&self) -> &Statement<'stmt> {
624        self.stmt
625    }
626}
627
628mod sealed {
629    /// This trait exists just to ensure that the only impls of `trait Params`
630    /// that are allowed are ones in this crate.
631    pub trait Sealed {}
632    impl Sealed for usize {}
633    impl Sealed for &str {}
634}
635
636/// A trait implemented by types that can index into columns of a row.
637///
638/// It is only implemented for `usize` and `&str`.
639pub trait RowIndex: sealed::Sealed {
640    /// Returns the index of the appropriate column, or `None` if no such
641    /// column exists.
642    fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
643}
644
645impl RowIndex for usize {
646    #[inline]
647    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
648        if *self >= stmt.column_count() {
649            Err(Error::InvalidColumnIndex(*self))
650        } else {
651            Ok(*self)
652        }
653    }
654}
655
656impl RowIndex for &'_ str {
657    #[inline]
658    fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
659        stmt.column_index(self)
660    }
661}
662
663macro_rules! tuple_try_from_row {
664    () => {
665        impl<'a> convert::TryFrom<&'a Row<'a>> for () {
666            type Error = crate::Error;
667
668            fn try_from(_: &'a Row<'a>) -> Result<Self> {
669                Ok(())
670            }
671        }
672    };
673    ($($field:ident),+) => {
674        impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
675            type Error = crate::Error;
676
677            fn try_from(row: &'a Row<'a>) -> Result<Self> {
678                let mut index = 0;
679                let values = (
680                    $({
681                        let value = row.get::<_, $field>(index)?;
682                        index += 1;
683                        value
684                    },)*
685                );
686                // Read the final increment so rustc does not flag it as an unused assignment.
687                let _ = index;
688                Ok(values)
689            }
690        }
691    }
692}
693
694macro_rules! tuples_try_from_row {
695    () => {
696        // not very useful, but maybe some other macro users will find this helpful
697        tuple_try_from_row!();
698    };
699    ($first:ident $(, $remaining:ident)*) => {
700        tuple_try_from_row!($first $(, $remaining)*);
701        tuples_try_from_row!($($remaining),*);
702    };
703}
704
705tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
706
707#[cfg(test)]
708mod tests {
709    use crate::{
710        Connection, Error, Result,
711        types::{Decimal, Type, Value, ValueRef},
712    };
713
714    const U128_MAX_SQL: &str = "340282366920938463463374607431768211455";
715    const I128_MAX_PLUS_ONE_SQL: &str = "170141183460469231731687303715884105728";
716    const WIDE_DECIMAL_SQL: &str = "123456789012345678901234567890.12";
717    const WIDE_DECIMAL_MANTISSA: i128 = 12_345_678_901_234_567_890_123_456_789_012;
718
719    #[test]
720    fn test_try_from_row_for_tuple_0() -> Result<()> {
721        use std::convert::TryFrom;
722
723        let conn = Connection::open_in_memory()?;
724        conn.query_row("SELECT 1", [], |row| <()>::try_from(row))?;
725        Ok(())
726    }
727
728    #[test]
729    fn test_try_from_row_for_tuple_1() -> Result<()> {
730        use crate::ToSql;
731        use std::convert::TryFrom;
732
733        let conn = Connection::open_in_memory()?;
734        conn.execute(
735            "CREATE TABLE test (a INTEGER)",
736            crate::params_from_iter(std::iter::empty::<&dyn ToSql>()),
737        )?;
738        conn.execute("INSERT INTO test VALUES (42)", [])?;
739        let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?;
740        assert_eq!(val, (42,));
741        let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row));
742        assert!(fail.is_err());
743        Ok(())
744    }
745
746    #[test]
747    fn test_try_from_row_for_tuple_2() -> Result<()> {
748        use std::convert::TryFrom;
749
750        let conn = Connection::open_in_memory()?;
751        conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])?;
752        conn.execute("INSERT INTO test VALUES (42, 47)", [])?;
753        let val = conn.query_row("SELECT a, b FROM test", [], |row| <(u32, u32)>::try_from(row))?;
754        assert_eq!(val, (42, 47));
755        let fail = conn.query_row("SELECT a, b FROM test", [], |row| <(u32, u32, u32)>::try_from(row));
756        assert!(fail.is_err());
757        Ok(())
758    }
759
760    #[test]
761    fn test_try_from_row_for_tuple_3() -> Result<()> {
762        use std::convert::TryFrom;
763
764        let conn = Connection::open_in_memory()?;
765        let val = conn.query_row("SELECT 3, 5, 8", [], |row| <(u32, u32, u32)>::try_from(row))?;
766        assert_eq!(val, (3, 5, 8));
767        Ok(())
768    }
769
770    #[test]
771    fn test_try_from_row_for_tuple_16() -> Result<()> {
772        use std::convert::TryFrom;
773
774        let create_table = "CREATE TABLE test (
775            a INTEGER,
776            b INTEGER,
777            c INTEGER,
778            d INTEGER,
779            e INTEGER,
780            f INTEGER,
781            g INTEGER,
782            h INTEGER,
783            i INTEGER,
784            j INTEGER,
785            k INTEGER,
786            l INTEGER,
787            m INTEGER,
788            n INTEGER,
789            o INTEGER,
790            p INTEGER
791        )";
792
793        let insert_values = "INSERT INTO test VALUES (
794            0,
795            1,
796            2,
797            3,
798            4,
799            5,
800            6,
801            7,
802            8,
803            9,
804            10,
805            11,
806            12,
807            13,
808            14,
809            15
810        )";
811
812        type BigTuple = (
813            u32,
814            u32,
815            u32,
816            u32,
817            u32,
818            u32,
819            u32,
820            u32,
821            u32,
822            u32,
823            u32,
824            u32,
825            u32,
826            u32,
827            u32,
828            u32,
829        );
830
831        let conn = Connection::open_in_memory()?;
832        conn.execute(create_table, [])?;
833        conn.execute(insert_values, [])?;
834        let val = conn.query_row("SELECT * FROM test", [], |row| BigTuple::try_from(row))?;
835        // Debug is not implemented for tuples of 16
836        assert_eq!(val.0, 0);
837        assert_eq!(val.1, 1);
838        assert_eq!(val.2, 2);
839        assert_eq!(val.3, 3);
840        assert_eq!(val.4, 4);
841        assert_eq!(val.5, 5);
842        assert_eq!(val.6, 6);
843        assert_eq!(val.7, 7);
844        assert_eq!(val.8, 8);
845        assert_eq!(val.9, 9);
846        assert_eq!(val.10, 10);
847        assert_eq!(val.11, 11);
848        assert_eq!(val.12, 12);
849        assert_eq!(val.13, 13);
850        assert_eq!(val.14, 14);
851        assert_eq!(val.15, 15);
852
853        // We don't test one bigger because it's unimplemented
854        Ok(())
855    }
856
857    #[test]
858    fn uhugeint_arrives_as_uhugeint() -> Result<()> {
859        let db = Connection::open_in_memory()?;
860
861        let mut stmt = db.prepare("SELECT (1)::UHUGEINT")?;
862        let mut rows = stmt.query([])?;
863        let row = rows.next()?.unwrap();
864        assert_eq!(row.get_ref(0)?, ValueRef::UHugeInt(1));
865        assert_eq!(row.get_ref(0)?.data_type(), Type::UHugeInt);
866        assert_eq!(row.get::<_, u128>(0)?, 1);
867
868        let mut stmt = db.prepare(&format!("SELECT ({U128_MAX_SQL})::UHUGEINT"))?;
869        let mut rows = stmt.query([])?;
870        let row = rows.next()?.unwrap();
871        assert_eq!(row.get_ref(0)?, ValueRef::UHugeInt(u128::MAX));
872        assert_eq!(row.get_ref(0)?.data_type(), Type::UHugeInt);
873        assert_eq!(row.get::<_, u128>(0)?, u128::MAX);
874
875        let i128_max_plus_one = (i128::MAX as u128) + 1;
876        let mut stmt = db.prepare(&format!("SELECT ({I128_MAX_PLUS_ONE_SQL})::UHUGEINT"))?;
877        let mut rows = stmt.query([])?;
878        let row = rows.next()?.unwrap();
879        assert_eq!(row.get_ref(0)?, ValueRef::UHugeInt(i128_max_plus_one));
880        assert_eq!(row.get::<_, u128>(0)?, i128_max_plus_one);
881
882        Ok(())
883    }
884
885    #[test]
886    fn uhugeint_promotion_uses_matching_result_column() -> Result<()> {
887        let db = Connection::open_in_memory()?;
888
889        let mut stmt = db.prepare(&format!(
890            "
891            SELECT
892                1::HUGEINT AS a,
893                2::UHUGEINT AS b,
894                3::DECIMAL(38,0) AS c,
895                ({U128_MAX_SQL})::UHUGEINT AS d
896            "
897        ))?;
898        let mut rows = stmt.query([])?;
899        let row = rows.next()?.unwrap();
900
901        assert_eq!(row.get_ref(0)?, ValueRef::HugeInt(1));
902        assert_eq!(row.get_ref(1)?, ValueRef::UHugeInt(2));
903        assert_eq!(row.get_ref(2)?, ValueRef::Decimal(Decimal::new(38, 0, 3)?));
904        assert_eq!(row.get_ref(3)?, ValueRef::UHugeInt(u128::MAX));
905
906        Ok(())
907    }
908
909    #[test]
910    fn bound_uhugeint_result_uses_logical_metadata_when_cast() -> Result<()> {
911        let db = Connection::open_in_memory()?;
912        let max = u128::MAX;
913
914        let value = db.query_row("SELECT ?::UHUGEINT", [&max], |row| {
915            assert_eq!(row.get_ref(0)?, ValueRef::UHugeInt(max));
916            row.get::<_, u128>(0)
917        })?;
918        assert_eq!(value, max);
919
920        Ok(())
921    }
922
923    #[test]
924    fn ambiguous_decimal_expression_uses_executed_metadata() -> Result<()> {
925        let db = Connection::open_in_memory()?;
926
927        let byte = 255_i128;
928        let byte_value = db.query_row("SELECT ?", [&byte], |row| {
929            assert_eq!(row.get_ref(0)?, ValueRef::HugeInt(byte));
930            row.get::<_, u8>(0)
931        })?;
932        assert_eq!(byte_value, 255);
933
934        let value = 5_u128;
935        let decimal = db.query_row("SELECT ? + 0::DECIMAL(38,0)", [&value], |row| {
936            row.get_ref(0).map(|value| value.to_owned())
937        })?;
938        assert_eq!(decimal, Value::Decimal(Decimal::new(38, 0, 5)?));
939
940        Ok(())
941    }
942
943    #[test]
944    fn decimal_columns_use_native_decimal_carrier() -> Result<()> {
945        let db = Connection::open_in_memory()?;
946
947        let value = db.query_row("SELECT 1.23::DECIMAL(38,2)", [], |row| {
948            row.get_ref(0).map(|value| value.to_owned())
949        })?;
950        assert_eq!(value, Value::Decimal(Decimal::new(38, 2, 123)?));
951
952        let value = db.query_row("SELECT 3::DECIMAL(38,0)", [], |row| {
953            row.get_ref(0).map(|value| value.to_owned())
954        })?;
955        match value {
956            Value::Decimal(decimal) => {
957                assert_eq!(decimal.width(), 38);
958                assert_eq!(decimal, Decimal::new(38, 0, 3)?);
959            }
960            other => panic!("expected decimal, got {other:?}"),
961        }
962
963        let decimal = db.query_row(&format!("SELECT {WIDE_DECIMAL_SQL}::DECIMAL(38,2)"), [], |row| {
964            assert_eq!(
965                row.get_ref(0)?,
966                ValueRef::Decimal(Decimal::new(38, 2, WIDE_DECIMAL_MANTISSA)?)
967            );
968            row.get::<_, Decimal>(0)
969        })?;
970        assert_eq!(decimal, Decimal::new(38, 2, WIDE_DECIMAL_MANTISSA)?);
971
972        Ok(())
973    }
974
975    #[test]
976    fn narrow_arrow_decimal_columns_use_native_decimal_carrier() -> Result<()> {
977        use arrow::datatypes::DataType;
978
979        let db = Connection::open_in_memory()?;
980        db.execute_batch("SET arrow_output_version = '1.5'")?;
981
982        {
983            let mut stmt = db.prepare("SELECT 1.23::DECIMAL(9,2) AS d32, 1.23::DECIMAL(18,2) AS d64")?;
984            let batch = stmt.query_arrow([])?.next().expect("no record batch");
985            assert_eq!(batch.column(0).data_type(), &DataType::Decimal32(9, 2));
986            assert_eq!(batch.column(1).data_type(), &DataType::Decimal64(18, 2));
987        }
988
989        let value = db.query_row("SELECT 1.23::DECIMAL(9,2)", [], |row| {
990            row.get_ref(0).map(|value| value.to_owned())
991        })?;
992        assert_eq!(value, Value::Decimal(Decimal::new(9, 2, 123)?));
993
994        let value = db.query_row("SELECT 1.23::DECIMAL(18,2)", [], |row| {
995            row.get_ref(0).map(|value| value.to_owned())
996        })?;
997        assert_eq!(value, Value::Decimal(Decimal::new(18, 2, 123)?));
998
999        Ok(())
1000    }
1001
1002    #[test]
1003    fn uhugeint_upper_half_is_out_of_range_for_i128() -> Result<()> {
1004        let db = Connection::open_in_memory()?;
1005
1006        let err = db
1007            .query_row(&format!("SELECT ({I128_MAX_PLUS_ONE_SQL})::UHUGEINT"), [], |row| {
1008                row.get::<_, i128>(0)
1009            })
1010            .unwrap_err();
1011
1012        match err {
1013            Error::UnsignedIntegralValueOutOfRange(0, value) => {
1014                assert_eq!(value, (i128::MAX as u128) + 1)
1015            }
1016            other => panic!("expected unsigned out-of-range error, got {other:?}"),
1017        }
1018
1019        let err = db
1020            .query_row(&format!("SELECT ({U128_MAX_SQL})::UHUGEINT"), [], |row| {
1021                row.get::<_, i128>(0)
1022            })
1023            .unwrap_err();
1024
1025        match err {
1026            Error::UnsignedIntegralValueOutOfRange(0, value) => assert_eq!(value, u128::MAX),
1027            other => panic!("expected unsigned out-of-range error, got {other:?}"),
1028        }
1029
1030        Ok(())
1031    }
1032
1033    #[test]
1034    fn nested_uhugeint_uses_metadata_less_hugeint_carrier() -> Result<()> {
1035        let db = Connection::open_in_memory()?;
1036
1037        let list = db.query_row(&format!("SELECT [({U128_MAX_SQL})::UHUGEINT]"), [], |row| {
1038            let value = row.get_ref(0)?;
1039            assert!(matches!(value.data_type(), Type::List(ref inner) if **inner == Type::Decimal));
1040            Ok(value.to_owned())
1041        })?;
1042
1043        assert_eq!(list, Value::List(vec![Value::HugeInt(-1)]));
1044
1045        let structure = db.query_row(
1046            &format!("SELECT struct_pack(v := ({U128_MAX_SQL})::UHUGEINT)"),
1047            [],
1048            |row| {
1049                let value = row.get_ref(0)?;
1050                assert!(matches!(value.data_type(), Type::Struct(_)));
1051                Ok(value.to_owned())
1052            },
1053        )?;
1054
1055        match structure {
1056            Value::Struct(fields) => {
1057                assert_eq!(fields.get(&"v".to_string()), Some(&Value::HugeInt(-1)));
1058            }
1059            other => panic!("expected struct, got {other:?}"),
1060        }
1061
1062        Ok(())
1063    }
1064
1065    #[test]
1066    fn nested_scale_zero_decimal_uses_decimal_when_width_is_unambiguous() -> Result<()> {
1067        let db = Connection::open_in_memory()?;
1068
1069        let list = db.query_row("SELECT [3::DECIMAL(5,0)]", [], |row| {
1070            let value = row.get_ref(0)?;
1071            assert!(matches!(value.data_type(), Type::List(ref inner) if **inner == Type::Decimal));
1072            Ok(value.to_owned())
1073        })?;
1074
1075        assert_eq!(list, Value::List(vec![Value::Decimal(Decimal::new(5, 0, 3)?)]));
1076
1077        let structure = db.query_row("SELECT struct_pack(a := 1.5::DECIMAL(5,1))", [], |row| {
1078            let value = row.get_ref(0)?;
1079            assert!(matches!(value.data_type(), Type::Struct(_)));
1080            Ok(value.to_owned())
1081        })?;
1082
1083        match structure {
1084            Value::Struct(fields) => {
1085                assert_eq!(
1086                    fields.get(&"a".to_string()),
1087                    Some(&Value::Decimal(Decimal::new(5, 1, 15)?))
1088                );
1089            }
1090            other => panic!("expected struct, got {other:?}"),
1091        }
1092
1093        Ok(())
1094    }
1095
1096    #[test]
1097    fn nested_decimal_38_0_keeps_metadata_less_hugeint_fallback() -> Result<()> {
1098        let db = Connection::open_in_memory()?;
1099
1100        let list = db.query_row("SELECT [3::DECIMAL(38,0)]", [], |row| {
1101            let value = row.get_ref(0)?;
1102            assert!(matches!(value.data_type(), Type::List(ref inner) if **inner == Type::Decimal));
1103            Ok(value.to_owned())
1104        })?;
1105
1106        assert_eq!(list, Value::List(vec![Value::HugeInt(3)]));
1107
1108        Ok(())
1109    }
1110
1111    #[test]
1112    fn nested_geometry_uses_metadata_less_blob_carrier() -> Result<()> {
1113        let db = Connection::open_in_memory()?;
1114
1115        let top_level = db.query_row("SELECT 'POINT EMPTY'::GEOMETRY", [], |row| {
1116            Ok(row.get_ref(0)?.to_owned())
1117        })?;
1118        assert!(matches!(top_level, Value::Geometry(_)));
1119
1120        let list = db.query_row("SELECT ['POINT EMPTY'::GEOMETRY]", [], |row| {
1121            let value = row.get_ref(0)?;
1122            assert!(matches!(value.data_type(), Type::List(ref inner) if **inner == Type::Blob));
1123            Ok(value.to_owned())
1124        })?;
1125
1126        assert!(matches!(list, Value::List(ref values) if matches!(values.as_slice(), [Value::Blob(_)])));
1127        Ok(())
1128    }
1129
1130    #[test]
1131    fn uhugeint_logical_type_cache_survives_later_batches() -> Result<()> {
1132        let db = Connection::open_in_memory()?;
1133
1134        let values = db
1135            .prepare(
1136                "
1137                SELECT
1138                    CASE
1139                        WHEN i = 2500 THEN NULL::UHUGEINT
1140                        ELSE i::UHUGEINT
1141                    END AS v
1142                FROM range(3000) AS t(i)
1143                ORDER BY i
1144                ",
1145            )?
1146            .query_map([], |row| row.get_ref(0).map(|value| value.to_owned()))?
1147            .collect::<Result<Vec<_>>>()?;
1148
1149        assert_eq!(values.len(), 3000);
1150        assert_eq!(values[0], Value::UHugeInt(0));
1151        assert_eq!(values[2048], Value::UHugeInt(2048));
1152        assert_eq!(values[2500], Value::Null);
1153        assert_eq!(values[2999], Value::UHugeInt(2999));
1154
1155        Ok(())
1156    }
1157
1158    #[test]
1159    fn zero_row_query_returns_none() -> Result<()> {
1160        let db = Connection::open_in_memory()?;
1161        let mut stmt = db.prepare("SELECT 1 AS x WHERE false")?;
1162        let mut rows = stmt.query([])?;
1163
1164        assert!(rows.next()?.is_none());
1165        assert!(rows.next()?.is_none());
1166
1167        Ok(())
1168    }
1169
1170    #[test]
1171    fn wide_multichunk_query_iterates_all_rows() -> Result<()> {
1172        let db = Connection::open_in_memory()?;
1173        let columns = (0..64)
1174            .map(|idx| format!("i + {idx} AS c{idx}"))
1175            .collect::<Vec<_>>()
1176            .join(", ");
1177        let sql = format!("SELECT {columns} FROM range(3000) AS t(i) ORDER BY i");
1178        let mut stmt = db.prepare(&sql)?;
1179        let mut rows = stmt.query([])?;
1180        let mut count = 0_i64;
1181
1182        while let Some(row) = rows.next()? {
1183            assert_eq!(row.get::<_, i64>(0)?, count);
1184            assert_eq!(row.get::<_, i64>(63)?, count + 63);
1185            count += 1;
1186        }
1187
1188        assert_eq!(count, 3000);
1189
1190        Ok(())
1191    }
1192
1193    #[test]
1194    fn uhugeint_nulls_use_cached_logical_type() -> Result<()> {
1195        let db = Connection::open_in_memory()?;
1196
1197        let values = db
1198            .prepare(&format!(
1199                "
1200                SELECT v FROM (
1201                    VALUES
1202                        (0, (0)::UHUGEINT),
1203                        (1, NULL::UHUGEINT),
1204                        (2, ({U128_MAX_SQL})::UHUGEINT)
1205                ) AS t(ord, v)
1206                ORDER BY ord
1207                "
1208            ))?
1209            .query_map([], |row| row.get_ref(0).map(|value| value.to_owned()))?
1210            .collect::<Result<Vec<_>>>()?;
1211
1212        assert_eq!(
1213            values,
1214            vec![Value::UHugeInt(0), Value::Null, Value::UHugeInt(u128::MAX)]
1215        );
1216
1217        Ok(())
1218    }
1219
1220    #[test]
1221    #[cfg(feature = "vtab-arrow")]
1222    fn test_fixed_size_binary_via_arrow() -> Result<()> {
1223        use crate::vtab::arrow::{ArrowVTab, arrow_recordbatch_to_query_params};
1224        use arrow::array::{Array, ArrayRef, BinaryArray, FixedSizeBinaryArray};
1225        use arrow::datatypes::{DataType, Field, Schema};
1226        use arrow::record_batch::RecordBatch;
1227        use std::sync::Arc;
1228
1229        let conn = Connection::open_in_memory()?;
1230        conn.register_table_function::<ArrowVTab>("arrow")?;
1231
1232        // Create FixedSizeBinary(16) array - like UUID
1233        let values = vec![
1234            vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
1235            vec![16u8, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
1236            vec![0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255],
1237        ];
1238
1239        let byte_array = FixedSizeBinaryArray::try_from_iter(values.into_iter()).unwrap();
1240        let arc: ArrayRef = Arc::new(byte_array);
1241        let schema = Schema::new(vec![Field::new("data", DataType::FixedSizeBinary(16), false)]);
1242        let batch = RecordBatch::try_new(Arc::new(schema), vec![arc]).unwrap();
1243
1244        let mut stmt = conn.prepare("SELECT data FROM arrow(?, ?)")?;
1245        let mut arr = stmt.query_arrow(arrow_recordbatch_to_query_params(batch))?;
1246        let rb = arr.next().expect("no record batch");
1247
1248        // DuckDB converts FixedSizeBinary to regular Binary
1249        let column = rb.column(0).as_any().downcast_ref::<BinaryArray>().unwrap();
1250        assert_eq!(column.len(), 3);
1251        assert_eq!(
1252            column.value(0),
1253            &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
1254        );
1255        assert_eq!(
1256            column.value(1),
1257            &[16u8, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1258        );
1259        assert_eq!(column.value(2), &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255]);
1260
1261        Ok(())
1262    }
1263
1264    #[test]
1265    #[cfg(feature = "vtab-arrow")]
1266    fn test_fixed_size_binary_with_nulls_via_arrow() -> Result<()> {
1267        use crate::vtab::arrow::{ArrowVTab, arrow_recordbatch_to_query_params};
1268        use arrow::array::{Array, ArrayRef, BinaryArray, FixedSizeBinaryArray};
1269        use arrow::datatypes::{DataType, Field, Schema};
1270        use arrow::record_batch::RecordBatch;
1271        use std::sync::Arc;
1272
1273        let conn = Connection::open_in_memory()?;
1274        conn.register_table_function::<ArrowVTab>("arrow")?;
1275
1276        // Create FixedSizeBinary(8) array with nulls
1277        let values = vec![
1278            Some(vec![1u8, 2, 3, 4, 5, 6, 7, 8]),
1279            None,
1280            Some(vec![9u8, 10, 11, 12, 13, 14, 15, 16]),
1281        ];
1282
1283        let byte_array = FixedSizeBinaryArray::try_from_sparse_iter_with_size(values.into_iter(), 8).unwrap();
1284        let arc: ArrayRef = Arc::new(byte_array);
1285        let schema = Schema::new(vec![Field::new("data", DataType::FixedSizeBinary(8), true)]);
1286        let batch = RecordBatch::try_new(Arc::new(schema), vec![arc]).unwrap();
1287
1288        let mut stmt = conn.prepare("SELECT data FROM arrow(?, ?)")?;
1289        let mut arr = stmt.query_arrow(arrow_recordbatch_to_query_params(batch))?;
1290        let rb = arr.next().expect("no record batch");
1291
1292        let column = rb.column(0).as_any().downcast_ref::<BinaryArray>().unwrap();
1293        assert_eq!(column.len(), 3);
1294        assert!(column.is_valid(0));
1295        assert!(!column.is_valid(1));
1296        assert!(column.is_valid(2));
1297        assert_eq!(column.value(0), &[1u8, 2, 3, 4, 5, 6, 7, 8]);
1298        assert_eq!(column.value(2), &[9u8, 10, 11, 12, 13, 14, 15, 16]);
1299
1300        Ok(())
1301    }
1302
1303    #[test]
1304    #[cfg(feature = "vtab-arrow")]
1305    fn test_fixed_size_binary_different_sizes_via_arrow() -> Result<()> {
1306        use crate::vtab::arrow::{ArrowVTab, arrow_recordbatch_to_query_params};
1307        use arrow::array::{ArrayRef, FixedSizeBinaryArray};
1308        use arrow::datatypes::{DataType, Field, Schema};
1309        use arrow::record_batch::RecordBatch;
1310        use std::sync::Arc;
1311
1312        let conn = Connection::open_in_memory()?;
1313        conn.register_table_function::<ArrowVTab>("arrow")?;
1314
1315        // Test with FixedSizeBinary(4)
1316        let values = vec![vec![1u8, 2, 3, 4], vec![5u8, 6, 7, 8]];
1317
1318        let byte_array = FixedSizeBinaryArray::try_from_iter(values.into_iter()).unwrap();
1319        let arc: ArrayRef = Arc::new(byte_array);
1320        let schema = Schema::new(vec![Field::new("data", DataType::FixedSizeBinary(4), false)]);
1321        let batch = RecordBatch::try_new(Arc::new(schema), vec![arc]).unwrap();
1322
1323        let mut stmt = conn.prepare("SELECT data FROM arrow(?, ?)")?;
1324        let mut rows = stmt.query(arrow_recordbatch_to_query_params(batch))?;
1325
1326        // Read via Row interface
1327        let row = rows.next()?.unwrap();
1328        let bytes: Vec<u8> = row.get(0)?;
1329        assert_eq!(bytes, vec![1u8, 2, 3, 4]);
1330
1331        let row = rows.next()?.unwrap();
1332        let bytes: Vec<u8> = row.get(0)?;
1333        assert_eq!(bytes, vec![5u8, 6, 7, 8]);
1334
1335        Ok(())
1336    }
1337
1338    #[test]
1339    #[cfg(feature = "vtab-arrow")]
1340    fn test_fixed_size_binary_value_ref_via_arrow() -> Result<()> {
1341        use crate::types::ValueRef;
1342        use crate::vtab::arrow::{ArrowVTab, arrow_recordbatch_to_query_params};
1343        use arrow::array::{ArrayRef, FixedSizeBinaryArray};
1344        use arrow::datatypes::{DataType, Field, Schema};
1345        use arrow::record_batch::RecordBatch;
1346        use std::sync::Arc;
1347
1348        let conn = Connection::open_in_memory()?;
1349        conn.register_table_function::<ArrowVTab>("arrow")?;
1350
1351        let values = vec![Some(vec![1u8, 2, 3, 4]), None];
1352
1353        let byte_array = FixedSizeBinaryArray::try_from_sparse_iter_with_size(values.into_iter(), 4).unwrap();
1354        let arc: ArrayRef = Arc::new(byte_array);
1355        let schema = Schema::new(vec![Field::new("data", DataType::FixedSizeBinary(4), true)]);
1356        let batch = RecordBatch::try_new(Arc::new(schema), vec![arc]).unwrap();
1357
1358        let mut stmt = conn.prepare("SELECT data FROM arrow(?, ?)")?;
1359        let mut rows = stmt.query(arrow_recordbatch_to_query_params(batch))?;
1360
1361        // First row - non-null
1362        let row = rows.next()?.unwrap();
1363        let value_ref = row.get_ref(0)?;
1364        match value_ref {
1365            ValueRef::Blob(bytes) => {
1366                assert_eq!(bytes, &[1u8, 2, 3, 4]);
1367            }
1368            _ => panic!("Expected Blob ValueRef, got {:?}", value_ref),
1369        }
1370
1371        // Second row - null
1372        let row = rows.next()?.unwrap();
1373        let value_ref = row.get_ref(0)?;
1374        match value_ref {
1375            ValueRef::Null => {}
1376            _ => panic!("Expected Null ValueRef, got {:?}", value_ref),
1377        }
1378
1379        Ok(())
1380    }
1381
1382    #[cfg(feature = "uuid")]
1383    #[test]
1384    fn test_fixed_size_binary_uuid() -> Result<()> {
1385        use uuid::Uuid;
1386
1387        let conn = Connection::open_in_memory()?;
1388        conn.execute_batch("CREATE TABLE test (id UUID)")?;
1389
1390        let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
1391        conn.execute("INSERT INTO test VALUES (?)", [uuid_str])?;
1392
1393        // Read back as UUID
1394        let uuid: Uuid = conn.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1395        assert_eq!(uuid.to_string(), uuid_str);
1396        Ok(())
1397    }
1398
1399    #[cfg(feature = "uuid")]
1400    #[test]
1401    fn test_fixed_size_binary_uuid_roundtrip() -> Result<()> {
1402        use uuid::Uuid;
1403
1404        let conn = Connection::open_in_memory()?;
1405        conn.execute_batch("CREATE TABLE test (id UUID)")?;
1406
1407        let original_uuid = Uuid::new_v4();
1408        conn.execute("INSERT INTO test VALUES (?)", [original_uuid])?;
1409
1410        let retrieved_uuid: Uuid = conn.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1411        assert_eq!(original_uuid, retrieved_uuid);
1412        Ok(())
1413    }
1414}