#![doc=include_str!("mod.md")]
#![deny(missing_docs)]
use crate::error::Error;
use crate::native::string::MaybeUtf8;
use bytes::Bytes;
use std::ops::Index;
use hashbrown::HashMap;
pub use array::{ArrayData, ArrayReader};
pub use reader::BlockReadError;
use crate::native::decode::Decode;
pub use clickhouse_types::DataTypeNode;
pub(crate) mod array;
pub mod builder;
pub mod decode;
pub mod encode;
pub(crate) mod reader;
pub(crate) mod string;
mod utils;
mod varuint;
pub(crate) mod writer;
#[cfg(test)]
mod tests;
pub struct Block {
column_names: HashMap<MaybeUtf8, usize>,
columns: Box<[Column]>,
num_rows: usize,
}
impl Block {
fn from_columns(columns: Vec<Column>, num_rows: usize) -> Self {
Self {
column_names: columns
.iter()
.enumerate()
.map(|(i, column)| (column.name.clone(), i))
.collect(),
columns: columns.into(),
num_rows,
}
}
pub fn num_rows(&self) -> usize {
self.num_rows
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn column_by_name(&self, name: &str) -> Option<&Column> {
let idx = *self.column_names.get(name)?;
Some(&self.columns[idx])
}
}
impl Index<&str> for Block {
type Output = Column;
fn index(&self, name: &str) -> &Self::Output {
self.column_by_name(name)
.unwrap_or_else(|| panic!("block does not contain a column with the name {name:?}"))
}
}
impl Index<usize> for Block {
type Output = Column;
fn index(&self, index: usize) -> &Self::Output {
&self.columns[index]
}
}
pub struct Column {
name: MaybeUtf8,
data_type: DataTypeNode,
layout: Layout,
}
struct Layout {
kind: LayoutKind,
nulls: Option<Bytes>,
num_values: usize,
}
enum LayoutKind {
Fixed {
type_width: usize,
data: Bytes,
},
Variable {
end_offsets: Box<[usize]>,
data: Bytes,
},
LowCardinality(LayoutLowCardinality),
Array {
end_indices: Box<[usize]>,
elem_layout: Box<Layout>,
},
Tuple {
layouts: Box<[Layout]>,
},
Map {
key_val_layouts: Box<[Layout; 2]>,
end_indices: Box<[usize]>,
},
}
struct LayoutLowCardinality {
keys: Box<[usize]>,
dict: Box<Layout>,
is_nullable: bool,
}
impl Column {
pub fn name(&self) -> &str {
self.name
.as_str()
.unwrap_or_else(|| panic!("column name {:?} is not valid UTF-8", self.name))
}
pub fn name_bytes(&self) -> &[u8] {
self.name.as_bytes()
}
pub fn data_type(&self) -> &DataTypeNode {
&self.data_type
}
pub fn iter<'a, T: Decode<'a>>(&'a self) -> Result<ColumnIter<'a, T>, Error> {
if !T::compatible(self.data_type.remove_compatible_wrappers()) {
return Err(Error::SchemaMismatch(format!(
"incompatible data type {:?} of column {:?}",
self.data_type, self.name
)));
}
Ok(ColumnIter {
column: self,
iter: ArrayData {
elem_type: &self.data_type,
layout: &self.layout,
indices: 0..self.layout.num_values,
}
.into_reader_unchecked(), })
}
}
pub struct ColumnIter<'a, T> {
column: &'a Column,
iter: ArrayReader<'a, T>,
}
impl<'a, T: 'a> Iterator for ColumnIter<'a, T>
where
T: Decode<'a>,
{
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a, T> ColumnIter<'a, T> {
pub fn column(&self) -> &'a Column {
self.column
}
}