use rudb_arrow::{DataType, Field, RecordBatch, Schema};
use rudb_common::{LogicalType, Result, Value};
use rudb_vector::Chunk;
#[derive(Debug, Clone)]
pub struct QueryResult {
names: Vec<String>,
types: Vec<LogicalType>,
chunks: Vec<Chunk>,
starts: Vec<usize>,
rows: usize,
}
impl QueryResult {
#[must_use]
pub(crate) fn new(names: Vec<String>, types: Vec<LogicalType>, chunks: Vec<Chunk>) -> Self {
let mut starts = Vec::with_capacity(chunks.len() + 1);
let mut rows = 0;
for chunk in &chunks {
starts.push(rows);
rows += chunk.len();
}
starts.push(rows);
Self { names, types, chunks, starts, rows }
}
#[must_use]
pub(crate) fn empty() -> Self {
Self::new(Vec::new(), Vec::new(), Vec::new())
}
#[must_use]
pub fn names(&self) -> &[String] {
&self.names
}
#[must_use]
pub fn types(&self) -> &[LogicalType] {
&self.types
}
#[must_use]
pub fn width(&self) -> usize {
self.names.len()
}
#[must_use]
pub fn len(&self) -> usize {
self.rows
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.rows == 0
}
#[must_use]
pub fn column_name(&self, column: usize) -> &str {
self.names.get(column).map_or("", String::as_str)
}
#[must_use]
pub fn column_type(&self, column: usize) -> LogicalType {
self.types.get(column).cloned().unwrap_or(LogicalType::Null)
}
#[must_use]
pub fn chunks(&self) -> &[Chunk] {
&self.chunks
}
#[must_use]
pub fn chunk_count(&self) -> usize {
self.chunks.len()
}
#[must_use]
pub fn chunk(&self, at: usize) -> Option<&Chunk> {
self.chunks.get(at)
}
pub fn chunk_iter(&self) -> impl ExactSizeIterator<Item = &Chunk> {
self.chunks.iter()
}
#[must_use]
pub fn into_chunks(self) -> Vec<Chunk> {
self.chunks
}
fn locate(&self, row: usize) -> Option<(usize, usize)> {
if row >= self.rows {
return None;
}
let at = self.starts.partition_point(|&start| start <= row) - 1;
Some((at, row - self.starts[at]))
}
#[must_use]
pub fn value_at(&self, row: usize, column: usize) -> Value {
match self.locate(row) {
Some((at, offset)) => self.chunks[at].value_at(offset, column),
None => Value::Null,
}
}
#[must_use]
pub fn row(&self, row: usize) -> Option<Vec<Value>> {
let (at, offset) = self.locate(row)?;
Some(self.chunks[at].row(offset).collect())
}
pub fn rows(&self) -> impl Iterator<Item = Vec<Value>> + '_ {
self.chunks.iter().flat_map(|chunk| (0..chunk.len()).map(|row| chunk.row(row).collect()))
}
pub fn column(&self, column: usize) -> impl Iterator<Item = Value> + '_ {
let width = self.width();
self.chunks
.iter()
.filter(move |_| column < width)
.flat_map(move |chunk| (0..chunk.len()).map(move |row| chunk.value_at(row, column)))
}
pub fn arrow_schema(&self) -> Result<Schema> {
let mut fields = Vec::with_capacity(self.width());
for (name, ty) in self.names.iter().zip(&self.types) {
fields.push(Field::new(name.clone(), DataType::of(ty)?));
}
Ok(Schema::new(fields))
}
pub fn to_arrow(&self) -> Result<Vec<RecordBatch>> {
self.chunks.iter().map(|chunk| RecordBatch::of(chunk, &self.names)).collect()
}
}