Skip to main content

hyperdb_api_core/client/
row.rs

1// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Row handling for query results.
5//!
6//! This module provides three row types optimized for different access patterns:
7//!
8//! - [`Row`] - Standard row with pre-computed offsets for random access
9//! - [`StreamRow`] - Zero-allocation row for high-throughput sequential scans
10//! - [`BatchRow`] - Pre-computed offsets for multi-column batch access
11//!
12//! # Example
13//!
14//! ```no_run
15//! # use hyperdb_api_core::client::{Client, Config};
16//! # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
17//! for row in client.query("SELECT id, name FROM users")? {
18//!     let id = row.get_i32(0);      // Option<i32>
19//!     let name = row.get_string(1); // Option<String>
20//!     println!("{:?}: {:?}", id, name);
21//! }
22//! # Ok(())
23//! # }
24//! ```
25//!
26//! # Attribution
27//!
28//! The [`Row`] / [`StreamRow`] / [`BatchRow`] type decomposition and the
29//! pre-computed-offset random-access pattern were adapted from
30//! [`tokio-postgres`](https://github.com/sfackler/rust-postgres) (Copyright
31//! (c) 2016 Steven Fackler, MIT or Apache-2.0). Hyper-specific performance
32//! changes were added on top. See the `NOTICE` file at the repo root for
33//! the full upstream copyright and reproduced license text.
34
35#![allow(
36    clippy::cast_precision_loss,
37    reason = "column accessor: explicit user-requested widening"
38)]
39
40use std::sync::Arc;
41
42use crate::protocol::message::backend::DataRowBody;
43use crate::types::FromHyperBinary;
44
45use super::error::{Error, ErrorKind, Result};
46use super::statement::Column;
47
48// =============================================================================
49// BYTEA Hex Escape Decoding
50// =============================================================================
51
52/// Decodes `PostgreSQL` hex-escaped BYTEA format to raw bytes.
53fn decode_bytea_hex(data: &[u8]) -> Option<Vec<u8>> {
54    if data.len() >= 2 && data[0] == b'\\' && data[1] == b'x' {
55        let hex_data = &data[2..];
56        if hex_data.len() % 2 != 0 {
57            return None;
58        }
59        let mut result = Vec::with_capacity(hex_data.len() / 2);
60        for chunk in hex_data.chunks(2) {
61            let high = hex_digit_to_value(chunk[0])?;
62            let low = hex_digit_to_value(chunk[1])?;
63            result.push((high << 4) | low);
64        }
65        Some(result)
66    } else {
67        Some(data.to_vec())
68    }
69}
70
71#[inline]
72fn hex_digit_to_value(c: u8) -> Option<u8> {
73    match c {
74        b'0'..=b'9' => Some(c - b'0'),
75        b'a'..=b'f' => Some(c - b'a' + 10),
76        b'A'..=b'F' => Some(c - b'A' + 10),
77        _ => None,
78    }
79}
80
81// =============================================================================
82// Row - Standard row with pre-computed offsets
83// =============================================================================
84
85/// A row of data from a query result.
86///
87/// Pre-computes column byte ranges for O(1) random access. Best for accessing
88/// multiple columns or accessing columns in random order.
89///
90/// # Example
91///
92/// ```no_run
93/// # use hyperdb_api_core::client::{Client, Config};
94/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
95/// for row in client.query("SELECT id, name, active FROM users")? {
96///     let id = row.get_i32(0);
97///     let name = row.get_string(1);
98///     let active = row.get_bool(2);
99///     println!("{:?}: {:?} (active={:?})", id, name, active);
100/// }
101/// # Ok(())
102/// # }
103/// ```
104pub struct Row {
105    columns: Arc<Vec<Column>>,
106    data: DataRowBody,
107    ranges: Vec<Option<std::ops::Range<usize>>>,
108}
109
110impl Row {
111    pub(crate) fn new(columns: Arc<Vec<Column>>, data: DataRowBody) -> Result<Self> {
112        let ranges: Vec<_> = data
113            .ranges()
114            .map(|r| r.map_err(|e| Error::protocol(format!("invalid data row: {e}"))))
115            .collect::<Result<Vec<_>>>()?;
116        Ok(Row {
117            columns,
118            data,
119            ranges,
120        })
121    }
122
123    /// Returns the number of columns.
124    #[inline]
125    pub fn column_count(&self) -> usize {
126        self.columns.len()
127    }
128
129    /// Returns true if the row has no columns.
130    #[inline]
131    pub fn is_empty(&self) -> bool {
132        self.columns.is_empty()
133    }
134
135    /// Returns the column metadata.
136    #[inline]
137    pub fn columns(&self) -> &[Column] {
138        &self.columns
139    }
140
141    /// Returns raw bytes for a column, or None if NULL.
142    #[inline]
143    pub fn get_bytes(&self, idx: usize) -> Option<&[u8]> {
144        match self.ranges.get(idx)? {
145            Some(range) => Some(&self.data.buffer()[range.start..range.end]),
146            None => None,
147        }
148    }
149
150    /// Returns true if the column is NULL.
151    #[inline]
152    pub fn is_null(&self, idx: usize) -> bool {
153        self.ranges
154            .get(idx)
155            .map_or(true, std::option::Option::is_none)
156    }
157
158    #[inline]
159    fn column_format(&self, idx: usize) -> super::statement::ColumnFormat {
160        self.columns
161            .get(idx)
162            .map(super::statement::Column::format)
163            .unwrap_or_default()
164    }
165
166    /// Gets an i16 value. Returns None if NULL or invalid format.
167    #[inline]
168    pub fn get_i16(&self, idx: usize) -> Option<i16> {
169        let bytes = self.get_bytes(idx)?;
170        if self.column_format(idx).is_binary() && bytes.len() == 2 {
171            Some(i16::from_le_bytes([bytes[0], bytes[1]]))
172        } else if !self.column_format(idx).is_binary() {
173            std::str::from_utf8(bytes).ok()?.trim().parse().ok()
174        } else {
175            None
176        }
177    }
178
179    /// Gets an i32 value. Returns None if NULL or invalid format.
180    #[inline]
181    pub fn get_i32(&self, idx: usize) -> Option<i32> {
182        let bytes = self.get_bytes(idx)?;
183        if self.column_format(idx).is_binary() && bytes.len() == 4 {
184            Some(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
185        } else if !self.column_format(idx).is_binary() {
186            std::str::from_utf8(bytes).ok()?.trim().parse().ok()
187        } else {
188            None
189        }
190    }
191
192    /// Gets an i64 value. Returns None if NULL or invalid format.
193    #[inline]
194    pub fn get_i64(&self, idx: usize) -> Option<i64> {
195        let bytes = self.get_bytes(idx)?;
196        if self.column_format(idx).is_binary() && bytes.len() == 8 {
197            Some(i64::from_le_bytes([
198                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
199            ]))
200        } else if !self.column_format(idx).is_binary() {
201            std::str::from_utf8(bytes).ok()?.trim().parse().ok()
202        } else {
203            None
204        }
205    }
206
207    /// Gets an f32 value. Returns None if NULL or invalid format.
208    #[inline]
209    pub fn get_f32(&self, idx: usize) -> Option<f32> {
210        let bytes = self.get_bytes(idx)?;
211        if self.column_format(idx).is_binary() && bytes.len() == 4 {
212            Some(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
213        } else if !self.column_format(idx).is_binary() {
214            std::str::from_utf8(bytes).ok()?.trim().parse().ok()
215        } else {
216            None
217        }
218    }
219
220    /// Gets an f64 value. Returns None if NULL or invalid format.
221    #[inline]
222    pub fn get_f64(&self, idx: usize) -> Option<f64> {
223        let bytes = self.get_bytes(idx)?;
224        if self.column_format(idx).is_binary() && bytes.len() == 8 {
225            Some(f64::from_le_bytes([
226                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
227            ]))
228        } else if !self.column_format(idx).is_binary() {
229            std::str::from_utf8(bytes).ok()?.trim().parse().ok()
230        } else {
231            None
232        }
233    }
234
235    /// Gets a bool value. Returns None if NULL or invalid format.
236    #[inline]
237    pub fn get_bool(&self, idx: usize) -> Option<bool> {
238        let bytes = self.get_bytes(idx)?;
239        if self.column_format(idx).is_binary() && bytes.len() == 1 {
240            match bytes[0] {
241                0 => Some(false),
242                1 => Some(true),
243                _ => None,
244            }
245        } else {
246            match bytes {
247                [b't' | b'T'] => Some(true),
248                [b'f' | b'F'] => Some(false),
249                b"true" => Some(true),
250                b"false" => Some(false),
251                _ => None,
252            }
253        }
254    }
255
256    /// Gets a String value. Returns None if NULL or invalid UTF-8.
257    #[inline]
258    pub fn get_string(&self, idx: usize) -> Option<String> {
259        let bytes = self.get_bytes(idx)?;
260        String::from_utf8(bytes.to_vec()).ok()
261    }
262
263    /// Gets raw bytes as owned Vec, decoding BYTEA hex format if needed.
264    #[inline]
265    pub fn get_bytes_owned(&self, idx: usize) -> Option<Vec<u8>> {
266        let bytes = self.get_bytes(idx)?;
267        if self.column_format(idx).is_binary() {
268            Some(bytes.to_vec())
269        } else {
270            decode_bytea_hex(bytes)
271        }
272    }
273
274    /// Gets a typed value using the `FromHyperBinary` trait.
275    ///
276    /// Returns None if the column is NULL or conversion fails.
277    #[inline]
278    pub fn get<T: FromHyperBinary>(&self, idx: usize) -> Option<T> {
279        let bytes = self.get_bytes(idx)?;
280        T::from_hyper_binary(bytes).ok()
281    }
282
283    /// Gets a typed value by column name.
284    ///
285    /// Returns None if the column is NULL, not found, or conversion fails.
286    pub fn get_by_name<T: FromHyperBinary>(&self, name: &str) -> Option<T> {
287        let idx = self.column_index(name).ok()?;
288        self.get(idx)
289    }
290
291    /// Returns the index of a column by name.
292    ///
293    /// # Errors
294    ///
295    /// Returns [`ErrorKind::Query`] with the column name in the message
296    /// if no column matches.
297    pub fn column_index(&self, name: &str) -> Result<usize> {
298        self.columns
299            .iter()
300            .position(|c| c.name() == name)
301            .ok_or_else(|| Error::new(ErrorKind::Query, format!("column not found: {name}")))
302    }
303}
304
305impl std::fmt::Debug for Row {
306    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307        f.debug_struct("Row")
308            .field("columns", &self.columns)
309            .field("column_count", &self.column_count())
310            .finish_non_exhaustive()
311    }
312}
313
314// =============================================================================
315// StreamRow - Zero-allocation row for high-throughput streaming
316// =============================================================================
317
318/// A streaming row optimized for sequential single-pass access.
319///
320/// Unlike [`Row`], this computes column offsets on-demand without pre-allocation.
321/// Best for high-throughput scans where each row is processed once.
322///
323/// # Example
324///
325/// ```no_run
326/// # use hyperdb_api_core::client::{Client, Config};
327/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
328/// // High-throughput streaming access
329/// let mut stream = client.query_streaming("SELECT id, value FROM sensors", 1000)?;
330/// while let Some(chunk) = stream.next_chunk()? {
331///     for row in chunk {
332///         if let Some(id) = row.get_i32(0) {
333///             process(id, row.get_f64(1));
334///         }
335///     }
336/// }
337/// # fn process(_: i32, _: Option<f64>) {}
338/// # Ok(())
339/// # }
340/// ```
341#[derive(Debug)]
342pub struct StreamRow {
343    data: DataRowBody,
344}
345
346impl StreamRow {
347    #[inline]
348    pub(crate) fn new(data: DataRowBody) -> Self {
349        StreamRow { data }
350    }
351
352    /// Returns the number of columns.
353    #[inline]
354    pub fn column_count(&self) -> usize {
355        self.data.column_count() as usize
356    }
357
358    /// Gets raw bytes for a column, or None if NULL.
359    #[inline]
360    pub fn get_bytes(&self, idx: usize) -> Option<&[u8]> {
361        self.data.get_column_bytes(idx)
362    }
363
364    /// Returns true if the column is NULL.
365    #[inline]
366    pub fn is_null(&self, idx: usize) -> bool {
367        self.data.is_column_null(idx)
368    }
369
370    /// Gets an i16 value. Returns None if NULL or wrong size.
371    #[inline]
372    pub fn get_i16(&self, idx: usize) -> Option<i16> {
373        let bytes = self.get_bytes(idx)?;
374        (bytes.len() == 2).then(|| i16::from_le_bytes([bytes[0], bytes[1]]))
375    }
376
377    /// Gets an i32 value. Returns None if NULL or wrong size.
378    #[inline]
379    pub fn get_i32(&self, idx: usize) -> Option<i32> {
380        let bytes = self.get_bytes(idx)?;
381        (bytes.len() == 4).then(|| i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
382    }
383
384    /// Gets an i64 value. Returns None if NULL or wrong size.
385    #[inline]
386    pub fn get_i64(&self, idx: usize) -> Option<i64> {
387        let bytes = self.get_bytes(idx)?;
388        (bytes.len() == 8).then(|| {
389            i64::from_le_bytes([
390                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
391            ])
392        })
393    }
394
395    /// Gets an f32 value. Returns None if NULL or wrong size.
396    #[inline]
397    pub fn get_f32(&self, idx: usize) -> Option<f32> {
398        let bytes = self.get_bytes(idx)?;
399        (bytes.len() == 4).then(|| f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
400    }
401
402    /// Gets an f64 value. Returns None if NULL or wrong size.
403    #[inline]
404    pub fn get_f64(&self, idx: usize) -> Option<f64> {
405        let bytes = self.get_bytes(idx)?;
406        (bytes.len() == 8).then(|| {
407            f64::from_le_bytes([
408                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
409            ])
410        })
411    }
412
413    /// Gets a bool value. Returns None if NULL or invalid.
414    #[inline]
415    pub fn get_bool(&self, idx: usize) -> Option<bool> {
416        let bytes = self.get_bytes(idx)?;
417        (bytes.len() == 1).then(|| match bytes[0] {
418            b't' | b'T' | 1 => Some(true),
419            b'f' | b'F' | 0 => Some(false),
420            _ => None,
421        })?
422    }
423
424    /// Gets a String value. Returns None if NULL or invalid UTF-8.
425    #[inline]
426    pub fn get_string(&self, idx: usize) -> Option<String> {
427        let bytes = self.get_bytes(idx)?;
428        String::from_utf8(bytes.to_vec()).ok()
429    }
430
431    /// Generic typed getter with automatic type coercion.
432    ///
433    /// Supports widening conversions (i16 → i32 → i64, f32 → f64).
434    #[inline]
435    pub fn get<T: FromBinaryValue>(&self, idx: usize) -> Option<T> {
436        T::from_stream_row(self, idx)
437    }
438
439    /// Converts to a `BatchRow` with pre-computed offsets for multi-column access.
440    #[inline]
441    pub fn to_batch(self) -> BatchRow {
442        BatchRow::from_stream(self)
443    }
444
445    /// Returns the underlying `DataRowBody` (for advanced use).
446    #[inline]
447    pub fn into_data(self) -> DataRowBody {
448        self.data
449    }
450}
451
452// =============================================================================
453// BatchRow - Pre-computed offsets for multi-column batch access
454// =============================================================================
455
456/// A row with pre-computed offsets for fast multi-column batch access.
457///
458/// Pre-computes all column offsets once (O(n) total), making subsequent
459/// column access O(1). Best for accessing many columns from each row.
460///
461/// # Example
462///
463/// ```no_run
464/// # use hyperdb_api_core::client::{Client, Config, BatchRow};
465/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
466/// let mut stream = client.query_streaming("SELECT * FROM events", 1000)?;
467/// while let Some(chunk) = stream.next_chunk()? {
468///     for row in chunk {
469///         let batch = BatchRow::from_stream(row); // Pre-compute all offsets
470///         let id = batch.get_i32(0);
471///         let sensor = batch.get_i32(1);
472///         let value = batch.get_f64(2);
473///         let ts = batch.get_i64(3);
474///     }
475/// }
476/// # Ok(())
477/// # }
478/// ```
479#[derive(Debug)]
480pub struct BatchRow {
481    data: DataRowBody,
482    offsets: Vec<Option<(usize, usize)>>,
483}
484
485impl BatchRow {
486    /// Creates a `BatchRow` from a `DataRowBody`.
487    #[inline]
488    pub fn new(data: DataRowBody) -> Self {
489        let offsets = data.compute_all_offsets();
490        BatchRow { data, offsets }
491    }
492
493    /// Creates a `BatchRow` from a `StreamRow`.
494    #[inline]
495    pub fn from_stream(row: StreamRow) -> Self {
496        Self::new(row.data)
497    }
498
499    /// Returns the number of columns.
500    #[inline]
501    pub fn column_count(&self) -> usize {
502        self.offsets.len()
503    }
504
505    /// Gets raw bytes for a column (O(1) access).
506    #[inline]
507    pub fn get_bytes(&self, idx: usize) -> Option<&[u8]> {
508        let (start, end) = self.offsets.get(idx)?.as_ref().copied()?;
509        self.data.buffer().get(start..end)
510    }
511
512    /// Returns true if the column is NULL (O(1) access).
513    #[inline]
514    pub fn is_null(&self, idx: usize) -> bool {
515        self.offsets
516            .get(idx)
517            .map_or(true, std::option::Option::is_none)
518    }
519
520    /// Gets an i16 value.
521    #[inline]
522    pub fn get_i16(&self, idx: usize) -> Option<i16> {
523        let bytes = self.get_bytes(idx)?;
524        (bytes.len() == 2).then(|| i16::from_le_bytes([bytes[0], bytes[1]]))
525    }
526
527    /// Gets an i32 value.
528    #[inline]
529    pub fn get_i32(&self, idx: usize) -> Option<i32> {
530        let bytes = self.get_bytes(idx)?;
531        (bytes.len() == 4).then(|| i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
532    }
533
534    /// Gets an i64 value.
535    #[inline]
536    pub fn get_i64(&self, idx: usize) -> Option<i64> {
537        let bytes = self.get_bytes(idx)?;
538        (bytes.len() == 8).then(|| {
539            i64::from_le_bytes([
540                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
541            ])
542        })
543    }
544
545    /// Gets an f32 value.
546    #[inline]
547    pub fn get_f32(&self, idx: usize) -> Option<f32> {
548        let bytes = self.get_bytes(idx)?;
549        (bytes.len() == 4).then(|| f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
550    }
551
552    /// Gets an f64 value.
553    #[inline]
554    pub fn get_f64(&self, idx: usize) -> Option<f64> {
555        let bytes = self.get_bytes(idx)?;
556        (bytes.len() == 8).then(|| {
557            f64::from_le_bytes([
558                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
559            ])
560        })
561    }
562
563    /// Gets a bool value.
564    #[inline]
565    pub fn get_bool(&self, idx: usize) -> Option<bool> {
566        let bytes = self.get_bytes(idx)?;
567        (bytes.len() == 1).then(|| match bytes[0] {
568            b't' | b'T' | 1 => Some(true),
569            b'f' | b'F' | 0 => Some(false),
570            _ => None,
571        })?
572    }
573
574    /// Gets a String value.
575    #[inline]
576    pub fn get_string(&self, idx: usize) -> Option<String> {
577        let bytes = self.get_bytes(idx)?;
578        String::from_utf8(bytes.to_vec()).ok()
579    }
580
581    /// Generic typed getter with automatic type coercion.
582    ///
583    /// Supports widening conversions (i16 → i32 → i64, f32 → f64).
584    #[inline]
585    pub fn get<T: FromBinaryValue>(&self, idx: usize) -> Option<T> {
586        T::from_batch_row(self, idx)
587    }
588}
589
590// =============================================================================
591// FromBinaryValue - Type coercion trait for row types
592// =============================================================================
593
594/// Trait for extracting typed values from binary row data with automatic coercion.
595pub trait FromBinaryValue: Sized {
596    /// Extracts a value from a `StreamRow` at the given column index.
597    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self>;
598
599    /// Extracts a value from a `BatchRow` at the given column index.
600    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self>;
601}
602
603impl FromBinaryValue for bool {
604    #[inline]
605    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
606        row.get_bool(idx)
607    }
608    #[inline]
609    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
610        row.get_bool(idx)
611    }
612}
613
614impl FromBinaryValue for i16 {
615    #[inline]
616    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
617        row.get_i16(idx)
618    }
619    #[inline]
620    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
621        row.get_i16(idx)
622    }
623}
624
625impl FromBinaryValue for i32 {
626    #[inline]
627    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
628        row.get_i32(idx).or_else(|| row.get_i16(idx).map(i32::from))
629    }
630    #[inline]
631    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
632        row.get_i32(idx).or_else(|| row.get_i16(idx).map(i32::from))
633    }
634}
635
636impl FromBinaryValue for i64 {
637    #[inline]
638    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
639        row.get_i64(idx)
640            .or_else(|| row.get_i32(idx).map(i64::from))
641            .or_else(|| row.get_i16(idx).map(i64::from))
642    }
643    #[inline]
644    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
645        row.get_i64(idx)
646            .or_else(|| row.get_i32(idx).map(i64::from))
647            .or_else(|| row.get_i16(idx).map(i64::from))
648    }
649}
650
651impl FromBinaryValue for f32 {
652    #[inline]
653    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
654        row.get_f32(idx)
655    }
656    #[inline]
657    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
658        row.get_f32(idx)
659    }
660}
661
662impl FromBinaryValue for f64 {
663    #[inline]
664    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
665        row.get_f64(idx)
666            .or_else(|| row.get_f32(idx).map(f64::from))
667            .or_else(|| row.get_i64(idx).map(|v| v as f64))
668            .or_else(|| row.get_i32(idx).map(f64::from))
669    }
670    #[inline]
671    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
672        row.get_f64(idx)
673            .or_else(|| row.get_f32(idx).map(f64::from))
674            .or_else(|| row.get_i64(idx).map(|v| v as f64))
675            .or_else(|| row.get_i32(idx).map(f64::from))
676    }
677}
678
679impl FromBinaryValue for String {
680    #[inline]
681    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
682        row.get_string(idx)
683    }
684    #[inline]
685    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
686        row.get_string(idx)
687    }
688}
689
690impl FromBinaryValue for Vec<u8> {
691    #[inline]
692    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
693        row.get_bytes(idx).map(<[u8]>::to_vec)
694    }
695    #[inline]
696    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
697        row.get_bytes(idx).map(<[u8]>::to_vec)
698    }
699}
700
701impl FromBinaryValue for crate::types::Date {
702    #[inline]
703    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
704        let bytes = row.get_bytes(idx)?;
705        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
706    }
707    #[inline]
708    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
709        let bytes = row.get_bytes(idx)?;
710        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
711    }
712}
713
714impl FromBinaryValue for crate::types::Time {
715    #[inline]
716    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
717        let bytes = row.get_bytes(idx)?;
718        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
719    }
720    #[inline]
721    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
722        let bytes = row.get_bytes(idx)?;
723        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
724    }
725}
726
727impl FromBinaryValue for crate::types::Timestamp {
728    #[inline]
729    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
730        let bytes = row.get_bytes(idx)?;
731        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
732    }
733    #[inline]
734    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
735        let bytes = row.get_bytes(idx)?;
736        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
737    }
738}
739
740impl FromBinaryValue for crate::types::Interval {
741    #[inline]
742    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
743        let bytes = row.get_bytes(idx)?;
744        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
745    }
746    #[inline]
747    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
748        let bytes = row.get_bytes(idx)?;
749        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
750    }
751}
752
753impl FromBinaryValue for crate::types::OffsetTimestamp {
754    #[inline]
755    fn from_stream_row(row: &StreamRow, idx: usize) -> Option<Self> {
756        let bytes = row.get_bytes(idx)?;
757        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
758    }
759    #[inline]
760    fn from_batch_row(row: &BatchRow, idx: usize) -> Option<Self> {
761        let bytes = row.get_bytes(idx)?;
762        crate::types::FromHyperBinary::from_hyper_binary(bytes).ok()
763    }
764}