#![expect(
dead_code,
reason = "infrastructure for unified result iteration; exercised per transport feature combo"
)]
#[allow(
unused_imports,
reason = "imported for use in doc comments that reference the type path"
)]
use crate::error::Result;
use crate::arrow_result::{ArrowChunk, ArrowRow, ArrowRowset};
#[derive(Debug)]
pub enum UnifiedRow<'a> {
Tcp(&'a hyperdb_api_core::client::StreamRow),
Arrow(ArrowRow<'a>),
}
impl UnifiedRow<'_> {
pub fn column_count(&self) -> usize {
match self {
UnifiedRow::Tcp(row) => row.column_count(),
UnifiedRow::Arrow(row) => row.column_count(),
}
}
pub fn get<T: UnifiedFromRow>(&self, col: usize) -> Option<T> {
T::from_unified_row(self, col)
}
pub fn get_i16(&self, col: usize) -> Option<i16> {
self.get::<i16>(col)
}
pub fn get_i32(&self, col: usize) -> Option<i32> {
self.get::<i32>(col)
}
pub fn get_i64(&self, col: usize) -> Option<i64> {
self.get::<i64>(col)
}
pub fn get_f32(&self, col: usize) -> Option<f32> {
self.get::<f32>(col)
}
pub fn get_f64(&self, col: usize) -> Option<f64> {
self.get::<f64>(col)
}
pub fn get_bool(&self, col: usize) -> Option<bool> {
self.get::<bool>(col)
}
pub fn get_string(&self, col: usize) -> Option<String> {
self.get::<String>(col)
}
pub fn is_null(&self, col: usize) -> bool {
match self {
UnifiedRow::Tcp(row) => row.is_null(col),
UnifiedRow::Arrow(row) => row.is_null(col),
}
}
}
pub trait UnifiedFromRow: Sized {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self>;
}
impl UnifiedFromRow for i16 {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<i16>(col),
UnifiedRow::Arrow(r) => r.get::<i16>(col),
}
}
}
impl UnifiedFromRow for i32 {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<i32>(col),
UnifiedRow::Arrow(r) => r.get::<i32>(col),
}
}
}
impl UnifiedFromRow for i64 {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<i64>(col),
UnifiedRow::Arrow(r) => r.get::<i64>(col),
}
}
}
impl UnifiedFromRow for f32 {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<f32>(col),
UnifiedRow::Arrow(r) => r.get::<f32>(col),
}
}
}
impl UnifiedFromRow for f64 {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<f64>(col),
UnifiedRow::Arrow(r) => r.get::<f64>(col),
}
}
}
impl UnifiedFromRow for bool {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<bool>(col),
UnifiedRow::Arrow(r) => r.get::<bool>(col),
}
}
}
impl UnifiedFromRow for String {
fn from_unified_row(row: &UnifiedRow<'_>, col: usize) -> Option<Self> {
match row {
UnifiedRow::Tcp(r) => r.get::<String>(col),
UnifiedRow::Arrow(r) => r.get::<String>(col),
}
}
}
#[derive(Debug)]
pub enum UnifiedChunk<'a> {
Tcp(&'a [hyperdb_api_core::client::StreamRow]),
Arrow(&'a ArrowChunk),
}
impl UnifiedChunk<'_> {
pub fn len(&self) -> usize {
match self {
UnifiedChunk::Tcp(rows) => rows.len(),
UnifiedChunk::Arrow(chunk) => chunk.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn first(&self) -> Option<UnifiedRow<'_>> {
match self {
UnifiedChunk::Tcp(rows) => rows.first().map(UnifiedRow::Tcp),
UnifiedChunk::Arrow(chunk) => chunk.first().map(UnifiedRow::Arrow),
}
}
pub fn iter(&self) -> UnifiedChunkIter<'_> {
UnifiedChunkIter {
chunk: self,
index: 0,
}
}
}
impl<'a, 'b> IntoIterator for &'b UnifiedChunk<'a>
where
'a: 'b,
{
type Item = UnifiedRow<'b>;
type IntoIter = UnifiedChunkIter<'b>;
fn into_iter(self) -> Self::IntoIter {
UnifiedChunkIter {
chunk: self,
index: 0,
}
}
}
#[derive(Debug)]
pub struct UnifiedChunkIter<'a> {
chunk: &'a UnifiedChunk<'a>,
index: usize,
}
impl<'a> Iterator for UnifiedChunkIter<'a> {
type Item = UnifiedRow<'a>;
fn next(&mut self) -> Option<Self::Item> {
let result = match self.chunk {
UnifiedChunk::Tcp(rows) => rows.get(self.index).map(UnifiedRow::Tcp),
UnifiedChunk::Arrow(chunk) => chunk.row(self.index).map(UnifiedRow::Arrow),
};
if result.is_some() {
self.index += 1;
}
result
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.chunk.len().saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for UnifiedChunkIter<'_> {}
pub(crate) enum QueryResult<'conn> {
Tcp(crate::result::Rowset<'conn>),
Arrow(ArrowQueryResult),
}
pub(crate) struct ArrowQueryResult {
rowset: ArrowRowset,
current_chunk: Option<ArrowChunk>,
}
impl ArrowQueryResult {
pub(crate) fn from_bytes(bytes: bytes::Bytes) -> Result<Self> {
let rowset = ArrowRowset::from_bytes(bytes)?;
Ok(ArrowQueryResult {
rowset,
current_chunk: None,
})
}
pub(crate) fn from_ipc_slice(data: &[u8]) -> Result<Self> {
let rowset = ArrowRowset::from_ipc_slice(data)?;
Ok(ArrowQueryResult {
rowset,
current_chunk: None,
})
}
pub(crate) fn next_chunk(&mut self) -> Result<Option<&ArrowChunk>> {
self.current_chunk = self.rowset.next_chunk()?;
Ok(self.current_chunk.as_ref())
}
pub(crate) fn into_rowset(self) -> ArrowRowset {
self.rowset
}
}
impl QueryResult<'_> {}